Char / Misc driver update for 3.10-rc1
Here's the big char / misc driver update for 3.10-rc1 A number of various driver updates, the majority being new functionality in the MEI driver subsystem (it's now a subsystem, it started out just a single driver), extcon updates, memory updates, hyper-v updates, and a bunch of other small stuff that doesn't fit in any other tree. All of these have been in linux-next for a while Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.19 (GNU/Linux) iEYEABECAAYFAlF+mtYACgkQMUfUDdst+ymFXQCfdLsD4Cxz+jkgW+tljh9i70XD OFkAnRPMMhLS8/kddf02lLMYzYUFdy1U =zaFJ -----END PGP SIGNATURE----- Merge tag 'char-misc-3.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc Pull char/misc driver update from Greg Kroah-Hartman: "Here's the big char / misc driver update for 3.10-rc1 A number of various driver updates, the majority being new functionality in the MEI driver subsystem (it's now a subsystem, it started out just a single driver), extcon updates, memory updates, hyper-v updates, and a bunch of other small stuff that doesn't fit in any other tree. All of these have been in linux-next for a while" * tag 'char-misc-3.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (148 commits) Tools: hv: Fix a checkpatch warning tools: hv: skip iso9660 mounts in hv_vss_daemon tools: hv: use FIFREEZE/FITHAW in hv_vss_daemon tools: hv: use getmntent in hv_vss_daemon Tools: hv: Fix a checkpatch warning tools: hv: fix checks for origin of netlink message in hv_vss_daemon Tools: hv: fix warnings in hv_vss_daemon misc: mark spear13xx-pcie-gadget as broken mei: fix krealloc() misuse in in mei_cl_irq_read_msg() mei: reduce flow control only for completed messages mei: reseting -> resetting mei: fix reading large reposnes mei: revamp mei_irq_read_client_message function mei: revamp mei_amthif_irq_read_message mei: revamp hbm state machine Revert "drivers/scsi: use module_pcmcia_driver() in pcmcia drivers" Revert "scsi: pcmcia: nsp_cs: remove module init/exit function prototypes" scsi: pcmcia: nsp_cs: remove module init/exit function prototypes mei: wd: fix line over 80 characters misc: tsl2550: Use dev_pm_ops ...
This commit is contained in:
commit
4f567cbc95
131 changed files with 4603 additions and 1338 deletions
7
Documentation/ABI/testing/sysfs-bus-mei
Normal file
7
Documentation/ABI/testing/sysfs-bus-mei
Normal file
|
@ -0,0 +1,7 @@
|
|||
What: /sys/bus/mei/devices/.../modalias
|
||||
Date: March 2013
|
||||
KernelVersion: 3.10
|
||||
Contact: Samuel Ortiz <sameo@linux.intel.com>
|
||||
linux-mei@linux.intel.com
|
||||
Description: Stores the same MODALIAS value emitted by uevent
|
||||
Format: mei:<mei device name>
|
18
Documentation/devicetree/bindings/arm/msm/ssbi.txt
Normal file
18
Documentation/devicetree/bindings/arm/msm/ssbi.txt
Normal file
|
@ -0,0 +1,18 @@
|
|||
* Qualcomm SSBI
|
||||
|
||||
Some Qualcomm MSM devices contain a point-to-point serial bus used to
|
||||
communicate with a limited range of devices (mostly power management
|
||||
chips).
|
||||
|
||||
These require the following properties:
|
||||
|
||||
- compatible: "qcom,ssbi"
|
||||
|
||||
- qcom,controller-type
|
||||
indicates the SSBI bus variant the controller should use to talk
|
||||
with the slave device. This should be one of "ssbi", "ssbi2", or
|
||||
"pmic-arbiter". The type chosen is determined by the attached
|
||||
slave.
|
||||
|
||||
The slave device should be the single child node of the ssbi device
|
||||
with a compatible field.
|
138
Documentation/misc-devices/mei/mei-client-bus.txt
Normal file
138
Documentation/misc-devices/mei/mei-client-bus.txt
Normal file
|
@ -0,0 +1,138 @@
|
|||
Intel(R) Management Engine (ME) Client bus API
|
||||
===============================================
|
||||
|
||||
|
||||
Rationale
|
||||
=========
|
||||
MEI misc character device is useful for dedicated applications to send and receive
|
||||
data to the many FW appliance found in Intel's ME from the user space.
|
||||
However for some of the ME functionalities it make sense to leverage existing software
|
||||
stack and expose them through existing kernel subsystems.
|
||||
|
||||
In order to plug seamlessly into the kernel device driver model we add kernel virtual
|
||||
bus abstraction on top of the MEI driver. This allows implementing linux kernel drivers
|
||||
for the various MEI features as a stand alone entities found in their respective subsystem.
|
||||
Existing device drivers can even potentially be re-used by adding an MEI CL bus layer to
|
||||
the existing code.
|
||||
|
||||
|
||||
MEI CL bus API
|
||||
===========
|
||||
A driver implementation for an MEI Client is very similar to existing bus
|
||||
based device drivers. The driver registers itself as an MEI CL bus driver through
|
||||
the mei_cl_driver structure:
|
||||
|
||||
struct mei_cl_driver {
|
||||
struct device_driver driver;
|
||||
const char *name;
|
||||
|
||||
const struct mei_cl_device_id *id_table;
|
||||
|
||||
int (*probe)(struct mei_cl_device *dev, const struct mei_cl_id *id);
|
||||
int (*remove)(struct mei_cl_device *dev);
|
||||
};
|
||||
|
||||
struct mei_cl_id {
|
||||
char name[MEI_NAME_SIZE];
|
||||
kernel_ulong_t driver_info;
|
||||
};
|
||||
|
||||
The mei_cl_id structure allows the driver to bind itself against a device name.
|
||||
|
||||
To actually register a driver on the ME Client bus one must call the mei_cl_add_driver()
|
||||
API. This is typically called at module init time.
|
||||
|
||||
Once registered on the ME Client bus, a driver will typically try to do some I/O on
|
||||
this bus and this should be done through the mei_cl_send() and mei_cl_recv()
|
||||
routines. The latter is synchronous (blocks and sleeps until data shows up).
|
||||
In order for drivers to be notified of pending events waiting for them (e.g.
|
||||
an Rx event) they can register an event handler through the
|
||||
mei_cl_register_event_cb() routine. Currently only the MEI_EVENT_RX event
|
||||
will trigger an event handler call and the driver implementation is supposed
|
||||
to call mei_recv() from the event handler in order to fetch the pending
|
||||
received buffers.
|
||||
|
||||
|
||||
Example
|
||||
=======
|
||||
As a theoretical example let's pretend the ME comes with a "contact" NFC IP.
|
||||
The driver init and exit routines for this device would look like:
|
||||
|
||||
#define CONTACT_DRIVER_NAME "contact"
|
||||
|
||||
static struct mei_cl_device_id contact_mei_cl_tbl[] = {
|
||||
{ CONTACT_DRIVER_NAME, },
|
||||
|
||||
/* required last entry */
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(mei_cl, contact_mei_cl_tbl);
|
||||
|
||||
static struct mei_cl_driver contact_driver = {
|
||||
.id_table = contact_mei_tbl,
|
||||
.name = CONTACT_DRIVER_NAME,
|
||||
|
||||
.probe = contact_probe,
|
||||
.remove = contact_remove,
|
||||
};
|
||||
|
||||
static int contact_init(void)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = mei_cl_driver_register(&contact_driver);
|
||||
if (r) {
|
||||
pr_err(CONTACT_DRIVER_NAME ": driver registration failed\n");
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit contact_exit(void)
|
||||
{
|
||||
mei_cl_driver_unregister(&contact_driver);
|
||||
}
|
||||
|
||||
module_init(contact_init);
|
||||
module_exit(contact_exit);
|
||||
|
||||
And the driver's simplified probe routine would look like that:
|
||||
|
||||
int contact_probe(struct mei_cl_device *dev, struct mei_cl_device_id *id)
|
||||
{
|
||||
struct contact_driver *contact;
|
||||
|
||||
[...]
|
||||
mei_cl_enable_device(dev);
|
||||
|
||||
mei_cl_register_event_cb(dev, contact_event_cb, contact);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
In the probe routine the driver first enable the MEI device and then registers
|
||||
an ME bus event handler which is as close as it can get to registering a
|
||||
threaded IRQ handler.
|
||||
The handler implementation will typically call some I/O routine depending on
|
||||
the pending events:
|
||||
|
||||
#define MAX_NFC_PAYLOAD 128
|
||||
|
||||
static void contact_event_cb(struct mei_cl_device *dev, u32 events,
|
||||
void *context)
|
||||
{
|
||||
struct contact_driver *contact = context;
|
||||
|
||||
if (events & BIT(MEI_EVENT_RX)) {
|
||||
u8 payload[MAX_NFC_PAYLOAD];
|
||||
int payload_size;
|
||||
|
||||
payload_size = mei_recv(dev, payload, MAX_NFC_PAYLOAD);
|
||||
if (payload_size <= 0)
|
||||
return;
|
||||
|
||||
/* Hook to the NFC subsystem */
|
||||
nfc_hci_recv_frame(contact->hdev, payload, payload_size);
|
||||
}
|
||||
}
|
|
@ -1031,6 +1031,7 @@ F: drivers/mmc/host/msm_sdcc.h
|
|||
F: drivers/tty/serial/msm_serial.h
|
||||
F: drivers/tty/serial/msm_serial.c
|
||||
F: drivers/*/pm8???-*
|
||||
F: drivers/ssbi/
|
||||
F: include/linux/mfd/pm8xxx/
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/davidb/linux-msm.git
|
||||
S: Maintained
|
||||
|
|
|
@ -38,4 +38,10 @@
|
|||
<0x19c00000 0x1000>;
|
||||
interrupts = <0 195 0x0>;
|
||||
};
|
||||
|
||||
qcom,ssbi@500000 {
|
||||
compatible = "qcom,ssbi";
|
||||
reg = <0x500000 0x1000>;
|
||||
qcom,controller-type = "pmic-arbiter";
|
||||
};
|
||||
};
|
||||
|
|
|
@ -38,4 +38,10 @@
|
|||
<0x16400000 0x1000>;
|
||||
interrupts = <0 154 0x0>;
|
||||
};
|
||||
|
||||
qcom,ssbi@500000 {
|
||||
compatible = "qcom,ssbi";
|
||||
reg = <0x500000 0x1000>;
|
||||
qcom,controller-type = "pmic-arbiter";
|
||||
};
|
||||
};
|
||||
|
|
|
@ -52,6 +52,8 @@ source "drivers/i2c/Kconfig"
|
|||
|
||||
source "drivers/spi/Kconfig"
|
||||
|
||||
source "drivers/ssbi/Kconfig"
|
||||
|
||||
source "drivers/hsi/Kconfig"
|
||||
|
||||
source "drivers/pps/Kconfig"
|
||||
|
|
|
@ -114,6 +114,7 @@ obj-y += firmware/
|
|||
obj-$(CONFIG_CRYPTO) += crypto/
|
||||
obj-$(CONFIG_SUPERH) += sh/
|
||||
obj-$(CONFIG_ARCH_SHMOBILE) += sh/
|
||||
obj-$(CONFIG_SSBI) += ssbi/
|
||||
ifndef CONFIG_ARCH_USES_GETTIMEOFFSET
|
||||
obj-y += clocksource/
|
||||
endif
|
||||
|
|
|
@ -387,21 +387,9 @@ static struct pcmcia_driver pcmcia_driver = {
|
|||
.probe = pcmcia_init_one,
|
||||
.remove = pcmcia_remove_one,
|
||||
};
|
||||
|
||||
static int __init pcmcia_init(void)
|
||||
{
|
||||
return pcmcia_register_driver(&pcmcia_driver);
|
||||
}
|
||||
|
||||
static void __exit pcmcia_exit(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&pcmcia_driver);
|
||||
}
|
||||
module_pcmcia_driver(pcmcia_driver);
|
||||
|
||||
MODULE_AUTHOR("Alan Cox");
|
||||
MODULE_DESCRIPTION("low-level driver for PCMCIA ATA");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(DRV_VERSION);
|
||||
|
||||
module_init(pcmcia_init);
|
||||
module_exit(pcmcia_exit);
|
||||
|
|
|
@ -934,17 +934,4 @@ static struct pcmcia_driver bluecard_driver = {
|
|||
.remove = bluecard_detach,
|
||||
.id_table = bluecard_ids,
|
||||
};
|
||||
|
||||
static int __init init_bluecard_cs(void)
|
||||
{
|
||||
return pcmcia_register_driver(&bluecard_driver);
|
||||
}
|
||||
|
||||
|
||||
static void __exit exit_bluecard_cs(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&bluecard_driver);
|
||||
}
|
||||
|
||||
module_init(init_bluecard_cs);
|
||||
module_exit(exit_bluecard_cs);
|
||||
module_pcmcia_driver(bluecard_driver);
|
||||
|
|
|
@ -760,17 +760,4 @@ static struct pcmcia_driver bt3c_driver = {
|
|||
.remove = bt3c_detach,
|
||||
.id_table = bt3c_ids,
|
||||
};
|
||||
|
||||
static int __init init_bt3c_cs(void)
|
||||
{
|
||||
return pcmcia_register_driver(&bt3c_driver);
|
||||
}
|
||||
|
||||
|
||||
static void __exit exit_bt3c_cs(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&bt3c_driver);
|
||||
}
|
||||
|
||||
module_init(init_bt3c_cs);
|
||||
module_exit(exit_bt3c_cs);
|
||||
module_pcmcia_driver(bt3c_driver);
|
||||
|
|
|
@ -688,17 +688,4 @@ static struct pcmcia_driver btuart_driver = {
|
|||
.remove = btuart_detach,
|
||||
.id_table = btuart_ids,
|
||||
};
|
||||
|
||||
static int __init init_btuart_cs(void)
|
||||
{
|
||||
return pcmcia_register_driver(&btuart_driver);
|
||||
}
|
||||
|
||||
|
||||
static void __exit exit_btuart_cs(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&btuart_driver);
|
||||
}
|
||||
|
||||
module_init(init_btuart_cs);
|
||||
module_exit(exit_btuart_cs);
|
||||
module_pcmcia_driver(btuart_driver);
|
||||
|
|
|
@ -628,17 +628,4 @@ static struct pcmcia_driver dtl1_driver = {
|
|||
.remove = dtl1_detach,
|
||||
.id_table = dtl1_ids,
|
||||
};
|
||||
|
||||
static int __init init_dtl1_cs(void)
|
||||
{
|
||||
return pcmcia_register_driver(&dtl1_driver);
|
||||
}
|
||||
|
||||
|
||||
static void __exit exit_dtl1_cs(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&dtl1_driver);
|
||||
}
|
||||
|
||||
module_init(init_dtl1_cs);
|
||||
module_exit(exit_dtl1_cs);
|
||||
module_pcmcia_driver(dtl1_driver);
|
||||
|
|
|
@ -804,8 +804,8 @@ static long ac_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|||
|
||||
printk(KERN_INFO "Prom version board %d ....... V%d.%d %s",
|
||||
i+1,
|
||||
(int)(readb(apbs[IndexCard].RamIO + VERS) >> 4),
|
||||
(int)(readb(apbs[IndexCard].RamIO + VERS) & 0xF),
|
||||
(int)(readb(apbs[i].RamIO + VERS) >> 4),
|
||||
(int)(readb(apbs[i].RamIO + VERS) & 0xF),
|
||||
boardname);
|
||||
|
||||
|
||||
|
|
|
@ -228,18 +228,7 @@ static struct platform_driver mxc_rnga_driver = {
|
|||
.remove = __exit_p(mxc_rnga_remove),
|
||||
};
|
||||
|
||||
static int __init mod_init(void)
|
||||
{
|
||||
return platform_driver_probe(&mxc_rnga_driver, mxc_rnga_probe);
|
||||
}
|
||||
|
||||
static void __exit mod_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&mxc_rnga_driver);
|
||||
}
|
||||
|
||||
module_init(mod_init);
|
||||
module_exit(mod_exit);
|
||||
module_platform_driver_probe(mxc_rnga_driver, mxc_rnga_probe);
|
||||
|
||||
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
||||
MODULE_DESCRIPTION("H/W RNGA driver for i.MX");
|
||||
|
|
|
@ -166,18 +166,7 @@ static struct platform_driver tx4939_rng_driver = {
|
|||
.remove = tx4939_rng_remove,
|
||||
};
|
||||
|
||||
static int __init tx4939rng_init(void)
|
||||
{
|
||||
return platform_driver_probe(&tx4939_rng_driver, tx4939_rng_probe);
|
||||
}
|
||||
|
||||
static void __exit tx4939rng_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&tx4939_rng_driver);
|
||||
}
|
||||
|
||||
module_init(tx4939rng_init);
|
||||
module_exit(tx4939rng_exit);
|
||||
module_platform_driver_probe(tx4939_rng_driver, tx4939_rng_probe);
|
||||
|
||||
MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver for TX4939");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -371,7 +371,7 @@ static int srom_setup_minor(struct srom_dev *srom, int index)
|
|||
|
||||
dev = device_create(srom_class, &platform_bus,
|
||||
MKDEV(srom_major, index), srom, "%d", index);
|
||||
return IS_ERR(dev) ? PTR_ERR(dev) : 0;
|
||||
return PTR_RET(dev);
|
||||
}
|
||||
|
||||
/** srom_init() - Initialize the driver's module. */
|
||||
|
|
|
@ -33,12 +33,17 @@
|
|||
#include <linux/mfd/arizona/pdata.h>
|
||||
#include <linux/mfd/arizona/registers.h>
|
||||
|
||||
#define ARIZONA_NUM_BUTTONS 6
|
||||
#define ARIZONA_MAX_MICD_RANGE 8
|
||||
|
||||
#define ARIZONA_ACCDET_MODE_MIC 0
|
||||
#define ARIZONA_ACCDET_MODE_HPL 1
|
||||
#define ARIZONA_ACCDET_MODE_HPR 2
|
||||
|
||||
#define ARIZONA_HPDET_MAX 10000
|
||||
|
||||
#define HPDET_DEBOUNCE 500
|
||||
#define DEFAULT_MICD_TIMEOUT 2000
|
||||
|
||||
struct arizona_extcon_info {
|
||||
struct device *dev;
|
||||
struct arizona *arizona;
|
||||
|
@ -46,17 +51,27 @@ struct arizona_extcon_info {
|
|||
struct regulator *micvdd;
|
||||
struct input_dev *input;
|
||||
|
||||
u16 last_jackdet;
|
||||
|
||||
int micd_mode;
|
||||
const struct arizona_micd_config *micd_modes;
|
||||
int micd_num_modes;
|
||||
|
||||
const struct arizona_micd_range *micd_ranges;
|
||||
int num_micd_ranges;
|
||||
|
||||
int micd_timeout;
|
||||
|
||||
bool micd_reva;
|
||||
bool micd_clamp;
|
||||
|
||||
struct delayed_work hpdet_work;
|
||||
struct delayed_work micd_detect_work;
|
||||
struct delayed_work micd_timeout_work;
|
||||
|
||||
bool hpdet_active;
|
||||
bool hpdet_done;
|
||||
bool hpdet_retried;
|
||||
|
||||
int num_hpdet_res;
|
||||
unsigned int hpdet_res[3];
|
||||
|
@ -71,20 +86,25 @@ struct arizona_extcon_info {
|
|||
};
|
||||
|
||||
static const struct arizona_micd_config micd_default_modes[] = {
|
||||
{ 0, 2 << ARIZONA_MICD_BIAS_SRC_SHIFT, 1 },
|
||||
{ ARIZONA_ACCDET_SRC, 1 << ARIZONA_MICD_BIAS_SRC_SHIFT, 0 },
|
||||
{ 0, 2 << ARIZONA_MICD_BIAS_SRC_SHIFT, 1 },
|
||||
};
|
||||
|
||||
static struct {
|
||||
u16 status;
|
||||
int report;
|
||||
} arizona_lvl_to_key[ARIZONA_NUM_BUTTONS] = {
|
||||
{ 0x1, BTN_0 },
|
||||
{ 0x2, BTN_1 },
|
||||
{ 0x4, BTN_2 },
|
||||
{ 0x8, BTN_3 },
|
||||
{ 0x10, BTN_4 },
|
||||
{ 0x20, BTN_5 },
|
||||
static const struct arizona_micd_range micd_default_ranges[] = {
|
||||
{ .max = 11, .key = BTN_0 },
|
||||
{ .max = 28, .key = BTN_1 },
|
||||
{ .max = 54, .key = BTN_2 },
|
||||
{ .max = 100, .key = BTN_3 },
|
||||
{ .max = 186, .key = BTN_4 },
|
||||
{ .max = 430, .key = BTN_5 },
|
||||
};
|
||||
|
||||
static const int arizona_micd_levels[] = {
|
||||
3, 6, 8, 11, 13, 16, 18, 21, 23, 26, 28, 31, 34, 36, 39, 41, 44, 46,
|
||||
49, 52, 54, 57, 60, 62, 65, 67, 70, 73, 75, 78, 81, 83, 89, 94, 100,
|
||||
105, 111, 116, 122, 127, 139, 150, 161, 173, 186, 196, 209, 220, 245,
|
||||
270, 295, 321, 348, 375, 402, 430, 489, 550, 614, 681, 752, 903, 1071,
|
||||
1257,
|
||||
};
|
||||
|
||||
#define ARIZONA_CABLE_MECHANICAL 0
|
||||
|
@ -100,10 +120,63 @@ static const char *arizona_cable[] = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info);
|
||||
|
||||
static void arizona_extcon_do_magic(struct arizona_extcon_info *info,
|
||||
unsigned int magic)
|
||||
{
|
||||
struct arizona *arizona = info->arizona;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&arizona->dapm->card->dapm_mutex);
|
||||
|
||||
arizona->hpdet_magic = magic;
|
||||
|
||||
/* Keep the HP output stages disabled while doing the magic */
|
||||
if (magic) {
|
||||
ret = regmap_update_bits(arizona->regmap,
|
||||
ARIZONA_OUTPUT_ENABLES_1,
|
||||
ARIZONA_OUT1L_ENA |
|
||||
ARIZONA_OUT1R_ENA, 0);
|
||||
if (ret != 0)
|
||||
dev_warn(arizona->dev,
|
||||
"Failed to disable headphone outputs: %d\n",
|
||||
ret);
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(arizona->regmap, 0x225, 0x4000,
|
||||
magic);
|
||||
if (ret != 0)
|
||||
dev_warn(arizona->dev, "Failed to do magic: %d\n",
|
||||
ret);
|
||||
|
||||
ret = regmap_update_bits(arizona->regmap, 0x226, 0x4000,
|
||||
magic);
|
||||
if (ret != 0)
|
||||
dev_warn(arizona->dev, "Failed to do magic: %d\n",
|
||||
ret);
|
||||
|
||||
/* Restore the desired state while not doing the magic */
|
||||
if (!magic) {
|
||||
ret = regmap_update_bits(arizona->regmap,
|
||||
ARIZONA_OUTPUT_ENABLES_1,
|
||||
ARIZONA_OUT1L_ENA |
|
||||
ARIZONA_OUT1R_ENA, arizona->hp_ena);
|
||||
if (ret != 0)
|
||||
dev_warn(arizona->dev,
|
||||
"Failed to restore headphone outputs: %d\n",
|
||||
ret);
|
||||
}
|
||||
|
||||
mutex_unlock(&arizona->dapm->card->dapm_mutex);
|
||||
}
|
||||
|
||||
static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode)
|
||||
{
|
||||
struct arizona *arizona = info->arizona;
|
||||
|
||||
mode %= info->micd_num_modes;
|
||||
|
||||
if (arizona->pdata.micd_pol_gpio > 0)
|
||||
gpio_set_value_cansleep(arizona->pdata.micd_pol_gpio,
|
||||
info->micd_modes[mode].gpio);
|
||||
|
@ -330,7 +403,7 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
|
|||
/* If we go out of range report top of range */
|
||||
if (val < 100 || val > 0x3fb) {
|
||||
dev_dbg(arizona->dev, "Measurement out of range\n");
|
||||
return 10000;
|
||||
return ARIZONA_HPDET_MAX;
|
||||
}
|
||||
|
||||
dev_dbg(arizona->dev, "HPDET read %d in range %d\n",
|
||||
|
@ -391,7 +464,8 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
|
|||
return val;
|
||||
}
|
||||
|
||||
static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading)
|
||||
static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading,
|
||||
bool *mic)
|
||||
{
|
||||
struct arizona *arizona = info->arizona;
|
||||
int id_gpio = arizona->pdata.hpdet_id_gpio;
|
||||
|
@ -403,32 +477,8 @@ static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading)
|
|||
if (arizona->pdata.hpdet_acc_id) {
|
||||
info->hpdet_res[info->num_hpdet_res++] = *reading;
|
||||
|
||||
/*
|
||||
* If the impedence is too high don't measure the
|
||||
* second ground.
|
||||
*/
|
||||
if (info->num_hpdet_res == 1 && *reading >= 45) {
|
||||
dev_dbg(arizona->dev, "Skipping ground flip\n");
|
||||
info->hpdet_res[info->num_hpdet_res++] = *reading;
|
||||
}
|
||||
|
||||
if (info->num_hpdet_res == 1) {
|
||||
dev_dbg(arizona->dev, "Flipping ground\n");
|
||||
|
||||
regmap_update_bits(arizona->regmap,
|
||||
ARIZONA_ACCESSORY_DETECT_MODE_1,
|
||||
ARIZONA_ACCDET_SRC,
|
||||
~info->micd_modes[0].src);
|
||||
|
||||
regmap_update_bits(arizona->regmap,
|
||||
ARIZONA_HEADPHONE_DETECT_1,
|
||||
ARIZONA_HP_POLL, ARIZONA_HP_POLL);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/* Only check the mic directly if we didn't already ID it */
|
||||
if (id_gpio && info->num_hpdet_res == 2 &&
|
||||
!((info->hpdet_res[0] > info->hpdet_res[1] * 2))) {
|
||||
if (id_gpio && info->num_hpdet_res == 1) {
|
||||
dev_dbg(arizona->dev, "Measuring mic\n");
|
||||
|
||||
regmap_update_bits(arizona->regmap,
|
||||
|
@ -447,22 +497,28 @@ static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading)
|
|||
}
|
||||
|
||||
/* OK, got both. Now, compare... */
|
||||
dev_dbg(arizona->dev, "HPDET measured %d %d %d\n",
|
||||
info->hpdet_res[0], info->hpdet_res[1],
|
||||
info->hpdet_res[2]);
|
||||
|
||||
dev_dbg(arizona->dev, "HPDET measured %d %d\n",
|
||||
info->hpdet_res[0], info->hpdet_res[1]);
|
||||
|
||||
/* Take the headphone impedance for the main report */
|
||||
*reading = info->hpdet_res[0];
|
||||
|
||||
/* Sometimes we get false readings due to slow insert */
|
||||
if (*reading >= ARIZONA_HPDET_MAX && !info->hpdet_retried) {
|
||||
dev_dbg(arizona->dev, "Retrying high impedance\n");
|
||||
info->num_hpdet_res = 0;
|
||||
info->hpdet_retried = true;
|
||||
arizona_start_hpdet_acc_id(info);
|
||||
pm_runtime_put(info->dev);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/*
|
||||
* Either the two grounds measure differently or we
|
||||
* measure the mic as high impedance.
|
||||
* If we measure the mic as
|
||||
*/
|
||||
if ((info->hpdet_res[0] > info->hpdet_res[1] * 2) ||
|
||||
(id_gpio && info->hpdet_res[2] > 10)) {
|
||||
if (!id_gpio || info->hpdet_res[1] > 50) {
|
||||
dev_dbg(arizona->dev, "Detected mic\n");
|
||||
info->mic = true;
|
||||
*mic = true;
|
||||
info->detecting = true;
|
||||
} else {
|
||||
dev_dbg(arizona->dev, "Detected headphone\n");
|
||||
|
@ -484,8 +540,8 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
|
|||
struct arizona *arizona = info->arizona;
|
||||
int id_gpio = arizona->pdata.hpdet_id_gpio;
|
||||
int report = ARIZONA_CABLE_HEADPHONE;
|
||||
unsigned int val;
|
||||
int ret, reading;
|
||||
bool mic = false;
|
||||
|
||||
mutex_lock(&info->lock);
|
||||
|
||||
|
@ -521,7 +577,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
|
|||
ARIZONA_HP_IMPEDANCE_RANGE_MASK | ARIZONA_HP_POLL,
|
||||
0);
|
||||
|
||||
ret = arizona_hpdet_do_id(info, &reading);
|
||||
ret = arizona_hpdet_do_id(info, &reading, &mic);
|
||||
if (ret == -EAGAIN) {
|
||||
goto out;
|
||||
} else if (ret < 0) {
|
||||
|
@ -539,28 +595,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
|
|||
dev_err(arizona->dev, "Failed to report HP/line: %d\n",
|
||||
ret);
|
||||
|
||||
mutex_lock(&arizona->dapm->card->dapm_mutex);
|
||||
|
||||
ret = regmap_read(arizona->regmap, ARIZONA_OUTPUT_ENABLES_1, &val);
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev, "Failed to read output enables: %d\n",
|
||||
ret);
|
||||
val = 0;
|
||||
}
|
||||
|
||||
if (!(val & (ARIZONA_OUT1L_ENA | ARIZONA_OUT1R_ENA))) {
|
||||
ret = regmap_update_bits(arizona->regmap, 0x225, 0x4000, 0);
|
||||
if (ret != 0)
|
||||
dev_warn(arizona->dev, "Failed to undo magic: %d\n",
|
||||
ret);
|
||||
|
||||
ret = regmap_update_bits(arizona->regmap, 0x226, 0x4000, 0);
|
||||
if (ret != 0)
|
||||
dev_warn(arizona->dev, "Failed to undo magic: %d\n",
|
||||
ret);
|
||||
}
|
||||
|
||||
mutex_unlock(&arizona->dapm->card->dapm_mutex);
|
||||
arizona_extcon_do_magic(info, 0);
|
||||
|
||||
done:
|
||||
if (id_gpio)
|
||||
|
@ -572,7 +607,7 @@ done:
|
|||
ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
|
||||
|
||||
/* If we have a mic then reenable MICDET */
|
||||
if (info->mic)
|
||||
if (mic || info->mic)
|
||||
arizona_start_mic(info);
|
||||
|
||||
if (info->hpdet_active) {
|
||||
|
@ -606,13 +641,7 @@ static void arizona_identify_headphone(struct arizona_extcon_info *info)
|
|||
if (info->mic)
|
||||
arizona_stop_mic(info);
|
||||
|
||||
ret = regmap_update_bits(arizona->regmap, 0x225, 0x4000, 0x4000);
|
||||
if (ret != 0)
|
||||
dev_warn(arizona->dev, "Failed to do magic: %d\n", ret);
|
||||
|
||||
ret = regmap_update_bits(arizona->regmap, 0x226, 0x4000, 0x4000);
|
||||
if (ret != 0)
|
||||
dev_warn(arizona->dev, "Failed to do magic: %d\n", ret);
|
||||
arizona_extcon_do_magic(info, 0x4000);
|
||||
|
||||
ret = regmap_update_bits(arizona->regmap,
|
||||
ARIZONA_ACCESSORY_DETECT_MODE_1,
|
||||
|
@ -653,7 +682,8 @@ err:
|
|||
static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info)
|
||||
{
|
||||
struct arizona *arizona = info->arizona;
|
||||
unsigned int val;
|
||||
int hp_reading = 32;
|
||||
bool mic;
|
||||
int ret;
|
||||
|
||||
dev_dbg(arizona->dev, "Starting identification via HPDET\n");
|
||||
|
@ -663,32 +693,7 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info)
|
|||
|
||||
info->hpdet_active = true;
|
||||
|
||||
arizona_extcon_pulse_micbias(info);
|
||||
|
||||
mutex_lock(&arizona->dapm->card->dapm_mutex);
|
||||
|
||||
ret = regmap_read(arizona->regmap, ARIZONA_OUTPUT_ENABLES_1, &val);
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev, "Failed to read output enables: %d\n",
|
||||
ret);
|
||||
val = 0;
|
||||
}
|
||||
|
||||
if (!(val & (ARIZONA_OUT1L_ENA | ARIZONA_OUT1R_ENA))) {
|
||||
ret = regmap_update_bits(arizona->regmap, 0x225, 0x4000,
|
||||
0x4000);
|
||||
if (ret != 0)
|
||||
dev_warn(arizona->dev, "Failed to do magic: %d\n",
|
||||
ret);
|
||||
|
||||
ret = regmap_update_bits(arizona->regmap, 0x226, 0x4000,
|
||||
0x4000);
|
||||
if (ret != 0)
|
||||
dev_warn(arizona->dev, "Failed to do magic: %d\n",
|
||||
ret);
|
||||
}
|
||||
|
||||
mutex_unlock(&arizona->dapm->card->dapm_mutex);
|
||||
arizona_extcon_do_magic(info, 0x4000);
|
||||
|
||||
ret = regmap_update_bits(arizona->regmap,
|
||||
ARIZONA_ACCESSORY_DETECT_MODE_1,
|
||||
|
@ -700,12 +705,18 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info)
|
|||
goto err;
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1,
|
||||
ARIZONA_HP_POLL, ARIZONA_HP_POLL);
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev, "Can't start HPDETL measurement: %d\n",
|
||||
ret);
|
||||
goto err;
|
||||
if (arizona->pdata.hpdet_acc_id_line) {
|
||||
ret = regmap_update_bits(arizona->regmap,
|
||||
ARIZONA_HEADPHONE_DETECT_1,
|
||||
ARIZONA_HP_POLL, ARIZONA_HP_POLL);
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev,
|
||||
"Can't start HPDETL measurement: %d\n",
|
||||
ret);
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
arizona_hpdet_do_id(info, &hp_reading, &mic);
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -724,28 +735,58 @@ err:
|
|||
info->hpdet_active = false;
|
||||
}
|
||||
|
||||
static irqreturn_t arizona_micdet(int irq, void *data)
|
||||
static void arizona_micd_timeout_work(struct work_struct *work)
|
||||
{
|
||||
struct arizona_extcon_info *info = data;
|
||||
struct arizona *arizona = info->arizona;
|
||||
unsigned int val, lvl;
|
||||
int ret, i;
|
||||
struct arizona_extcon_info *info = container_of(work,
|
||||
struct arizona_extcon_info,
|
||||
micd_timeout_work.work);
|
||||
|
||||
mutex_lock(&info->lock);
|
||||
|
||||
ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_3, &val);
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev, "Failed to read MICDET: %d\n", ret);
|
||||
mutex_unlock(&info->lock);
|
||||
return IRQ_NONE;
|
||||
dev_dbg(info->arizona->dev, "MICD timed out, reporting HP\n");
|
||||
arizona_identify_headphone(info);
|
||||
|
||||
info->detecting = false;
|
||||
|
||||
arizona_stop_mic(info);
|
||||
|
||||
mutex_unlock(&info->lock);
|
||||
}
|
||||
|
||||
static void arizona_micd_detect(struct work_struct *work)
|
||||
{
|
||||
struct arizona_extcon_info *info = container_of(work,
|
||||
struct arizona_extcon_info,
|
||||
micd_detect_work.work);
|
||||
struct arizona *arizona = info->arizona;
|
||||
unsigned int val = 0, lvl;
|
||||
int ret, i, key;
|
||||
|
||||
cancel_delayed_work_sync(&info->micd_timeout_work);
|
||||
|
||||
mutex_lock(&info->lock);
|
||||
|
||||
for (i = 0; i < 10 && !(val & 0x7fc); i++) {
|
||||
ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_3, &val);
|
||||
if (ret != 0) {
|
||||
dev_err(arizona->dev, "Failed to read MICDET: %d\n", ret);
|
||||
mutex_unlock(&info->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
dev_dbg(arizona->dev, "MICDET: %x\n", val);
|
||||
|
||||
if (!(val & ARIZONA_MICD_VALID)) {
|
||||
dev_warn(arizona->dev, "Microphone detection state invalid\n");
|
||||
mutex_unlock(&info->lock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dev_dbg(arizona->dev, "MICDET: %x\n", val);
|
||||
|
||||
if (!(val & ARIZONA_MICD_VALID)) {
|
||||
dev_warn(arizona->dev, "Microphone detection state invalid\n");
|
||||
if (i == 10 && !(val & 0x7fc)) {
|
||||
dev_err(arizona->dev, "Failed to get valid MICDET value\n");
|
||||
mutex_unlock(&info->lock);
|
||||
return IRQ_NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Due to jack detect this should never happen */
|
||||
|
@ -786,7 +827,7 @@ static irqreturn_t arizona_micdet(int irq, void *data)
|
|||
* impedence then give up and report headphones.
|
||||
*/
|
||||
if (info->detecting && (val & 0x3f8)) {
|
||||
if (info->jack_flips >= info->micd_num_modes) {
|
||||
if (info->jack_flips >= info->micd_num_modes * 10) {
|
||||
dev_dbg(arizona->dev, "Detected HP/line\n");
|
||||
arizona_identify_headphone(info);
|
||||
|
||||
|
@ -816,12 +857,17 @@ static irqreturn_t arizona_micdet(int irq, void *data)
|
|||
lvl = val & ARIZONA_MICD_LVL_MASK;
|
||||
lvl >>= ARIZONA_MICD_LVL_SHIFT;
|
||||
|
||||
for (i = 0; i < ARIZONA_NUM_BUTTONS; i++)
|
||||
if (lvl & arizona_lvl_to_key[i].status)
|
||||
input_report_key(info->input,
|
||||
arizona_lvl_to_key[i].report,
|
||||
1);
|
||||
input_sync(info->input);
|
||||
for (i = 0; i < info->num_micd_ranges; i++)
|
||||
input_report_key(info->input,
|
||||
info->micd_ranges[i].key, 0);
|
||||
|
||||
WARN_ON(!lvl);
|
||||
WARN_ON(ffs(lvl) - 1 >= info->num_micd_ranges);
|
||||
if (lvl && ffs(lvl) - 1 < info->num_micd_ranges) {
|
||||
key = info->micd_ranges[ffs(lvl) - 1].key;
|
||||
input_report_key(info->input, key, 1);
|
||||
input_sync(info->input);
|
||||
}
|
||||
|
||||
} else if (info->detecting) {
|
||||
dev_dbg(arizona->dev, "Headphone detected\n");
|
||||
|
@ -835,16 +881,41 @@ static irqreturn_t arizona_micdet(int irq, void *data)
|
|||
}
|
||||
} else {
|
||||
dev_dbg(arizona->dev, "Mic button released\n");
|
||||
for (i = 0; i < ARIZONA_NUM_BUTTONS; i++)
|
||||
for (i = 0; i < info->num_micd_ranges; i++)
|
||||
input_report_key(info->input,
|
||||
arizona_lvl_to_key[i].report, 0);
|
||||
info->micd_ranges[i].key, 0);
|
||||
input_sync(info->input);
|
||||
arizona_extcon_pulse_micbias(info);
|
||||
}
|
||||
|
||||
handled:
|
||||
if (info->detecting)
|
||||
schedule_delayed_work(&info->micd_timeout_work,
|
||||
msecs_to_jiffies(info->micd_timeout));
|
||||
|
||||
pm_runtime_mark_last_busy(info->dev);
|
||||
mutex_unlock(&info->lock);
|
||||
}
|
||||
|
||||
static irqreturn_t arizona_micdet(int irq, void *data)
|
||||
{
|
||||
struct arizona_extcon_info *info = data;
|
||||
struct arizona *arizona = info->arizona;
|
||||
int debounce = arizona->pdata.micd_detect_debounce;
|
||||
|
||||
cancel_delayed_work_sync(&info->micd_detect_work);
|
||||
cancel_delayed_work_sync(&info->micd_timeout_work);
|
||||
|
||||
mutex_lock(&info->lock);
|
||||
if (!info->detecting)
|
||||
debounce = 0;
|
||||
mutex_unlock(&info->lock);
|
||||
|
||||
if (debounce)
|
||||
schedule_delayed_work(&info->micd_detect_work,
|
||||
msecs_to_jiffies(debounce));
|
||||
else
|
||||
arizona_micd_detect(&info->micd_detect_work.work);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
@ -865,11 +936,13 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
|
|||
struct arizona_extcon_info *info = data;
|
||||
struct arizona *arizona = info->arizona;
|
||||
unsigned int val, present, mask;
|
||||
bool cancelled_hp, cancelled_mic;
|
||||
int ret, i;
|
||||
|
||||
pm_runtime_get_sync(info->dev);
|
||||
cancelled_hp = cancel_delayed_work_sync(&info->hpdet_work);
|
||||
cancelled_mic = cancel_delayed_work_sync(&info->micd_timeout_work);
|
||||
|
||||
cancel_delayed_work_sync(&info->hpdet_work);
|
||||
pm_runtime_get_sync(info->dev);
|
||||
|
||||
mutex_lock(&info->lock);
|
||||
|
||||
|
@ -890,7 +963,22 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
|
|||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
if ((val & mask) == present) {
|
||||
val &= mask;
|
||||
if (val == info->last_jackdet) {
|
||||
dev_dbg(arizona->dev, "Suppressing duplicate JACKDET\n");
|
||||
if (cancelled_hp)
|
||||
schedule_delayed_work(&info->hpdet_work,
|
||||
msecs_to_jiffies(HPDET_DEBOUNCE));
|
||||
|
||||
if (cancelled_mic)
|
||||
schedule_delayed_work(&info->micd_timeout_work,
|
||||
msecs_to_jiffies(info->micd_timeout));
|
||||
|
||||
goto out;
|
||||
}
|
||||
info->last_jackdet = val;
|
||||
|
||||
if (info->last_jackdet == present) {
|
||||
dev_dbg(arizona->dev, "Detected jack\n");
|
||||
ret = extcon_set_cable_state_(&info->edev,
|
||||
ARIZONA_CABLE_MECHANICAL, true);
|
||||
|
@ -907,7 +995,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
|
|||
arizona_start_mic(info);
|
||||
} else {
|
||||
schedule_delayed_work(&info->hpdet_work,
|
||||
msecs_to_jiffies(250));
|
||||
msecs_to_jiffies(HPDET_DEBOUNCE));
|
||||
}
|
||||
|
||||
regmap_update_bits(arizona->regmap,
|
||||
|
@ -923,10 +1011,11 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
|
|||
info->hpdet_res[i] = 0;
|
||||
info->mic = false;
|
||||
info->hpdet_done = false;
|
||||
info->hpdet_retried = false;
|
||||
|
||||
for (i = 0; i < ARIZONA_NUM_BUTTONS; i++)
|
||||
for (i = 0; i < info->num_micd_ranges; i++)
|
||||
input_report_key(info->input,
|
||||
arizona_lvl_to_key[i].report, 0);
|
||||
info->micd_ranges[i].key, 0);
|
||||
input_sync(info->input);
|
||||
|
||||
ret = extcon_update_state(&info->edev, 0xffffffff, 0);
|
||||
|
@ -940,6 +1029,11 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
|
|||
ARIZONA_MICD_CLAMP_DB | ARIZONA_JD1_DB);
|
||||
}
|
||||
|
||||
if (arizona->pdata.micd_timeout)
|
||||
info->micd_timeout = arizona->pdata.micd_timeout;
|
||||
else
|
||||
info->micd_timeout = DEFAULT_MICD_TIMEOUT;
|
||||
|
||||
/* Clear trig_sts to make sure DCVDD is not forced up */
|
||||
regmap_write(arizona->regmap, ARIZONA_AOD_WKUP_AND_TRIG,
|
||||
ARIZONA_MICD_CLAMP_FALL_TRIG_STS |
|
||||
|
@ -947,6 +1041,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
|
|||
ARIZONA_JD1_FALL_TRIG_STS |
|
||||
ARIZONA_JD1_RISE_TRIG_STS);
|
||||
|
||||
out:
|
||||
mutex_unlock(&info->lock);
|
||||
|
||||
pm_runtime_mark_last_busy(info->dev);
|
||||
|
@ -955,13 +1050,34 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* Map a level onto a slot in the register bank */
|
||||
static void arizona_micd_set_level(struct arizona *arizona, int index,
|
||||
unsigned int level)
|
||||
{
|
||||
int reg;
|
||||
unsigned int mask;
|
||||
|
||||
reg = ARIZONA_MIC_DETECT_LEVEL_4 - (index / 2);
|
||||
|
||||
if (!(index % 2)) {
|
||||
mask = 0x3f00;
|
||||
level <<= 8;
|
||||
} else {
|
||||
mask = 0x3f;
|
||||
}
|
||||
|
||||
/* Program the level itself */
|
||||
regmap_update_bits(arizona->regmap, reg, mask, level);
|
||||
}
|
||||
|
||||
static int arizona_extcon_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
|
||||
struct arizona_pdata *pdata;
|
||||
struct arizona_extcon_info *info;
|
||||
unsigned int val;
|
||||
int jack_irq_fall, jack_irq_rise;
|
||||
int ret, mode, i;
|
||||
int ret, mode, i, j;
|
||||
|
||||
if (!arizona->dapm || !arizona->dapm->card)
|
||||
return -EPROBE_DEFER;
|
||||
|
@ -985,7 +1101,10 @@ static int arizona_extcon_probe(struct platform_device *pdev)
|
|||
mutex_init(&info->lock);
|
||||
info->arizona = arizona;
|
||||
info->dev = &pdev->dev;
|
||||
info->last_jackdet = ~(ARIZONA_MICD_CLAMP_STS | ARIZONA_JD1_STS);
|
||||
INIT_DELAYED_WORK(&info->hpdet_work, arizona_hpdet_work);
|
||||
INIT_DELAYED_WORK(&info->micd_detect_work, arizona_micd_detect);
|
||||
INIT_DELAYED_WORK(&info->micd_timeout_work, arizona_micd_timeout_work);
|
||||
platform_set_drvdata(pdev, info);
|
||||
|
||||
switch (arizona->type) {
|
||||
|
@ -1014,6 +1133,17 @@ static int arizona_extcon_probe(struct platform_device *pdev)
|
|||
goto err;
|
||||
}
|
||||
|
||||
info->input = devm_input_allocate_device(&pdev->dev);
|
||||
if (!info->input) {
|
||||
dev_err(arizona->dev, "Can't allocate input dev\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_register;
|
||||
}
|
||||
|
||||
info->input->name = "Headset";
|
||||
info->input->phys = "arizona/extcon";
|
||||
info->input->dev.parent = &pdev->dev;
|
||||
|
||||
if (pdata->num_micd_configs) {
|
||||
info->micd_modes = pdata->micd_configs;
|
||||
info->micd_num_modes = pdata->num_micd_configs;
|
||||
|
@ -1069,15 +1199,79 @@ static int arizona_extcon_probe(struct platform_device *pdev)
|
|||
arizona->pdata.micd_dbtime
|
||||
<< ARIZONA_MICD_DBTIME_SHIFT);
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(arizona_micd_levels) != 0x40);
|
||||
|
||||
if (arizona->pdata.num_micd_ranges) {
|
||||
info->micd_ranges = pdata->micd_ranges;
|
||||
info->num_micd_ranges = pdata->num_micd_ranges;
|
||||
} else {
|
||||
info->micd_ranges = micd_default_ranges;
|
||||
info->num_micd_ranges = ARRAY_SIZE(micd_default_ranges);
|
||||
}
|
||||
|
||||
if (arizona->pdata.num_micd_ranges > ARIZONA_MAX_MICD_RANGE) {
|
||||
dev_err(arizona->dev, "Too many MICD ranges: %d\n",
|
||||
arizona->pdata.num_micd_ranges);
|
||||
}
|
||||
|
||||
if (info->num_micd_ranges > 1) {
|
||||
for (i = 1; i < info->num_micd_ranges; i++) {
|
||||
if (info->micd_ranges[i - 1].max >
|
||||
info->micd_ranges[i].max) {
|
||||
dev_err(arizona->dev,
|
||||
"MICD ranges must be sorted\n");
|
||||
ret = -EINVAL;
|
||||
goto err_input;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable all buttons by default */
|
||||
regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_2,
|
||||
ARIZONA_MICD_LVL_SEL_MASK, 0x81);
|
||||
|
||||
/* Set up all the buttons the user specified */
|
||||
for (i = 0; i < info->num_micd_ranges; i++) {
|
||||
for (j = 0; j < ARRAY_SIZE(arizona_micd_levels); j++)
|
||||
if (arizona_micd_levels[j] >= info->micd_ranges[i].max)
|
||||
break;
|
||||
|
||||
if (j == ARRAY_SIZE(arizona_micd_levels)) {
|
||||
dev_err(arizona->dev, "Unsupported MICD level %d\n",
|
||||
info->micd_ranges[i].max);
|
||||
ret = -EINVAL;
|
||||
goto err_input;
|
||||
}
|
||||
|
||||
dev_dbg(arizona->dev, "%d ohms for MICD threshold %d\n",
|
||||
arizona_micd_levels[j], i);
|
||||
|
||||
arizona_micd_set_level(arizona, i, j);
|
||||
input_set_capability(info->input, EV_KEY,
|
||||
info->micd_ranges[i].key);
|
||||
|
||||
/* Enable reporting of that range */
|
||||
regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_2,
|
||||
1 << i, 1 << i);
|
||||
}
|
||||
|
||||
/* Set all the remaining keys to a maximum */
|
||||
for (; i < ARIZONA_MAX_MICD_RANGE; i++)
|
||||
arizona_micd_set_level(arizona, i, 0x3f);
|
||||
|
||||
/*
|
||||
* If we have a clamp use it, activating in conjunction with
|
||||
* GPIO5 if that is connected for jack detect operation.
|
||||
*/
|
||||
if (info->micd_clamp) {
|
||||
if (arizona->pdata.jd_gpio5) {
|
||||
/* Put the GPIO into input mode */
|
||||
/* Put the GPIO into input mode with optional pull */
|
||||
val = 0xc101;
|
||||
if (arizona->pdata.jd_gpio5_nopull)
|
||||
val &= ~ARIZONA_GPN_PU;
|
||||
|
||||
regmap_write(arizona->regmap, ARIZONA_GPIO5_CTRL,
|
||||
0xc101);
|
||||
val);
|
||||
|
||||
regmap_update_bits(arizona->regmap,
|
||||
ARIZONA_MICD_CLAMP_CONTROL,
|
||||
|
@ -1096,20 +1290,6 @@ static int arizona_extcon_probe(struct platform_device *pdev)
|
|||
|
||||
arizona_extcon_set_mode(info, 0);
|
||||
|
||||
info->input = devm_input_allocate_device(&pdev->dev);
|
||||
if (!info->input) {
|
||||
dev_err(arizona->dev, "Can't allocate input dev\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_register;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARIZONA_NUM_BUTTONS; i++)
|
||||
input_set_capability(info->input, EV_KEY,
|
||||
arizona_lvl_to_key[i].report);
|
||||
info->input->name = "Headset";
|
||||
info->input->phys = "arizona/extcon";
|
||||
info->input->dev.parent = &pdev->dev;
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_idle(&pdev->dev);
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
* extcon-max77693 driver use 'default_init_data' to bring up base operation
|
||||
* of MAX77693 MUIC device.
|
||||
*/
|
||||
struct max77693_reg_data default_init_data[] = {
|
||||
static struct max77693_reg_data default_init_data[] = {
|
||||
{
|
||||
/* STATUS2 - [3]ChgDetRun */
|
||||
.addr = MAX77693_MUIC_REG_STATUS2,
|
||||
|
@ -258,7 +258,7 @@ static int max77693_muic_set_debounce_time(struct max77693_muic_info *info,
|
|||
CONTROL3_ADCDBSET_MASK);
|
||||
if (ret) {
|
||||
dev_err(info->dev, "failed to set ADC debounce time\n");
|
||||
return -EAGAIN;
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -294,7 +294,7 @@ static int max77693_muic_set_path(struct max77693_muic_info *info,
|
|||
MAX77693_MUIC_REG_CTRL1, ctrl1, COMP_SW_MASK);
|
||||
if (ret < 0) {
|
||||
dev_err(info->dev, "failed to update MUIC register\n");
|
||||
return -EAGAIN;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (attached)
|
||||
|
@ -307,7 +307,7 @@ static int max77693_muic_set_path(struct max77693_muic_info *info,
|
|||
CONTROL2_LOWPWR_MASK | CONTROL2_CPEN_MASK);
|
||||
if (ret < 0) {
|
||||
dev_err(info->dev, "failed to update MUIC register\n");
|
||||
return -EAGAIN;
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_info(info->dev,
|
||||
|
@ -1035,7 +1035,7 @@ static int max77693_muic_detect_accessory(struct max77693_muic_info *info)
|
|||
if (ret) {
|
||||
dev_err(info->dev, "failed to read MUIC register\n");
|
||||
mutex_unlock(&info->mutex);
|
||||
return -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
adc = max77693_muic_get_cable_type(info, MAX77693_CABLE_GROUP_ADC,
|
||||
|
|
|
@ -196,7 +196,7 @@ static int max8997_muic_set_debounce_time(struct max8997_muic_info *info,
|
|||
CONTROL3_ADCDBSET_MASK);
|
||||
if (ret) {
|
||||
dev_err(info->dev, "failed to set ADC debounce time\n");
|
||||
return -EAGAIN;
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -232,7 +232,7 @@ static int max8997_muic_set_path(struct max8997_muic_info *info,
|
|||
MAX8997_MUIC_REG_CONTROL1, ctrl1, COMP_SW_MASK);
|
||||
if (ret < 0) {
|
||||
dev_err(info->dev, "failed to update MUIC register\n");
|
||||
return -EAGAIN;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (attached)
|
||||
|
@ -245,7 +245,7 @@ static int max8997_muic_set_path(struct max8997_muic_info *info,
|
|||
CONTROL2_LOWPWR_MASK | CONTROL2_CPEN_MASK);
|
||||
if (ret < 0) {
|
||||
dev_err(info->dev, "failed to update MUIC register\n");
|
||||
return -EAGAIN;
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_info(info->dev,
|
||||
|
@ -397,7 +397,7 @@ static int max8997_muic_handle_jig_uart(struct max8997_muic_info *info,
|
|||
ret = max8997_muic_set_path(info, info->path_uart, attached);
|
||||
if (ret) {
|
||||
dev_err(info->dev, "failed to update muic register\n");
|
||||
return -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
extcon_set_cable_state(info->edev, "JIG", attached);
|
||||
|
@ -608,7 +608,7 @@ static int max8997_muic_detect_dev(struct max8997_muic_info *info)
|
|||
if (ret) {
|
||||
dev_err(info->dev, "failed to read MUIC register\n");
|
||||
mutex_unlock(&info->mutex);
|
||||
return -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
adc = max8997_muic_get_cable_type(info, MAX8997_CABLE_GROUP_ADC,
|
||||
|
@ -646,7 +646,7 @@ static void max8997_muic_detect_cable_wq(struct work_struct *work)
|
|||
|
||||
ret = max8997_muic_detect_dev(info);
|
||||
if (ret < 0)
|
||||
pr_err("failed to detect cable type\n");
|
||||
dev_err(info->dev, "failed to detect cable type\n");
|
||||
}
|
||||
|
||||
static int max8997_muic_probe(struct platform_device *pdev)
|
||||
|
|
|
@ -5,4 +5,4 @@ obj-$(CONFIG_HYPERV_BALLOON) += hv_balloon.o
|
|||
hv_vmbus-y := vmbus_drv.o \
|
||||
hv.o connection.o channel.o \
|
||||
channel_mgmt.o ring_buffer.o
|
||||
hv_utils-y := hv_util.o hv_kvp.o
|
||||
hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o
|
||||
|
|
|
@ -165,8 +165,19 @@ static void vmbus_process_rescind_offer(struct work_struct *work)
|
|||
struct vmbus_channel *channel = container_of(work,
|
||||
struct vmbus_channel,
|
||||
work);
|
||||
unsigned long flags;
|
||||
struct vmbus_channel_relid_released msg;
|
||||
|
||||
vmbus_device_unregister(channel->device_obj);
|
||||
memset(&msg, 0, sizeof(struct vmbus_channel_relid_released));
|
||||
msg.child_relid = channel->offermsg.child_relid;
|
||||
msg.header.msgtype = CHANNELMSG_RELID_RELEASED;
|
||||
vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released));
|
||||
|
||||
spin_lock_irqsave(&vmbus_connection.channel_lock, flags);
|
||||
list_del(&channel->listentry);
|
||||
spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags);
|
||||
free_channel(channel);
|
||||
}
|
||||
|
||||
void vmbus_free_channels(void)
|
||||
|
|
|
@ -289,9 +289,8 @@ void hv_synic_init(void *arg)
|
|||
/* Check the version */
|
||||
rdmsrl(HV_X64_MSR_SVERSION, version);
|
||||
|
||||
hv_context.event_dpc[cpu] = (struct tasklet_struct *)
|
||||
kmalloc(sizeof(struct tasklet_struct),
|
||||
GFP_ATOMIC);
|
||||
hv_context.event_dpc[cpu] = kmalloc(sizeof(struct tasklet_struct),
|
||||
GFP_ATOMIC);
|
||||
if (hv_context.event_dpc[cpu] == NULL) {
|
||||
pr_err("Unable to allocate event dpc\n");
|
||||
goto cleanup;
|
||||
|
|
|
@ -117,7 +117,14 @@ union dm_caps {
|
|||
struct {
|
||||
__u64 balloon:1;
|
||||
__u64 hot_add:1;
|
||||
__u64 reservedz:62;
|
||||
/*
|
||||
* To support guests that may have alignment
|
||||
* limitations on hot-add, the guest can specify
|
||||
* its alignment requirements; a value of n
|
||||
* represents an alignment of 2^n in mega bytes.
|
||||
*/
|
||||
__u64 hot_add_alignment:4;
|
||||
__u64 reservedz:58;
|
||||
} cap_bits;
|
||||
__u64 caps;
|
||||
} __packed;
|
||||
|
@ -412,13 +419,45 @@ struct dm_info_msg {
|
|||
* End protocol definitions.
|
||||
*/
|
||||
|
||||
static bool hot_add;
|
||||
/*
|
||||
* State to manage hot adding memory into the guest.
|
||||
* The range start_pfn : end_pfn specifies the range
|
||||
* that the host has asked us to hot add. The range
|
||||
* start_pfn : ha_end_pfn specifies the range that we have
|
||||
* currently hot added. We hot add in multiples of 128M
|
||||
* chunks; it is possible that we may not be able to bring
|
||||
* online all the pages in the region. The range
|
||||
* covered_start_pfn : covered_end_pfn defines the pages that can
|
||||
* be brough online.
|
||||
*/
|
||||
|
||||
struct hv_hotadd_state {
|
||||
struct list_head list;
|
||||
unsigned long start_pfn;
|
||||
unsigned long covered_start_pfn;
|
||||
unsigned long covered_end_pfn;
|
||||
unsigned long ha_end_pfn;
|
||||
unsigned long end_pfn;
|
||||
};
|
||||
|
||||
struct balloon_state {
|
||||
__u32 num_pages;
|
||||
struct work_struct wrk;
|
||||
};
|
||||
|
||||
struct hot_add_wrk {
|
||||
union dm_mem_page_range ha_page_range;
|
||||
union dm_mem_page_range ha_region_range;
|
||||
struct work_struct wrk;
|
||||
};
|
||||
|
||||
static bool hot_add = true;
|
||||
static bool do_hot_add;
|
||||
/*
|
||||
* Delay reporting memory pressure by
|
||||
* the specified number of seconds.
|
||||
*/
|
||||
static uint pressure_report_delay = 30;
|
||||
static uint pressure_report_delay = 45;
|
||||
|
||||
module_param(hot_add, bool, (S_IRUGO | S_IWUSR));
|
||||
MODULE_PARM_DESC(hot_add, "If set attempt memory hot_add");
|
||||
|
@ -446,6 +485,7 @@ enum hv_dm_state {
|
|||
static __u8 recv_buffer[PAGE_SIZE];
|
||||
static __u8 *send_buffer;
|
||||
#define PAGES_IN_2M 512
|
||||
#define HA_CHUNK (32 * 1024)
|
||||
|
||||
struct hv_dynmem_device {
|
||||
struct hv_device *dev;
|
||||
|
@ -459,13 +499,39 @@ struct hv_dynmem_device {
|
|||
unsigned int num_pages_ballooned;
|
||||
|
||||
/*
|
||||
* This thread handles both balloon/hot-add
|
||||
* State to manage the ballooning (up) operation.
|
||||
*/
|
||||
struct balloon_state balloon_wrk;
|
||||
|
||||
/*
|
||||
* State to execute the "hot-add" operation.
|
||||
*/
|
||||
struct hot_add_wrk ha_wrk;
|
||||
|
||||
/*
|
||||
* This state tracks if the host has specified a hot-add
|
||||
* region.
|
||||
*/
|
||||
bool host_specified_ha_region;
|
||||
|
||||
/*
|
||||
* State to synchronize hot-add.
|
||||
*/
|
||||
struct completion ol_waitevent;
|
||||
bool ha_waiting;
|
||||
/*
|
||||
* This thread handles hot-add
|
||||
* requests from the host as well as notifying
|
||||
* the host with regards to memory pressure in
|
||||
* the guest.
|
||||
*/
|
||||
struct task_struct *thread;
|
||||
|
||||
/*
|
||||
* A list of hot-add regions.
|
||||
*/
|
||||
struct list_head ha_region_list;
|
||||
|
||||
/*
|
||||
* We start with the highest version we can support
|
||||
* and downgrade based on the host; we save here the
|
||||
|
@ -476,35 +542,358 @@ struct hv_dynmem_device {
|
|||
|
||||
static struct hv_dynmem_device dm_device;
|
||||
|
||||
static void hot_add_req(struct hv_dynmem_device *dm, struct dm_hot_add *msg)
|
||||
#ifdef CONFIG_MEMORY_HOTPLUG
|
||||
|
||||
static void hv_bring_pgs_online(unsigned long start_pfn, unsigned long size)
|
||||
{
|
||||
int i;
|
||||
|
||||
struct dm_hot_add_response resp;
|
||||
for (i = 0; i < size; i++) {
|
||||
struct page *pg;
|
||||
pg = pfn_to_page(start_pfn + i);
|
||||
__online_page_set_limits(pg);
|
||||
__online_page_increment_counters(pg);
|
||||
__online_page_free(pg);
|
||||
}
|
||||
}
|
||||
|
||||
if (do_hot_add) {
|
||||
static void hv_mem_hot_add(unsigned long start, unsigned long size,
|
||||
unsigned long pfn_count,
|
||||
struct hv_hotadd_state *has)
|
||||
{
|
||||
int ret = 0;
|
||||
int i, nid, t;
|
||||
unsigned long start_pfn;
|
||||
unsigned long processed_pfn;
|
||||
unsigned long total_pfn = pfn_count;
|
||||
|
||||
pr_info("Memory hot add not supported\n");
|
||||
for (i = 0; i < (size/HA_CHUNK); i++) {
|
||||
start_pfn = start + (i * HA_CHUNK);
|
||||
has->ha_end_pfn += HA_CHUNK;
|
||||
|
||||
if (total_pfn > HA_CHUNK) {
|
||||
processed_pfn = HA_CHUNK;
|
||||
total_pfn -= HA_CHUNK;
|
||||
} else {
|
||||
processed_pfn = total_pfn;
|
||||
total_pfn = 0;
|
||||
}
|
||||
|
||||
has->covered_end_pfn += processed_pfn;
|
||||
|
||||
init_completion(&dm_device.ol_waitevent);
|
||||
dm_device.ha_waiting = true;
|
||||
|
||||
nid = memory_add_physaddr_to_nid(PFN_PHYS(start_pfn));
|
||||
ret = add_memory(nid, PFN_PHYS((start_pfn)),
|
||||
(HA_CHUNK << PAGE_SHIFT));
|
||||
|
||||
if (ret) {
|
||||
pr_info("hot_add memory failed error is %d\n", ret);
|
||||
if (ret == -EEXIST) {
|
||||
/*
|
||||
* This error indicates that the error
|
||||
* is not a transient failure. This is the
|
||||
* case where the guest's physical address map
|
||||
* precludes hot adding memory. Stop all further
|
||||
* memory hot-add.
|
||||
*/
|
||||
do_hot_add = false;
|
||||
}
|
||||
has->ha_end_pfn -= HA_CHUNK;
|
||||
has->covered_end_pfn -= processed_pfn;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Currently we do not support hot add.
|
||||
* Just fail the request.
|
||||
* Wait for the memory block to be onlined.
|
||||
*/
|
||||
t = wait_for_completion_timeout(&dm_device.ol_waitevent, 5*HZ);
|
||||
if (t == 0) {
|
||||
pr_info("hot_add memory timedout\n");
|
||||
has->ha_end_pfn -= HA_CHUNK;
|
||||
has->covered_end_pfn -= processed_pfn;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void hv_online_page(struct page *pg)
|
||||
{
|
||||
struct list_head *cur;
|
||||
struct hv_hotadd_state *has;
|
||||
unsigned long cur_start_pgp;
|
||||
unsigned long cur_end_pgp;
|
||||
|
||||
if (dm_device.ha_waiting) {
|
||||
dm_device.ha_waiting = false;
|
||||
complete(&dm_device.ol_waitevent);
|
||||
}
|
||||
|
||||
list_for_each(cur, &dm_device.ha_region_list) {
|
||||
has = list_entry(cur, struct hv_hotadd_state, list);
|
||||
cur_start_pgp = (unsigned long)
|
||||
pfn_to_page(has->covered_start_pfn);
|
||||
cur_end_pgp = (unsigned long)pfn_to_page(has->covered_end_pfn);
|
||||
|
||||
if (((unsigned long)pg >= cur_start_pgp) &&
|
||||
((unsigned long)pg < cur_end_pgp)) {
|
||||
/*
|
||||
* This frame is currently backed; online the
|
||||
* page.
|
||||
*/
|
||||
__online_page_set_limits(pg);
|
||||
__online_page_increment_counters(pg);
|
||||
__online_page_free(pg);
|
||||
has->covered_start_pfn++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt)
|
||||
{
|
||||
struct list_head *cur;
|
||||
struct hv_hotadd_state *has;
|
||||
unsigned long residual, new_inc;
|
||||
|
||||
if (list_empty(&dm_device.ha_region_list))
|
||||
return false;
|
||||
|
||||
list_for_each(cur, &dm_device.ha_region_list) {
|
||||
has = list_entry(cur, struct hv_hotadd_state, list);
|
||||
|
||||
/*
|
||||
* If the pfn range we are dealing with is not in the current
|
||||
* "hot add block", move on.
|
||||
*/
|
||||
if ((start_pfn >= has->end_pfn))
|
||||
continue;
|
||||
/*
|
||||
* If the current hot add-request extends beyond
|
||||
* our current limit; extend it.
|
||||
*/
|
||||
if ((start_pfn + pfn_cnt) > has->end_pfn) {
|
||||
residual = (start_pfn + pfn_cnt - has->end_pfn);
|
||||
/*
|
||||
* Extend the region by multiples of HA_CHUNK.
|
||||
*/
|
||||
new_inc = (residual / HA_CHUNK) * HA_CHUNK;
|
||||
if (residual % HA_CHUNK)
|
||||
new_inc += HA_CHUNK;
|
||||
|
||||
has->end_pfn += new_inc;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the current start pfn is not where the covered_end
|
||||
* is, update it.
|
||||
*/
|
||||
|
||||
if (has->covered_end_pfn != start_pfn) {
|
||||
has->covered_end_pfn = start_pfn;
|
||||
has->covered_start_pfn = start_pfn;
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static unsigned long handle_pg_range(unsigned long pg_start,
|
||||
unsigned long pg_count)
|
||||
{
|
||||
unsigned long start_pfn = pg_start;
|
||||
unsigned long pfn_cnt = pg_count;
|
||||
unsigned long size;
|
||||
struct list_head *cur;
|
||||
struct hv_hotadd_state *has;
|
||||
unsigned long pgs_ol = 0;
|
||||
unsigned long old_covered_state;
|
||||
|
||||
if (list_empty(&dm_device.ha_region_list))
|
||||
return 0;
|
||||
|
||||
list_for_each(cur, &dm_device.ha_region_list) {
|
||||
has = list_entry(cur, struct hv_hotadd_state, list);
|
||||
|
||||
/*
|
||||
* If the pfn range we are dealing with is not in the current
|
||||
* "hot add block", move on.
|
||||
*/
|
||||
if ((start_pfn >= has->end_pfn))
|
||||
continue;
|
||||
|
||||
old_covered_state = has->covered_end_pfn;
|
||||
|
||||
if (start_pfn < has->ha_end_pfn) {
|
||||
/*
|
||||
* This is the case where we are backing pages
|
||||
* in an already hot added region. Bring
|
||||
* these pages online first.
|
||||
*/
|
||||
pgs_ol = has->ha_end_pfn - start_pfn;
|
||||
if (pgs_ol > pfn_cnt)
|
||||
pgs_ol = pfn_cnt;
|
||||
hv_bring_pgs_online(start_pfn, pgs_ol);
|
||||
has->covered_end_pfn += pgs_ol;
|
||||
has->covered_start_pfn += pgs_ol;
|
||||
pfn_cnt -= pgs_ol;
|
||||
}
|
||||
|
||||
if ((has->ha_end_pfn < has->end_pfn) && (pfn_cnt > 0)) {
|
||||
/*
|
||||
* We have some residual hot add range
|
||||
* that needs to be hot added; hot add
|
||||
* it now. Hot add a multiple of
|
||||
* of HA_CHUNK that fully covers the pages
|
||||
* we have.
|
||||
*/
|
||||
size = (has->end_pfn - has->ha_end_pfn);
|
||||
if (pfn_cnt <= size) {
|
||||
size = ((pfn_cnt / HA_CHUNK) * HA_CHUNK);
|
||||
if (pfn_cnt % HA_CHUNK)
|
||||
size += HA_CHUNK;
|
||||
} else {
|
||||
pfn_cnt = size;
|
||||
}
|
||||
hv_mem_hot_add(has->ha_end_pfn, size, pfn_cnt, has);
|
||||
}
|
||||
/*
|
||||
* If we managed to online any pages that were given to us,
|
||||
* we declare success.
|
||||
*/
|
||||
return has->covered_end_pfn - old_covered_state;
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long process_hot_add(unsigned long pg_start,
|
||||
unsigned long pfn_cnt,
|
||||
unsigned long rg_start,
|
||||
unsigned long rg_size)
|
||||
{
|
||||
struct hv_hotadd_state *ha_region = NULL;
|
||||
|
||||
if (pfn_cnt == 0)
|
||||
return 0;
|
||||
|
||||
if (!dm_device.host_specified_ha_region)
|
||||
if (pfn_covered(pg_start, pfn_cnt))
|
||||
goto do_pg_range;
|
||||
|
||||
/*
|
||||
* If the host has specified a hot-add range; deal with it first.
|
||||
*/
|
||||
|
||||
if (rg_size != 0) {
|
||||
ha_region = kzalloc(sizeof(struct hv_hotadd_state), GFP_KERNEL);
|
||||
if (!ha_region)
|
||||
return 0;
|
||||
|
||||
INIT_LIST_HEAD(&ha_region->list);
|
||||
|
||||
list_add_tail(&ha_region->list, &dm_device.ha_region_list);
|
||||
ha_region->start_pfn = rg_start;
|
||||
ha_region->ha_end_pfn = rg_start;
|
||||
ha_region->covered_start_pfn = pg_start;
|
||||
ha_region->covered_end_pfn = pg_start;
|
||||
ha_region->end_pfn = rg_start + rg_size;
|
||||
}
|
||||
|
||||
do_pg_range:
|
||||
/*
|
||||
* Process the page range specified; bringing them
|
||||
* online if possible.
|
||||
*/
|
||||
return handle_pg_range(pg_start, pfn_cnt);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void hot_add_req(struct work_struct *dummy)
|
||||
{
|
||||
struct dm_hot_add_response resp;
|
||||
#ifdef CONFIG_MEMORY_HOTPLUG
|
||||
unsigned long pg_start, pfn_cnt;
|
||||
unsigned long rg_start, rg_sz;
|
||||
#endif
|
||||
struct hv_dynmem_device *dm = &dm_device;
|
||||
|
||||
memset(&resp, 0, sizeof(struct dm_hot_add_response));
|
||||
resp.hdr.type = DM_MEM_HOT_ADD_RESPONSE;
|
||||
resp.hdr.size = sizeof(struct dm_hot_add_response);
|
||||
resp.hdr.trans_id = atomic_inc_return(&trans_id);
|
||||
|
||||
resp.page_count = 0;
|
||||
resp.result = 0;
|
||||
#ifdef CONFIG_MEMORY_HOTPLUG
|
||||
pg_start = dm->ha_wrk.ha_page_range.finfo.start_page;
|
||||
pfn_cnt = dm->ha_wrk.ha_page_range.finfo.page_cnt;
|
||||
|
||||
rg_start = dm->ha_wrk.ha_region_range.finfo.start_page;
|
||||
rg_sz = dm->ha_wrk.ha_region_range.finfo.page_cnt;
|
||||
|
||||
if ((rg_start == 0) && (!dm->host_specified_ha_region)) {
|
||||
unsigned long region_size;
|
||||
unsigned long region_start;
|
||||
|
||||
/*
|
||||
* The host has not specified the hot-add region.
|
||||
* Based on the hot-add page range being specified,
|
||||
* compute a hot-add region that can cover the pages
|
||||
* that need to be hot-added while ensuring the alignment
|
||||
* and size requirements of Linux as it relates to hot-add.
|
||||
*/
|
||||
region_start = pg_start;
|
||||
region_size = (pfn_cnt / HA_CHUNK) * HA_CHUNK;
|
||||
if (pfn_cnt % HA_CHUNK)
|
||||
region_size += HA_CHUNK;
|
||||
|
||||
region_start = (pg_start / HA_CHUNK) * HA_CHUNK;
|
||||
|
||||
rg_start = region_start;
|
||||
rg_sz = region_size;
|
||||
}
|
||||
|
||||
if (do_hot_add)
|
||||
resp.page_count = process_hot_add(pg_start, pfn_cnt,
|
||||
rg_start, rg_sz);
|
||||
#endif
|
||||
/*
|
||||
* The result field of the response structure has the
|
||||
* following semantics:
|
||||
*
|
||||
* 1. If all or some pages hot-added: Guest should return success.
|
||||
*
|
||||
* 2. If no pages could be hot-added:
|
||||
*
|
||||
* If the guest returns success, then the host
|
||||
* will not attempt any further hot-add operations. This
|
||||
* signifies a permanent failure.
|
||||
*
|
||||
* If the guest returns failure, then this failure will be
|
||||
* treated as a transient failure and the host may retry the
|
||||
* hot-add operation after some delay.
|
||||
*/
|
||||
if (resp.page_count > 0)
|
||||
resp.result = 1;
|
||||
else if (!do_hot_add)
|
||||
resp.result = 1;
|
||||
else
|
||||
resp.result = 0;
|
||||
|
||||
if (!do_hot_add || (resp.page_count == 0))
|
||||
pr_info("Memory hot add failed\n");
|
||||
|
||||
dm->state = DM_INITIALIZED;
|
||||
vmbus_sendpacket(dm->dev->channel, &resp,
|
||||
sizeof(struct dm_hot_add_response),
|
||||
(unsigned long)NULL,
|
||||
VM_PKT_DATA_INBAND, 0);
|
||||
|
||||
}
|
||||
|
||||
static void process_info(struct hv_dynmem_device *dm, struct dm_info_msg *msg)
|
||||
|
@ -523,7 +912,7 @@ static void process_info(struct hv_dynmem_device *dm, struct dm_info_msg *msg)
|
|||
}
|
||||
}
|
||||
|
||||
unsigned long compute_balloon_floor(void)
|
||||
static unsigned long compute_balloon_floor(void)
|
||||
{
|
||||
unsigned long min_pages;
|
||||
#define MB2PAGES(mb) ((mb) << (20 - PAGE_SHIFT))
|
||||
|
@ -644,6 +1033,14 @@ static int alloc_balloon_pages(struct hv_dynmem_device *dm, int num_pages,
|
|||
|
||||
dm->num_pages_ballooned += alloc_unit;
|
||||
|
||||
/*
|
||||
* If we allocatted 2M pages; split them so we
|
||||
* can free them in any order we get.
|
||||
*/
|
||||
|
||||
if (alloc_unit != 1)
|
||||
split_page(pg, get_order(alloc_unit << PAGE_SHIFT));
|
||||
|
||||
bl_resp->range_count++;
|
||||
bl_resp->range_array[i].finfo.start_page =
|
||||
page_to_pfn(pg);
|
||||
|
@ -657,9 +1054,9 @@ static int alloc_balloon_pages(struct hv_dynmem_device *dm, int num_pages,
|
|||
|
||||
|
||||
|
||||
static void balloon_up(struct hv_dynmem_device *dm, struct dm_balloon *req)
|
||||
static void balloon_up(struct work_struct *dummy)
|
||||
{
|
||||
int num_pages = req->num_pages;
|
||||
int num_pages = dm_device.balloon_wrk.num_pages;
|
||||
int num_ballooned = 0;
|
||||
struct dm_balloon_response *bl_resp;
|
||||
int alloc_unit;
|
||||
|
@ -670,9 +1067,10 @@ static void balloon_up(struct hv_dynmem_device *dm, struct dm_balloon *req)
|
|||
|
||||
|
||||
/*
|
||||
* Currently, we only support 4k allocations.
|
||||
* We will attempt 2M allocations. However, if we fail to
|
||||
* allocate 2M chunks, we will go back to 4k allocations.
|
||||
*/
|
||||
alloc_unit = 1;
|
||||
alloc_unit = 512;
|
||||
|
||||
while (!done) {
|
||||
bl_resp = (struct dm_balloon_response *)send_buffer;
|
||||
|
@ -684,14 +1082,19 @@ static void balloon_up(struct hv_dynmem_device *dm, struct dm_balloon *req)
|
|||
|
||||
|
||||
num_pages -= num_ballooned;
|
||||
num_ballooned = alloc_balloon_pages(dm, num_pages,
|
||||
num_ballooned = alloc_balloon_pages(&dm_device, num_pages,
|
||||
bl_resp, alloc_unit,
|
||||
&alloc_error);
|
||||
|
||||
if ((alloc_error) && (alloc_unit != 1)) {
|
||||
alloc_unit = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((alloc_error) || (num_ballooned == num_pages)) {
|
||||
bl_resp->more_pages = 0;
|
||||
done = true;
|
||||
dm->state = DM_INITIALIZED;
|
||||
dm_device.state = DM_INITIALIZED;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -719,7 +1122,7 @@ static void balloon_up(struct hv_dynmem_device *dm, struct dm_balloon *req)
|
|||
pr_info("Balloon response failed\n");
|
||||
|
||||
for (i = 0; i < bl_resp->range_count; i++)
|
||||
free_balloon_pages(dm,
|
||||
free_balloon_pages(&dm_device,
|
||||
&bl_resp->range_array[i]);
|
||||
|
||||
done = true;
|
||||
|
@ -761,7 +1164,6 @@ static int dm_thread_func(void *dm_dev)
|
|||
{
|
||||
struct hv_dynmem_device *dm = dm_dev;
|
||||
int t;
|
||||
unsigned long scan_start;
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
t = wait_for_completion_timeout(&dm_device.config_event, 1*HZ);
|
||||
|
@ -773,22 +1175,6 @@ static int dm_thread_func(void *dm_dev)
|
|||
if (t == 0)
|
||||
post_status(dm);
|
||||
|
||||
scan_start = jiffies;
|
||||
switch (dm->state) {
|
||||
case DM_BALLOON_UP:
|
||||
balloon_up(dm, (struct dm_balloon *)recv_buffer);
|
||||
break;
|
||||
|
||||
case DM_HOT_ADD:
|
||||
hot_add_req(dm, (struct dm_hot_add *)recv_buffer);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!time_in_range(jiffies, scan_start, scan_start + HZ))
|
||||
post_status(dm);
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -861,6 +1247,10 @@ static void balloon_onchannelcallback(void *context)
|
|||
struct dm_message *dm_msg;
|
||||
struct dm_header *dm_hdr;
|
||||
struct hv_dynmem_device *dm = hv_get_drvdata(dev);
|
||||
struct dm_balloon *bal_msg;
|
||||
struct dm_hot_add *ha_msg;
|
||||
union dm_mem_page_range *ha_pg_range;
|
||||
union dm_mem_page_range *ha_region;
|
||||
|
||||
memset(recv_buffer, 0, sizeof(recv_buffer));
|
||||
vmbus_recvpacket(dev->channel, recv_buffer,
|
||||
|
@ -882,8 +1272,12 @@ static void balloon_onchannelcallback(void *context)
|
|||
break;
|
||||
|
||||
case DM_BALLOON_REQUEST:
|
||||
if (dm->state == DM_BALLOON_UP)
|
||||
pr_warn("Currently ballooning\n");
|
||||
bal_msg = (struct dm_balloon *)recv_buffer;
|
||||
dm->state = DM_BALLOON_UP;
|
||||
complete(&dm->config_event);
|
||||
dm_device.balloon_wrk.num_pages = bal_msg->num_pages;
|
||||
schedule_work(&dm_device.balloon_wrk.wrk);
|
||||
break;
|
||||
|
||||
case DM_UNBALLOON_REQUEST:
|
||||
|
@ -893,8 +1287,31 @@ static void balloon_onchannelcallback(void *context)
|
|||
break;
|
||||
|
||||
case DM_MEM_HOT_ADD_REQUEST:
|
||||
if (dm->state == DM_HOT_ADD)
|
||||
pr_warn("Currently hot-adding\n");
|
||||
dm->state = DM_HOT_ADD;
|
||||
complete(&dm->config_event);
|
||||
ha_msg = (struct dm_hot_add *)recv_buffer;
|
||||
if (ha_msg->hdr.size == sizeof(struct dm_hot_add)) {
|
||||
/*
|
||||
* This is a normal hot-add request specifying
|
||||
* hot-add memory.
|
||||
*/
|
||||
ha_pg_range = &ha_msg->range;
|
||||
dm->ha_wrk.ha_page_range = *ha_pg_range;
|
||||
dm->ha_wrk.ha_region_range.page_range = 0;
|
||||
} else {
|
||||
/*
|
||||
* Host is specifying that we first hot-add
|
||||
* a region and then partially populate this
|
||||
* region.
|
||||
*/
|
||||
dm->host_specified_ha_region = true;
|
||||
ha_pg_range = &ha_msg->range;
|
||||
ha_region = &ha_pg_range[1];
|
||||
dm->ha_wrk.ha_page_range = *ha_pg_range;
|
||||
dm->ha_wrk.ha_region_range = *ha_region;
|
||||
}
|
||||
schedule_work(&dm_device.ha_wrk.wrk);
|
||||
break;
|
||||
|
||||
case DM_INFO_MESSAGE:
|
||||
|
@ -937,6 +1354,10 @@ static int balloon_probe(struct hv_device *dev,
|
|||
dm_device.next_version = DYNMEM_PROTOCOL_VERSION_WIN7;
|
||||
init_completion(&dm_device.host_event);
|
||||
init_completion(&dm_device.config_event);
|
||||
INIT_LIST_HEAD(&dm_device.ha_region_list);
|
||||
INIT_WORK(&dm_device.balloon_wrk.wrk, balloon_up);
|
||||
INIT_WORK(&dm_device.ha_wrk.wrk, hot_add_req);
|
||||
dm_device.host_specified_ha_region = false;
|
||||
|
||||
dm_device.thread =
|
||||
kthread_run(dm_thread_func, &dm_device, "hv_balloon");
|
||||
|
@ -945,6 +1366,10 @@ static int balloon_probe(struct hv_device *dev,
|
|||
goto probe_error1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MEMORY_HOTPLUG
|
||||
set_online_page_callback(&hv_online_page);
|
||||
#endif
|
||||
|
||||
hv_set_drvdata(dev, &dm_device);
|
||||
/*
|
||||
* Initiate the hand shake with the host and negotiate
|
||||
|
@ -962,8 +1387,7 @@ static int balloon_probe(struct hv_device *dev,
|
|||
ret = vmbus_sendpacket(dev->channel, &version_req,
|
||||
sizeof(struct dm_version_request),
|
||||
(unsigned long)NULL,
|
||||
VM_PKT_DATA_INBAND,
|
||||
VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
|
||||
VM_PKT_DATA_INBAND, 0);
|
||||
if (ret)
|
||||
goto probe_error2;
|
||||
|
||||
|
@ -990,14 +1414,14 @@ static int balloon_probe(struct hv_device *dev,
|
|||
cap_msg.hdr.trans_id = atomic_inc_return(&trans_id);
|
||||
|
||||
cap_msg.caps.cap_bits.balloon = 1;
|
||||
/*
|
||||
* While we currently don't support hot-add,
|
||||
* we still advertise this capability since the
|
||||
* host requires that guests partcipating in the
|
||||
* dynamic memory protocol support hot add.
|
||||
*/
|
||||
cap_msg.caps.cap_bits.hot_add = 1;
|
||||
|
||||
/*
|
||||
* Specify our alignment requirements as it relates
|
||||
* memory hot-add. Specify 128MB alignment.
|
||||
*/
|
||||
cap_msg.caps.cap_bits.hot_add_alignment = 7;
|
||||
|
||||
/*
|
||||
* Currently the host does not use these
|
||||
* values and we set them to what is done in the
|
||||
|
@ -1009,8 +1433,7 @@ static int balloon_probe(struct hv_device *dev,
|
|||
ret = vmbus_sendpacket(dev->channel, &cap_msg,
|
||||
sizeof(struct dm_capabilities),
|
||||
(unsigned long)NULL,
|
||||
VM_PKT_DATA_INBAND,
|
||||
VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
|
||||
VM_PKT_DATA_INBAND, 0);
|
||||
if (ret)
|
||||
goto probe_error2;
|
||||
|
||||
|
@ -1034,6 +1457,9 @@ static int balloon_probe(struct hv_device *dev,
|
|||
return 0;
|
||||
|
||||
probe_error2:
|
||||
#ifdef CONFIG_MEMORY_HOTPLUG
|
||||
restore_online_page_callback(&hv_online_page);
|
||||
#endif
|
||||
kthread_stop(dm_device.thread);
|
||||
|
||||
probe_error1:
|
||||
|
@ -1046,13 +1472,26 @@ probe_error0:
|
|||
static int balloon_remove(struct hv_device *dev)
|
||||
{
|
||||
struct hv_dynmem_device *dm = hv_get_drvdata(dev);
|
||||
struct list_head *cur, *tmp;
|
||||
struct hv_hotadd_state *has;
|
||||
|
||||
if (dm->num_pages_ballooned != 0)
|
||||
pr_warn("Ballooned pages: %d\n", dm->num_pages_ballooned);
|
||||
|
||||
cancel_work_sync(&dm->balloon_wrk.wrk);
|
||||
cancel_work_sync(&dm->ha_wrk.wrk);
|
||||
|
||||
vmbus_close(dev->channel);
|
||||
kthread_stop(dm->thread);
|
||||
kfree(send_buffer);
|
||||
#ifdef CONFIG_MEMORY_HOTPLUG
|
||||
restore_online_page_callback(&hv_online_page);
|
||||
#endif
|
||||
list_for_each_safe(cur, tmp, &dm->ha_region_list) {
|
||||
has = list_entry(cur, struct hv_hotadd_state, list);
|
||||
list_del(&has->list);
|
||||
kfree(has);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1079,14 +1518,7 @@ static int __init init_balloon_drv(void)
|
|||
return vmbus_driver_register(&balloon_drv);
|
||||
}
|
||||
|
||||
static void exit_balloon_drv(void)
|
||||
{
|
||||
|
||||
vmbus_driver_unregister(&balloon_drv);
|
||||
}
|
||||
|
||||
module_init(init_balloon_drv);
|
||||
module_exit(exit_balloon_drv);
|
||||
|
||||
MODULE_DESCRIPTION("Hyper-V Balloon");
|
||||
MODULE_VERSION(HV_DRV_VERSION);
|
||||
|
|
287
drivers/hv/hv_snapshot.c
Normal file
287
drivers/hv/hv_snapshot.c
Normal file
|
@ -0,0 +1,287 @@
|
|||
/*
|
||||
* An implementation of host initiated guest snapshot.
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2013, Microsoft, Inc.
|
||||
* Author : K. Y. Srinivasan <kys@microsoft.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
|
||||
* NON INFRINGEMENT. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
*/
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/net.h>
|
||||
#include <linux/nls.h>
|
||||
#include <linux/connector.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/hyperv.h>
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Global state maintained for transaction that is being processed.
|
||||
* Note that only one transaction can be active at any point in time.
|
||||
*
|
||||
* This state is set when we receive a request from the host; we
|
||||
* cleanup this state when the transaction is completed - when we respond
|
||||
* to the host with the key value.
|
||||
*/
|
||||
|
||||
static struct {
|
||||
bool active; /* transaction status - active or not */
|
||||
int recv_len; /* number of bytes received. */
|
||||
struct vmbus_channel *recv_channel; /* chn we got the request */
|
||||
u64 recv_req_id; /* request ID. */
|
||||
struct hv_vss_msg *msg; /* current message */
|
||||
} vss_transaction;
|
||||
|
||||
|
||||
static void vss_respond_to_host(int error);
|
||||
|
||||
static struct cb_id vss_id = { CN_VSS_IDX, CN_VSS_VAL };
|
||||
static const char vss_name[] = "vss_kernel_module";
|
||||
static __u8 *recv_buffer;
|
||||
|
||||
static void vss_send_op(struct work_struct *dummy);
|
||||
static DECLARE_WORK(vss_send_op_work, vss_send_op);
|
||||
|
||||
/*
|
||||
* Callback when data is received from user mode.
|
||||
*/
|
||||
|
||||
static void
|
||||
vss_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
|
||||
{
|
||||
struct hv_vss_msg *vss_msg;
|
||||
|
||||
vss_msg = (struct hv_vss_msg *)msg->data;
|
||||
|
||||
if (vss_msg->vss_hdr.operation == VSS_OP_REGISTER) {
|
||||
pr_info("VSS daemon registered\n");
|
||||
vss_transaction.active = false;
|
||||
if (vss_transaction.recv_channel != NULL)
|
||||
hv_vss_onchannelcallback(vss_transaction.recv_channel);
|
||||
return;
|
||||
|
||||
}
|
||||
vss_respond_to_host(vss_msg->error);
|
||||
}
|
||||
|
||||
|
||||
static void vss_send_op(struct work_struct *dummy)
|
||||
{
|
||||
int op = vss_transaction.msg->vss_hdr.operation;
|
||||
struct cn_msg *msg;
|
||||
struct hv_vss_msg *vss_msg;
|
||||
|
||||
msg = kzalloc(sizeof(*msg) + sizeof(*vss_msg), GFP_ATOMIC);
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
vss_msg = (struct hv_vss_msg *)msg->data;
|
||||
|
||||
msg->id.idx = CN_VSS_IDX;
|
||||
msg->id.val = CN_VSS_VAL;
|
||||
|
||||
vss_msg->vss_hdr.operation = op;
|
||||
msg->len = sizeof(struct hv_vss_msg);
|
||||
|
||||
cn_netlink_send(msg, 0, GFP_ATOMIC);
|
||||
kfree(msg);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a response back to the host.
|
||||
*/
|
||||
|
||||
static void
|
||||
vss_respond_to_host(int error)
|
||||
{
|
||||
struct icmsg_hdr *icmsghdrp;
|
||||
u32 buf_len;
|
||||
struct vmbus_channel *channel;
|
||||
u64 req_id;
|
||||
|
||||
/*
|
||||
* If a transaction is not active; log and return.
|
||||
*/
|
||||
|
||||
if (!vss_transaction.active) {
|
||||
/*
|
||||
* This is a spurious call!
|
||||
*/
|
||||
pr_warn("VSS: Transaction not active\n");
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Copy the global state for completing the transaction. Note that
|
||||
* only one transaction can be active at a time.
|
||||
*/
|
||||
|
||||
buf_len = vss_transaction.recv_len;
|
||||
channel = vss_transaction.recv_channel;
|
||||
req_id = vss_transaction.recv_req_id;
|
||||
vss_transaction.active = false;
|
||||
|
||||
icmsghdrp = (struct icmsg_hdr *)
|
||||
&recv_buffer[sizeof(struct vmbuspipe_hdr)];
|
||||
|
||||
if (channel->onchannel_callback == NULL)
|
||||
/*
|
||||
* We have raced with util driver being unloaded;
|
||||
* silently return.
|
||||
*/
|
||||
return;
|
||||
|
||||
icmsghdrp->status = error;
|
||||
|
||||
icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
|
||||
|
||||
vmbus_sendpacket(channel, recv_buffer, buf_len, req_id,
|
||||
VM_PKT_DATA_INBAND, 0);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* This callback is invoked when we get a VSS message from the host.
|
||||
* The host ensures that only one VSS transaction can be active at a time.
|
||||
*/
|
||||
|
||||
void hv_vss_onchannelcallback(void *context)
|
||||
{
|
||||
struct vmbus_channel *channel = context;
|
||||
u32 recvlen;
|
||||
u64 requestid;
|
||||
struct hv_vss_msg *vss_msg;
|
||||
|
||||
|
||||
struct icmsg_hdr *icmsghdrp;
|
||||
struct icmsg_negotiate *negop = NULL;
|
||||
|
||||
if (vss_transaction.active) {
|
||||
/*
|
||||
* We will defer processing this callback once
|
||||
* the current transaction is complete.
|
||||
*/
|
||||
vss_transaction.recv_channel = channel;
|
||||
return;
|
||||
}
|
||||
|
||||
vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 2, &recvlen,
|
||||
&requestid);
|
||||
|
||||
if (recvlen > 0) {
|
||||
icmsghdrp = (struct icmsg_hdr *)&recv_buffer[
|
||||
sizeof(struct vmbuspipe_hdr)];
|
||||
|
||||
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
|
||||
vmbus_prep_negotiate_resp(icmsghdrp, negop,
|
||||
recv_buffer, MAX_SRV_VER, MAX_SRV_VER);
|
||||
/*
|
||||
* We currently negotiate the highest number the
|
||||
* host has presented. If this version is not
|
||||
* atleast 5.0, reject.
|
||||
*/
|
||||
negop = (struct icmsg_negotiate *)&recv_buffer[
|
||||
sizeof(struct vmbuspipe_hdr) +
|
||||
sizeof(struct icmsg_hdr)];
|
||||
|
||||
if (negop->icversion_data[1].major < 5)
|
||||
negop->icframe_vercnt = 0;
|
||||
} else {
|
||||
vss_msg = (struct hv_vss_msg *)&recv_buffer[
|
||||
sizeof(struct vmbuspipe_hdr) +
|
||||
sizeof(struct icmsg_hdr)];
|
||||
|
||||
/*
|
||||
* Stash away this global state for completing the
|
||||
* transaction; note transactions are serialized.
|
||||
*/
|
||||
|
||||
vss_transaction.recv_len = recvlen;
|
||||
vss_transaction.recv_channel = channel;
|
||||
vss_transaction.recv_req_id = requestid;
|
||||
vss_transaction.active = true;
|
||||
vss_transaction.msg = (struct hv_vss_msg *)vss_msg;
|
||||
|
||||
switch (vss_msg->vss_hdr.operation) {
|
||||
/*
|
||||
* Initiate a "freeze/thaw"
|
||||
* operation in the guest.
|
||||
* We respond to the host once
|
||||
* the operation is complete.
|
||||
*
|
||||
* We send the message to the
|
||||
* user space daemon and the
|
||||
* operation is performed in
|
||||
* the daemon.
|
||||
*/
|
||||
case VSS_OP_FREEZE:
|
||||
case VSS_OP_THAW:
|
||||
schedule_work(&vss_send_op_work);
|
||||
return;
|
||||
|
||||
case VSS_OP_HOT_BACKUP:
|
||||
vss_msg->vss_cf.flags =
|
||||
VSS_HBU_NO_AUTO_RECOVERY;
|
||||
vss_respond_to_host(0);
|
||||
return;
|
||||
|
||||
case VSS_OP_GET_DM_INFO:
|
||||
vss_msg->dm_info.flags = 0;
|
||||
vss_respond_to_host(0);
|
||||
return;
|
||||
|
||||
default:
|
||||
vss_respond_to_host(0);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
|
||||
| ICMSGHDRFLAG_RESPONSE;
|
||||
|
||||
vmbus_sendpacket(channel, recv_buffer,
|
||||
recvlen, requestid,
|
||||
VM_PKT_DATA_INBAND, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
hv_vss_init(struct hv_util_service *srv)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = cn_add_callback(&vss_id, vss_name, vss_cn_callback);
|
||||
if (err)
|
||||
return err;
|
||||
recv_buffer = srv->recv_buffer;
|
||||
|
||||
/*
|
||||
* When this driver loads, the user level daemon that
|
||||
* processes the host requests may not yet be running.
|
||||
* Defer processing channel callbacks until the daemon
|
||||
* has registered.
|
||||
*/
|
||||
vss_transaction.active = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hv_vss_deinit(void)
|
||||
{
|
||||
cn_del_callback(&vss_id);
|
||||
cancel_work_sync(&vss_send_op_work);
|
||||
}
|
|
@ -49,6 +49,12 @@ static struct hv_util_service util_kvp = {
|
|||
.util_deinit = hv_kvp_deinit,
|
||||
};
|
||||
|
||||
static struct hv_util_service util_vss = {
|
||||
.util_cb = hv_vss_onchannelcallback,
|
||||
.util_init = hv_vss_init,
|
||||
.util_deinit = hv_vss_deinit,
|
||||
};
|
||||
|
||||
static void perform_shutdown(struct work_struct *dummy)
|
||||
{
|
||||
orderly_poweroff(true);
|
||||
|
@ -339,6 +345,10 @@ static const struct hv_vmbus_device_id id_table[] = {
|
|||
{ HV_KVP_GUID,
|
||||
.driver_data = (unsigned long)&util_kvp
|
||||
},
|
||||
/* VSS GUID */
|
||||
{ HV_VSS_GUID,
|
||||
.driver_data = (unsigned long)&util_vss
|
||||
},
|
||||
{ },
|
||||
};
|
||||
|
||||
|
|
|
@ -71,6 +71,7 @@ u32 hv_end_read(struct hv_ring_buffer_info *rbi)
|
|||
|
||||
static bool hv_need_to_signal(u32 old_write, struct hv_ring_buffer_info *rbi)
|
||||
{
|
||||
smp_mb();
|
||||
if (rbi->ring_buffer->interrupt_mask)
|
||||
return false;
|
||||
|
||||
|
|
|
@ -480,6 +480,7 @@ static void tpci200_release_device(struct ipack_device *dev)
|
|||
|
||||
static int tpci200_create_device(struct tpci200_board *tpci200, int i)
|
||||
{
|
||||
int ret;
|
||||
enum ipack_space space;
|
||||
struct ipack_device *dev =
|
||||
kzalloc(sizeof(struct ipack_device), GFP_KERNEL);
|
||||
|
@ -495,7 +496,18 @@ static int tpci200_create_device(struct tpci200_board *tpci200, int i)
|
|||
+ tpci200_space_interval[space] * i;
|
||||
dev->region[space].size = tpci200_space_size[space];
|
||||
}
|
||||
return ipack_device_register(dev);
|
||||
|
||||
ret = ipack_device_init(dev);
|
||||
if (ret < 0) {
|
||||
ipack_put_device(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ipack_device_add(dev);
|
||||
if (ret < 0)
|
||||
ipack_put_device(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tpci200_pci_probe(struct pci_dev *pdev,
|
||||
|
|
|
@ -227,7 +227,7 @@ static int ipack_unregister_bus_member(struct device *dev, void *data)
|
|||
struct ipack_bus_device *bus = data;
|
||||
|
||||
if (idev->bus == bus)
|
||||
ipack_device_unregister(idev);
|
||||
ipack_device_del(idev);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -419,7 +419,7 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
int ipack_device_register(struct ipack_device *dev)
|
||||
int ipack_device_init(struct ipack_device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
@ -428,6 +428,7 @@ int ipack_device_register(struct ipack_device *dev)
|
|||
dev->dev.parent = dev->bus->parent;
|
||||
dev_set_name(&dev->dev,
|
||||
"ipack-dev.%u.%u", dev->bus->bus_nr, dev->slot);
|
||||
device_initialize(&dev->dev);
|
||||
|
||||
if (dev->bus->ops->set_clockrate(dev, 8))
|
||||
dev_warn(&dev->dev, "failed to switch to 8 MHz operation for reading of device ID.\n");
|
||||
|
@ -447,19 +448,34 @@ int ipack_device_register(struct ipack_device *dev)
|
|||
dev_err(&dev->dev, "failed to switch to 32 MHz operation.\n");
|
||||
}
|
||||
|
||||
ret = device_register(&dev->dev);
|
||||
if (ret < 0)
|
||||
kfree(dev->id);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipack_device_register);
|
||||
EXPORT_SYMBOL_GPL(ipack_device_init);
|
||||
|
||||
void ipack_device_unregister(struct ipack_device *dev)
|
||||
int ipack_device_add(struct ipack_device *dev)
|
||||
{
|
||||
device_unregister(&dev->dev);
|
||||
return device_add(&dev->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipack_device_unregister);
|
||||
EXPORT_SYMBOL_GPL(ipack_device_add);
|
||||
|
||||
void ipack_device_del(struct ipack_device *dev)
|
||||
{
|
||||
device_del(&dev->dev);
|
||||
ipack_put_device(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipack_device_del);
|
||||
|
||||
void ipack_get_device(struct ipack_device *dev)
|
||||
{
|
||||
get_device(&dev->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipack_get_device);
|
||||
|
||||
void ipack_put_device(struct ipack_device *dev)
|
||||
{
|
||||
put_device(&dev->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipack_put_device);
|
||||
|
||||
static int __init ipack_init(void)
|
||||
{
|
||||
|
|
|
@ -163,16 +163,4 @@ static struct pcmcia_driver avmcs_driver = {
|
|||
.remove = avmcs_detach,
|
||||
.id_table = avmcs_ids,
|
||||
};
|
||||
|
||||
static int __init avmcs_init(void)
|
||||
{
|
||||
return pcmcia_register_driver(&avmcs_driver);
|
||||
}
|
||||
|
||||
static void __exit avmcs_exit(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&avmcs_driver);
|
||||
}
|
||||
|
||||
module_init(avmcs_init);
|
||||
module_exit(avmcs_exit);
|
||||
module_pcmcia_driver(avmcs_driver);
|
||||
|
|
|
@ -159,16 +159,4 @@ static struct pcmcia_driver avma1cs_driver = {
|
|||
.remove = avma1cs_detach,
|
||||
.id_table = avma1cs_ids,
|
||||
};
|
||||
|
||||
static int __init init_avma1_cs(void)
|
||||
{
|
||||
return pcmcia_register_driver(&avma1cs_driver);
|
||||
}
|
||||
|
||||
static void __exit exit_avma1_cs(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&avma1cs_driver);
|
||||
}
|
||||
|
||||
module_init(init_avma1_cs);
|
||||
module_exit(exit_avma1_cs);
|
||||
module_pcmcia_driver(avma1cs_driver);
|
||||
|
|
|
@ -215,16 +215,4 @@ static struct pcmcia_driver elsa_cs_driver = {
|
|||
.suspend = elsa_suspend,
|
||||
.resume = elsa_resume,
|
||||
};
|
||||
|
||||
static int __init init_elsa_cs(void)
|
||||
{
|
||||
return pcmcia_register_driver(&elsa_cs_driver);
|
||||
}
|
||||
|
||||
static void __exit exit_elsa_cs(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&elsa_cs_driver);
|
||||
}
|
||||
|
||||
module_init(init_elsa_cs);
|
||||
module_exit(exit_elsa_cs);
|
||||
module_pcmcia_driver(elsa_cs_driver);
|
||||
|
|
|
@ -206,16 +206,4 @@ static struct pcmcia_driver sedlbauer_driver = {
|
|||
.suspend = sedlbauer_suspend,
|
||||
.resume = sedlbauer_resume,
|
||||
};
|
||||
|
||||
static int __init init_sedlbauer_cs(void)
|
||||
{
|
||||
return pcmcia_register_driver(&sedlbauer_driver);
|
||||
}
|
||||
|
||||
static void __exit exit_sedlbauer_cs(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&sedlbauer_driver);
|
||||
}
|
||||
|
||||
module_init(init_sedlbauer_cs);
|
||||
module_exit(exit_sedlbauer_cs);
|
||||
module_pcmcia_driver(sedlbauer_driver);
|
||||
|
|
|
@ -197,16 +197,4 @@ static struct pcmcia_driver teles_cs_driver = {
|
|||
.suspend = teles_suspend,
|
||||
.resume = teles_resume,
|
||||
};
|
||||
|
||||
static int __init init_teles_cs(void)
|
||||
{
|
||||
return pcmcia_register_driver(&teles_cs_driver);
|
||||
}
|
||||
|
||||
static void __exit exit_teles_cs(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&teles_cs_driver);
|
||||
}
|
||||
|
||||
module_init(init_teles_cs);
|
||||
module_exit(exit_teles_cs);
|
||||
module_pcmcia_driver(teles_cs_driver);
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/pm.h>
|
||||
#include <memory/jedec_ddr.h>
|
||||
#include "emif.h"
|
||||
#include "of_memory.h"
|
||||
|
@ -256,6 +257,41 @@ static void set_lpmode(struct emif_data *emif, u8 lpmode)
|
|||
u32 temp;
|
||||
void __iomem *base = emif->base;
|
||||
|
||||
/*
|
||||
* Workaround for errata i743 - LPDDR2 Power-Down State is Not
|
||||
* Efficient
|
||||
*
|
||||
* i743 DESCRIPTION:
|
||||
* The EMIF supports power-down state for low power. The EMIF
|
||||
* automatically puts the SDRAM into power-down after the memory is
|
||||
* not accessed for a defined number of cycles and the
|
||||
* EMIF_PWR_MGMT_CTRL[10:8] REG_LP_MODE bit field is set to 0x4.
|
||||
* As the EMIF supports automatic output impedance calibration, a ZQ
|
||||
* calibration long command is issued every time it exits active
|
||||
* power-down and precharge power-down modes. The EMIF waits and
|
||||
* blocks any other command during this calibration.
|
||||
* The EMIF does not allow selective disabling of ZQ calibration upon
|
||||
* exit of power-down mode. Due to very short periods of power-down
|
||||
* cycles, ZQ calibration overhead creates bandwidth issues and
|
||||
* increases overall system power consumption. On the other hand,
|
||||
* issuing ZQ calibration long commands when exiting self-refresh is
|
||||
* still required.
|
||||
*
|
||||
* WORKAROUND
|
||||
* Because there is no power consumption benefit of the power-down due
|
||||
* to the calibration and there is a performance risk, the guideline
|
||||
* is to not allow power-down state and, therefore, to not have set
|
||||
* the EMIF_PWR_MGMT_CTRL[10:8] REG_LP_MODE bit field to 0x4.
|
||||
*/
|
||||
if ((emif->plat_data->ip_rev == EMIF_4D) &&
|
||||
(EMIF_LP_MODE_PWR_DN == lpmode)) {
|
||||
WARN_ONCE(1,
|
||||
"REG_LP_MODE = LP_MODE_PWR_DN(4) is prohibited by"
|
||||
"erratum i743 switch to LP_MODE_SELF_REFRESH(2)\n");
|
||||
/* rollback LP_MODE to Self-refresh mode */
|
||||
lpmode = EMIF_LP_MODE_SELF_REFRESH;
|
||||
}
|
||||
|
||||
temp = readl(base + EMIF_POWER_MANAGEMENT_CONTROL);
|
||||
temp &= ~LP_MODE_MASK;
|
||||
temp |= (lpmode << LP_MODE_SHIFT);
|
||||
|
@ -715,6 +751,8 @@ static u32 get_pwr_mgmt_ctrl(u32 freq, struct emif_data *emif, u32 ip_rev)
|
|||
u32 timeout_perf = EMIF_LP_MODE_TIMEOUT_PERFORMANCE;
|
||||
u32 timeout_pwr = EMIF_LP_MODE_TIMEOUT_POWER;
|
||||
u32 freq_threshold = EMIF_LP_MODE_FREQ_THRESHOLD;
|
||||
u32 mask;
|
||||
u8 shift;
|
||||
|
||||
struct emif_custom_configs *cust_cfgs = emif->plat_data->custom_configs;
|
||||
|
||||
|
@ -728,37 +766,59 @@ static u32 get_pwr_mgmt_ctrl(u32 freq, struct emif_data *emif, u32 ip_rev)
|
|||
/* Timeout based on DDR frequency */
|
||||
timeout = freq >= freq_threshold ? timeout_perf : timeout_pwr;
|
||||
|
||||
/* The value to be set in register is "log2(timeout) - 3" */
|
||||
/*
|
||||
* The value to be set in register is "log2(timeout) - 3"
|
||||
* if timeout < 16 load 0 in register
|
||||
* if timeout is not a power of 2, round to next highest power of 2
|
||||
*/
|
||||
if (timeout < 16) {
|
||||
timeout = 0;
|
||||
} else {
|
||||
timeout = __fls(timeout) - 3;
|
||||
if (timeout & (timeout - 1))
|
||||
timeout++;
|
||||
timeout <<= 1;
|
||||
timeout = __fls(timeout) - 3;
|
||||
}
|
||||
|
||||
switch (lpmode) {
|
||||
case EMIF_LP_MODE_CLOCK_STOP:
|
||||
pwr_mgmt_ctrl = (timeout << CS_TIM_SHIFT) |
|
||||
SR_TIM_MASK | PD_TIM_MASK;
|
||||
shift = CS_TIM_SHIFT;
|
||||
mask = CS_TIM_MASK;
|
||||
break;
|
||||
case EMIF_LP_MODE_SELF_REFRESH:
|
||||
/* Workaround for errata i735 */
|
||||
if (timeout < 6)
|
||||
timeout = 6;
|
||||
|
||||
pwr_mgmt_ctrl = (timeout << SR_TIM_SHIFT) |
|
||||
CS_TIM_MASK | PD_TIM_MASK;
|
||||
shift = SR_TIM_SHIFT;
|
||||
mask = SR_TIM_MASK;
|
||||
break;
|
||||
case EMIF_LP_MODE_PWR_DN:
|
||||
pwr_mgmt_ctrl = (timeout << PD_TIM_SHIFT) |
|
||||
CS_TIM_MASK | SR_TIM_MASK;
|
||||
shift = PD_TIM_SHIFT;
|
||||
mask = PD_TIM_MASK;
|
||||
break;
|
||||
case EMIF_LP_MODE_DISABLE:
|
||||
default:
|
||||
pwr_mgmt_ctrl = CS_TIM_MASK |
|
||||
PD_TIM_MASK | SR_TIM_MASK;
|
||||
mask = 0;
|
||||
shift = 0;
|
||||
break;
|
||||
}
|
||||
/* Round to maximum in case of overflow, BUT warn! */
|
||||
if (lpmode != EMIF_LP_MODE_DISABLE && timeout > mask >> shift) {
|
||||
pr_err("TIMEOUT Overflow - lpmode=%d perf=%d pwr=%d freq=%d\n",
|
||||
lpmode,
|
||||
timeout_perf,
|
||||
timeout_pwr,
|
||||
freq_threshold);
|
||||
WARN(1, "timeout=0x%02x greater than 0x%02x. Using max\n",
|
||||
timeout, mask >> shift);
|
||||
timeout = mask >> shift;
|
||||
}
|
||||
|
||||
/* Setup required timing */
|
||||
pwr_mgmt_ctrl = (timeout << shift) & mask;
|
||||
/* setup a default mask for rest of the modes */
|
||||
pwr_mgmt_ctrl |= (SR_TIM_MASK | CS_TIM_MASK | PD_TIM_MASK) &
|
||||
~mask;
|
||||
|
||||
/* No CS_TIM in EMIF_4D5 */
|
||||
if (ip_rev == EMIF_4D5)
|
||||
|
@ -815,6 +875,8 @@ static void setup_registers(struct emif_data *emif, struct emif_regs *regs)
|
|||
|
||||
writel(regs->sdram_tim2_shdw, base + EMIF_SDRAM_TIMING_2_SHDW);
|
||||
writel(regs->phy_ctrl_1_shdw, base + EMIF_DDR_PHY_CTRL_1_SHDW);
|
||||
writel(regs->pwr_mgmt_ctrl_shdw,
|
||||
base + EMIF_POWER_MANAGEMENT_CTRL_SHDW);
|
||||
|
||||
/* Settings specific for EMIF4D5 */
|
||||
if (emif->plat_data->ip_rev != EMIF_4D5)
|
||||
|
@ -892,6 +954,7 @@ static irqreturn_t handle_temp_alert(void __iomem *base, struct emif_data *emif)
|
|||
{
|
||||
u32 old_temp_level;
|
||||
irqreturn_t ret = IRQ_HANDLED;
|
||||
struct emif_custom_configs *custom_configs;
|
||||
|
||||
spin_lock_irqsave(&emif_lock, irq_state);
|
||||
old_temp_level = emif->temperature_level;
|
||||
|
@ -904,6 +967,29 @@ static irqreturn_t handle_temp_alert(void __iomem *base, struct emif_data *emif)
|
|||
goto out;
|
||||
}
|
||||
|
||||
custom_configs = emif->plat_data->custom_configs;
|
||||
|
||||
/*
|
||||
* IF we detect higher than "nominal rating" from DDR sensor
|
||||
* on an unsupported DDR part, shutdown system
|
||||
*/
|
||||
if (custom_configs && !(custom_configs->mask &
|
||||
EMIF_CUSTOM_CONFIG_EXTENDED_TEMP_PART)) {
|
||||
if (emif->temperature_level >= SDRAM_TEMP_HIGH_DERATE_REFRESH) {
|
||||
dev_err(emif->dev,
|
||||
"%s:NOT Extended temperature capable memory."
|
||||
"Converting MR4=0x%02x as shutdown event\n",
|
||||
__func__, emif->temperature_level);
|
||||
/*
|
||||
* Temperature far too high - do kernel_power_off()
|
||||
* from thread context
|
||||
*/
|
||||
emif->temperature_level = SDRAM_TEMP_VERY_HIGH_SHUTDOWN;
|
||||
ret = IRQ_WAKE_THREAD;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (emif->temperature_level < old_temp_level ||
|
||||
emif->temperature_level == SDRAM_TEMP_VERY_HIGH_SHUTDOWN) {
|
||||
/*
|
||||
|
@ -965,7 +1051,14 @@ static irqreturn_t emif_threaded_isr(int irq, void *dev_id)
|
|||
|
||||
if (emif->temperature_level == SDRAM_TEMP_VERY_HIGH_SHUTDOWN) {
|
||||
dev_emerg(emif->dev, "SDRAM temperature exceeds operating limit.. Needs shut down!!!\n");
|
||||
kernel_power_off();
|
||||
|
||||
/* If we have Power OFF ability, use it, else try restarting */
|
||||
if (pm_power_off) {
|
||||
kernel_power_off();
|
||||
} else {
|
||||
WARN(1, "FIXME: NO pm_power_off!!! trying restart\n");
|
||||
kernel_restart("SDRAM Over-temp Emergency restart");
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
@ -1170,7 +1263,7 @@ static void __init_or_module of_get_custom_configs(struct device_node *np_emif,
|
|||
{
|
||||
struct emif_custom_configs *cust_cfgs = NULL;
|
||||
int len;
|
||||
const int *lpmode, *poll_intvl;
|
||||
const __be32 *lpmode, *poll_intvl;
|
||||
|
||||
lpmode = of_get_property(np_emif, "low-power-mode", &len);
|
||||
poll_intvl = of_get_property(np_emif, "temp-alert-poll-interval", &len);
|
||||
|
@ -1184,7 +1277,7 @@ static void __init_or_module of_get_custom_configs(struct device_node *np_emif,
|
|||
|
||||
if (lpmode) {
|
||||
cust_cfgs->mask |= EMIF_CUSTOM_CONFIG_LPMODE;
|
||||
cust_cfgs->lpmode = *lpmode;
|
||||
cust_cfgs->lpmode = be32_to_cpup(lpmode);
|
||||
of_property_read_u32(np_emif,
|
||||
"low-power-mode-timeout-performance",
|
||||
&cust_cfgs->lpmode_timeout_performance);
|
||||
|
@ -1199,9 +1292,13 @@ static void __init_or_module of_get_custom_configs(struct device_node *np_emif,
|
|||
if (poll_intvl) {
|
||||
cust_cfgs->mask |=
|
||||
EMIF_CUSTOM_CONFIG_TEMP_ALERT_POLL_INTERVAL;
|
||||
cust_cfgs->temp_alert_poll_interval_ms = *poll_intvl;
|
||||
cust_cfgs->temp_alert_poll_interval_ms =
|
||||
be32_to_cpup(poll_intvl);
|
||||
}
|
||||
|
||||
if (of_find_property(np_emif, "extended-temp-part", &len))
|
||||
cust_cfgs->mask |= EMIF_CUSTOM_CONFIG_EXTENDED_TEMP_PART;
|
||||
|
||||
if (!is_custom_config_valid(cust_cfgs, emif->dev)) {
|
||||
devm_kfree(emif->dev, cust_cfgs);
|
||||
return;
|
||||
|
@ -1407,7 +1504,7 @@ static struct emif_data *__init_or_module get_device_details(
|
|||
if (pd->timings) {
|
||||
temp = devm_kzalloc(dev, size, GFP_KERNEL);
|
||||
if (temp) {
|
||||
memcpy(temp, pd->timings, sizeof(*pd->timings));
|
||||
memcpy(temp, pd->timings, size);
|
||||
pd->timings = temp;
|
||||
} else {
|
||||
dev_warn(dev, "%s:%d: allocation error\n", __func__,
|
||||
|
@ -1841,18 +1938,8 @@ static struct platform_driver emif_driver = {
|
|||
},
|
||||
};
|
||||
|
||||
static int __init_or_module emif_register(void)
|
||||
{
|
||||
return platform_driver_probe(&emif_driver, emif_probe);
|
||||
}
|
||||
module_platform_driver_probe(emif_driver, emif_probe);
|
||||
|
||||
static void __exit emif_unregister(void)
|
||||
{
|
||||
platform_driver_unregister(&emif_driver);
|
||||
}
|
||||
|
||||
module_init(emif_register);
|
||||
module_exit(emif_unregister);
|
||||
MODULE_DESCRIPTION("TI EMIF SDRAM Controller Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:emif");
|
||||
|
|
|
@ -268,6 +268,7 @@ static const u32 tegra30_mc_ctx[] = {
|
|||
MC_INTMASK,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int tegra30_mc_suspend(struct device *dev)
|
||||
{
|
||||
int i;
|
||||
|
@ -291,6 +292,7 @@ static int tegra30_mc_resume(struct device *dev)
|
|||
mc_readl(mc, MC_TIMING_CONTROL);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static UNIVERSAL_DEV_PM_OPS(tegra30_mc_pm,
|
||||
tegra30_mc_suspend,
|
||||
|
|
|
@ -991,7 +991,7 @@ config MFD_PM8XXX
|
|||
|
||||
config MFD_PM8921_CORE
|
||||
tristate "Qualcomm PM8921 PMIC chip"
|
||||
depends on MSM_SSBI
|
||||
depends on SSBI && BROKEN
|
||||
select MFD_CORE
|
||||
select MFD_PM8XXX
|
||||
help
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/msm_ssbi.h>
|
||||
#include <linux/ssbi.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/pm8xxx/pm8921.h>
|
||||
#include <linux/mfd/pm8xxx/core.h>
|
||||
|
@ -35,7 +35,7 @@ static int pm8921_readb(const struct device *dev, u16 addr, u8 *val)
|
|||
const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
|
||||
const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
|
||||
|
||||
return msm_ssbi_read(pmic->dev->parent, addr, val, 1);
|
||||
return ssbi_read(pmic->dev->parent, addr, val, 1);
|
||||
}
|
||||
|
||||
static int pm8921_writeb(const struct device *dev, u16 addr, u8 val)
|
||||
|
@ -43,7 +43,7 @@ static int pm8921_writeb(const struct device *dev, u16 addr, u8 val)
|
|||
const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
|
||||
const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
|
||||
|
||||
return msm_ssbi_write(pmic->dev->parent, addr, &val, 1);
|
||||
return ssbi_write(pmic->dev->parent, addr, &val, 1);
|
||||
}
|
||||
|
||||
static int pm8921_read_buf(const struct device *dev, u16 addr, u8 *buf,
|
||||
|
@ -52,7 +52,7 @@ static int pm8921_read_buf(const struct device *dev, u16 addr, u8 *buf,
|
|||
const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
|
||||
const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
|
||||
|
||||
return msm_ssbi_read(pmic->dev->parent, addr, buf, cnt);
|
||||
return ssbi_read(pmic->dev->parent, addr, buf, cnt);
|
||||
}
|
||||
|
||||
static int pm8921_write_buf(const struct device *dev, u16 addr, u8 *buf,
|
||||
|
@ -61,7 +61,7 @@ static int pm8921_write_buf(const struct device *dev, u16 addr, u8 *buf,
|
|||
const struct pm8xxx_drvdata *pm8921_drvdata = dev_get_drvdata(dev);
|
||||
const struct pm8921 *pmic = pm8921_drvdata->pm_chip_data;
|
||||
|
||||
return msm_ssbi_write(pmic->dev->parent, addr, buf, cnt);
|
||||
return ssbi_write(pmic->dev->parent, addr, buf, cnt);
|
||||
}
|
||||
|
||||
static int pm8921_read_irq_stat(const struct device *dev, int irq)
|
||||
|
@ -124,7 +124,7 @@ static int pm8921_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
/* Read PMIC chip revision */
|
||||
rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV, &val, sizeof(val));
|
||||
rc = ssbi_read(pdev->dev.parent, REG_HWREV, &val, sizeof(val));
|
||||
if (rc) {
|
||||
pr_err("Failed to read hw rev reg %d:rc=%d\n", REG_HWREV, rc);
|
||||
goto err_read_rev;
|
||||
|
@ -133,7 +133,7 @@ static int pm8921_probe(struct platform_device *pdev)
|
|||
rev = val;
|
||||
|
||||
/* Read PMIC chip revision 2 */
|
||||
rc = msm_ssbi_read(pdev->dev.parent, REG_HWREV_2, &val, sizeof(val));
|
||||
rc = ssbi_read(pdev->dev.parent, REG_HWREV_2, &val, sizeof(val));
|
||||
if (rc) {
|
||||
pr_err("Failed to read hw rev 2 reg %d:rc=%d\n",
|
||||
REG_HWREV_2, rc);
|
||||
|
|
|
@ -331,6 +331,10 @@ static const struct reg_default wm5102_reg_default[] = {
|
|||
{ 0x000002A3, 0x1102 }, /* R675 - Mic Detect 1 */
|
||||
{ 0x000002A4, 0x009F }, /* R676 - Mic Detect 2 */
|
||||
{ 0x000002A5, 0x0000 }, /* R677 - Mic Detect 3 */
|
||||
{ 0x000002A6, 0x3737 }, /* R678 - Mic Detect Level 1 */
|
||||
{ 0x000002A7, 0x372C }, /* R679 - Mic Detect Level 2 */
|
||||
{ 0x000002A8, 0x1422 }, /* R680 - Mic Detect Level 3 */
|
||||
{ 0x000002A9, 0x030A }, /* R681 - Mic Detect Level 4 */
|
||||
{ 0x000002C3, 0x0000 }, /* R707 - Mic noise mix control 1 */
|
||||
{ 0x000002CB, 0x0000 }, /* R715 - Isolation control */
|
||||
{ 0x000002D3, 0x0000 }, /* R723 - Jack detect analogue */
|
||||
|
@ -1090,6 +1094,10 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg)
|
|||
case ARIZONA_MIC_DETECT_1:
|
||||
case ARIZONA_MIC_DETECT_2:
|
||||
case ARIZONA_MIC_DETECT_3:
|
||||
case ARIZONA_MIC_DETECT_LEVEL_1:
|
||||
case ARIZONA_MIC_DETECT_LEVEL_2:
|
||||
case ARIZONA_MIC_DETECT_LEVEL_3:
|
||||
case ARIZONA_MIC_DETECT_LEVEL_4:
|
||||
case ARIZONA_MIC_NOISE_MIX_CONTROL_1:
|
||||
case ARIZONA_ISOLATION_CONTROL:
|
||||
case ARIZONA_JACK_DETECT_ANALOGUE:
|
||||
|
|
|
@ -93,6 +93,14 @@ config ATMEL_TCB_CLKSRC_BLOCK
|
|||
TC can be used for other purposes, such as PWM generation and
|
||||
interval timing.
|
||||
|
||||
config DUMMY_IRQ
|
||||
tristate "Dummy IRQ handler"
|
||||
default n
|
||||
---help---
|
||||
This module accepts a single 'irq' parameter, which it should register for.
|
||||
The sole purpose of this module is to help with debugging of systems on
|
||||
which spurious IRQs would happen on disabled IRQ vector.
|
||||
|
||||
config IBM_ASM
|
||||
tristate "Device driver for IBM RSA service processor"
|
||||
depends on X86 && PCI && INPUT
|
||||
|
@ -398,7 +406,7 @@ config DS1682
|
|||
|
||||
config SPEAR13XX_PCIE_GADGET
|
||||
bool "PCIe gadget support for SPEAr13XX platform"
|
||||
depends on ARCH_SPEAR13XX
|
||||
depends on ARCH_SPEAR13XX && BROKEN
|
||||
default n
|
||||
help
|
||||
This option enables gadget support for PCIe controller. If
|
||||
|
|
|
@ -13,6 +13,7 @@ obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o
|
|||
obj-$(CONFIG_BMP085) += bmp085.o
|
||||
obj-$(CONFIG_BMP085_I2C) += bmp085-i2c.o
|
||||
obj-$(CONFIG_BMP085_SPI) += bmp085-spi.o
|
||||
obj-$(CONFIG_DUMMY_IRQ) += dummy-irq.o
|
||||
obj-$(CONFIG_ICS932S401) += ics932s401.o
|
||||
obj-$(CONFIG_LKDTM) += lkdtm.o
|
||||
obj-$(CONFIG_TIFM_CORE) += tifm_core.o
|
||||
|
@ -49,6 +50,5 @@ obj-y += carma/
|
|||
obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
|
||||
obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/
|
||||
obj-$(CONFIG_INTEL_MEI) += mei/
|
||||
obj-$(CONFIG_MAX8997_MUIC) += max8997-muic.o
|
||||
obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/
|
||||
obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o
|
||||
|
|
|
@ -272,19 +272,8 @@ static int apds9802als_remove(struct i2c_client *client)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int apds9802als_suspend(struct i2c_client *client, pm_message_t mesg)
|
||||
{
|
||||
als_set_power_state(client, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apds9802als_resume(struct i2c_client *client)
|
||||
{
|
||||
als_set_default_config(client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apds9802als_runtime_suspend(struct device *dev)
|
||||
static int apds9802als_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
|
@ -292,7 +281,7 @@ static int apds9802als_runtime_suspend(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int apds9802als_runtime_resume(struct device *dev)
|
||||
static int apds9802als_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
|
@ -300,16 +289,12 @@ static int apds9802als_runtime_resume(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops apds9802als_pm_ops = {
|
||||
.runtime_suspend = apds9802als_runtime_suspend,
|
||||
.runtime_resume = apds9802als_runtime_resume,
|
||||
};
|
||||
static UNIVERSAL_DEV_PM_OPS(apds9802als_pm_ops, apds9802als_suspend,
|
||||
apds9802als_resume, NULL);
|
||||
|
||||
#define APDS9802ALS_PM_OPS (&apds9802als_pm_ops)
|
||||
|
||||
#else /* CONFIG_PM */
|
||||
#define apds9802als_suspend NULL
|
||||
#define apds9802als_resume NULL
|
||||
#define APDS9802ALS_PM_OPS NULL
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
|
@ -327,8 +312,6 @@ static struct i2c_driver apds9802als_driver = {
|
|||
},
|
||||
.probe = apds9802als_probe,
|
||||
.remove = apds9802als_remove,
|
||||
.suspend = apds9802als_suspend,
|
||||
.resume = apds9802als_resume,
|
||||
.id_table = apds9802als_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -700,9 +700,6 @@ static ssize_t apds990x_lux_calib_store(struct device *dev,
|
|||
if (strict_strtoul(buf, 0, &value))
|
||||
return -EINVAL;
|
||||
|
||||
if (chip->lux_calib > APDS_RANGE)
|
||||
return -EINVAL;
|
||||
|
||||
chip->lux_calib = value;
|
||||
|
||||
return len;
|
||||
|
@ -1204,7 +1201,7 @@ static int apds990x_remove(struct i2c_client *client)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int apds990x_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
|
||||
|
@ -1227,10 +1224,6 @@ static int apds990x_resume(struct device *dev)
|
|||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define apds990x_suspend NULL
|
||||
#define apds990x_resume NULL
|
||||
#define apds990x_shutdown NULL
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
|
|
|
@ -378,18 +378,7 @@ static struct platform_driver charlcd_driver = {
|
|||
.remove = __exit_p(charlcd_remove),
|
||||
};
|
||||
|
||||
static int __init charlcd_init(void)
|
||||
{
|
||||
return platform_driver_probe(&charlcd_driver, charlcd_probe);
|
||||
}
|
||||
|
||||
static void __exit charlcd_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&charlcd_driver);
|
||||
}
|
||||
|
||||
module_init(charlcd_init);
|
||||
module_exit(charlcd_exit);
|
||||
module_platform_driver_probe(charlcd_driver, charlcd_probe);
|
||||
|
||||
MODULE_AUTHOR("Linus Walleij <triad@df.lth.se>");
|
||||
MODULE_DESCRIPTION("ARM Character LCD Driver");
|
||||
|
|
|
@ -393,17 +393,7 @@ static struct platform_driver atmel_pwm_driver = {
|
|||
*/
|
||||
};
|
||||
|
||||
static int __init pwm_init(void)
|
||||
{
|
||||
return platform_driver_probe(&atmel_pwm_driver, pwm_probe);
|
||||
}
|
||||
module_init(pwm_init);
|
||||
|
||||
static void __exit pwm_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&atmel_pwm_driver);
|
||||
}
|
||||
module_exit(pwm_exit);
|
||||
module_platform_driver_probe(atmel_pwm_driver, pwm_probe);
|
||||
|
||||
MODULE_DESCRIPTION("Driver for AT32/AT91 PWM module");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -1310,7 +1310,7 @@ static int bh1770_remove(struct i2c_client *client)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int bh1770_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = container_of(dev, struct i2c_client, dev);
|
||||
|
@ -1346,11 +1346,6 @@ static int bh1770_resume(struct device *dev)
|
|||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else
|
||||
#define bh1770_suspend NULL
|
||||
#define bh1770_shutdown NULL
|
||||
#define bh1770_resume NULL
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
|
|
|
@ -196,7 +196,7 @@ static int bh1780_remove(struct i2c_client *client)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int bh1780_suspend(struct device *dev)
|
||||
{
|
||||
struct bh1780_data *ddata;
|
||||
|
@ -235,11 +235,9 @@ static int bh1780_resume(struct device *dev)
|
|||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(bh1780_pm, bh1780_suspend, bh1780_resume);
|
||||
#define BH1780_PMOPS (&bh1780_pm)
|
||||
#else
|
||||
#define BH1780_PMOPS NULL
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct i2c_device_id bh1780_id[] = {
|
||||
{ "bh1780", 0 },
|
||||
|
@ -252,7 +250,7 @@ static struct i2c_driver bh1780_driver = {
|
|||
.id_table = bh1780_id,
|
||||
.driver = {
|
||||
.name = "bh1780",
|
||||
.pm = BH1780_PMOPS,
|
||||
.pm = &bh1780_pm,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -24,8 +24,11 @@
|
|||
|
||||
static int mfgpt_reset_timers;
|
||||
module_param_named(mfgptfix, mfgpt_reset_timers, int, 0644);
|
||||
MODULE_PARM_DESC(mfgptfix, "Reset the MFGPT timers during init; "
|
||||
"required by some broken BIOSes (ie, TinyBIOS < 0.99).");
|
||||
MODULE_PARM_DESC(mfgptfix, "Try to reset the MFGPT timers during init; "
|
||||
"required by some broken BIOSes (ie, TinyBIOS < 0.99) or kexec "
|
||||
"(1 = reset the MFGPT using an undocumented bit, "
|
||||
"2 = perform a soft reset by unconfiguring all timers); "
|
||||
"use what works best for you.");
|
||||
|
||||
struct cs5535_mfgpt_timer {
|
||||
struct cs5535_mfgpt_chip *chip;
|
||||
|
@ -255,6 +258,28 @@ static void reset_all_timers(void)
|
|||
wrmsr(MSR_MFGPT_SETUP, val, dummy);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is another sledgehammer to reset all MFGPT timers.
|
||||
* Instead of using the undocumented bit method it clears
|
||||
* IRQ, NMI and RESET settings.
|
||||
*/
|
||||
static void soft_reset(void)
|
||||
{
|
||||
int i;
|
||||
struct cs5535_mfgpt_timer t;
|
||||
|
||||
for (i = 0; i < MFGPT_MAX_TIMERS; i++) {
|
||||
t.nr = i;
|
||||
|
||||
cs5535_mfgpt_toggle_event(&t, MFGPT_CMP1, MFGPT_EVENT_RESET, 0);
|
||||
cs5535_mfgpt_toggle_event(&t, MFGPT_CMP2, MFGPT_EVENT_RESET, 0);
|
||||
cs5535_mfgpt_toggle_event(&t, MFGPT_CMP1, MFGPT_EVENT_NMI, 0);
|
||||
cs5535_mfgpt_toggle_event(&t, MFGPT_CMP2, MFGPT_EVENT_NMI, 0);
|
||||
cs5535_mfgpt_toggle_event(&t, MFGPT_CMP1, MFGPT_EVENT_IRQ, 0);
|
||||
cs5535_mfgpt_toggle_event(&t, MFGPT_CMP2, MFGPT_EVENT_IRQ, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether any MFGPTs are available for the kernel to use. In most
|
||||
* cases, firmware that uses AMD's VSA code will claim all timers during
|
||||
|
@ -271,15 +296,17 @@ static int scan_timers(struct cs5535_mfgpt_chip *mfgpt)
|
|||
int i;
|
||||
|
||||
/* bios workaround */
|
||||
if (mfgpt_reset_timers)
|
||||
if (mfgpt_reset_timers == 1)
|
||||
reset_all_timers();
|
||||
else if (mfgpt_reset_timers == 2)
|
||||
soft_reset();
|
||||
|
||||
/* just to be safe, protect this section w/ lock */
|
||||
spin_lock_irqsave(&mfgpt->lock, flags);
|
||||
for (i = 0; i < MFGPT_MAX_TIMERS; i++) {
|
||||
timer.nr = i;
|
||||
val = cs5535_mfgpt_read(&timer, MFGPT_REG_SETUP);
|
||||
if (!(val & MFGPT_SETUP_SETUP)) {
|
||||
if (!(val & MFGPT_SETUP_SETUP) || mfgpt_reset_timers == 2) {
|
||||
__set_bit(i, mfgpt->avail);
|
||||
timers++;
|
||||
}
|
||||
|
@ -294,6 +321,12 @@ static int cs5535_mfgpt_probe(struct platform_device *pdev)
|
|||
struct resource *res;
|
||||
int err = -EIO, t;
|
||||
|
||||
if (mfgpt_reset_timers < 0 || mfgpt_reset_timers > 2) {
|
||||
dev_err(&pdev->dev, "Bad mfgpt_reset_timers value: %i\n",
|
||||
mfgpt_reset_timers);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* There are two ways to get the MFGPT base address; one is by
|
||||
* fetching it from MSR_LBAR_MFGPT, the other is by reading the
|
||||
* PCI BAR info. The latter method is easier (especially across
|
||||
|
|
59
drivers/misc/dummy-irq.c
Normal file
59
drivers/misc/dummy-irq.c
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Dummy IRQ handler driver.
|
||||
*
|
||||
* This module only registers itself as a handler that is specified to it
|
||||
* by the 'irq' parameter.
|
||||
*
|
||||
* The sole purpose of this module is to help with debugging of systems on
|
||||
* which spurious IRQs would happen on disabled IRQ vector.
|
||||
*
|
||||
* Copyright (C) 2013 Jiri Kosina
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
static int irq;
|
||||
|
||||
static irqreturn_t dummy_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
static int count = 0;
|
||||
|
||||
if (count == 0) {
|
||||
printk(KERN_INFO "dummy-irq: interrupt occured on IRQ %d\n",
|
||||
irq);
|
||||
count++;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static int __init dummy_irq_init(void)
|
||||
{
|
||||
if (request_irq(irq, &dummy_interrupt, IRQF_SHARED, "dummy_irq", &irq)) {
|
||||
printk(KERN_ERR "dummy-irq: cannot register IRQ %d\n", irq);
|
||||
return -EIO;
|
||||
}
|
||||
printk(KERN_INFO "dummy-irq: registered for IRQ %d\n", irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit dummy_irq_exit(void)
|
||||
{
|
||||
printk(KERN_INFO "dummy-irq unloaded\n");
|
||||
free_irq(irq, &irq);
|
||||
}
|
||||
|
||||
module_init(dummy_irq_init);
|
||||
module_exit(dummy_irq_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Jiri Kosina");
|
||||
module_param(irq, uint, 0444);
|
||||
MODULE_PARM_DESC(irq, "The IRQ to register for");
|
|
@ -412,7 +412,7 @@ static int at25_probe(struct spi_device *spi)
|
|||
mutex_init(&at25->lock);
|
||||
at25->chip = chip;
|
||||
at25->spi = spi_dev_get(spi);
|
||||
dev_set_drvdata(&spi->dev, at25);
|
||||
spi_set_drvdata(spi, at25);
|
||||
at25->addrlen = addrlen;
|
||||
|
||||
/* Export the EEPROM bytes through sysfs, since that's convenient.
|
||||
|
@ -463,7 +463,7 @@ static int at25_remove(struct spi_device *spi)
|
|||
{
|
||||
struct at25_data *at25;
|
||||
|
||||
at25 = dev_get_drvdata(&spi->dev);
|
||||
at25 = spi_get_drvdata(spi);
|
||||
sysfs_remove_bin_file(&spi->dev.kobj, &at25->bin);
|
||||
kfree(at25);
|
||||
return 0;
|
||||
|
|
|
@ -363,7 +363,7 @@ static int eeprom_93xx46_probe(struct spi_device *spi)
|
|||
dev_err(&spi->dev, "can't create erase interface\n");
|
||||
}
|
||||
|
||||
dev_set_drvdata(&spi->dev, edev);
|
||||
spi_set_drvdata(spi, edev);
|
||||
return 0;
|
||||
fail:
|
||||
kfree(edev);
|
||||
|
@ -372,13 +372,13 @@ fail:
|
|||
|
||||
static int eeprom_93xx46_remove(struct spi_device *spi)
|
||||
{
|
||||
struct eeprom_93xx46_dev *edev = dev_get_drvdata(&spi->dev);
|
||||
struct eeprom_93xx46_dev *edev = spi_get_drvdata(spi);
|
||||
|
||||
if (!(edev->pdata->flags & EE_READONLY))
|
||||
device_remove_file(&spi->dev, &dev_attr_erase);
|
||||
|
||||
sysfs_remove_bin_file(&spi->dev.kobj, &edev->bin);
|
||||
dev_set_drvdata(&spi->dev, NULL);
|
||||
spi_set_drvdata(spi, NULL);
|
||||
kfree(edev);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -365,18 +365,7 @@ static struct platform_driver ep93xx_pwm_driver = {
|
|||
.remove = __exit_p(ep93xx_pwm_remove),
|
||||
};
|
||||
|
||||
static int __init ep93xx_pwm_init(void)
|
||||
{
|
||||
return platform_driver_probe(&ep93xx_pwm_driver, ep93xx_pwm_probe);
|
||||
}
|
||||
|
||||
static void __exit ep93xx_pwm_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ep93xx_pwm_driver);
|
||||
}
|
||||
|
||||
module_init(ep93xx_pwm_init);
|
||||
module_exit(ep93xx_pwm_exit);
|
||||
module_platform_driver_probe(ep93xx_pwm_driver, ep93xx_pwm_probe);
|
||||
|
||||
MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>, "
|
||||
"H Hartley Sweeten <hsweeten@visionengravers.com>");
|
||||
|
|
|
@ -474,10 +474,11 @@ static int fsa9480_remove(struct i2c_client *client)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
static int fsa9480_suspend(struct i2c_client *client, pm_message_t state)
|
||||
static int fsa9480_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct fsa9480_usbsw *usbsw = i2c_get_clientdata(client);
|
||||
struct fsa9480_platform_data *pdata = usbsw->pdata;
|
||||
|
||||
|
@ -490,8 +491,9 @@ static int fsa9480_suspend(struct i2c_client *client, pm_message_t state)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int fsa9480_resume(struct i2c_client *client)
|
||||
static int fsa9480_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct fsa9480_usbsw *usbsw = i2c_get_clientdata(client);
|
||||
int dev1, dev2;
|
||||
|
||||
|
@ -515,12 +517,14 @@ static int fsa9480_resume(struct i2c_client *client)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(fsa9480_pm_ops, fsa9480_suspend, fsa9480_resume);
|
||||
#define FSA9480_PM_OPS (&fsa9480_pm_ops)
|
||||
|
||||
#else
|
||||
|
||||
#define fsa9480_suspend NULL
|
||||
#define fsa9480_resume NULL
|
||||
#define FSA9480_PM_OPS NULL
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static const struct i2c_device_id fsa9480_id[] = {
|
||||
{"fsa9480", 0},
|
||||
|
@ -531,11 +535,10 @@ MODULE_DEVICE_TABLE(i2c, fsa9480_id);
|
|||
static struct i2c_driver fsa9480_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "fsa9480",
|
||||
.pm = FSA9480_PM_OPS,
|
||||
},
|
||||
.probe = fsa9480_probe,
|
||||
.remove = fsa9480_remove,
|
||||
.resume = fsa9480_resume,
|
||||
.suspend = fsa9480_suspend,
|
||||
.id_table = fsa9480_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -409,18 +409,20 @@ static int isl29003_remove(struct i2c_client *client)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int isl29003_suspend(struct i2c_client *client, pm_message_t mesg)
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int isl29003_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct isl29003_data *data = i2c_get_clientdata(client);
|
||||
|
||||
data->power_state_before_suspend = isl29003_get_power_state(client);
|
||||
return isl29003_set_power_state(client, 0);
|
||||
}
|
||||
|
||||
static int isl29003_resume(struct i2c_client *client)
|
||||
static int isl29003_resume(struct device *dev)
|
||||
{
|
||||
int i;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct isl29003_data *data = i2c_get_clientdata(client);
|
||||
|
||||
/* restore registers from cache */
|
||||
|
@ -432,10 +434,12 @@ static int isl29003_resume(struct i2c_client *client)
|
|||
data->power_state_before_suspend);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(isl29003_pm_ops, isl29003_suspend, isl29003_resume);
|
||||
#define ISL29003_PM_OPS (&isl29003_pm_ops)
|
||||
|
||||
#else
|
||||
#define isl29003_suspend NULL
|
||||
#define isl29003_resume NULL
|
||||
#endif /* CONFIG_PM */
|
||||
#define ISL29003_PM_OPS NULL
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static const struct i2c_device_id isl29003_id[] = {
|
||||
{ "isl29003", 0 },
|
||||
|
@ -447,9 +451,8 @@ static struct i2c_driver isl29003_driver = {
|
|||
.driver = {
|
||||
.name = ISL29003_DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = ISL29003_PM_OPS,
|
||||
},
|
||||
.suspend = isl29003_suspend,
|
||||
.resume = isl29003_resume,
|
||||
.probe = isl29003_probe,
|
||||
.remove = isl29003_remove,
|
||||
.id_table = isl29003_id,
|
||||
|
|
|
@ -69,7 +69,7 @@ static const struct ecp3_dev ecp3_dev[] = {
|
|||
static void firmware_load(const struct firmware *fw, void *context)
|
||||
{
|
||||
struct spi_device *spi = (struct spi_device *)context;
|
||||
struct fpga_data *data = dev_get_drvdata(&spi->dev);
|
||||
struct fpga_data *data = spi_get_drvdata(spi);
|
||||
u8 *buffer;
|
||||
int ret;
|
||||
u8 txbuf[8];
|
||||
|
|
|
@ -10,10 +10,9 @@ config INTEL_MEI
|
|||
<http://software.intel.com/en-us/manageability/>
|
||||
|
||||
config INTEL_MEI_ME
|
||||
bool "ME Enabled Intel Chipsets"
|
||||
depends on INTEL_MEI
|
||||
tristate "ME Enabled Intel Chipsets"
|
||||
select INTEL_MEI
|
||||
depends on X86 && PCI && WATCHDOG_CORE
|
||||
default y
|
||||
help
|
||||
MEI support for ME Enabled Intel chipsets.
|
||||
|
||||
|
|
|
@ -10,5 +10,10 @@ mei-objs += client.o
|
|||
mei-objs += main.o
|
||||
mei-objs += amthif.o
|
||||
mei-objs += wd.o
|
||||
mei-$(CONFIG_INTEL_MEI_ME) += pci-me.o
|
||||
mei-$(CONFIG_INTEL_MEI_ME) += hw-me.o
|
||||
mei-objs += bus.o
|
||||
mei-objs += nfc.o
|
||||
mei-$(CONFIG_DEBUG_FS) += debugfs.o
|
||||
|
||||
obj-$(CONFIG_INTEL_MEI_ME) += mei-me.o
|
||||
mei-me-objs := pci-me.o
|
||||
mei-me-objs += hw-me.o
|
||||
|
|
|
@ -449,7 +449,7 @@ int mei_amthif_irq_write_complete(struct mei_device *dev, s32 *slots,
|
|||
struct mei_msg_hdr mei_hdr;
|
||||
struct mei_cl *cl = cb->cl;
|
||||
size_t len = dev->iamthif_msg_buf_size - dev->iamthif_msg_buf_index;
|
||||
size_t msg_slots = mei_data2slots(len);
|
||||
u32 msg_slots = mei_data2slots(len);
|
||||
|
||||
mei_hdr.host_addr = cl->host_client_id;
|
||||
mei_hdr.me_addr = cl->me_client_id;
|
||||
|
@ -505,14 +505,15 @@ int mei_amthif_irq_write_complete(struct mei_device *dev, s32 *slots,
|
|||
* mei_amthif_irq_read_message - read routine after ISR to
|
||||
* handle the read amthif message
|
||||
*
|
||||
* @complete_list: An instance of our list structure
|
||||
* @dev: the device structure
|
||||
* @mei_hdr: header of amthif message
|
||||
* @complete_list: An instance of our list structure
|
||||
*
|
||||
* returns 0 on success, <0 on failure.
|
||||
*/
|
||||
int mei_amthif_irq_read_message(struct mei_cl_cb *complete_list,
|
||||
struct mei_device *dev, struct mei_msg_hdr *mei_hdr)
|
||||
int mei_amthif_irq_read_msg(struct mei_device *dev,
|
||||
struct mei_msg_hdr *mei_hdr,
|
||||
struct mei_cl_cb *complete_list)
|
||||
{
|
||||
struct mei_cl_cb *cb;
|
||||
unsigned char *buffer;
|
||||
|
@ -530,8 +531,7 @@ int mei_amthif_irq_read_message(struct mei_cl_cb *complete_list,
|
|||
if (!mei_hdr->msg_complete)
|
||||
return 0;
|
||||
|
||||
dev_dbg(&dev->pdev->dev,
|
||||
"amthif_message_buffer_index =%d\n",
|
||||
dev_dbg(&dev->pdev->dev, "amthif_message_buffer_index =%d\n",
|
||||
mei_hdr->length);
|
||||
|
||||
dev_dbg(&dev->pdev->dev, "completed amthif read.\n ");
|
||||
|
@ -566,12 +566,13 @@ int mei_amthif_irq_read_message(struct mei_cl_cb *complete_list,
|
|||
*/
|
||||
int mei_amthif_irq_read(struct mei_device *dev, s32 *slots)
|
||||
{
|
||||
u32 msg_slots = mei_data2slots(sizeof(struct hbm_flow_control));
|
||||
|
||||
if (((*slots) * sizeof(u32)) < (sizeof(struct mei_msg_hdr)
|
||||
+ sizeof(struct hbm_flow_control))) {
|
||||
if (*slots < msg_slots)
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
*slots -= mei_data2slots(sizeof(struct hbm_flow_control));
|
||||
|
||||
*slots -= msg_slots;
|
||||
|
||||
if (mei_hbm_cl_flow_control_req(dev, &dev->iamthif_cl)) {
|
||||
dev_dbg(&dev->pdev->dev, "iamthif flow control failed\n");
|
||||
return -EIO;
|
||||
|
|
528
drivers/misc/mei/bus.c
Normal file
528
drivers/misc/mei/bus.c
Normal file
|
@ -0,0 +1,528 @@
|
|||
/*
|
||||
* Intel Management Engine Interface (Intel MEI) Linux driver
|
||||
* Copyright (c) 2012-2013, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/mei_cl_bus.h>
|
||||
|
||||
#include "mei_dev.h"
|
||||
#include "hw-me.h"
|
||||
#include "client.h"
|
||||
|
||||
#define to_mei_cl_driver(d) container_of(d, struct mei_cl_driver, driver)
|
||||
#define to_mei_cl_device(d) container_of(d, struct mei_cl_device, dev)
|
||||
|
||||
static int mei_cl_device_match(struct device *dev, struct device_driver *drv)
|
||||
{
|
||||
struct mei_cl_device *device = to_mei_cl_device(dev);
|
||||
struct mei_cl_driver *driver = to_mei_cl_driver(drv);
|
||||
const struct mei_cl_device_id *id;
|
||||
|
||||
if (!device)
|
||||
return 0;
|
||||
|
||||
if (!driver || !driver->id_table)
|
||||
return 0;
|
||||
|
||||
id = driver->id_table;
|
||||
|
||||
while (id->name[0]) {
|
||||
if (!strcmp(dev_name(dev), id->name))
|
||||
return 1;
|
||||
|
||||
id++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mei_cl_device_probe(struct device *dev)
|
||||
{
|
||||
struct mei_cl_device *device = to_mei_cl_device(dev);
|
||||
struct mei_cl_driver *driver;
|
||||
struct mei_cl_device_id id;
|
||||
|
||||
if (!device)
|
||||
return 0;
|
||||
|
||||
driver = to_mei_cl_driver(dev->driver);
|
||||
if (!driver || !driver->probe)
|
||||
return -ENODEV;
|
||||
|
||||
dev_dbg(dev, "Device probe\n");
|
||||
|
||||
strncpy(id.name, dev_name(dev), MEI_CL_NAME_SIZE);
|
||||
|
||||
return driver->probe(device, &id);
|
||||
}
|
||||
|
||||
static int mei_cl_device_remove(struct device *dev)
|
||||
{
|
||||
struct mei_cl_device *device = to_mei_cl_device(dev);
|
||||
struct mei_cl_driver *driver;
|
||||
|
||||
if (!device || !dev->driver)
|
||||
return 0;
|
||||
|
||||
if (device->event_cb) {
|
||||
device->event_cb = NULL;
|
||||
cancel_work_sync(&device->event_work);
|
||||
}
|
||||
|
||||
driver = to_mei_cl_driver(dev->driver);
|
||||
if (!driver->remove) {
|
||||
dev->driver = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return driver->remove(device);
|
||||
}
|
||||
|
||||
static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
|
||||
char *buf)
|
||||
{
|
||||
int len;
|
||||
|
||||
len = snprintf(buf, PAGE_SIZE, "mei:%s\n", dev_name(dev));
|
||||
|
||||
return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
|
||||
}
|
||||
|
||||
static struct device_attribute mei_cl_dev_attrs[] = {
|
||||
__ATTR_RO(modalias),
|
||||
__ATTR_NULL,
|
||||
};
|
||||
|
||||
static int mei_cl_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
{
|
||||
if (add_uevent_var(env, "MODALIAS=mei:%s", dev_name(dev)))
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct bus_type mei_cl_bus_type = {
|
||||
.name = "mei",
|
||||
.dev_attrs = mei_cl_dev_attrs,
|
||||
.match = mei_cl_device_match,
|
||||
.probe = mei_cl_device_probe,
|
||||
.remove = mei_cl_device_remove,
|
||||
.uevent = mei_cl_uevent,
|
||||
};
|
||||
|
||||
static void mei_cl_dev_release(struct device *dev)
|
||||
{
|
||||
kfree(to_mei_cl_device(dev));
|
||||
}
|
||||
|
||||
static struct device_type mei_cl_device_type = {
|
||||
.release = mei_cl_dev_release,
|
||||
};
|
||||
|
||||
static struct mei_cl *mei_bus_find_mei_cl_by_uuid(struct mei_device *dev,
|
||||
uuid_le uuid)
|
||||
{
|
||||
struct mei_cl *cl, *next;
|
||||
|
||||
list_for_each_entry_safe(cl, next, &dev->device_list, device_link) {
|
||||
if (!uuid_le_cmp(uuid, cl->device_uuid))
|
||||
return cl;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
struct mei_cl_device *mei_cl_add_device(struct mei_device *dev,
|
||||
uuid_le uuid, char *name,
|
||||
struct mei_cl_ops *ops)
|
||||
{
|
||||
struct mei_cl_device *device;
|
||||
struct mei_cl *cl;
|
||||
int status;
|
||||
|
||||
cl = mei_bus_find_mei_cl_by_uuid(dev, uuid);
|
||||
if (cl == NULL)
|
||||
return NULL;
|
||||
|
||||
device = kzalloc(sizeof(struct mei_cl_device), GFP_KERNEL);
|
||||
if (!device)
|
||||
return NULL;
|
||||
|
||||
device->cl = cl;
|
||||
device->ops = ops;
|
||||
|
||||
device->dev.parent = &dev->pdev->dev;
|
||||
device->dev.bus = &mei_cl_bus_type;
|
||||
device->dev.type = &mei_cl_device_type;
|
||||
|
||||
dev_set_name(&device->dev, "%s", name);
|
||||
|
||||
status = device_register(&device->dev);
|
||||
if (status) {
|
||||
dev_err(&dev->pdev->dev, "Failed to register MEI device\n");
|
||||
kfree(device);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cl->device = device;
|
||||
|
||||
dev_dbg(&device->dev, "client %s registered\n", name);
|
||||
|
||||
return device;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mei_cl_add_device);
|
||||
|
||||
void mei_cl_remove_device(struct mei_cl_device *device)
|
||||
{
|
||||
device_unregister(&device->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mei_cl_remove_device);
|
||||
|
||||
int __mei_cl_driver_register(struct mei_cl_driver *driver, struct module *owner)
|
||||
{
|
||||
int err;
|
||||
|
||||
driver->driver.name = driver->name;
|
||||
driver->driver.owner = owner;
|
||||
driver->driver.bus = &mei_cl_bus_type;
|
||||
|
||||
err = driver_register(&driver->driver);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
pr_debug("mei: driver [%s] registered\n", driver->driver.name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__mei_cl_driver_register);
|
||||
|
||||
void mei_cl_driver_unregister(struct mei_cl_driver *driver)
|
||||
{
|
||||
driver_unregister(&driver->driver);
|
||||
|
||||
pr_debug("mei: driver [%s] unregistered\n", driver->driver.name);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mei_cl_driver_unregister);
|
||||
|
||||
static int ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
|
||||
bool blocking)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_cl_cb *cb;
|
||||
int id;
|
||||
int rets;
|
||||
|
||||
if (WARN_ON(!cl || !cl->dev))
|
||||
return -ENODEV;
|
||||
|
||||
dev = cl->dev;
|
||||
|
||||
if (cl->state != MEI_FILE_CONNECTED)
|
||||
return -ENODEV;
|
||||
|
||||
/* Check if we have an ME client device */
|
||||
id = mei_me_cl_by_id(dev, cl->me_client_id);
|
||||
if (id < 0)
|
||||
return -ENODEV;
|
||||
|
||||
if (length > dev->me_clients[id].props.max_msg_length)
|
||||
return -EINVAL;
|
||||
|
||||
cb = mei_io_cb_init(cl, NULL);
|
||||
if (!cb)
|
||||
return -ENOMEM;
|
||||
|
||||
rets = mei_io_cb_alloc_req_buf(cb, length);
|
||||
if (rets < 0) {
|
||||
mei_io_cb_free(cb);
|
||||
return rets;
|
||||
}
|
||||
|
||||
memcpy(cb->request_buffer.data, buf, length);
|
||||
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
rets = mei_cl_write(cl, cb, blocking);
|
||||
|
||||
mutex_unlock(&dev->device_lock);
|
||||
if (rets < 0)
|
||||
mei_io_cb_free(cb);
|
||||
|
||||
return rets;
|
||||
}
|
||||
|
||||
int __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_cl_cb *cb;
|
||||
size_t r_length;
|
||||
int err;
|
||||
|
||||
if (WARN_ON(!cl || !cl->dev))
|
||||
return -ENODEV;
|
||||
|
||||
dev = cl->dev;
|
||||
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
if (!cl->read_cb) {
|
||||
err = mei_cl_read_start(cl, length);
|
||||
if (err < 0) {
|
||||
mutex_unlock(&dev->device_lock);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (cl->reading_state != MEI_READ_COMPLETE &&
|
||||
!waitqueue_active(&cl->rx_wait)) {
|
||||
mutex_unlock(&dev->device_lock);
|
||||
|
||||
if (wait_event_interruptible(cl->rx_wait,
|
||||
(MEI_READ_COMPLETE == cl->reading_state))) {
|
||||
if (signal_pending(current))
|
||||
return -EINTR;
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
|
||||
mutex_lock(&dev->device_lock);
|
||||
}
|
||||
|
||||
cb = cl->read_cb;
|
||||
|
||||
if (cl->reading_state != MEI_READ_COMPLETE) {
|
||||
r_length = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
r_length = min_t(size_t, length, cb->buf_idx);
|
||||
|
||||
memcpy(buf, cb->response_buffer.data, r_length);
|
||||
|
||||
mei_io_cb_free(cb);
|
||||
cl->reading_state = MEI_IDLE;
|
||||
cl->read_cb = NULL;
|
||||
|
||||
out:
|
||||
mutex_unlock(&dev->device_lock);
|
||||
|
||||
return r_length;
|
||||
}
|
||||
|
||||
inline int __mei_cl_async_send(struct mei_cl *cl, u8 *buf, size_t length)
|
||||
{
|
||||
return ___mei_cl_send(cl, buf, length, 0);
|
||||
}
|
||||
|
||||
inline int __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length)
|
||||
{
|
||||
return ___mei_cl_send(cl, buf, length, 1);
|
||||
}
|
||||
|
||||
int mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length)
|
||||
{
|
||||
struct mei_cl *cl = device->cl;
|
||||
|
||||
if (cl == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
if (device->ops && device->ops->send)
|
||||
return device->ops->send(device, buf, length);
|
||||
|
||||
return __mei_cl_send(cl, buf, length);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mei_cl_send);
|
||||
|
||||
int mei_cl_recv(struct mei_cl_device *device, u8 *buf, size_t length)
|
||||
{
|
||||
struct mei_cl *cl = device->cl;
|
||||
|
||||
if (cl == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
if (device->ops && device->ops->recv)
|
||||
return device->ops->recv(device, buf, length);
|
||||
|
||||
return __mei_cl_recv(cl, buf, length);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mei_cl_recv);
|
||||
|
||||
static void mei_bus_event_work(struct work_struct *work)
|
||||
{
|
||||
struct mei_cl_device *device;
|
||||
|
||||
device = container_of(work, struct mei_cl_device, event_work);
|
||||
|
||||
if (device->event_cb)
|
||||
device->event_cb(device, device->events, device->event_context);
|
||||
|
||||
device->events = 0;
|
||||
|
||||
/* Prepare for the next read */
|
||||
mei_cl_read_start(device->cl, 0);
|
||||
}
|
||||
|
||||
int mei_cl_register_event_cb(struct mei_cl_device *device,
|
||||
mei_cl_event_cb_t event_cb, void *context)
|
||||
{
|
||||
if (device->event_cb)
|
||||
return -EALREADY;
|
||||
|
||||
device->events = 0;
|
||||
device->event_cb = event_cb;
|
||||
device->event_context = context;
|
||||
INIT_WORK(&device->event_work, mei_bus_event_work);
|
||||
|
||||
mei_cl_read_start(device->cl, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mei_cl_register_event_cb);
|
||||
|
||||
void *mei_cl_get_drvdata(const struct mei_cl_device *device)
|
||||
{
|
||||
return dev_get_drvdata(&device->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mei_cl_get_drvdata);
|
||||
|
||||
void mei_cl_set_drvdata(struct mei_cl_device *device, void *data)
|
||||
{
|
||||
dev_set_drvdata(&device->dev, data);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mei_cl_set_drvdata);
|
||||
|
||||
int mei_cl_enable_device(struct mei_cl_device *device)
|
||||
{
|
||||
int err;
|
||||
struct mei_device *dev;
|
||||
struct mei_cl *cl = device->cl;
|
||||
|
||||
if (cl == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
dev = cl->dev;
|
||||
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
cl->state = MEI_FILE_CONNECTING;
|
||||
|
||||
err = mei_cl_connect(cl, NULL);
|
||||
if (err < 0) {
|
||||
mutex_unlock(&dev->device_lock);
|
||||
dev_err(&dev->pdev->dev, "Could not connect to the ME client");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
mutex_unlock(&dev->device_lock);
|
||||
|
||||
if (device->event_cb && !cl->read_cb)
|
||||
mei_cl_read_start(device->cl, 0);
|
||||
|
||||
if (!device->ops || !device->ops->enable)
|
||||
return 0;
|
||||
|
||||
return device->ops->enable(device);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mei_cl_enable_device);
|
||||
|
||||
int mei_cl_disable_device(struct mei_cl_device *device)
|
||||
{
|
||||
int err;
|
||||
struct mei_device *dev;
|
||||
struct mei_cl *cl = device->cl;
|
||||
|
||||
if (cl == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
dev = cl->dev;
|
||||
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
if (cl->state != MEI_FILE_CONNECTED) {
|
||||
mutex_unlock(&dev->device_lock);
|
||||
dev_err(&dev->pdev->dev, "Already disconnected");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
cl->state = MEI_FILE_DISCONNECTING;
|
||||
|
||||
err = mei_cl_disconnect(cl);
|
||||
if (err < 0) {
|
||||
mutex_unlock(&dev->device_lock);
|
||||
dev_err(&dev->pdev->dev,
|
||||
"Could not disconnect from the ME client");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Flush queues and remove any pending read */
|
||||
mei_cl_flush_queues(cl);
|
||||
|
||||
if (cl->read_cb) {
|
||||
struct mei_cl_cb *cb = NULL;
|
||||
|
||||
cb = mei_cl_find_read_cb(cl);
|
||||
/* Remove entry from read list */
|
||||
if (cb)
|
||||
list_del(&cb->list);
|
||||
|
||||
cb = cl->read_cb;
|
||||
cl->read_cb = NULL;
|
||||
|
||||
if (cb) {
|
||||
mei_io_cb_free(cb);
|
||||
cb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&dev->device_lock);
|
||||
|
||||
if (!device->ops || !device->ops->disable)
|
||||
return 0;
|
||||
|
||||
return device->ops->disable(device);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mei_cl_disable_device);
|
||||
|
||||
void mei_cl_bus_rx_event(struct mei_cl *cl)
|
||||
{
|
||||
struct mei_cl_device *device = cl->device;
|
||||
|
||||
if (!device || !device->event_cb)
|
||||
return;
|
||||
|
||||
set_bit(MEI_CL_EVENT_RX, &device->events);
|
||||
|
||||
schedule_work(&device->event_work);
|
||||
}
|
||||
|
||||
int __init mei_cl_bus_init(void)
|
||||
{
|
||||
return bus_register(&mei_cl_bus_type);
|
||||
}
|
||||
|
||||
void __exit mei_cl_bus_exit(void)
|
||||
{
|
||||
bus_unregister(&mei_cl_bus_type);
|
||||
}
|
|
@ -216,6 +216,7 @@ void mei_cl_init(struct mei_cl *cl, struct mei_device *dev)
|
|||
init_waitqueue_head(&cl->rx_wait);
|
||||
init_waitqueue_head(&cl->tx_wait);
|
||||
INIT_LIST_HEAD(&cl->link);
|
||||
INIT_LIST_HEAD(&cl->device_link);
|
||||
cl->reading_state = MEI_IDLE;
|
||||
cl->writing_state = MEI_IDLE;
|
||||
cl->dev = dev;
|
||||
|
@ -357,6 +358,9 @@ void mei_host_client_init(struct work_struct *work)
|
|||
mei_amthif_host_init(dev);
|
||||
else if (!uuid_le_cmp(client_props->protocol_name, mei_wd_guid))
|
||||
mei_wd_host_init(dev);
|
||||
else if (!uuid_le_cmp(client_props->protocol_name, mei_nfc_guid))
|
||||
mei_nfc_host_init(dev);
|
||||
|
||||
}
|
||||
|
||||
dev->dev_state = MEI_DEV_ENABLED;
|
||||
|
@ -620,7 +624,7 @@ int mei_cl_flow_ctrl_reduce(struct mei_cl *cl)
|
|||
*
|
||||
* returns 0 on success, <0 on failure.
|
||||
*/
|
||||
int mei_cl_read_start(struct mei_cl *cl)
|
||||
int mei_cl_read_start(struct mei_cl *cl, size_t length)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_cl_cb *cb;
|
||||
|
@ -653,8 +657,9 @@ int mei_cl_read_start(struct mei_cl *cl)
|
|||
if (!cb)
|
||||
return -ENOMEM;
|
||||
|
||||
rets = mei_io_cb_alloc_resp_buf(cb,
|
||||
dev->me_clients[i].props.max_msg_length);
|
||||
/* always allocate at least client max message */
|
||||
length = max_t(size_t, length, dev->me_clients[i].props.max_msg_length);
|
||||
rets = mei_io_cb_alloc_resp_buf(cb, length);
|
||||
if (rets)
|
||||
goto err;
|
||||
|
||||
|
@ -676,6 +681,111 @@ err:
|
|||
return rets;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_cl_write - submit a write cb to mei device
|
||||
assumes device_lock is locked
|
||||
*
|
||||
* @cl: host client
|
||||
* @cl: write callback with filled data
|
||||
*
|
||||
* returns numbe of bytes sent on success, <0 on failure.
|
||||
*/
|
||||
int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_msg_data *buf;
|
||||
struct mei_msg_hdr mei_hdr;
|
||||
int rets;
|
||||
|
||||
|
||||
if (WARN_ON(!cl || !cl->dev))
|
||||
return -ENODEV;
|
||||
|
||||
if (WARN_ON(!cb))
|
||||
return -EINVAL;
|
||||
|
||||
dev = cl->dev;
|
||||
|
||||
|
||||
buf = &cb->request_buffer;
|
||||
|
||||
dev_dbg(&dev->pdev->dev, "mei_cl_write %d\n", buf->size);
|
||||
|
||||
|
||||
cb->fop_type = MEI_FOP_WRITE;
|
||||
|
||||
rets = mei_cl_flow_ctrl_creds(cl);
|
||||
if (rets < 0)
|
||||
goto err;
|
||||
|
||||
/* Host buffer is not ready, we queue the request */
|
||||
if (rets == 0 || !dev->hbuf_is_ready) {
|
||||
cb->buf_idx = 0;
|
||||
/* unseting complete will enqueue the cb for write */
|
||||
mei_hdr.msg_complete = 0;
|
||||
cl->writing_state = MEI_WRITING;
|
||||
rets = buf->size;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev->hbuf_is_ready = false;
|
||||
|
||||
/* Check for a maximum length */
|
||||
if (buf->size > mei_hbuf_max_len(dev)) {
|
||||
mei_hdr.length = mei_hbuf_max_len(dev);
|
||||
mei_hdr.msg_complete = 0;
|
||||
} else {
|
||||
mei_hdr.length = buf->size;
|
||||
mei_hdr.msg_complete = 1;
|
||||
}
|
||||
|
||||
mei_hdr.host_addr = cl->host_client_id;
|
||||
mei_hdr.me_addr = cl->me_client_id;
|
||||
mei_hdr.reserved = 0;
|
||||
|
||||
dev_dbg(&dev->pdev->dev, "write " MEI_HDR_FMT "\n",
|
||||
MEI_HDR_PRM(&mei_hdr));
|
||||
|
||||
|
||||
if (mei_write_message(dev, &mei_hdr, buf->data)) {
|
||||
rets = -EIO;
|
||||
goto err;
|
||||
}
|
||||
|
||||
cl->writing_state = MEI_WRITING;
|
||||
cb->buf_idx = mei_hdr.length;
|
||||
|
||||
rets = buf->size;
|
||||
out:
|
||||
if (mei_hdr.msg_complete) {
|
||||
if (mei_cl_flow_ctrl_reduce(cl)) {
|
||||
rets = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
list_add_tail(&cb->list, &dev->write_waiting_list.list);
|
||||
} else {
|
||||
list_add_tail(&cb->list, &dev->write_list.list);
|
||||
}
|
||||
|
||||
|
||||
if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) {
|
||||
|
||||
mutex_unlock(&dev->device_lock);
|
||||
if (wait_event_interruptible(cl->tx_wait,
|
||||
cl->writing_state == MEI_WRITE_COMPLETE)) {
|
||||
if (signal_pending(current))
|
||||
rets = -EINTR;
|
||||
else
|
||||
rets = -ERESTARTSYS;
|
||||
}
|
||||
mutex_lock(&dev->device_lock);
|
||||
}
|
||||
err:
|
||||
return rets;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* mei_cl_all_disconnect - disconnect forcefully all connected clients
|
||||
*
|
||||
|
|
|
@ -86,17 +86,16 @@ int mei_cl_flow_ctrl_reduce(struct mei_cl *cl);
|
|||
*/
|
||||
bool mei_cl_is_other_connecting(struct mei_cl *cl);
|
||||
int mei_cl_disconnect(struct mei_cl *cl);
|
||||
|
||||
int mei_cl_read_start(struct mei_cl *cl);
|
||||
|
||||
int mei_cl_connect(struct mei_cl *cl, struct file *file);
|
||||
int mei_cl_read_start(struct mei_cl *cl, size_t length);
|
||||
int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking);
|
||||
|
||||
void mei_host_client_init(struct work_struct *work);
|
||||
|
||||
|
||||
|
||||
void mei_cl_all_disconnect(struct mei_device *dev);
|
||||
void mei_cl_all_read_wakeup(struct mei_device *dev);
|
||||
void mei_cl_all_write_clear(struct mei_device *dev);
|
||||
|
||||
|
||||
#endif /* _MEI_CLIENT_H_ */
|
||||
|
|
143
drivers/misc/mei/debugfs.c
Normal file
143
drivers/misc/mei/debugfs.c
Normal file
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
*
|
||||
* Intel Management Engine Interface (Intel MEI) Linux driver
|
||||
* Copyright (c) 2012-2013, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include <linux/mei.h>
|
||||
|
||||
#include "mei_dev.h"
|
||||
#include "hw.h"
|
||||
|
||||
static ssize_t mei_dbgfs_read_meclients(struct file *fp, char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
{
|
||||
struct mei_device *dev = fp->private_data;
|
||||
struct mei_me_client *cl;
|
||||
const size_t bufsz = 1024;
|
||||
char *buf = kzalloc(bufsz, GFP_KERNEL);
|
||||
int i;
|
||||
int pos = 0;
|
||||
int ret;
|
||||
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
pos += scnprintf(buf + pos, bufsz - pos,
|
||||
" |id|addr| UUID |con|msg len|\n");
|
||||
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
/* if the driver is not enabled the list won't b consitent */
|
||||
if (dev->dev_state != MEI_DEV_ENABLED)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < dev->me_clients_num; i++) {
|
||||
cl = &dev->me_clients[i];
|
||||
|
||||
/* skip me clients that cannot be connected */
|
||||
if (cl->props.max_number_of_connections == 0)
|
||||
continue;
|
||||
|
||||
pos += scnprintf(buf + pos, bufsz - pos,
|
||||
"%2d|%2d|%4d|%pUl|%3d|%7d|\n",
|
||||
i, cl->client_id,
|
||||
cl->props.fixed_address,
|
||||
&cl->props.protocol_name,
|
||||
cl->props.max_number_of_connections,
|
||||
cl->props.max_msg_length);
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&dev->device_lock);
|
||||
ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos);
|
||||
kfree(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations mei_dbgfs_fops_meclients = {
|
||||
.open = simple_open,
|
||||
.read = mei_dbgfs_read_meclients,
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
{
|
||||
struct mei_device *dev = fp->private_data;
|
||||
const size_t bufsz = 1024;
|
||||
char *buf = kzalloc(bufsz, GFP_KERNEL);
|
||||
int pos = 0;
|
||||
int ret;
|
||||
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
pos += scnprintf(buf + pos, bufsz - pos, "%s\n",
|
||||
mei_dev_state_str(dev->dev_state));
|
||||
ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos);
|
||||
kfree(buf);
|
||||
return ret;
|
||||
}
|
||||
static const struct file_operations mei_dbgfs_fops_devstate = {
|
||||
.open = simple_open,
|
||||
.read = mei_dbgfs_read_devstate,
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
/**
|
||||
* mei_dbgfs_deregister - Remove the debugfs files and directories
|
||||
* @mei - pointer to mei device private dat
|
||||
*/
|
||||
void mei_dbgfs_deregister(struct mei_device *dev)
|
||||
{
|
||||
if (!dev->dbgfs_dir)
|
||||
return;
|
||||
debugfs_remove_recursive(dev->dbgfs_dir);
|
||||
dev->dbgfs_dir = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the debugfs files
|
||||
*
|
||||
*/
|
||||
int mei_dbgfs_register(struct mei_device *dev, const char *name)
|
||||
{
|
||||
struct dentry *dir, *f;
|
||||
dir = debugfs_create_dir(name, NULL);
|
||||
if (!dir)
|
||||
return -ENOMEM;
|
||||
|
||||
f = debugfs_create_file("meclients", S_IRUSR, dir,
|
||||
dev, &mei_dbgfs_fops_meclients);
|
||||
if (!f) {
|
||||
dev_err(&dev->pdev->dev, "meclients: registration failed\n");
|
||||
goto err;
|
||||
}
|
||||
f = debugfs_create_file("devstate", S_IRUSR, dir,
|
||||
dev, &mei_dbgfs_fops_devstate);
|
||||
if (!f) {
|
||||
dev_err(&dev->pdev->dev, "devstate: registration failed\n");
|
||||
goto err;
|
||||
}
|
||||
dev->dbgfs_dir = dir;
|
||||
return 0;
|
||||
err:
|
||||
mei_dbgfs_deregister(dev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
|
@ -52,7 +52,7 @@ static void mei_hbm_me_cl_allocate(struct mei_device *dev)
|
|||
sizeof(struct mei_me_client), GFP_KERNEL);
|
||||
if (!clients) {
|
||||
dev_err(&dev->pdev->dev, "memory allocation for ME clients failed.\n");
|
||||
dev->dev_state = MEI_DEV_RESETING;
|
||||
dev->dev_state = MEI_DEV_RESETTING;
|
||||
mei_reset(dev, 1);
|
||||
return;
|
||||
}
|
||||
|
@ -123,12 +123,33 @@ static bool is_treat_specially_client(struct mei_cl *cl,
|
|||
return false;
|
||||
}
|
||||
|
||||
int mei_hbm_start_wait(struct mei_device *dev)
|
||||
{
|
||||
int ret;
|
||||
if (dev->hbm_state > MEI_HBM_START)
|
||||
return 0;
|
||||
|
||||
mutex_unlock(&dev->device_lock);
|
||||
ret = wait_event_interruptible_timeout(dev->wait_recvd_msg,
|
||||
dev->hbm_state == MEI_HBM_IDLE ||
|
||||
dev->hbm_state > MEI_HBM_START,
|
||||
mei_secs_to_jiffies(MEI_INTEROP_TIMEOUT));
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
if (ret <= 0 && (dev->hbm_state <= MEI_HBM_START)) {
|
||||
dev->hbm_state = MEI_HBM_IDLE;
|
||||
dev_err(&dev->pdev->dev, "wating for mei start failed\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_hbm_start_req - sends start request message.
|
||||
*
|
||||
* @dev: the device structure
|
||||
*/
|
||||
void mei_hbm_start_req(struct mei_device *dev)
|
||||
int mei_hbm_start_req(struct mei_device *dev)
|
||||
{
|
||||
struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
|
||||
struct hbm_host_version_request *start_req;
|
||||
|
@ -143,18 +164,19 @@ void mei_hbm_start_req(struct mei_device *dev)
|
|||
start_req->host_version.major_version = HBM_MAJOR_VERSION;
|
||||
start_req->host_version.minor_version = HBM_MINOR_VERSION;
|
||||
|
||||
dev->recvd_msg = false;
|
||||
dev->hbm_state = MEI_HBM_IDLE;
|
||||
if (mei_write_message(dev, mei_hdr, dev->wr_msg.data)) {
|
||||
dev_dbg(&dev->pdev->dev, "write send version message to FW fail.\n");
|
||||
dev->dev_state = MEI_DEV_RESETING;
|
||||
dev_err(&dev->pdev->dev, "version message writet failed\n");
|
||||
dev->dev_state = MEI_DEV_RESETTING;
|
||||
mei_reset(dev, 1);
|
||||
return -ENODEV;
|
||||
}
|
||||
dev->init_clients_state = MEI_START_MESSAGE;
|
||||
dev->hbm_state = MEI_HBM_START;
|
||||
dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
|
||||
return ;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* mei_hbm_enum_clients_req - sends enumeration client request message.
|
||||
*
|
||||
* @dev: the device structure
|
||||
|
@ -174,11 +196,11 @@ static void mei_hbm_enum_clients_req(struct mei_device *dev)
|
|||
enum_req->hbm_cmd = HOST_ENUM_REQ_CMD;
|
||||
|
||||
if (mei_write_message(dev, mei_hdr, dev->wr_msg.data)) {
|
||||
dev->dev_state = MEI_DEV_RESETING;
|
||||
dev_dbg(&dev->pdev->dev, "write send enumeration request message to FW fail.\n");
|
||||
dev->dev_state = MEI_DEV_RESETTING;
|
||||
dev_err(&dev->pdev->dev, "enumeration request write failed.\n");
|
||||
mei_reset(dev, 1);
|
||||
}
|
||||
dev->init_clients_state = MEI_ENUM_CLIENTS_MESSAGE;
|
||||
dev->hbm_state = MEI_HBM_ENUM_CLIENTS;
|
||||
dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
|
||||
return;
|
||||
}
|
||||
|
@ -208,6 +230,7 @@ static int mei_hbm_prop_req(struct mei_device *dev)
|
|||
|
||||
/* We got all client properties */
|
||||
if (next_client_index == MEI_CLIENTS_MAX) {
|
||||
dev->hbm_state = MEI_HBM_STARTED;
|
||||
schedule_work(&dev->init_work);
|
||||
|
||||
return 0;
|
||||
|
@ -226,8 +249,8 @@ static int mei_hbm_prop_req(struct mei_device *dev)
|
|||
prop_req->address = next_client_index;
|
||||
|
||||
if (mei_write_message(dev, mei_hdr, dev->wr_msg.data)) {
|
||||
dev->dev_state = MEI_DEV_RESETING;
|
||||
dev_err(&dev->pdev->dev, "Properties request command failed\n");
|
||||
dev->dev_state = MEI_DEV_RESETTING;
|
||||
dev_err(&dev->pdev->dev, "properties request write failed\n");
|
||||
mei_reset(dev, 1);
|
||||
|
||||
return -EIO;
|
||||
|
@ -542,27 +565,28 @@ void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
|
|||
dev->version = version_res->me_max_version;
|
||||
dev_dbg(&dev->pdev->dev, "version mismatch.\n");
|
||||
|
||||
dev->hbm_state = MEI_HBM_STOP;
|
||||
mei_hbm_stop_req_prepare(dev, &dev->wr_msg.hdr,
|
||||
dev->wr_msg.data);
|
||||
mei_write_message(dev, &dev->wr_msg.hdr,
|
||||
dev->wr_msg.data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
dev->version.major_version = HBM_MAJOR_VERSION;
|
||||
dev->version.minor_version = HBM_MINOR_VERSION;
|
||||
if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
|
||||
dev->init_clients_state == MEI_START_MESSAGE) {
|
||||
dev->hbm_state == MEI_HBM_START) {
|
||||
dev->init_clients_timer = 0;
|
||||
mei_hbm_enum_clients_req(dev);
|
||||
} else {
|
||||
dev->recvd_msg = false;
|
||||
dev_dbg(&dev->pdev->dev, "reset due to received hbm: host start\n");
|
||||
dev_err(&dev->pdev->dev, "reset: wrong host start response\n");
|
||||
mei_reset(dev, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
dev->recvd_msg = true;
|
||||
wake_up_interruptible(&dev->wait_recvd_msg);
|
||||
dev_dbg(&dev->pdev->dev, "host start response message received.\n");
|
||||
break;
|
||||
|
||||
|
@ -591,23 +615,20 @@ void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
|
|||
me_client = &dev->me_clients[dev->me_client_presentation_num];
|
||||
|
||||
if (props_res->status || !dev->me_clients) {
|
||||
dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message wrong status.\n");
|
||||
dev_err(&dev->pdev->dev, "reset: properties response hbm wrong status.\n");
|
||||
mei_reset(dev, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (me_client->client_id != props_res->address) {
|
||||
dev_err(&dev->pdev->dev,
|
||||
"Host client properties reply mismatch\n");
|
||||
dev_err(&dev->pdev->dev, "reset: host properties response address mismatch\n");
|
||||
mei_reset(dev, 1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
|
||||
dev->init_clients_state != MEI_CLIENT_PROPERTIES_MESSAGE) {
|
||||
dev_err(&dev->pdev->dev,
|
||||
"Unexpected client properties reply\n");
|
||||
dev->hbm_state != MEI_HBM_CLIENT_PROPERTIES) {
|
||||
dev_err(&dev->pdev->dev, "reset: unexpected properties response\n");
|
||||
mei_reset(dev, 1);
|
||||
|
||||
return;
|
||||
|
@ -626,26 +647,28 @@ void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
|
|||
enum_res = (struct hbm_host_enum_response *) mei_msg;
|
||||
memcpy(dev->me_clients_map, enum_res->valid_addresses, 32);
|
||||
if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
|
||||
dev->init_clients_state == MEI_ENUM_CLIENTS_MESSAGE) {
|
||||
dev->hbm_state == MEI_HBM_ENUM_CLIENTS) {
|
||||
dev->init_clients_timer = 0;
|
||||
dev->me_client_presentation_num = 0;
|
||||
dev->me_client_index = 0;
|
||||
mei_hbm_me_cl_allocate(dev);
|
||||
dev->init_clients_state =
|
||||
MEI_CLIENT_PROPERTIES_MESSAGE;
|
||||
dev->hbm_state = MEI_HBM_CLIENT_PROPERTIES;
|
||||
|
||||
/* first property reqeust */
|
||||
mei_hbm_prop_req(dev);
|
||||
} else {
|
||||
dev_dbg(&dev->pdev->dev, "reset due to received host enumeration clients response bus message.\n");
|
||||
dev_err(&dev->pdev->dev, "reset: unexpected enumeration response hbm.\n");
|
||||
mei_reset(dev, 1);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case HOST_STOP_RES_CMD:
|
||||
|
||||
if (dev->hbm_state != MEI_HBM_STOP)
|
||||
dev_err(&dev->pdev->dev, "unexpected stop response hbm.\n");
|
||||
dev->dev_state = MEI_DEV_DISABLED;
|
||||
dev_dbg(&dev->pdev->dev, "resetting because of FW stop response.\n");
|
||||
dev_info(&dev->pdev->dev, "reset: FW stop response.\n");
|
||||
mei_reset(dev, 1);
|
||||
break;
|
||||
|
||||
|
@ -657,6 +680,7 @@ void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
|
|||
|
||||
case ME_STOP_REQ_CMD:
|
||||
|
||||
dev->hbm_state = MEI_HBM_STOP;
|
||||
mei_hbm_stop_req_prepare(dev, &dev->wr_ext_msg.hdr,
|
||||
dev->wr_ext_msg.data);
|
||||
break;
|
||||
|
|
|
@ -17,6 +17,27 @@
|
|||
#ifndef _MEI_HBM_H_
|
||||
#define _MEI_HBM_H_
|
||||
|
||||
struct mei_device;
|
||||
struct mei_msg_hdr;
|
||||
struct mei_cl;
|
||||
|
||||
/**
|
||||
* enum mei_hbm_state - host bus message protocol state
|
||||
*
|
||||
* @MEI_HBM_IDLE : protocol not started
|
||||
* @MEI_HBM_START : start request message was sent
|
||||
* @MEI_HBM_ENUM_CLIENTS : enumeration request was sent
|
||||
* @MEI_HBM_CLIENT_PROPERTIES : acquiring clients properties
|
||||
*/
|
||||
enum mei_hbm_state {
|
||||
MEI_HBM_IDLE = 0,
|
||||
MEI_HBM_START,
|
||||
MEI_HBM_ENUM_CLIENTS,
|
||||
MEI_HBM_CLIENT_PROPERTIES,
|
||||
MEI_HBM_STARTED,
|
||||
MEI_HBM_STOP,
|
||||
};
|
||||
|
||||
void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr);
|
||||
|
||||
static inline void mei_hbm_hdr(struct mei_msg_hdr *hdr, size_t length)
|
||||
|
@ -28,8 +49,8 @@ static inline void mei_hbm_hdr(struct mei_msg_hdr *hdr, size_t length)
|
|||
hdr->reserved = 0;
|
||||
}
|
||||
|
||||
void mei_hbm_start_req(struct mei_device *dev);
|
||||
|
||||
int mei_hbm_start_req(struct mei_device *dev);
|
||||
int mei_hbm_start_wait(struct mei_device *dev);
|
||||
int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl);
|
||||
int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl);
|
||||
int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl);
|
||||
|
|
|
@ -26,14 +26,14 @@
|
|||
|
||||
|
||||
/**
|
||||
* mei_reg_read - Reads 32bit data from the mei device
|
||||
* mei_me_reg_read - Reads 32bit data from the mei device
|
||||
*
|
||||
* @dev: the device structure
|
||||
* @offset: offset from which to read the data
|
||||
*
|
||||
* returns register value (u32)
|
||||
*/
|
||||
static inline u32 mei_reg_read(const struct mei_me_hw *hw,
|
||||
static inline u32 mei_me_reg_read(const struct mei_me_hw *hw,
|
||||
unsigned long offset)
|
||||
{
|
||||
return ioread32(hw->mem_addr + offset);
|
||||
|
@ -41,20 +41,20 @@ static inline u32 mei_reg_read(const struct mei_me_hw *hw,
|
|||
|
||||
|
||||
/**
|
||||
* mei_reg_write - Writes 32bit data to the mei device
|
||||
* mei_me_reg_write - Writes 32bit data to the mei device
|
||||
*
|
||||
* @dev: the device structure
|
||||
* @offset: offset from which to write the data
|
||||
* @value: register value to write (u32)
|
||||
*/
|
||||
static inline void mei_reg_write(const struct mei_me_hw *hw,
|
||||
static inline void mei_me_reg_write(const struct mei_me_hw *hw,
|
||||
unsigned long offset, u32 value)
|
||||
{
|
||||
iowrite32(value, hw->mem_addr + offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_mecbrw_read - Reads 32bit data from ME circular buffer
|
||||
* mei_me_mecbrw_read - Reads 32bit data from ME circular buffer
|
||||
* read window register
|
||||
*
|
||||
* @dev: the device structure
|
||||
|
@ -63,18 +63,18 @@ static inline void mei_reg_write(const struct mei_me_hw *hw,
|
|||
*/
|
||||
static u32 mei_me_mecbrw_read(const struct mei_device *dev)
|
||||
{
|
||||
return mei_reg_read(to_me_hw(dev), ME_CB_RW);
|
||||
return mei_me_reg_read(to_me_hw(dev), ME_CB_RW);
|
||||
}
|
||||
/**
|
||||
* mei_mecsr_read - Reads 32bit data from the ME CSR
|
||||
* mei_me_mecsr_read - Reads 32bit data from the ME CSR
|
||||
*
|
||||
* @dev: the device structure
|
||||
*
|
||||
* returns ME_CSR_HA register value (u32)
|
||||
*/
|
||||
static inline u32 mei_mecsr_read(const struct mei_me_hw *hw)
|
||||
static inline u32 mei_me_mecsr_read(const struct mei_me_hw *hw)
|
||||
{
|
||||
return mei_reg_read(hw, ME_CSR_HA);
|
||||
return mei_me_reg_read(hw, ME_CSR_HA);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -86,7 +86,7 @@ static inline u32 mei_mecsr_read(const struct mei_me_hw *hw)
|
|||
*/
|
||||
static inline u32 mei_hcsr_read(const struct mei_me_hw *hw)
|
||||
{
|
||||
return mei_reg_read(hw, H_CSR);
|
||||
return mei_me_reg_read(hw, H_CSR);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -98,7 +98,7 @@ static inline u32 mei_hcsr_read(const struct mei_me_hw *hw)
|
|||
static inline void mei_hcsr_set(struct mei_me_hw *hw, u32 hcsr)
|
||||
{
|
||||
hcsr &= ~H_IS;
|
||||
mei_reg_write(hw, H_CSR, hcsr);
|
||||
mei_me_reg_write(hw, H_CSR, hcsr);
|
||||
}
|
||||
|
||||
|
||||
|
@ -123,7 +123,7 @@ static void mei_me_intr_clear(struct mei_device *dev)
|
|||
struct mei_me_hw *hw = to_me_hw(dev);
|
||||
u32 hcsr = mei_hcsr_read(hw);
|
||||
if ((hcsr & H_IS) == H_IS)
|
||||
mei_reg_write(hw, H_CSR, hcsr);
|
||||
mei_me_reg_write(hw, H_CSR, hcsr);
|
||||
}
|
||||
/**
|
||||
* mei_me_intr_enable - enables mei device interrupts
|
||||
|
@ -228,10 +228,42 @@ static bool mei_me_host_is_ready(struct mei_device *dev)
|
|||
static bool mei_me_hw_is_ready(struct mei_device *dev)
|
||||
{
|
||||
struct mei_me_hw *hw = to_me_hw(dev);
|
||||
hw->me_hw_state = mei_mecsr_read(hw);
|
||||
hw->me_hw_state = mei_me_mecsr_read(hw);
|
||||
return (hw->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA;
|
||||
}
|
||||
|
||||
static int mei_me_hw_ready_wait(struct mei_device *dev)
|
||||
{
|
||||
int err;
|
||||
if (mei_me_hw_is_ready(dev))
|
||||
return 0;
|
||||
|
||||
mutex_unlock(&dev->device_lock);
|
||||
err = wait_event_interruptible_timeout(dev->wait_hw_ready,
|
||||
dev->recvd_hw_ready, MEI_INTEROP_TIMEOUT);
|
||||
mutex_lock(&dev->device_lock);
|
||||
if (!err && !dev->recvd_hw_ready) {
|
||||
dev_err(&dev->pdev->dev,
|
||||
"wait hw ready failed. status = 0x%x\n", err);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
dev->recvd_hw_ready = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mei_me_hw_start(struct mei_device *dev)
|
||||
{
|
||||
int ret = mei_me_hw_ready_wait(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
dev_dbg(&dev->pdev->dev, "hw is ready\n");
|
||||
|
||||
mei_me_host_set_ready(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* mei_hbuf_filled_slots - gets number of device filled buffer slots
|
||||
*
|
||||
|
@ -305,10 +337,11 @@ static int mei_me_write_message(struct mei_device *dev,
|
|||
unsigned char *buf)
|
||||
{
|
||||
struct mei_me_hw *hw = to_me_hw(dev);
|
||||
unsigned long rem, dw_cnt;
|
||||
unsigned long rem;
|
||||
unsigned long length = header->length;
|
||||
u32 *reg_buf = (u32 *)buf;
|
||||
u32 hcsr;
|
||||
u32 dw_cnt;
|
||||
int i;
|
||||
int empty_slots;
|
||||
|
||||
|
@ -321,16 +354,16 @@ static int mei_me_write_message(struct mei_device *dev,
|
|||
if (empty_slots < 0 || dw_cnt > empty_slots)
|
||||
return -EIO;
|
||||
|
||||
mei_reg_write(hw, H_CB_WW, *((u32 *) header));
|
||||
mei_me_reg_write(hw, H_CB_WW, *((u32 *) header));
|
||||
|
||||
for (i = 0; i < length / 4; i++)
|
||||
mei_reg_write(hw, H_CB_WW, reg_buf[i]);
|
||||
mei_me_reg_write(hw, H_CB_WW, reg_buf[i]);
|
||||
|
||||
rem = length & 0x3;
|
||||
if (rem > 0) {
|
||||
u32 reg = 0;
|
||||
memcpy(®, &buf[length - rem], rem);
|
||||
mei_reg_write(hw, H_CB_WW, reg);
|
||||
mei_me_reg_write(hw, H_CB_WW, reg);
|
||||
}
|
||||
|
||||
hcsr = mei_hcsr_read(hw) | H_IG;
|
||||
|
@ -354,7 +387,7 @@ static int mei_me_count_full_read_slots(struct mei_device *dev)
|
|||
char read_ptr, write_ptr;
|
||||
unsigned char buffer_depth, filled_slots;
|
||||
|
||||
hw->me_hw_state = mei_mecsr_read(hw);
|
||||
hw->me_hw_state = mei_me_mecsr_read(hw);
|
||||
buffer_depth = (unsigned char)((hw->me_hw_state & ME_CBD_HRA) >> 24);
|
||||
read_ptr = (char) ((hw->me_hw_state & ME_CBRP_HRA) >> 8);
|
||||
write_ptr = (char) ((hw->me_hw_state & ME_CBWP_HRA) >> 16);
|
||||
|
@ -414,7 +447,7 @@ irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id)
|
|||
return IRQ_NONE;
|
||||
|
||||
/* clear H_IS bit in H_CSR */
|
||||
mei_reg_write(hw, H_CSR, csr_reg);
|
||||
mei_me_reg_write(hw, H_CSR, csr_reg);
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
@ -433,12 +466,8 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
|
|||
{
|
||||
struct mei_device *dev = (struct mei_device *) dev_id;
|
||||
struct mei_cl_cb complete_list;
|
||||
struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL;
|
||||
struct mei_cl *cl;
|
||||
s32 slots;
|
||||
int rets;
|
||||
bool bus_message_received;
|
||||
|
||||
|
||||
dev_dbg(&dev->pdev->dev, "function called after ISR to handle the interrupt processing.\n");
|
||||
/* initialize our complete list */
|
||||
|
@ -452,7 +481,7 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
|
|||
|
||||
/* check if ME wants a reset */
|
||||
if (!mei_hw_is_ready(dev) &&
|
||||
dev->dev_state != MEI_DEV_RESETING &&
|
||||
dev->dev_state != MEI_DEV_RESETTING &&
|
||||
dev->dev_state != MEI_DEV_INITIALIZING) {
|
||||
dev_dbg(&dev->pdev->dev, "FW not ready.\n");
|
||||
mei_reset(dev, 1);
|
||||
|
@ -465,14 +494,9 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
|
|||
if (mei_hw_is_ready(dev)) {
|
||||
dev_dbg(&dev->pdev->dev, "we need to start the dev.\n");
|
||||
|
||||
mei_host_set_ready(dev);
|
||||
dev->recvd_hw_ready = true;
|
||||
wake_up_interruptible(&dev->wait_hw_ready);
|
||||
|
||||
dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n");
|
||||
/* link is established * start sending messages. */
|
||||
|
||||
dev->dev_state = MEI_DEV_INIT_CLIENTS;
|
||||
|
||||
mei_hbm_start_req(dev);
|
||||
mutex_unlock(&dev->device_lock);
|
||||
return IRQ_HANDLED;
|
||||
} else {
|
||||
|
@ -499,44 +523,20 @@ end:
|
|||
dev_dbg(&dev->pdev->dev, "end of bottom half function.\n");
|
||||
dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
|
||||
|
||||
bus_message_received = false;
|
||||
if (dev->recvd_msg && waitqueue_active(&dev->wait_recvd_msg)) {
|
||||
dev_dbg(&dev->pdev->dev, "received waiting bus message\n");
|
||||
bus_message_received = true;
|
||||
}
|
||||
mutex_unlock(&dev->device_lock);
|
||||
if (bus_message_received) {
|
||||
dev_dbg(&dev->pdev->dev, "wake up dev->wait_recvd_msg\n");
|
||||
wake_up_interruptible(&dev->wait_recvd_msg);
|
||||
bus_message_received = false;
|
||||
}
|
||||
if (list_empty(&complete_list.list))
|
||||
return IRQ_HANDLED;
|
||||
|
||||
mei_irq_compl_handler(dev, &complete_list);
|
||||
|
||||
list_for_each_entry_safe(cb_pos, cb_next, &complete_list.list, list) {
|
||||
cl = cb_pos->cl;
|
||||
list_del(&cb_pos->list);
|
||||
if (cl) {
|
||||
if (cl != &dev->iamthif_cl) {
|
||||
dev_dbg(&dev->pdev->dev, "completing call back.\n");
|
||||
mei_irq_complete_handler(cl, cb_pos);
|
||||
cb_pos = NULL;
|
||||
} else if (cl == &dev->iamthif_cl) {
|
||||
mei_amthif_complete(dev, cb_pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
static const struct mei_hw_ops mei_me_hw_ops = {
|
||||
|
||||
.host_set_ready = mei_me_host_set_ready,
|
||||
.host_is_ready = mei_me_host_is_ready,
|
||||
|
||||
.hw_is_ready = mei_me_hw_is_ready,
|
||||
.hw_reset = mei_me_hw_reset,
|
||||
.hw_config = mei_me_hw_config,
|
||||
.hw_config = mei_me_hw_config,
|
||||
.hw_start = mei_me_hw_start,
|
||||
|
||||
.intr_clear = mei_me_intr_clear,
|
||||
.intr_enable = mei_me_intr_enable,
|
||||
|
@ -571,14 +571,6 @@ struct mei_device *mei_me_dev_init(struct pci_dev *pdev)
|
|||
|
||||
mei_device_init(dev);
|
||||
|
||||
INIT_LIST_HEAD(&dev->wd_cl.link);
|
||||
INIT_LIST_HEAD(&dev->iamthif_cl.link);
|
||||
mei_io_list_init(&dev->amthif_cmd_list);
|
||||
mei_io_list_init(&dev->amthif_rd_complete_list);
|
||||
|
||||
INIT_DELAYED_WORK(&dev->timer_work, mei_timer);
|
||||
INIT_WORK(&dev->init_work, mei_host_client_init);
|
||||
|
||||
dev->ops = &mei_me_hw_ops;
|
||||
|
||||
dev->pdev = pdev;
|
||||
|
|
|
@ -36,12 +36,6 @@ struct mei_me_hw {
|
|||
|
||||
struct mei_device *mei_me_dev_init(struct pci_dev *pdev);
|
||||
|
||||
/* get slots (dwords) from a message length + header (bytes) */
|
||||
static inline unsigned char mei_data2slots(size_t length)
|
||||
{
|
||||
return DIV_ROUND_UP(sizeof(struct mei_msg_hdr) + length, 4);
|
||||
}
|
||||
|
||||
irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id);
|
||||
irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id);
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/wait.h>
|
||||
|
@ -22,6 +23,7 @@
|
|||
#include <linux/mei.h>
|
||||
|
||||
#include "mei_dev.h"
|
||||
#include "hbm.h"
|
||||
#include "client.h"
|
||||
|
||||
const char *mei_dev_state_str(int state)
|
||||
|
@ -31,9 +33,8 @@ const char *mei_dev_state_str(int state)
|
|||
MEI_DEV_STATE(INITIALIZING);
|
||||
MEI_DEV_STATE(INIT_CLIENTS);
|
||||
MEI_DEV_STATE(ENABLED);
|
||||
MEI_DEV_STATE(RESETING);
|
||||
MEI_DEV_STATE(RESETTING);
|
||||
MEI_DEV_STATE(DISABLED);
|
||||
MEI_DEV_STATE(RECOVERING_FROM_RESET);
|
||||
MEI_DEV_STATE(POWER_DOWN);
|
||||
MEI_DEV_STATE(POWER_UP);
|
||||
default:
|
||||
|
@ -46,7 +47,9 @@ void mei_device_init(struct mei_device *dev)
|
|||
{
|
||||
/* setup our list array */
|
||||
INIT_LIST_HEAD(&dev->file_list);
|
||||
INIT_LIST_HEAD(&dev->device_list);
|
||||
mutex_init(&dev->device_lock);
|
||||
init_waitqueue_head(&dev->wait_hw_ready);
|
||||
init_waitqueue_head(&dev->wait_recvd_msg);
|
||||
init_waitqueue_head(&dev->wait_stop_wd);
|
||||
dev->dev_state = MEI_DEV_INITIALIZING;
|
||||
|
@ -56,19 +59,27 @@ void mei_device_init(struct mei_device *dev)
|
|||
mei_io_list_init(&dev->write_waiting_list);
|
||||
mei_io_list_init(&dev->ctrl_wr_list);
|
||||
mei_io_list_init(&dev->ctrl_rd_list);
|
||||
|
||||
INIT_DELAYED_WORK(&dev->timer_work, mei_timer);
|
||||
INIT_WORK(&dev->init_work, mei_host_client_init);
|
||||
|
||||
INIT_LIST_HEAD(&dev->wd_cl.link);
|
||||
INIT_LIST_HEAD(&dev->iamthif_cl.link);
|
||||
mei_io_list_init(&dev->amthif_cmd_list);
|
||||
mei_io_list_init(&dev->amthif_rd_complete_list);
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mei_device_init);
|
||||
|
||||
/**
|
||||
* mei_hw_init - initializes host and fw to start work.
|
||||
* mei_start - initializes host and fw to start work.
|
||||
*
|
||||
* @dev: the device structure
|
||||
*
|
||||
* returns 0 on success, <0 on failure.
|
||||
*/
|
||||
int mei_hw_init(struct mei_device *dev)
|
||||
int mei_start(struct mei_device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
/* acknowledge interrupt and stop interupts */
|
||||
|
@ -76,29 +87,15 @@ int mei_hw_init(struct mei_device *dev)
|
|||
|
||||
mei_hw_config(dev);
|
||||
|
||||
dev->recvd_msg = false;
|
||||
dev_dbg(&dev->pdev->dev, "reset in start the mei device.\n");
|
||||
|
||||
mei_reset(dev, 1);
|
||||
|
||||
/* wait for ME to turn on ME_RDY */
|
||||
if (!dev->recvd_msg) {
|
||||
mutex_unlock(&dev->device_lock);
|
||||
ret = wait_event_interruptible_timeout(dev->wait_recvd_msg,
|
||||
dev->recvd_msg,
|
||||
mei_secs_to_jiffies(MEI_INTEROP_TIMEOUT));
|
||||
mutex_lock(&dev->device_lock);
|
||||
}
|
||||
|
||||
if (ret <= 0 && !dev->recvd_msg) {
|
||||
dev->dev_state = MEI_DEV_DISABLED;
|
||||
dev_dbg(&dev->pdev->dev,
|
||||
"wait_event_interruptible_timeout failed"
|
||||
"on wait for ME to turn on ME_RDY.\n");
|
||||
if (mei_hbm_start_wait(dev)) {
|
||||
dev_err(&dev->pdev->dev, "HBM haven't started");
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
||||
if (!mei_host_is_ready(dev)) {
|
||||
dev_err(&dev->pdev->dev, "host is not ready.\n");
|
||||
goto err;
|
||||
|
@ -115,7 +112,6 @@ int mei_hw_init(struct mei_device *dev)
|
|||
goto err;
|
||||
}
|
||||
|
||||
dev->recvd_msg = false;
|
||||
dev_dbg(&dev->pdev->dev, "link layer has been established.\n");
|
||||
|
||||
mutex_unlock(&dev->device_lock);
|
||||
|
@ -126,6 +122,7 @@ err:
|
|||
mutex_unlock(&dev->device_lock);
|
||||
return -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mei_start);
|
||||
|
||||
/**
|
||||
* mei_reset - resets host and fw.
|
||||
|
@ -137,9 +134,6 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
|
|||
{
|
||||
bool unexpected;
|
||||
|
||||
if (dev->dev_state == MEI_DEV_RECOVERING_FROM_RESET)
|
||||
return;
|
||||
|
||||
unexpected = (dev->dev_state != MEI_DEV_INITIALIZING &&
|
||||
dev->dev_state != MEI_DEV_DISABLED &&
|
||||
dev->dev_state != MEI_DEV_POWER_DOWN &&
|
||||
|
@ -147,11 +141,12 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
|
|||
|
||||
mei_hw_reset(dev, interrupts_enabled);
|
||||
|
||||
dev->hbm_state = MEI_HBM_IDLE;
|
||||
|
||||
if (dev->dev_state != MEI_DEV_INITIALIZING) {
|
||||
if (dev->dev_state != MEI_DEV_DISABLED &&
|
||||
dev->dev_state != MEI_DEV_POWER_DOWN)
|
||||
dev->dev_state = MEI_DEV_RESETING;
|
||||
dev->dev_state = MEI_DEV_RESETTING;
|
||||
|
||||
mei_cl_all_disconnect(dev);
|
||||
|
||||
|
@ -176,12 +171,27 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
|
|||
dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n",
|
||||
mei_dev_state_str(dev->dev_state));
|
||||
|
||||
if (!interrupts_enabled) {
|
||||
dev_dbg(&dev->pdev->dev, "intr not enabled end of reset\n");
|
||||
return;
|
||||
}
|
||||
|
||||
mei_hw_start(dev);
|
||||
|
||||
dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n");
|
||||
/* link is established * start sending messages. */
|
||||
|
||||
dev->dev_state = MEI_DEV_INIT_CLIENTS;
|
||||
|
||||
mei_hbm_start_req(dev);
|
||||
|
||||
/* wake up all readings so they can be interrupted */
|
||||
mei_cl_all_read_wakeup(dev);
|
||||
|
||||
/* remove all waiting requests */
|
||||
mei_cl_all_write_clear(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mei_reset);
|
||||
|
||||
void mei_stop(struct mei_device *dev)
|
||||
{
|
||||
|
@ -193,14 +203,18 @@ void mei_stop(struct mei_device *dev)
|
|||
|
||||
mei_wd_stop(dev);
|
||||
|
||||
mei_nfc_host_exit();
|
||||
|
||||
dev->dev_state = MEI_DEV_POWER_DOWN;
|
||||
mei_reset(dev, 0);
|
||||
|
||||
mutex_unlock(&dev->device_lock);
|
||||
|
||||
flush_scheduled_work();
|
||||
|
||||
mei_watchdog_unregister(dev);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(mei_stop);
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
@ -30,103 +31,153 @@
|
|||
|
||||
|
||||
/**
|
||||
* mei_complete_handler - processes completed operation.
|
||||
* mei_cl_complete_handler - processes completed operation for a client
|
||||
*
|
||||
* @cl: private data of the file object.
|
||||
* @cb_pos: callback block.
|
||||
* @cb: callback block.
|
||||
*/
|
||||
void mei_irq_complete_handler(struct mei_cl *cl, struct mei_cl_cb *cb_pos)
|
||||
static void mei_cl_complete_handler(struct mei_cl *cl, struct mei_cl_cb *cb)
|
||||
{
|
||||
if (cb_pos->fop_type == MEI_FOP_WRITE) {
|
||||
mei_io_cb_free(cb_pos);
|
||||
cb_pos = NULL;
|
||||
if (cb->fop_type == MEI_FOP_WRITE) {
|
||||
mei_io_cb_free(cb);
|
||||
cb = NULL;
|
||||
cl->writing_state = MEI_WRITE_COMPLETE;
|
||||
if (waitqueue_active(&cl->tx_wait))
|
||||
wake_up_interruptible(&cl->tx_wait);
|
||||
|
||||
} else if (cb_pos->fop_type == MEI_FOP_READ &&
|
||||
} else if (cb->fop_type == MEI_FOP_READ &&
|
||||
MEI_READING == cl->reading_state) {
|
||||
cl->reading_state = MEI_READ_COMPLETE;
|
||||
if (waitqueue_active(&cl->rx_wait))
|
||||
wake_up_interruptible(&cl->rx_wait);
|
||||
else
|
||||
mei_cl_bus_rx_event(cl);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* _mei_irq_thread_state_ok - checks if mei header matches file private data
|
||||
* mei_irq_compl_handler - dispatch complete handelers
|
||||
* for the completed callbacks
|
||||
*
|
||||
* @cl: private data of the file object
|
||||
* @dev - mei device
|
||||
* @compl_list - list of completed cbs
|
||||
*/
|
||||
void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *compl_list)
|
||||
{
|
||||
struct mei_cl_cb *cb, *next;
|
||||
struct mei_cl *cl;
|
||||
|
||||
list_for_each_entry_safe(cb, next, &compl_list->list, list) {
|
||||
cl = cb->cl;
|
||||
list_del(&cb->list);
|
||||
if (!cl)
|
||||
continue;
|
||||
|
||||
dev_dbg(&dev->pdev->dev, "completing call back.\n");
|
||||
if (cl == &dev->iamthif_cl)
|
||||
mei_amthif_complete(dev, cb);
|
||||
else
|
||||
mei_cl_complete_handler(cl, cb);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mei_irq_compl_handler);
|
||||
|
||||
/**
|
||||
* mei_cl_hbm_equal - check if hbm is addressed to the client
|
||||
*
|
||||
* @cl: host client
|
||||
* @mei_hdr: header of mei client message
|
||||
*
|
||||
* returns !=0 if matches, 0 if no match.
|
||||
* returns true if matches, false otherwise
|
||||
*/
|
||||
static int _mei_irq_thread_state_ok(struct mei_cl *cl,
|
||||
struct mei_msg_hdr *mei_hdr)
|
||||
static inline int mei_cl_hbm_equal(struct mei_cl *cl,
|
||||
struct mei_msg_hdr *mei_hdr)
|
||||
{
|
||||
return (cl->host_client_id == mei_hdr->host_addr &&
|
||||
cl->me_client_id == mei_hdr->me_addr &&
|
||||
return cl->host_client_id == mei_hdr->host_addr &&
|
||||
cl->me_client_id == mei_hdr->me_addr;
|
||||
}
|
||||
/**
|
||||
* mei_cl_is_reading - checks if the client
|
||||
is the one to read this message
|
||||
*
|
||||
* @cl: mei client
|
||||
* @mei_hdr: header of mei message
|
||||
*
|
||||
* returns true on match and false otherwise
|
||||
*/
|
||||
static bool mei_cl_is_reading(struct mei_cl *cl, struct mei_msg_hdr *mei_hdr)
|
||||
{
|
||||
return mei_cl_hbm_equal(cl, mei_hdr) &&
|
||||
cl->state == MEI_FILE_CONNECTED &&
|
||||
MEI_READ_COMPLETE != cl->reading_state);
|
||||
cl->reading_state != MEI_READ_COMPLETE;
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_irq_thread_read_client_message - bottom half read routine after ISR to
|
||||
* handle the read mei client message data processing.
|
||||
* mei_irq_read_client_message - process client message
|
||||
*
|
||||
* @complete_list: An instance of our list structure
|
||||
* @dev: the device structure
|
||||
* @mei_hdr: header of mei client message
|
||||
* @complete_list: An instance of our list structure
|
||||
*
|
||||
* returns 0 on success, <0 on failure.
|
||||
*/
|
||||
static int mei_irq_thread_read_client_message(struct mei_cl_cb *complete_list,
|
||||
struct mei_device *dev,
|
||||
struct mei_msg_hdr *mei_hdr)
|
||||
static int mei_cl_irq_read_msg(struct mei_device *dev,
|
||||
struct mei_msg_hdr *mei_hdr,
|
||||
struct mei_cl_cb *complete_list)
|
||||
{
|
||||
struct mei_cl *cl;
|
||||
struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL;
|
||||
struct mei_cl_cb *cb, *next;
|
||||
unsigned char *buffer = NULL;
|
||||
|
||||
dev_dbg(&dev->pdev->dev, "start client msg\n");
|
||||
if (list_empty(&dev->read_list.list))
|
||||
goto quit;
|
||||
list_for_each_entry_safe(cb, next, &dev->read_list.list, list) {
|
||||
cl = cb->cl;
|
||||
if (!cl || !mei_cl_is_reading(cl, mei_hdr))
|
||||
continue;
|
||||
|
||||
list_for_each_entry_safe(cb_pos, cb_next, &dev->read_list.list, list) {
|
||||
cl = cb_pos->cl;
|
||||
if (cl && _mei_irq_thread_state_ok(cl, mei_hdr)) {
|
||||
cl->reading_state = MEI_READING;
|
||||
buffer = cb_pos->response_buffer.data + cb_pos->buf_idx;
|
||||
cl->reading_state = MEI_READING;
|
||||
|
||||
if (cb_pos->response_buffer.size <
|
||||
mei_hdr->length + cb_pos->buf_idx) {
|
||||
dev_dbg(&dev->pdev->dev, "message overflow.\n");
|
||||
list_del(&cb_pos->list);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (buffer)
|
||||
mei_read_slots(dev, buffer, mei_hdr->length);
|
||||
|
||||
cb_pos->buf_idx += mei_hdr->length;
|
||||
if (mei_hdr->msg_complete) {
|
||||
cl->status = 0;
|
||||
list_del(&cb_pos->list);
|
||||
dev_dbg(&dev->pdev->dev,
|
||||
"completed read H cl = %d, ME cl = %d, length = %lu\n",
|
||||
cl->host_client_id,
|
||||
cl->me_client_id,
|
||||
cb_pos->buf_idx);
|
||||
|
||||
list_add_tail(&cb_pos->list,
|
||||
&complete_list->list);
|
||||
}
|
||||
|
||||
break;
|
||||
if (cb->response_buffer.size == 0 ||
|
||||
cb->response_buffer.data == NULL) {
|
||||
dev_err(&dev->pdev->dev, "response buffer is not allocated.\n");
|
||||
list_del(&cb->list);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (cb->response_buffer.size < mei_hdr->length + cb->buf_idx) {
|
||||
dev_dbg(&dev->pdev->dev, "message overflow. size %d len %d idx %ld\n",
|
||||
cb->response_buffer.size,
|
||||
mei_hdr->length, cb->buf_idx);
|
||||
buffer = krealloc(cb->response_buffer.data,
|
||||
mei_hdr->length + cb->buf_idx,
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!buffer) {
|
||||
dev_err(&dev->pdev->dev, "allocation failed.\n");
|
||||
list_del(&cb->list);
|
||||
return -ENOMEM;
|
||||
}
|
||||
cb->response_buffer.data = buffer;
|
||||
cb->response_buffer.size =
|
||||
mei_hdr->length + cb->buf_idx;
|
||||
}
|
||||
|
||||
buffer = cb->response_buffer.data + cb->buf_idx;
|
||||
mei_read_slots(dev, buffer, mei_hdr->length);
|
||||
|
||||
cb->buf_idx += mei_hdr->length;
|
||||
if (mei_hdr->msg_complete) {
|
||||
cl->status = 0;
|
||||
list_del(&cb->list);
|
||||
dev_dbg(&dev->pdev->dev, "completed read H cl = %d, ME cl = %d, length = %lu\n",
|
||||
cl->host_client_id,
|
||||
cl->me_client_id,
|
||||
cb->buf_idx);
|
||||
list_add_tail(&cb->list, &complete_list->list);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
quit:
|
||||
dev_dbg(&dev->pdev->dev, "message read\n");
|
||||
if (!buffer) {
|
||||
mei_read_slots(dev, dev->rd_msg_buf, mei_hdr->length);
|
||||
|
@ -153,25 +204,27 @@ static int _mei_irq_thread_close(struct mei_device *dev, s32 *slots,
|
|||
struct mei_cl *cl,
|
||||
struct mei_cl_cb *cmpl_list)
|
||||
{
|
||||
if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) +
|
||||
sizeof(struct hbm_client_connect_request)))
|
||||
return -EBADMSG;
|
||||
u32 msg_slots =
|
||||
mei_data2slots(sizeof(struct hbm_client_connect_request));
|
||||
|
||||
*slots -= mei_data2slots(sizeof(struct hbm_client_connect_request));
|
||||
if (*slots < msg_slots)
|
||||
return -EMSGSIZE;
|
||||
|
||||
*slots -= msg_slots;
|
||||
|
||||
if (mei_hbm_cl_disconnect_req(dev, cl)) {
|
||||
cl->status = 0;
|
||||
cb_pos->buf_idx = 0;
|
||||
list_move_tail(&cb_pos->list, &cmpl_list->list);
|
||||
return -EMSGSIZE;
|
||||
} else {
|
||||
cl->state = MEI_FILE_DISCONNECTING;
|
||||
cl->status = 0;
|
||||
cb_pos->buf_idx = 0;
|
||||
list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list);
|
||||
cl->timer_count = MEI_CONNECT_TIMEOUT;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
cl->state = MEI_FILE_DISCONNECTING;
|
||||
cl->status = 0;
|
||||
cb_pos->buf_idx = 0;
|
||||
list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list);
|
||||
cl->timer_count = MEI_CONNECT_TIMEOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -192,14 +245,15 @@ static int _mei_irq_thread_read(struct mei_device *dev, s32 *slots,
|
|||
struct mei_cl *cl,
|
||||
struct mei_cl_cb *cmpl_list)
|
||||
{
|
||||
if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) +
|
||||
sizeof(struct hbm_flow_control))) {
|
||||
u32 msg_slots = mei_data2slots(sizeof(struct hbm_flow_control));
|
||||
|
||||
if (*slots < msg_slots) {
|
||||
/* return the cancel routine */
|
||||
list_del(&cb_pos->list);
|
||||
return -EBADMSG;
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
*slots -= mei_data2slots(sizeof(struct hbm_flow_control));
|
||||
*slots -= msg_slots;
|
||||
|
||||
if (mei_hbm_cl_flow_control_req(dev, cl)) {
|
||||
cl->status = -ENODEV;
|
||||
|
@ -229,15 +283,19 @@ static int _mei_irq_thread_ioctl(struct mei_device *dev, s32 *slots,
|
|||
struct mei_cl *cl,
|
||||
struct mei_cl_cb *cmpl_list)
|
||||
{
|
||||
if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) +
|
||||
sizeof(struct hbm_client_connect_request))) {
|
||||
u32 msg_slots =
|
||||
mei_data2slots(sizeof(struct hbm_client_connect_request));
|
||||
|
||||
if (*slots < msg_slots) {
|
||||
/* return the cancel routine */
|
||||
list_del(&cb_pos->list);
|
||||
return -EBADMSG;
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
*slots -= msg_slots;
|
||||
|
||||
cl->state = MEI_FILE_CONNECTING;
|
||||
*slots -= mei_data2slots(sizeof(struct hbm_client_connect_request));
|
||||
|
||||
if (mei_hbm_cl_connect_req(dev, cl)) {
|
||||
cl->status = -ENODEV;
|
||||
cb_pos->buf_idx = 0;
|
||||
|
@ -266,7 +324,7 @@ static int mei_irq_thread_write_complete(struct mei_device *dev, s32 *slots,
|
|||
struct mei_msg_hdr mei_hdr;
|
||||
struct mei_cl *cl = cb->cl;
|
||||
size_t len = cb->request_buffer.size - cb->buf_idx;
|
||||
size_t msg_slots = mei_data2slots(len);
|
||||
u32 msg_slots = mei_data2slots(len);
|
||||
|
||||
mei_hdr.host_addr = cl->host_client_id;
|
||||
mei_hdr.me_addr = cl->me_client_id;
|
||||
|
@ -298,13 +356,14 @@ static int mei_irq_thread_write_complete(struct mei_device *dev, s32 *slots,
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (mei_cl_flow_ctrl_reduce(cl))
|
||||
return -ENODEV;
|
||||
|
||||
cl->status = 0;
|
||||
cb->buf_idx += mei_hdr.length;
|
||||
if (mei_hdr.msg_complete)
|
||||
if (mei_hdr.msg_complete) {
|
||||
if (mei_cl_flow_ctrl_reduce(cl))
|
||||
return -ENODEV;
|
||||
list_move_tail(&cb->list, &dev->write_waiting_list.list);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -350,8 +409,7 @@ int mei_irq_read_handler(struct mei_device *dev,
|
|||
" client = %d, ME client = %d\n",
|
||||
cl_pos->host_client_id,
|
||||
cl_pos->me_client_id);
|
||||
if (cl_pos->host_client_id == mei_hdr->host_addr &&
|
||||
cl_pos->me_client_id == mei_hdr->me_addr)
|
||||
if (mei_cl_hbm_equal(cl_pos, mei_hdr))
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -362,7 +420,7 @@ int mei_irq_read_handler(struct mei_device *dev,
|
|||
}
|
||||
}
|
||||
if (((*slots) * sizeof(u32)) < mei_hdr->length) {
|
||||
dev_dbg(&dev->pdev->dev,
|
||||
dev_err(&dev->pdev->dev,
|
||||
"we can't read the message slots =%08x.\n",
|
||||
*slots);
|
||||
/* we can't read the message */
|
||||
|
@ -378,20 +436,19 @@ int mei_irq_read_handler(struct mei_device *dev,
|
|||
} else if (mei_hdr->host_addr == dev->iamthif_cl.host_client_id &&
|
||||
(MEI_FILE_CONNECTED == dev->iamthif_cl.state) &&
|
||||
(dev->iamthif_state == MEI_IAMTHIF_READING)) {
|
||||
dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_iamthif_message.\n");
|
||||
|
||||
dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_iamthif_message.\n");
|
||||
dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr));
|
||||
|
||||
ret = mei_amthif_irq_read_message(cmpl_list, dev, mei_hdr);
|
||||
ret = mei_amthif_irq_read_msg(dev, mei_hdr, cmpl_list);
|
||||
if (ret)
|
||||
goto end;
|
||||
} else {
|
||||
dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_client_message.\n");
|
||||
ret = mei_irq_thread_read_client_message(cmpl_list,
|
||||
dev, mei_hdr);
|
||||
dev_dbg(&dev->pdev->dev, "call mei_cl_irq_read_msg.\n");
|
||||
dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr));
|
||||
ret = mei_cl_irq_read_msg(dev, mei_hdr, cmpl_list);
|
||||
if (ret)
|
||||
goto end;
|
||||
|
||||
}
|
||||
|
||||
/* reset the number of slots and header */
|
||||
|
@ -400,7 +457,7 @@ int mei_irq_read_handler(struct mei_device *dev,
|
|||
|
||||
if (*slots == -EOVERFLOW) {
|
||||
/* overflow - reset */
|
||||
dev_dbg(&dev->pdev->dev, "resetting due to slots overflow.\n");
|
||||
dev_err(&dev->pdev->dev, "resetting due to slots overflow.\n");
|
||||
/* set the event since message has been read */
|
||||
ret = -ERANGE;
|
||||
goto end;
|
||||
|
@ -408,6 +465,7 @@ int mei_irq_read_handler(struct mei_device *dev,
|
|||
end:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mei_irq_read_handler);
|
||||
|
||||
|
||||
/**
|
||||
|
@ -419,8 +477,7 @@ end:
|
|||
*
|
||||
* returns 0 on success, <0 on failure.
|
||||
*/
|
||||
int mei_irq_write_handler(struct mei_device *dev,
|
||||
struct mei_cl_cb *cmpl_list)
|
||||
int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list)
|
||||
{
|
||||
|
||||
struct mei_cl *cl;
|
||||
|
@ -559,6 +616,7 @@ int mei_irq_write_handler(struct mei_device *dev,
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mei_irq_write_handler);
|
||||
|
||||
|
||||
|
||||
|
@ -586,8 +644,8 @@ void mei_timer(struct work_struct *work)
|
|||
if (dev->dev_state == MEI_DEV_INIT_CLIENTS) {
|
||||
if (dev->init_clients_timer) {
|
||||
if (--dev->init_clients_timer == 0) {
|
||||
dev_dbg(&dev->pdev->dev, "IMEI reset due to init clients timeout ,init clients state = %d.\n",
|
||||
dev->init_clients_state);
|
||||
dev_err(&dev->pdev->dev, "reset: init clients timeout hbm_state = %d.\n",
|
||||
dev->hbm_state);
|
||||
mei_reset(dev, 1);
|
||||
}
|
||||
}
|
||||
|
@ -598,7 +656,7 @@ void mei_timer(struct work_struct *work)
|
|||
list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
|
||||
if (cl_pos->timer_count) {
|
||||
if (--cl_pos->timer_count == 0) {
|
||||
dev_dbg(&dev->pdev->dev, "HECI reset due to connect/disconnect timeout.\n");
|
||||
dev_err(&dev->pdev->dev, "reset: connect/disconnect timeout.\n");
|
||||
mei_reset(dev, 1);
|
||||
goto out;
|
||||
}
|
||||
|
@ -607,7 +665,7 @@ void mei_timer(struct work_struct *work)
|
|||
|
||||
if (dev->iamthif_stall_timer) {
|
||||
if (--dev->iamthif_stall_timer == 0) {
|
||||
dev_dbg(&dev->pdev->dev, "resetting because of hang to amthi.\n");
|
||||
dev_err(&dev->pdev->dev, "reset: amthif hanged.\n");
|
||||
mei_reset(dev, 1);
|
||||
dev->iamthif_msg_buf_size = 0;
|
||||
dev->iamthif_msg_buf_index = 0;
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
*
|
||||
* @inode: pointer to inode structure
|
||||
* @file: pointer to file structure
|
||||
*
|
||||
e
|
||||
* returns 0 on success, <0 on error
|
||||
*/
|
||||
static int mei_open(struct inode *inode, struct file *file)
|
||||
|
@ -244,7 +244,7 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
|
|||
goto out;
|
||||
}
|
||||
|
||||
err = mei_cl_read_start(cl);
|
||||
err = mei_cl_read_start(cl, length);
|
||||
if (err && err != -EBUSY) {
|
||||
dev_dbg(&dev->pdev->dev,
|
||||
"mei start read failure with status = %d\n", err);
|
||||
|
@ -292,9 +292,8 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
|
|||
}
|
||||
/* now copy the data to user space */
|
||||
copy_buffer:
|
||||
dev_dbg(&dev->pdev->dev, "cb->response_buffer size - %d\n",
|
||||
cb->response_buffer.size);
|
||||
dev_dbg(&dev->pdev->dev, "cb->buf_idx - %lu\n", cb->buf_idx);
|
||||
dev_dbg(&dev->pdev->dev, "buf.size = %d buf.idx= %ld\n",
|
||||
cb->response_buffer.size, cb->buf_idx);
|
||||
if (length == 0 || ubuf == NULL || *offset > cb->buf_idx) {
|
||||
rets = -EMSGSIZE;
|
||||
goto free;
|
||||
|
@ -342,11 +341,10 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
|
|||
{
|
||||
struct mei_cl *cl = file->private_data;
|
||||
struct mei_cl_cb *write_cb = NULL;
|
||||
struct mei_msg_hdr mei_hdr;
|
||||
struct mei_device *dev;
|
||||
unsigned long timeout = 0;
|
||||
int rets;
|
||||
int i;
|
||||
int id;
|
||||
|
||||
if (WARN_ON(!cl || !cl->dev))
|
||||
return -ENODEV;
|
||||
|
@ -357,24 +355,24 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
|
|||
|
||||
if (dev->dev_state != MEI_DEV_ENABLED) {
|
||||
rets = -ENODEV;
|
||||
goto err;
|
||||
goto out;
|
||||
}
|
||||
|
||||
i = mei_me_cl_by_id(dev, cl->me_client_id);
|
||||
if (i < 0) {
|
||||
id = mei_me_cl_by_id(dev, cl->me_client_id);
|
||||
if (id < 0) {
|
||||
rets = -ENODEV;
|
||||
goto err;
|
||||
goto out;
|
||||
}
|
||||
if (length > dev->me_clients[i].props.max_msg_length || length <= 0) {
|
||||
if (length > dev->me_clients[id].props.max_msg_length || length <= 0) {
|
||||
rets = -EMSGSIZE;
|
||||
goto err;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (cl->state != MEI_FILE_CONNECTED) {
|
||||
rets = -ENODEV;
|
||||
dev_err(&dev->pdev->dev, "host client = %d, is not connected to ME client = %d",
|
||||
cl->host_client_id, cl->me_client_id);
|
||||
goto err;
|
||||
rets = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
if (cl == &dev->iamthif_cl) {
|
||||
write_cb = mei_amthif_find_read_list_entry(dev, file);
|
||||
|
@ -412,17 +410,15 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
|
|||
if (!write_cb) {
|
||||
dev_err(&dev->pdev->dev, "write cb allocation failed\n");
|
||||
rets = -ENOMEM;
|
||||
goto err;
|
||||
goto out;
|
||||
}
|
||||
rets = mei_io_cb_alloc_req_buf(write_cb, length);
|
||||
if (rets)
|
||||
goto err;
|
||||
|
||||
dev_dbg(&dev->pdev->dev, "cb request size = %zd\n", length);
|
||||
goto out;
|
||||
|
||||
rets = copy_from_user(write_cb->request_buffer.data, ubuf, length);
|
||||
if (rets)
|
||||
goto err;
|
||||
goto out;
|
||||
|
||||
cl->sm_state = 0;
|
||||
if (length == 4 &&
|
||||
|
@ -440,65 +436,17 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
|
|||
if (rets) {
|
||||
dev_err(&dev->pdev->dev,
|
||||
"amthif write failed with status = %d\n", rets);
|
||||
goto err;
|
||||
goto out;
|
||||
}
|
||||
mutex_unlock(&dev->device_lock);
|
||||
return length;
|
||||
}
|
||||
|
||||
write_cb->fop_type = MEI_FOP_WRITE;
|
||||
|
||||
dev_dbg(&dev->pdev->dev, "host client = %d, ME client = %d\n",
|
||||
cl->host_client_id, cl->me_client_id);
|
||||
rets = mei_cl_flow_ctrl_creds(cl);
|
||||
if (rets < 0)
|
||||
goto err;
|
||||
|
||||
if (rets == 0 || !dev->hbuf_is_ready) {
|
||||
write_cb->buf_idx = 0;
|
||||
mei_hdr.msg_complete = 0;
|
||||
cl->writing_state = MEI_WRITING;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev->hbuf_is_ready = false;
|
||||
if (length > mei_hbuf_max_len(dev)) {
|
||||
mei_hdr.length = mei_hbuf_max_len(dev);
|
||||
mei_hdr.msg_complete = 0;
|
||||
} else {
|
||||
mei_hdr.length = length;
|
||||
mei_hdr.msg_complete = 1;
|
||||
}
|
||||
mei_hdr.host_addr = cl->host_client_id;
|
||||
mei_hdr.me_addr = cl->me_client_id;
|
||||
mei_hdr.reserved = 0;
|
||||
|
||||
dev_dbg(&dev->pdev->dev, "write " MEI_HDR_FMT "\n",
|
||||
MEI_HDR_PRM(&mei_hdr));
|
||||
if (mei_write_message(dev, &mei_hdr, write_cb->request_buffer.data)) {
|
||||
rets = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
cl->writing_state = MEI_WRITING;
|
||||
write_cb->buf_idx = mei_hdr.length;
|
||||
|
||||
rets = mei_cl_write(cl, write_cb, false);
|
||||
out:
|
||||
if (mei_hdr.msg_complete) {
|
||||
if (mei_cl_flow_ctrl_reduce(cl)) {
|
||||
rets = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
list_add_tail(&write_cb->list, &dev->write_waiting_list.list);
|
||||
} else {
|
||||
list_add_tail(&write_cb->list, &dev->write_list.list);
|
||||
}
|
||||
|
||||
mutex_unlock(&dev->device_lock);
|
||||
return length;
|
||||
|
||||
err:
|
||||
mutex_unlock(&dev->device_lock);
|
||||
mei_io_cb_free(write_cb);
|
||||
if (rets < 0)
|
||||
mei_io_cb_free(write_cb);
|
||||
return rets;
|
||||
}
|
||||
|
||||
|
@ -753,17 +701,44 @@ static struct miscdevice mei_misc_device = {
|
|||
.minor = MISC_DYNAMIC_MINOR,
|
||||
};
|
||||
|
||||
int mei_register(struct device *dev)
|
||||
{
|
||||
mei_misc_device.parent = dev;
|
||||
return misc_register(&mei_misc_device);
|
||||
}
|
||||
|
||||
void mei_deregister(void)
|
||||
int mei_register(struct mei_device *dev)
|
||||
{
|
||||
int ret;
|
||||
mei_misc_device.parent = &dev->pdev->dev;
|
||||
ret = misc_register(&mei_misc_device);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (mei_dbgfs_register(dev, mei_misc_device.name))
|
||||
dev_err(&dev->pdev->dev, "cannot register debugfs\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mei_register);
|
||||
|
||||
void mei_deregister(struct mei_device *dev)
|
||||
{
|
||||
mei_dbgfs_deregister(dev);
|
||||
misc_deregister(&mei_misc_device);
|
||||
mei_misc_device.parent = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mei_deregister);
|
||||
|
||||
static int __init mei_init(void)
|
||||
{
|
||||
return mei_cl_bus_init();
|
||||
}
|
||||
|
||||
static void __exit mei_exit(void)
|
||||
{
|
||||
mei_cl_bus_exit();
|
||||
}
|
||||
|
||||
module_init(mei_init);
|
||||
module_exit(mei_exit);
|
||||
|
||||
MODULE_AUTHOR("Intel Corporation");
|
||||
MODULE_DESCRIPTION("Intel(R) Management Engine Interface");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
|
|
|
@ -21,9 +21,11 @@
|
|||
#include <linux/watchdog.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/mei.h>
|
||||
#include <linux/mei_cl_bus.h>
|
||||
|
||||
#include "hw.h"
|
||||
#include "hw-me-regs.h"
|
||||
#include "hbm.h"
|
||||
|
||||
/*
|
||||
* watch dog definition
|
||||
|
@ -95,22 +97,14 @@ enum mei_dev_state {
|
|||
MEI_DEV_INITIALIZING = 0,
|
||||
MEI_DEV_INIT_CLIENTS,
|
||||
MEI_DEV_ENABLED,
|
||||
MEI_DEV_RESETING,
|
||||
MEI_DEV_RESETTING,
|
||||
MEI_DEV_DISABLED,
|
||||
MEI_DEV_RECOVERING_FROM_RESET,
|
||||
MEI_DEV_POWER_DOWN,
|
||||
MEI_DEV_POWER_UP
|
||||
};
|
||||
|
||||
const char *mei_dev_state_str(int state);
|
||||
|
||||
/* init clients states*/
|
||||
enum mei_init_clients_states {
|
||||
MEI_START_MESSAGE = 0,
|
||||
MEI_ENUM_CLIENTS_MESSAGE,
|
||||
MEI_CLIENT_PROPERTIES_MESSAGE
|
||||
};
|
||||
|
||||
enum iamthif_states {
|
||||
MEI_IAMTHIF_IDLE,
|
||||
MEI_IAMTHIF_WRITING,
|
||||
|
@ -153,7 +147,7 @@ enum mei_cb_file_ops {
|
|||
/*
|
||||
* Intel MEI message data struct
|
||||
*/
|
||||
struct mei_message_data {
|
||||
struct mei_msg_data {
|
||||
u32 size;
|
||||
unsigned char *data;
|
||||
};
|
||||
|
@ -184,8 +178,8 @@ struct mei_cl_cb {
|
|||
struct list_head list;
|
||||
struct mei_cl *cl;
|
||||
enum mei_cb_file_ops fop_type;
|
||||
struct mei_message_data request_buffer;
|
||||
struct mei_message_data response_buffer;
|
||||
struct mei_msg_data request_buffer;
|
||||
struct mei_msg_data response_buffer;
|
||||
unsigned long buf_idx;
|
||||
unsigned long read_time;
|
||||
struct file *file_object;
|
||||
|
@ -209,15 +203,20 @@ struct mei_cl {
|
|||
enum mei_file_transaction_states writing_state;
|
||||
int sm_state;
|
||||
struct mei_cl_cb *read_cb;
|
||||
|
||||
/* MEI CL bus data */
|
||||
struct mei_cl_device *device;
|
||||
struct list_head device_link;
|
||||
uuid_le device_uuid;
|
||||
};
|
||||
|
||||
/** struct mei_hw_ops
|
||||
*
|
||||
* @host_set_ready - notify FW that host side is ready
|
||||
* @host_is_ready - query for host readiness
|
||||
|
||||
* @hw_is_ready - query if hw is ready
|
||||
* @hw_reset - reset hw
|
||||
* @hw_start - start hw after reset
|
||||
* @hw_config - configure hw
|
||||
|
||||
* @intr_clear - clear pending interrupts
|
||||
|
@ -237,11 +236,11 @@ struct mei_cl {
|
|||
*/
|
||||
struct mei_hw_ops {
|
||||
|
||||
void (*host_set_ready) (struct mei_device *dev);
|
||||
bool (*host_is_ready) (struct mei_device *dev);
|
||||
|
||||
bool (*hw_is_ready) (struct mei_device *dev);
|
||||
void (*hw_reset) (struct mei_device *dev, bool enable);
|
||||
int (*hw_start) (struct mei_device *dev);
|
||||
void (*hw_config) (struct mei_device *dev);
|
||||
|
||||
void (*intr_clear) (struct mei_device *dev);
|
||||
|
@ -263,9 +262,77 @@ struct mei_hw_ops {
|
|||
unsigned char *buf, unsigned long len);
|
||||
};
|
||||
|
||||
/* MEI bus API*/
|
||||
|
||||
/**
|
||||
* struct mei_cl_ops - MEI CL device ops
|
||||
* This structure allows ME host clients to implement technology
|
||||
* specific operations.
|
||||
*
|
||||
* @enable: Enable an MEI CL device. Some devices require specific
|
||||
* HECI commands to initialize completely.
|
||||
* @disable: Disable an MEI CL device.
|
||||
* @send: Tx hook for the device. This allows ME host clients to trap
|
||||
* the device driver buffers before actually physically
|
||||
* pushing it to the ME.
|
||||
* @recv: Rx hook for the device. This allows ME host clients to trap the
|
||||
* ME buffers before forwarding them to the device driver.
|
||||
*/
|
||||
struct mei_cl_ops {
|
||||
int (*enable)(struct mei_cl_device *device);
|
||||
int (*disable)(struct mei_cl_device *device);
|
||||
int (*send)(struct mei_cl_device *device, u8 *buf, size_t length);
|
||||
int (*recv)(struct mei_cl_device *device, u8 *buf, size_t length);
|
||||
};
|
||||
|
||||
struct mei_cl_device *mei_cl_add_device(struct mei_device *dev,
|
||||
uuid_le uuid, char *name,
|
||||
struct mei_cl_ops *ops);
|
||||
void mei_cl_remove_device(struct mei_cl_device *device);
|
||||
|
||||
int __mei_cl_async_send(struct mei_cl *cl, u8 *buf, size_t length);
|
||||
int __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length);
|
||||
int __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length);
|
||||
void mei_cl_bus_rx_event(struct mei_cl *cl);
|
||||
int mei_cl_bus_init(void);
|
||||
void mei_cl_bus_exit(void);
|
||||
|
||||
|
||||
/**
|
||||
* struct mei_cl_device - MEI device handle
|
||||
* An mei_cl_device pointer is returned from mei_add_device()
|
||||
* and links MEI bus clients to their actual ME host client pointer.
|
||||
* Drivers for MEI devices will get an mei_cl_device pointer
|
||||
* when being probed and shall use it for doing ME bus I/O.
|
||||
*
|
||||
* @dev: linux driver model device pointer
|
||||
* @uuid: me client uuid
|
||||
* @cl: mei client
|
||||
* @ops: ME transport ops
|
||||
* @event_cb: Drivers register this callback to get asynchronous ME
|
||||
* events (e.g. Rx buffer pending) notifications.
|
||||
* @events: Events bitmask sent to the driver.
|
||||
* @priv_data: client private data
|
||||
*/
|
||||
struct mei_cl_device {
|
||||
struct device dev;
|
||||
|
||||
struct mei_cl *cl;
|
||||
|
||||
const struct mei_cl_ops *ops;
|
||||
|
||||
struct work_struct event_work;
|
||||
mei_cl_event_cb_t event_cb;
|
||||
void *event_context;
|
||||
unsigned long events;
|
||||
|
||||
void *priv_data;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mei_device - MEI private device struct
|
||||
|
||||
* @hbm_state - state of host bus message protocol
|
||||
* @mem_addr - mem mapped base register address
|
||||
|
||||
* @hbuf_depth - depth of hardware host/write buffer is slots
|
||||
|
@ -296,11 +363,12 @@ struct mei_device {
|
|||
*/
|
||||
struct mutex device_lock; /* device lock */
|
||||
struct delayed_work timer_work; /* MEI timer delayed work (timeouts) */
|
||||
bool recvd_msg;
|
||||
|
||||
bool recvd_hw_ready;
|
||||
/*
|
||||
* waiting queue for receive message from FW
|
||||
*/
|
||||
wait_queue_head_t wait_hw_ready;
|
||||
wait_queue_head_t wait_recvd_msg;
|
||||
wait_queue_head_t wait_stop_wd;
|
||||
|
||||
|
@ -308,7 +376,7 @@ struct mei_device {
|
|||
* mei device states
|
||||
*/
|
||||
enum mei_dev_state dev_state;
|
||||
enum mei_init_clients_states init_clients_state;
|
||||
enum mei_hbm_state hbm_state;
|
||||
u16 init_clients_timer;
|
||||
|
||||
unsigned char rd_msg_buf[MEI_RD_MSG_BUF_SIZE]; /* control messages */
|
||||
|
@ -365,6 +433,14 @@ struct mei_device {
|
|||
|
||||
struct work_struct init_work;
|
||||
|
||||
/* List of bus devices */
|
||||
struct list_head device_list;
|
||||
|
||||
#if IS_ENABLED(CONFIG_DEBUG_FS)
|
||||
struct dentry *dbgfs_dir;
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
|
||||
|
||||
const struct mei_hw_ops *ops;
|
||||
char hw[0] __aligned(sizeof(void *));
|
||||
};
|
||||
|
@ -374,13 +450,23 @@ static inline unsigned long mei_secs_to_jiffies(unsigned long sec)
|
|||
return msecs_to_jiffies(sec * MSEC_PER_SEC);
|
||||
}
|
||||
|
||||
/**
|
||||
* mei_data2slots - get slots - number of (dwords) from a message length
|
||||
* + size of the mei header
|
||||
* @length - size of the messages in bytes
|
||||
* returns - number of slots
|
||||
*/
|
||||
static inline u32 mei_data2slots(size_t length)
|
||||
{
|
||||
return DIV_ROUND_UP(sizeof(struct mei_msg_hdr) + length, 4);
|
||||
}
|
||||
|
||||
/*
|
||||
* mei init function prototypes
|
||||
*/
|
||||
void mei_device_init(struct mei_device *dev);
|
||||
void mei_reset(struct mei_device *dev, int interrupts);
|
||||
int mei_hw_init(struct mei_device *dev);
|
||||
int mei_start(struct mei_device *dev);
|
||||
void mei_stop(struct mei_device *dev);
|
||||
|
||||
/*
|
||||
|
@ -392,8 +478,7 @@ int mei_irq_read_handler(struct mei_device *dev,
|
|||
struct mei_cl_cb *cmpl_list, s32 *slots);
|
||||
|
||||
int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list);
|
||||
|
||||
void mei_irq_complete_handler(struct mei_cl *cl, struct mei_cl_cb *cb_pos);
|
||||
void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list);
|
||||
|
||||
/*
|
||||
* AMTHIF - AMT Host Interface Functions
|
||||
|
@ -417,6 +502,25 @@ struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev,
|
|||
|
||||
void mei_amthif_run_next_cmd(struct mei_device *dev);
|
||||
|
||||
int mei_amthif_irq_write_complete(struct mei_device *dev, s32 *slots,
|
||||
struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list);
|
||||
|
||||
void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb);
|
||||
int mei_amthif_irq_read_msg(struct mei_device *dev,
|
||||
struct mei_msg_hdr *mei_hdr,
|
||||
struct mei_cl_cb *complete_list);
|
||||
int mei_amthif_irq_read(struct mei_device *dev, s32 *slots);
|
||||
|
||||
/*
|
||||
* NFC functions
|
||||
*/
|
||||
int mei_nfc_host_init(struct mei_device *dev);
|
||||
void mei_nfc_host_exit(void);
|
||||
|
||||
/*
|
||||
* NFC Client UUID
|
||||
*/
|
||||
extern const uuid_le mei_nfc_guid;
|
||||
|
||||
int mei_amthif_irq_write_complete(struct mei_device *dev, s32 *slots,
|
||||
struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list);
|
||||
|
@ -455,6 +559,11 @@ static inline void mei_hw_reset(struct mei_device *dev, bool enable)
|
|||
dev->ops->hw_reset(dev, enable);
|
||||
}
|
||||
|
||||
static inline void mei_hw_start(struct mei_device *dev)
|
||||
{
|
||||
dev->ops->hw_start(dev);
|
||||
}
|
||||
|
||||
static inline void mei_clear_interrupts(struct mei_device *dev)
|
||||
{
|
||||
dev->ops->intr_clear(dev);
|
||||
|
@ -470,10 +579,6 @@ static inline void mei_disable_interrupts(struct mei_device *dev)
|
|||
dev->ops->intr_disable(dev);
|
||||
}
|
||||
|
||||
static inline void mei_host_set_ready(struct mei_device *dev)
|
||||
{
|
||||
dev->ops->host_set_ready(dev);
|
||||
}
|
||||
static inline bool mei_host_is_ready(struct mei_device *dev)
|
||||
{
|
||||
return dev->ops->host_is_ready(dev);
|
||||
|
@ -521,8 +626,19 @@ static inline int mei_count_full_read_slots(struct mei_device *dev)
|
|||
return dev->ops->rdbuf_full_slots(dev);
|
||||
}
|
||||
|
||||
int mei_register(struct device *dev);
|
||||
void mei_deregister(void);
|
||||
#if IS_ENABLED(CONFIG_DEBUG_FS)
|
||||
int mei_dbgfs_register(struct mei_device *dev, const char *name);
|
||||
void mei_dbgfs_deregister(struct mei_device *dev);
|
||||
#else
|
||||
static inline int mei_dbgfs_register(struct mei_device *dev, const char *name)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void mei_dbgfs_deregister(struct mei_device *dev) {}
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
|
||||
int mei_register(struct mei_device *dev);
|
||||
void mei_deregister(struct mei_device *dev);
|
||||
|
||||
#define MEI_HDR_FMT "hdr:host=%02d me=%02d len=%d comp=%1d"
|
||||
#define MEI_HDR_PRM(hdr) \
|
||||
|
|
554
drivers/misc/mei/nfc.c
Normal file
554
drivers/misc/mei/nfc.c
Normal file
|
@ -0,0 +1,554 @@
|
|||
/*
|
||||
*
|
||||
* Intel Management Engine Interface (Intel MEI) Linux driver
|
||||
* Copyright (c) 2003-2013, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/mei_cl_bus.h>
|
||||
|
||||
#include "mei_dev.h"
|
||||
#include "client.h"
|
||||
|
||||
struct mei_nfc_cmd {
|
||||
u8 command;
|
||||
u8 status;
|
||||
u16 req_id;
|
||||
u32 reserved;
|
||||
u16 data_size;
|
||||
u8 sub_command;
|
||||
u8 data[];
|
||||
} __packed;
|
||||
|
||||
struct mei_nfc_reply {
|
||||
u8 command;
|
||||
u8 status;
|
||||
u16 req_id;
|
||||
u32 reserved;
|
||||
u16 data_size;
|
||||
u8 sub_command;
|
||||
u8 reply_status;
|
||||
u8 data[];
|
||||
} __packed;
|
||||
|
||||
struct mei_nfc_if_version {
|
||||
u8 radio_version_sw[3];
|
||||
u8 reserved[3];
|
||||
u8 radio_version_hw[3];
|
||||
u8 i2c_addr;
|
||||
u8 fw_ivn;
|
||||
u8 vendor_id;
|
||||
u8 radio_type;
|
||||
} __packed;
|
||||
|
||||
struct mei_nfc_connect {
|
||||
u8 fw_ivn;
|
||||
u8 vendor_id;
|
||||
} __packed;
|
||||
|
||||
struct mei_nfc_connect_resp {
|
||||
u8 fw_ivn;
|
||||
u8 vendor_id;
|
||||
u16 me_major;
|
||||
u16 me_minor;
|
||||
u16 me_hotfix;
|
||||
u16 me_build;
|
||||
} __packed;
|
||||
|
||||
struct mei_nfc_hci_hdr {
|
||||
u8 cmd;
|
||||
u8 status;
|
||||
u16 req_id;
|
||||
u32 reserved;
|
||||
u16 data_size;
|
||||
} __packed;
|
||||
|
||||
#define MEI_NFC_CMD_MAINTENANCE 0x00
|
||||
#define MEI_NFC_CMD_HCI_SEND 0x01
|
||||
#define MEI_NFC_CMD_HCI_RECV 0x02
|
||||
|
||||
#define MEI_NFC_SUBCMD_CONNECT 0x00
|
||||
#define MEI_NFC_SUBCMD_IF_VERSION 0x01
|
||||
|
||||
#define MEI_NFC_HEADER_SIZE 10
|
||||
|
||||
/** mei_nfc_dev - NFC mei device
|
||||
*
|
||||
* @cl: NFC host client
|
||||
* @cl_info: NFC info host client
|
||||
* @init_work: perform connection to the info client
|
||||
* @fw_ivn: NFC Intervace Version Number
|
||||
* @vendor_id: NFC manufacturer ID
|
||||
* @radio_type: NFC radio type
|
||||
*/
|
||||
struct mei_nfc_dev {
|
||||
struct mei_cl *cl;
|
||||
struct mei_cl *cl_info;
|
||||
struct work_struct init_work;
|
||||
wait_queue_head_t send_wq;
|
||||
u8 fw_ivn;
|
||||
u8 vendor_id;
|
||||
u8 radio_type;
|
||||
char *bus_name;
|
||||
|
||||
u16 req_id;
|
||||
u16 recv_req_id;
|
||||
};
|
||||
|
||||
static struct mei_nfc_dev nfc_dev;
|
||||
|
||||
/* UUIDs for NFC F/W clients */
|
||||
const uuid_le mei_nfc_guid = UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50,
|
||||
0x94, 0xd4, 0x50, 0x26,
|
||||
0x67, 0x23, 0x77, 0x5c);
|
||||
|
||||
static const uuid_le mei_nfc_info_guid = UUID_LE(0xd2de1625, 0x382d, 0x417d,
|
||||
0x48, 0xa4, 0xef, 0xab,
|
||||
0xba, 0x8a, 0x12, 0x06);
|
||||
|
||||
/* Vendors */
|
||||
#define MEI_NFC_VENDOR_INSIDE 0x00
|
||||
#define MEI_NFC_VENDOR_NXP 0x01
|
||||
|
||||
/* Radio types */
|
||||
#define MEI_NFC_VENDOR_INSIDE_UREAD 0x00
|
||||
#define MEI_NFC_VENDOR_NXP_PN544 0x01
|
||||
|
||||
static void mei_nfc_free(struct mei_nfc_dev *ndev)
|
||||
{
|
||||
if (ndev->cl) {
|
||||
list_del(&ndev->cl->device_link);
|
||||
mei_cl_unlink(ndev->cl);
|
||||
kfree(ndev->cl);
|
||||
}
|
||||
|
||||
if (ndev->cl_info) {
|
||||
list_del(&ndev->cl_info->device_link);
|
||||
mei_cl_unlink(ndev->cl_info);
|
||||
kfree(ndev->cl_info);
|
||||
}
|
||||
}
|
||||
|
||||
static int mei_nfc_build_bus_name(struct mei_nfc_dev *ndev)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
|
||||
if (!ndev->cl)
|
||||
return -ENODEV;
|
||||
|
||||
dev = ndev->cl->dev;
|
||||
|
||||
switch (ndev->vendor_id) {
|
||||
case MEI_NFC_VENDOR_INSIDE:
|
||||
switch (ndev->radio_type) {
|
||||
case MEI_NFC_VENDOR_INSIDE_UREAD:
|
||||
ndev->bus_name = "microread";
|
||||
return 0;
|
||||
|
||||
default:
|
||||
dev_err(&dev->pdev->dev, "Unknow radio type 0x%x\n",
|
||||
ndev->radio_type);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
case MEI_NFC_VENDOR_NXP:
|
||||
switch (ndev->radio_type) {
|
||||
case MEI_NFC_VENDOR_NXP_PN544:
|
||||
ndev->bus_name = "pn544";
|
||||
return 0;
|
||||
default:
|
||||
dev_err(&dev->pdev->dev, "Unknow radio type 0x%x\n",
|
||||
ndev->radio_type);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
default:
|
||||
dev_err(&dev->pdev->dev, "Unknow vendor ID 0x%x\n",
|
||||
ndev->vendor_id);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mei_nfc_connect(struct mei_nfc_dev *ndev)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_cl *cl;
|
||||
struct mei_nfc_cmd *cmd, *reply;
|
||||
struct mei_nfc_connect *connect;
|
||||
struct mei_nfc_connect_resp *connect_resp;
|
||||
size_t connect_length, connect_resp_length;
|
||||
int bytes_recv, ret;
|
||||
|
||||
cl = ndev->cl;
|
||||
dev = cl->dev;
|
||||
|
||||
connect_length = sizeof(struct mei_nfc_cmd) +
|
||||
sizeof(struct mei_nfc_connect);
|
||||
|
||||
connect_resp_length = sizeof(struct mei_nfc_cmd) +
|
||||
sizeof(struct mei_nfc_connect_resp);
|
||||
|
||||
cmd = kzalloc(connect_length, GFP_KERNEL);
|
||||
if (!cmd)
|
||||
return -ENOMEM;
|
||||
connect = (struct mei_nfc_connect *)cmd->data;
|
||||
|
||||
reply = kzalloc(connect_resp_length, GFP_KERNEL);
|
||||
if (!reply) {
|
||||
kfree(cmd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
connect_resp = (struct mei_nfc_connect_resp *)reply->data;
|
||||
|
||||
cmd->command = MEI_NFC_CMD_MAINTENANCE;
|
||||
cmd->data_size = 3;
|
||||
cmd->sub_command = MEI_NFC_SUBCMD_CONNECT;
|
||||
connect->fw_ivn = ndev->fw_ivn;
|
||||
connect->vendor_id = ndev->vendor_id;
|
||||
|
||||
ret = __mei_cl_send(cl, (u8 *)cmd, connect_length);
|
||||
if (ret < 0) {
|
||||
dev_err(&dev->pdev->dev, "Could not send connect cmd\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
bytes_recv = __mei_cl_recv(cl, (u8 *)reply, connect_resp_length);
|
||||
if (bytes_recv < 0) {
|
||||
dev_err(&dev->pdev->dev, "Could not read connect response\n");
|
||||
ret = bytes_recv;
|
||||
goto err;
|
||||
}
|
||||
|
||||
dev_info(&dev->pdev->dev, "IVN 0x%x Vendor ID 0x%x\n",
|
||||
connect_resp->fw_ivn, connect_resp->vendor_id);
|
||||
|
||||
dev_info(&dev->pdev->dev, "ME FW %d.%d.%d.%d\n",
|
||||
connect_resp->me_major, connect_resp->me_minor,
|
||||
connect_resp->me_hotfix, connect_resp->me_build);
|
||||
|
||||
ret = 0;
|
||||
|
||||
err:
|
||||
kfree(reply);
|
||||
kfree(cmd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mei_nfc_if_version(struct mei_nfc_dev *ndev)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_cl *cl;
|
||||
|
||||
struct mei_nfc_cmd cmd;
|
||||
struct mei_nfc_reply *reply = NULL;
|
||||
struct mei_nfc_if_version *version;
|
||||
size_t if_version_length;
|
||||
int bytes_recv, ret;
|
||||
|
||||
cl = ndev->cl_info;
|
||||
dev = cl->dev;
|
||||
|
||||
memset(&cmd, 0, sizeof(struct mei_nfc_cmd));
|
||||
cmd.command = MEI_NFC_CMD_MAINTENANCE;
|
||||
cmd.data_size = 1;
|
||||
cmd.sub_command = MEI_NFC_SUBCMD_IF_VERSION;
|
||||
|
||||
ret = __mei_cl_send(cl, (u8 *)&cmd, sizeof(struct mei_nfc_cmd));
|
||||
if (ret < 0) {
|
||||
dev_err(&dev->pdev->dev, "Could not send IF version cmd\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* to be sure on the stack we alloc memory */
|
||||
if_version_length = sizeof(struct mei_nfc_reply) +
|
||||
sizeof(struct mei_nfc_if_version);
|
||||
|
||||
reply = kzalloc(if_version_length, GFP_KERNEL);
|
||||
if (!reply)
|
||||
return -ENOMEM;
|
||||
|
||||
bytes_recv = __mei_cl_recv(cl, (u8 *)reply, if_version_length);
|
||||
if (bytes_recv < 0 || bytes_recv < sizeof(struct mei_nfc_reply)) {
|
||||
dev_err(&dev->pdev->dev, "Could not read IF version\n");
|
||||
ret = -EIO;
|
||||
goto err;
|
||||
}
|
||||
|
||||
version = (struct mei_nfc_if_version *)reply->data;
|
||||
|
||||
ndev->fw_ivn = version->fw_ivn;
|
||||
ndev->vendor_id = version->vendor_id;
|
||||
ndev->radio_type = version->radio_type;
|
||||
|
||||
err:
|
||||
kfree(reply);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mei_nfc_enable(struct mei_cl_device *cldev)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_nfc_dev *ndev = &nfc_dev;
|
||||
int ret;
|
||||
|
||||
dev = ndev->cl->dev;
|
||||
|
||||
ret = mei_nfc_connect(ndev);
|
||||
if (ret < 0) {
|
||||
dev_err(&dev->pdev->dev, "Could not connect to NFC");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mei_nfc_disable(struct mei_cl_device *cldev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mei_nfc_send(struct mei_cl_device *cldev, u8 *buf, size_t length)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_nfc_dev *ndev;
|
||||
struct mei_nfc_hci_hdr *hdr;
|
||||
u8 *mei_buf;
|
||||
int err;
|
||||
|
||||
ndev = (struct mei_nfc_dev *) cldev->priv_data;
|
||||
dev = ndev->cl->dev;
|
||||
|
||||
mei_buf = kzalloc(length + MEI_NFC_HEADER_SIZE, GFP_KERNEL);
|
||||
if (!mei_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
hdr = (struct mei_nfc_hci_hdr *) mei_buf;
|
||||
hdr->cmd = MEI_NFC_CMD_HCI_SEND;
|
||||
hdr->status = 0;
|
||||
hdr->req_id = ndev->req_id;
|
||||
hdr->reserved = 0;
|
||||
hdr->data_size = length;
|
||||
|
||||
memcpy(mei_buf + MEI_NFC_HEADER_SIZE, buf, length);
|
||||
|
||||
err = __mei_cl_send(ndev->cl, mei_buf, length + MEI_NFC_HEADER_SIZE);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
kfree(mei_buf);
|
||||
|
||||
if (!wait_event_interruptible_timeout(ndev->send_wq,
|
||||
ndev->recv_req_id == ndev->req_id, HZ)) {
|
||||
dev_err(&dev->pdev->dev, "NFC MEI command timeout\n");
|
||||
err = -ETIMEDOUT;
|
||||
} else {
|
||||
ndev->req_id++;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mei_nfc_recv(struct mei_cl_device *cldev, u8 *buf, size_t length)
|
||||
{
|
||||
struct mei_nfc_dev *ndev;
|
||||
struct mei_nfc_hci_hdr *hci_hdr;
|
||||
int received_length;
|
||||
|
||||
ndev = (struct mei_nfc_dev *)cldev->priv_data;
|
||||
|
||||
received_length = __mei_cl_recv(ndev->cl, buf, length);
|
||||
if (received_length < 0)
|
||||
return received_length;
|
||||
|
||||
hci_hdr = (struct mei_nfc_hci_hdr *) buf;
|
||||
|
||||
if (hci_hdr->cmd == MEI_NFC_CMD_HCI_SEND) {
|
||||
ndev->recv_req_id = hci_hdr->req_id;
|
||||
wake_up(&ndev->send_wq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return received_length;
|
||||
}
|
||||
|
||||
static struct mei_cl_ops nfc_ops = {
|
||||
.enable = mei_nfc_enable,
|
||||
.disable = mei_nfc_disable,
|
||||
.send = mei_nfc_send,
|
||||
.recv = mei_nfc_recv,
|
||||
};
|
||||
|
||||
static void mei_nfc_init(struct work_struct *work)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_cl_device *cldev;
|
||||
struct mei_nfc_dev *ndev;
|
||||
struct mei_cl *cl_info;
|
||||
|
||||
ndev = container_of(work, struct mei_nfc_dev, init_work);
|
||||
|
||||
cl_info = ndev->cl_info;
|
||||
dev = cl_info->dev;
|
||||
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
if (mei_cl_connect(cl_info, NULL) < 0) {
|
||||
mutex_unlock(&dev->device_lock);
|
||||
dev_err(&dev->pdev->dev,
|
||||
"Could not connect to the NFC INFO ME client");
|
||||
|
||||
goto err;
|
||||
}
|
||||
|
||||
mutex_unlock(&dev->device_lock);
|
||||
|
||||
if (mei_nfc_if_version(ndev) < 0) {
|
||||
dev_err(&dev->pdev->dev, "Could not get the NFC interfave version");
|
||||
|
||||
goto err;
|
||||
}
|
||||
|
||||
dev_info(&dev->pdev->dev,
|
||||
"NFC MEI VERSION: IVN 0x%x Vendor ID 0x%x Type 0x%x\n",
|
||||
ndev->fw_ivn, ndev->vendor_id, ndev->radio_type);
|
||||
|
||||
mutex_lock(&dev->device_lock);
|
||||
|
||||
if (mei_cl_disconnect(cl_info) < 0) {
|
||||
mutex_unlock(&dev->device_lock);
|
||||
dev_err(&dev->pdev->dev,
|
||||
"Could not disconnect the NFC INFO ME client");
|
||||
|
||||
goto err;
|
||||
}
|
||||
|
||||
mutex_unlock(&dev->device_lock);
|
||||
|
||||
if (mei_nfc_build_bus_name(ndev) < 0) {
|
||||
dev_err(&dev->pdev->dev,
|
||||
"Could not build the bus ID name\n");
|
||||
return;
|
||||
}
|
||||
|
||||
cldev = mei_cl_add_device(dev, mei_nfc_guid, ndev->bus_name, &nfc_ops);
|
||||
if (!cldev) {
|
||||
dev_err(&dev->pdev->dev,
|
||||
"Could not add the NFC device to the MEI bus\n");
|
||||
|
||||
goto err;
|
||||
}
|
||||
|
||||
cldev->priv_data = ndev;
|
||||
|
||||
|
||||
return;
|
||||
|
||||
err:
|
||||
mei_nfc_free(ndev);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
int mei_nfc_host_init(struct mei_device *dev)
|
||||
{
|
||||
struct mei_nfc_dev *ndev = &nfc_dev;
|
||||
struct mei_cl *cl_info, *cl = NULL;
|
||||
int i, ret;
|
||||
|
||||
/* already initialzed */
|
||||
if (ndev->cl_info)
|
||||
return 0;
|
||||
|
||||
cl_info = mei_cl_allocate(dev);
|
||||
cl = mei_cl_allocate(dev);
|
||||
|
||||
if (!cl || !cl_info) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* check for valid client id */
|
||||
i = mei_me_cl_by_uuid(dev, &mei_nfc_info_guid);
|
||||
if (i < 0) {
|
||||
dev_info(&dev->pdev->dev, "nfc: failed to find the client\n");
|
||||
ret = -ENOENT;
|
||||
goto err;
|
||||
}
|
||||
|
||||
cl_info->me_client_id = dev->me_clients[i].client_id;
|
||||
|
||||
ret = mei_cl_link(cl_info, MEI_HOST_CLIENT_ID_ANY);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
cl_info->device_uuid = mei_nfc_info_guid;
|
||||
|
||||
list_add_tail(&cl_info->device_link, &dev->device_list);
|
||||
|
||||
/* check for valid client id */
|
||||
i = mei_me_cl_by_uuid(dev, &mei_nfc_guid);
|
||||
if (i < 0) {
|
||||
dev_info(&dev->pdev->dev, "nfc: failed to find the client\n");
|
||||
ret = -ENOENT;
|
||||
goto err;
|
||||
}
|
||||
|
||||
cl->me_client_id = dev->me_clients[i].client_id;
|
||||
|
||||
ret = mei_cl_link(cl, MEI_HOST_CLIENT_ID_ANY);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
cl->device_uuid = mei_nfc_guid;
|
||||
|
||||
list_add_tail(&cl->device_link, &dev->device_list);
|
||||
|
||||
ndev->cl_info = cl_info;
|
||||
ndev->cl = cl;
|
||||
ndev->req_id = 1;
|
||||
|
||||
INIT_WORK(&ndev->init_work, mei_nfc_init);
|
||||
init_waitqueue_head(&ndev->send_wq);
|
||||
schedule_work(&ndev->init_work);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
mei_nfc_free(ndev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void mei_nfc_host_exit(void)
|
||||
{
|
||||
struct mei_nfc_dev *ndev = &nfc_dev;
|
||||
|
||||
if (ndev->cl && ndev->cl->device)
|
||||
mei_cl_remove_device(ndev->cl->device);
|
||||
|
||||
mei_nfc_free(ndev);
|
||||
}
|
|
@ -47,7 +47,7 @@
|
|||
static struct pci_dev *mei_pdev;
|
||||
|
||||
/* mei_pci_tbl - PCI Device ID Table */
|
||||
static DEFINE_PCI_DEVICE_TABLE(mei_pci_tbl) = {
|
||||
static DEFINE_PCI_DEVICE_TABLE(mei_me_pci_tbl) = {
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82946GZ)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82G35)},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82Q965)},
|
||||
|
@ -86,7 +86,7 @@ static DEFINE_PCI_DEVICE_TABLE(mei_pci_tbl) = {
|
|||
{0, }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, mei_pci_tbl);
|
||||
MODULE_DEVICE_TABLE(pci, mei_me_pci_tbl);
|
||||
|
||||
static DEFINE_MUTEX(mei_mutex);
|
||||
|
||||
|
@ -97,7 +97,7 @@ static DEFINE_MUTEX(mei_mutex);
|
|||
*
|
||||
* returns true if ME Interface is valid, false otherwise
|
||||
*/
|
||||
static bool mei_quirk_probe(struct pci_dev *pdev,
|
||||
static bool mei_me_quirk_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *ent)
|
||||
{
|
||||
u32 reg;
|
||||
|
@ -119,7 +119,7 @@ static bool mei_quirk_probe(struct pci_dev *pdev,
|
|||
*
|
||||
* returns 0 on success, <0 on failure.
|
||||
*/
|
||||
static int mei_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_me_hw *hw;
|
||||
|
@ -127,7 +127,7 @@ static int mei_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
|
||||
mutex_lock(&mei_mutex);
|
||||
|
||||
if (!mei_quirk_probe(pdev, ent)) {
|
||||
if (!mei_me_quirk_probe(pdev, ent)) {
|
||||
err = -ENODEV;
|
||||
goto end;
|
||||
}
|
||||
|
@ -184,20 +184,19 @@ static int mei_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
goto disable_msi;
|
||||
}
|
||||
|
||||
if (mei_hw_init(dev)) {
|
||||
if (mei_start(dev)) {
|
||||
dev_err(&pdev->dev, "init hw failure.\n");
|
||||
err = -ENODEV;
|
||||
goto release_irq;
|
||||
}
|
||||
|
||||
err = mei_register(&pdev->dev);
|
||||
err = mei_register(dev);
|
||||
if (err)
|
||||
goto release_irq;
|
||||
|
||||
mei_pdev = pdev;
|
||||
pci_set_drvdata(pdev, dev);
|
||||
|
||||
|
||||
schedule_delayed_work(&dev->timer_work, HZ);
|
||||
|
||||
mutex_unlock(&mei_mutex);
|
||||
|
@ -233,7 +232,7 @@ end:
|
|||
* mei_remove is called by the PCI subsystem to alert the driver
|
||||
* that it should release a PCI device.
|
||||
*/
|
||||
static void mei_remove(struct pci_dev *pdev)
|
||||
static void mei_me_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
struct mei_me_hw *hw;
|
||||
|
@ -253,8 +252,6 @@ static void mei_remove(struct pci_dev *pdev)
|
|||
|
||||
mei_pdev = NULL;
|
||||
|
||||
mei_watchdog_unregister(dev);
|
||||
|
||||
/* disable interrupts */
|
||||
mei_disable_interrupts(dev);
|
||||
|
||||
|
@ -265,16 +262,17 @@ static void mei_remove(struct pci_dev *pdev)
|
|||
if (hw->mem_addr)
|
||||
pci_iounmap(pdev, hw->mem_addr);
|
||||
|
||||
mei_deregister(dev);
|
||||
|
||||
kfree(dev);
|
||||
|
||||
pci_release_regions(pdev);
|
||||
pci_disable_device(pdev);
|
||||
|
||||
mei_deregister();
|
||||
|
||||
}
|
||||
#ifdef CONFIG_PM
|
||||
static int mei_pci_suspend(struct device *device)
|
||||
static int mei_me_pci_suspend(struct device *device)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(device);
|
||||
struct mei_device *dev = pci_get_drvdata(pdev);
|
||||
|
@ -294,7 +292,7 @@ static int mei_pci_suspend(struct device *device)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int mei_pci_resume(struct device *device)
|
||||
static int mei_me_pci_resume(struct device *device)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(device);
|
||||
struct mei_device *dev;
|
||||
|
@ -334,24 +332,24 @@ static int mei_pci_resume(struct device *device)
|
|||
|
||||
return err;
|
||||
}
|
||||
static SIMPLE_DEV_PM_OPS(mei_pm_ops, mei_pci_suspend, mei_pci_resume);
|
||||
#define MEI_PM_OPS (&mei_pm_ops)
|
||||
static SIMPLE_DEV_PM_OPS(mei_me_pm_ops, mei_me_pci_suspend, mei_me_pci_resume);
|
||||
#define MEI_ME_PM_OPS (&mei_me_pm_ops)
|
||||
#else
|
||||
#define MEI_PM_OPS NULL
|
||||
#define MEI_ME_PM_OPS NULL
|
||||
#endif /* CONFIG_PM */
|
||||
/*
|
||||
* PCI driver structure
|
||||
*/
|
||||
static struct pci_driver mei_driver = {
|
||||
static struct pci_driver mei_me_driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.id_table = mei_pci_tbl,
|
||||
.probe = mei_probe,
|
||||
.remove = mei_remove,
|
||||
.shutdown = mei_remove,
|
||||
.driver.pm = MEI_PM_OPS,
|
||||
.id_table = mei_me_pci_tbl,
|
||||
.probe = mei_me_probe,
|
||||
.remove = mei_me_remove,
|
||||
.shutdown = mei_me_remove,
|
||||
.driver.pm = MEI_ME_PM_OPS,
|
||||
};
|
||||
|
||||
module_pci_driver(mei_driver);
|
||||
module_pci_driver(mei_me_driver);
|
||||
|
||||
MODULE_AUTHOR("Intel Corporation");
|
||||
MODULE_DESCRIPTION("Intel(R) Management Engine Interface");
|
||||
|
|
|
@ -317,7 +317,8 @@ end:
|
|||
*
|
||||
* returns 0 if success, negative errno code for failure
|
||||
*/
|
||||
static int mei_wd_ops_set_timeout(struct watchdog_device *wd_dev, unsigned int timeout)
|
||||
static int mei_wd_ops_set_timeout(struct watchdog_device *wd_dev,
|
||||
unsigned int timeout)
|
||||
{
|
||||
struct mei_device *dev;
|
||||
|
||||
|
|
|
@ -417,24 +417,26 @@ static int tsl2550_remove(struct i2c_client *client)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
static int tsl2550_suspend(struct i2c_client *client, pm_message_t mesg)
|
||||
static int tsl2550_suspend(struct device *dev)
|
||||
{
|
||||
return tsl2550_set_power_state(client, 0);
|
||||
return tsl2550_set_power_state(to_i2c_client(dev), 0);
|
||||
}
|
||||
|
||||
static int tsl2550_resume(struct i2c_client *client)
|
||||
static int tsl2550_resume(struct device *dev)
|
||||
{
|
||||
return tsl2550_set_power_state(client, 1);
|
||||
return tsl2550_set_power_state(to_i2c_client(dev), 1);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(tsl2550_pm_ops, tsl2550_suspend, tsl2550_resume);
|
||||
#define TSL2550_PM_OPS (&tsl2550_pm_ops)
|
||||
|
||||
#else
|
||||
|
||||
#define tsl2550_suspend NULL
|
||||
#define tsl2550_resume NULL
|
||||
#define TSL2550_PM_OPS NULL
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static const struct i2c_device_id tsl2550_id[] = {
|
||||
{ "tsl2550", 0 },
|
||||
|
@ -446,9 +448,8 @@ static struct i2c_driver tsl2550_driver = {
|
|||
.driver = {
|
||||
.name = TSL2550_DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = TSL2550_PM_OPS,
|
||||
},
|
||||
.suspend = tsl2550_suspend,
|
||||
.resume = tsl2550_resume,
|
||||
.probe = tsl2550_probe,
|
||||
.remove = tsl2550_remove,
|
||||
.id_table = tsl2550_id,
|
||||
|
|
|
@ -543,25 +543,7 @@ static struct pcmcia_driver sdricoh_driver = {
|
|||
.suspend = sdricoh_pcmcia_suspend,
|
||||
.resume = sdricoh_pcmcia_resume,
|
||||
};
|
||||
|
||||
/*****************************************************************************\
|
||||
* *
|
||||
* Driver init/exit *
|
||||
* *
|
||||
\*****************************************************************************/
|
||||
|
||||
static int __init sdricoh_drv_init(void)
|
||||
{
|
||||
return pcmcia_register_driver(&sdricoh_driver);
|
||||
}
|
||||
|
||||
static void __exit sdricoh_drv_exit(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&sdricoh_driver);
|
||||
}
|
||||
|
||||
module_init(sdricoh_drv_init);
|
||||
module_exit(sdricoh_drv_exit);
|
||||
module_pcmcia_driver(sdricoh_driver);
|
||||
|
||||
module_param(switchlocked, uint, 0444);
|
||||
|
||||
|
|
|
@ -333,16 +333,4 @@ static struct pcmcia_driver com20020_cs_driver = {
|
|||
.suspend = com20020_suspend,
|
||||
.resume = com20020_resume,
|
||||
};
|
||||
|
||||
static int __init init_com20020_cs(void)
|
||||
{
|
||||
return pcmcia_register_driver(&com20020_cs_driver);
|
||||
}
|
||||
|
||||
static void __exit exit_com20020_cs(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&com20020_cs_driver);
|
||||
}
|
||||
|
||||
module_init(init_com20020_cs);
|
||||
module_exit(exit_com20020_cs);
|
||||
module_pcmcia_driver(com20020_cs_driver);
|
||||
|
|
|
@ -316,15 +316,4 @@ static struct pcmcia_driver ems_pcmcia_driver = {
|
|||
.remove = ems_pcmcia_remove,
|
||||
.id_table = ems_pcmcia_tbl,
|
||||
};
|
||||
|
||||
static int __init ems_pcmcia_init(void)
|
||||
{
|
||||
return pcmcia_register_driver(&ems_pcmcia_driver);
|
||||
}
|
||||
module_init(ems_pcmcia_init);
|
||||
|
||||
static void __exit ems_pcmcia_exit(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&ems_pcmcia_driver);
|
||||
}
|
||||
module_exit(ems_pcmcia_exit);
|
||||
module_pcmcia_driver(ems_pcmcia_driver);
|
||||
|
|
|
@ -740,15 +740,4 @@ static struct pcmcia_driver pcan_driver = {
|
|||
.remove = pcan_remove,
|
||||
.id_table = pcan_table,
|
||||
};
|
||||
|
||||
static int __init pcan_init(void)
|
||||
{
|
||||
return pcmcia_register_driver(&pcan_driver);
|
||||
}
|
||||
module_init(pcan_init);
|
||||
|
||||
static void __exit pcan_exit(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&pcan_driver);
|
||||
}
|
||||
module_exit(pcan_exit);
|
||||
module_pcmcia_driver(pcan_driver);
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
#include "softing_platform.h"
|
||||
|
||||
static int softingcs_index;
|
||||
static spinlock_t softingcs_index_lock;
|
||||
static DEFINE_SPINLOCK(softingcs_index_lock);
|
||||
|
||||
static int softingcs_reset(struct platform_device *pdev, int v);
|
||||
static int softingcs_enable_irq(struct platform_device *pdev, int v);
|
||||
|
@ -340,19 +340,7 @@ static struct pcmcia_driver softingcs_driver = {
|
|||
.remove = softingcs_remove,
|
||||
};
|
||||
|
||||
static int __init softingcs_start(void)
|
||||
{
|
||||
spin_lock_init(&softingcs_index_lock);
|
||||
return pcmcia_register_driver(&softingcs_driver);
|
||||
}
|
||||
|
||||
static void __exit softingcs_stop(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&softingcs_driver);
|
||||
}
|
||||
|
||||
module_init(softingcs_start);
|
||||
module_exit(softingcs_stop);
|
||||
module_pcmcia_driver(softingcs_driver);
|
||||
|
||||
MODULE_DESCRIPTION("softing CANcard driver"
|
||||
", links PCMCIA card to softing driver");
|
||||
|
|
|
@ -1165,16 +1165,4 @@ static struct pcmcia_driver tc574_driver = {
|
|||
.suspend = tc574_suspend,
|
||||
.resume = tc574_resume,
|
||||
};
|
||||
|
||||
static int __init init_tc574(void)
|
||||
{
|
||||
return pcmcia_register_driver(&tc574_driver);
|
||||
}
|
||||
|
||||
static void __exit exit_tc574(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&tc574_driver);
|
||||
}
|
||||
|
||||
module_init(init_tc574);
|
||||
module_exit(exit_tc574);
|
||||
module_pcmcia_driver(tc574_driver);
|
||||
|
|
|
@ -928,16 +928,4 @@ static struct pcmcia_driver tc589_driver = {
|
|||
.suspend = tc589_suspend,
|
||||
.resume = tc589_resume,
|
||||
};
|
||||
|
||||
static int __init init_tc589(void)
|
||||
{
|
||||
return pcmcia_register_driver(&tc589_driver);
|
||||
}
|
||||
|
||||
static void __exit exit_tc589(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&tc589_driver);
|
||||
}
|
||||
|
||||
module_init(init_tc589);
|
||||
module_exit(exit_tc589);
|
||||
module_pcmcia_driver(tc589_driver);
|
||||
|
|
|
@ -728,19 +728,7 @@ static struct pcmcia_driver axnet_cs_driver = {
|
|||
.suspend = axnet_suspend,
|
||||
.resume = axnet_resume,
|
||||
};
|
||||
|
||||
static int __init init_axnet_cs(void)
|
||||
{
|
||||
return pcmcia_register_driver(&axnet_cs_driver);
|
||||
}
|
||||
|
||||
static void __exit exit_axnet_cs(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&axnet_cs_driver);
|
||||
}
|
||||
|
||||
module_init(init_axnet_cs);
|
||||
module_exit(exit_axnet_cs);
|
||||
module_pcmcia_driver(axnet_cs_driver);
|
||||
|
||||
/*====================================================================*/
|
||||
|
||||
|
|
|
@ -1694,16 +1694,4 @@ static struct pcmcia_driver pcnet_driver = {
|
|||
.suspend = pcnet_suspend,
|
||||
.resume = pcnet_resume,
|
||||
};
|
||||
|
||||
static int __init init_pcnet_cs(void)
|
||||
{
|
||||
return pcmcia_register_driver(&pcnet_driver);
|
||||
}
|
||||
|
||||
static void __exit exit_pcnet_cs(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&pcnet_driver);
|
||||
}
|
||||
|
||||
module_init(init_pcnet_cs);
|
||||
module_exit(exit_pcnet_cs);
|
||||
module_pcmcia_driver(pcnet_driver);
|
||||
|
|
|
@ -1508,16 +1508,4 @@ static struct pcmcia_driver nmclan_cs_driver = {
|
|||
.suspend = nmclan_suspend,
|
||||
.resume = nmclan_resume,
|
||||
};
|
||||
|
||||
static int __init init_nmclan_cs(void)
|
||||
{
|
||||
return pcmcia_register_driver(&nmclan_cs_driver);
|
||||
}
|
||||
|
||||
static void __exit exit_nmclan_cs(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&nmclan_cs_driver);
|
||||
}
|
||||
|
||||
module_init(init_nmclan_cs);
|
||||
module_exit(exit_nmclan_cs);
|
||||
module_pcmcia_driver(nmclan_cs_driver);
|
||||
|
|
|
@ -705,19 +705,7 @@ static struct pcmcia_driver fmvj18x_cs_driver = {
|
|||
.suspend = fmvj18x_suspend,
|
||||
.resume = fmvj18x_resume,
|
||||
};
|
||||
|
||||
static int __init init_fmvj18x_cs(void)
|
||||
{
|
||||
return pcmcia_register_driver(&fmvj18x_cs_driver);
|
||||
}
|
||||
|
||||
static void __exit exit_fmvj18x_cs(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&fmvj18x_cs_driver);
|
||||
}
|
||||
|
||||
module_init(init_fmvj18x_cs);
|
||||
module_exit(exit_fmvj18x_cs);
|
||||
module_pcmcia_driver(fmvj18x_cs_driver);
|
||||
|
||||
/*====================================================================*/
|
||||
|
||||
|
|
|
@ -2054,16 +2054,4 @@ static struct pcmcia_driver smc91c92_cs_driver = {
|
|||
.suspend = smc91c92_suspend,
|
||||
.resume = smc91c92_resume,
|
||||
};
|
||||
|
||||
static int __init init_smc91c92_cs(void)
|
||||
{
|
||||
return pcmcia_register_driver(&smc91c92_cs_driver);
|
||||
}
|
||||
|
||||
static void __exit exit_smc91c92_cs(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&smc91c92_cs_driver);
|
||||
}
|
||||
|
||||
module_init(init_smc91c92_cs);
|
||||
module_exit(exit_smc91c92_cs);
|
||||
module_pcmcia_driver(smc91c92_cs_driver);
|
||||
|
|
|
@ -1775,21 +1775,7 @@ static struct pcmcia_driver xirc2ps_cs_driver = {
|
|||
.suspend = xirc2ps_suspend,
|
||||
.resume = xirc2ps_resume,
|
||||
};
|
||||
|
||||
static int __init
|
||||
init_xirc2ps_cs(void)
|
||||
{
|
||||
return pcmcia_register_driver(&xirc2ps_cs_driver);
|
||||
}
|
||||
|
||||
static void __exit
|
||||
exit_xirc2ps_cs(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&xirc2ps_cs_driver);
|
||||
}
|
||||
|
||||
module_init(init_xirc2ps_cs);
|
||||
module_exit(exit_xirc2ps_cs);
|
||||
module_pcmcia_driver(xirc2ps_cs_driver);
|
||||
|
||||
#ifndef MODULE
|
||||
static int __init setup_xirc2ps_cs(char *str)
|
||||
|
|
|
@ -180,16 +180,7 @@ static struct pcmcia_driver airo_driver = {
|
|||
.suspend = airo_suspend,
|
||||
.resume = airo_resume,
|
||||
};
|
||||
|
||||
static int __init airo_cs_init(void)
|
||||
{
|
||||
return pcmcia_register_driver(&airo_driver);
|
||||
}
|
||||
|
||||
static void __exit airo_cs_cleanup(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&airo_driver);
|
||||
}
|
||||
module_pcmcia_driver(airo_driver);
|
||||
|
||||
/*
|
||||
This program is free software; you can redistribute it and/or
|
||||
|
@ -229,6 +220,3 @@ static void __exit airo_cs_cleanup(void)
|
|||
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
module_init(airo_cs_init);
|
||||
module_exit(airo_cs_cleanup);
|
||||
|
|
|
@ -245,16 +245,7 @@ static struct pcmcia_driver atmel_driver = {
|
|||
.suspend = atmel_suspend,
|
||||
.resume = atmel_resume,
|
||||
};
|
||||
|
||||
static int __init atmel_cs_init(void)
|
||||
{
|
||||
return pcmcia_register_driver(&atmel_driver);
|
||||
}
|
||||
|
||||
static void __exit atmel_cs_cleanup(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&atmel_driver);
|
||||
}
|
||||
module_pcmcia_driver(atmel_driver);
|
||||
|
||||
/*
|
||||
This program is free software; you can redistribute it and/or
|
||||
|
@ -294,6 +285,3 @@ static void __exit atmel_cs_cleanup(void)
|
|||
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
module_init(atmel_cs_init);
|
||||
module_exit(atmel_cs_cleanup);
|
||||
|
|
|
@ -130,6 +130,10 @@ static struct pcmcia_driver b43_pcmcia_driver = {
|
|||
.resume = b43_pcmcia_resume,
|
||||
};
|
||||
|
||||
/*
|
||||
* These are not module init/exit functions!
|
||||
* The module_pcmcia_driver() helper cannot be used here.
|
||||
*/
|
||||
int b43_pcmcia_init(void)
|
||||
{
|
||||
return pcmcia_register_driver(&b43_pcmcia_driver);
|
||||
|
|
|
@ -709,17 +709,4 @@ static struct pcmcia_driver hostap_driver = {
|
|||
.suspend = hostap_cs_suspend,
|
||||
.resume = hostap_cs_resume,
|
||||
};
|
||||
|
||||
static int __init init_prism2_pccard(void)
|
||||
{
|
||||
return pcmcia_register_driver(&hostap_driver);
|
||||
}
|
||||
|
||||
static void __exit exit_prism2_pccard(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&hostap_driver);
|
||||
}
|
||||
|
||||
|
||||
module_init(init_prism2_pccard);
|
||||
module_exit(exit_prism2_pccard);
|
||||
module_pcmcia_driver(hostap_driver);
|
||||
|
|
|
@ -999,7 +999,6 @@ static const struct pcmcia_device_id if_cs_ids[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(pcmcia, if_cs_ids);
|
||||
|
||||
|
||||
static struct pcmcia_driver lbs_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = DRV_NAME,
|
||||
|
@ -1007,26 +1006,4 @@ static struct pcmcia_driver lbs_driver = {
|
|||
.remove = if_cs_detach,
|
||||
.id_table = if_cs_ids,
|
||||
};
|
||||
|
||||
|
||||
static int __init if_cs_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
lbs_deb_enter(LBS_DEB_CS);
|
||||
ret = pcmcia_register_driver(&lbs_driver);
|
||||
lbs_deb_leave(LBS_DEB_CS);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void __exit if_cs_exit(void)
|
||||
{
|
||||
lbs_deb_enter(LBS_DEB_CS);
|
||||
pcmcia_unregister_driver(&lbs_driver);
|
||||
lbs_deb_leave(LBS_DEB_CS);
|
||||
}
|
||||
|
||||
|
||||
module_init(if_cs_init);
|
||||
module_exit(if_cs_exit);
|
||||
module_pcmcia_driver(lbs_driver);
|
||||
|
|
|
@ -338,18 +338,4 @@ static struct pcmcia_driver orinoco_driver = {
|
|||
.suspend = orinoco_cs_suspend,
|
||||
.resume = orinoco_cs_resume,
|
||||
};
|
||||
|
||||
static int __init
|
||||
init_orinoco_cs(void)
|
||||
{
|
||||
return pcmcia_register_driver(&orinoco_driver);
|
||||
}
|
||||
|
||||
static void __exit
|
||||
exit_orinoco_cs(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&orinoco_driver);
|
||||
}
|
||||
|
||||
module_init(init_orinoco_cs);
|
||||
module_exit(exit_orinoco_cs);
|
||||
module_pcmcia_driver(orinoco_driver);
|
||||
|
|
|
@ -318,18 +318,4 @@ static struct pcmcia_driver orinoco_driver = {
|
|||
.resume = spectrum_cs_resume,
|
||||
.id_table = spectrum_cs_ids,
|
||||
};
|
||||
|
||||
static int __init
|
||||
init_spectrum_cs(void)
|
||||
{
|
||||
return pcmcia_register_driver(&orinoco_driver);
|
||||
}
|
||||
|
||||
static void __exit
|
||||
exit_spectrum_cs(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&orinoco_driver);
|
||||
}
|
||||
|
||||
module_init(init_spectrum_cs);
|
||||
module_exit(exit_spectrum_cs);
|
||||
module_pcmcia_driver(orinoco_driver);
|
||||
|
|
|
@ -2013,19 +2013,7 @@ static struct pcmcia_driver wl3501_driver = {
|
|||
.suspend = wl3501_suspend,
|
||||
.resume = wl3501_resume,
|
||||
};
|
||||
|
||||
static int __init wl3501_init_module(void)
|
||||
{
|
||||
return pcmcia_register_driver(&wl3501_driver);
|
||||
}
|
||||
|
||||
static void __exit wl3501_exit_module(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&wl3501_driver);
|
||||
}
|
||||
|
||||
module_init(wl3501_init_module);
|
||||
module_exit(wl3501_exit_module);
|
||||
module_pcmcia_driver(wl3501_driver);
|
||||
|
||||
MODULE_AUTHOR("Fox Chen <mhchen@golf.ccl.itri.org.tw>, "
|
||||
"Arnaldo Carvalho de Melo <acme@conectiva.com.br>,"
|
||||
|
|
|
@ -244,20 +244,7 @@ static struct platform_driver amiga_parallel_driver = {
|
|||
},
|
||||
};
|
||||
|
||||
static int __init amiga_parallel_init(void)
|
||||
{
|
||||
return platform_driver_probe(&amiga_parallel_driver,
|
||||
amiga_parallel_probe);
|
||||
}
|
||||
|
||||
module_init(amiga_parallel_init);
|
||||
|
||||
static void __exit amiga_parallel_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&amiga_parallel_driver);
|
||||
}
|
||||
|
||||
module_exit(amiga_parallel_exit);
|
||||
module_platform_driver_probe(amiga_parallel_driver, amiga_parallel_probe);
|
||||
|
||||
MODULE_AUTHOR("Joerg Dorchain <joerg@dorchain.net>");
|
||||
MODULE_DESCRIPTION("Parport Driver for Amiga builtin Port");
|
||||
|
|
|
@ -193,16 +193,4 @@ static struct pcmcia_driver parport_cs_driver = {
|
|||
.remove = parport_detach,
|
||||
.id_table = parport_ids,
|
||||
};
|
||||
|
||||
static int __init init_parport_cs(void)
|
||||
{
|
||||
return pcmcia_register_driver(&parport_cs_driver);
|
||||
}
|
||||
|
||||
static void __exit exit_parport_cs(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&parport_cs_driver);
|
||||
}
|
||||
|
||||
module_init(init_parport_cs);
|
||||
module_exit(exit_parport_cs);
|
||||
module_pcmcia_driver(parport_cs_driver);
|
||||
|
|
|
@ -246,14 +246,14 @@ struct parport *parport_gsc_probe_port(unsigned long base,
|
|||
printk (KERN_DEBUG "parport (0x%lx): no memory!\n", base);
|
||||
return NULL;
|
||||
}
|
||||
ops = kmalloc (sizeof (struct parport_operations), GFP_KERNEL);
|
||||
ops = kmemdup(&parport_gsc_ops, sizeof(struct parport_operations),
|
||||
GFP_KERNEL);
|
||||
if (!ops) {
|
||||
printk (KERN_DEBUG "parport (0x%lx): no memory for ops!\n",
|
||||
base);
|
||||
kfree (priv);
|
||||
return NULL;
|
||||
}
|
||||
memcpy (ops, &parport_gsc_ops, sizeof (struct parport_operations));
|
||||
priv->ctr = 0xc;
|
||||
priv->ctr_writable = 0xff;
|
||||
priv->dma_buf = 0;
|
||||
|
|
|
@ -284,12 +284,11 @@ static int bpp_probe(struct platform_device *op)
|
|||
size = resource_size(&op->resource[0]);
|
||||
dma = PARPORT_DMA_NONE;
|
||||
|
||||
ops = kmalloc(sizeof(struct parport_operations), GFP_KERNEL);
|
||||
ops = kmemdup(&parport_sunbpp_ops, sizeof(struct parport_operations),
|
||||
GFP_KERNEL);
|
||||
if (!ops)
|
||||
goto out_unmap;
|
||||
|
||||
memcpy (ops, &parport_sunbpp_ops, sizeof(struct parport_operations));
|
||||
|
||||
dprintk(("register_port\n"));
|
||||
if (!(p = parport_register_port((unsigned long)base, irq, dma, ops)))
|
||||
goto out_free_ops;
|
||||
|
|
|
@ -476,10 +476,9 @@ int parport_proc_register(struct parport *port)
|
|||
struct parport_sysctl_table *t;
|
||||
int i;
|
||||
|
||||
t = kmalloc(sizeof(*t), GFP_KERNEL);
|
||||
t = kmemdup(&parport_sysctl_template, sizeof(*t), GFP_KERNEL);
|
||||
if (t == NULL)
|
||||
return -ENOMEM;
|
||||
memcpy(t, &parport_sysctl_template, sizeof(*t));
|
||||
|
||||
t->device_dir[0].extra1 = port;
|
||||
|
||||
|
@ -523,10 +522,9 @@ int parport_device_proc_register(struct pardevice *device)
|
|||
struct parport_device_sysctl_table *t;
|
||||
struct parport * port = device->port;
|
||||
|
||||
t = kmalloc(sizeof(*t), GFP_KERNEL);
|
||||
t = kmemdup(&parport_device_sysctl_template, sizeof(*t), GFP_KERNEL);
|
||||
if (t == NULL)
|
||||
return -ENOMEM;
|
||||
memcpy(t, &parport_device_sysctl_template, sizeof(*t));
|
||||
|
||||
t->dev_dir[0].child = t->parport_dir;
|
||||
t->parport_dir[0].child = t->port_dir;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue