1
0
Fork 0

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
  ...
hifive-unleashed-5.1
Linus Torvalds 2013-04-29 11:18:34 -07:00
commit 4f567cbc95
131 changed files with 4603 additions and 1338 deletions

View 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>

View 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.

View 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);
}
}

View File

@ -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

View File

@ -38,4 +38,10 @@
<0x19c00000 0x1000>;
interrupts = <0 195 0x0>;
};
qcom,ssbi@500000 {
compatible = "qcom,ssbi";
reg = <0x500000 0x1000>;
qcom,controller-type = "pmic-arbiter";
};
};

View File

@ -38,4 +38,10 @@
<0x16400000 0x1000>;
interrupts = <0 154 0x0>;
};
qcom,ssbi@500000 {
compatible = "qcom,ssbi";
reg = <0x500000 0x1000>;
qcom,controller-type = "pmic-arbiter";
};
};

View File

@ -52,6 +52,8 @@ source "drivers/i2c/Kconfig"
source "drivers/spi/Kconfig"
source "drivers/ssbi/Kconfig"
source "drivers/hsi/Kconfig"
source "drivers/pps/Kconfig"

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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");

View File

@ -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");

View File

@ -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. */

View File

@ -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);

View File

@ -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,

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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;

View File

@ -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);

View 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);
}

View File

@ -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
},
{ },
};

View File

@ -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;

View File

@ -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,

View File

@ -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)
{

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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");

View File

@ -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,

View File

@ -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

View File

@ -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);

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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,
};

View File

@ -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

View File

@ -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");

View File

@ -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");

View File

@ -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

View File

@ -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,
},
};

View File

@ -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

View 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");

View File

@ -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;

View File

@ -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;
}

View File

@ -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>");

View File

@ -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,
};

View File

@ -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,

View File

@ -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];

View File

@ -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.

View File

@ -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

View File

@ -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;

View 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);
}

View File

@ -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
*

View File

@ -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_ */

View 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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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(&reg, &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;

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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");

View File

@ -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) \

View 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);
}

View File

@ -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");

View File

@ -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;

View File

@ -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,

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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");

View File

@ -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);

View File

@ -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);

View File

@ -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);
/*====================================================================*/

View File

@ -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);

View File

@ -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);

View File

@ -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);
/*====================================================================*/

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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>,"

View File

@ -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");

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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