Merge tag 'extcon-for-3.10' of git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo...
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 8 Apr 2013 23:50:23 +0000 (16:50 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 8 Apr 2013 23:50:23 +0000 (16:50 -0700)
Chanwoo writes:

This is small fixes for extcon driver.

MAX77693 extcon driver
- Add 'static' keryword to internal data structure
- Fix return value using 'ret' instead of hardcoding

MAX8997 extcon driver
- Use dev_err() instead of pr_err()
- Fix return value using 'ret' instead of hardcoding

126 files changed:
Documentation/ABI/testing/sysfs-bus-mei [new file with mode: 0644]
Documentation/devicetree/bindings/arm/msm/ssbi.txt [new file with mode: 0644]
Documentation/misc-devices/mei/mei-client-bus.txt [new file with mode: 0644]
MAINTAINERS
arch/arm/boot/dts/msm8660-surf.dts
arch/arm/boot/dts/msm8960-cdp.dts
drivers/Kconfig
drivers/Makefile
drivers/ata/pata_pcmcia.c
drivers/bluetooth/bluecard_cs.c
drivers/bluetooth/bt3c_cs.c
drivers/bluetooth/btuart_cs.c
drivers/bluetooth/dtl1_cs.c
drivers/char/applicom.c
drivers/char/hw_random/mxc-rnga.c
drivers/char/hw_random/tx4939-rng.c
drivers/char/tile-srom.c
drivers/extcon/extcon-arizona.c
drivers/hv/Makefile
drivers/hv/channel_mgmt.c
drivers/hv/hv.c
drivers/hv/hv_balloon.c
drivers/hv/hv_snapshot.c [new file with mode: 0644]
drivers/hv/hv_util.c
drivers/hv/ring_buffer.c
drivers/ipack/carriers/tpci200.c
drivers/ipack/ipack.c
drivers/isdn/hardware/avm/avm_cs.c
drivers/isdn/hisax/avma1_cs.c
drivers/isdn/hisax/elsa_cs.c
drivers/isdn/hisax/sedlbauer_cs.c
drivers/isdn/hisax/teles_cs.c
drivers/memory/emif.c
drivers/memory/tegra30-mc.c
drivers/mfd/Kconfig
drivers/mfd/pm8921-core.c
drivers/mfd/wm5102-tables.c
drivers/misc/Kconfig
drivers/misc/Makefile
drivers/misc/apds990x.c
drivers/misc/arm-charlcd.c
drivers/misc/atmel_pwm.c
drivers/misc/bh1770glc.c
drivers/misc/bh1780gli.c
drivers/misc/cs5535-mfgpt.c
drivers/misc/dummy-irq.c [new file with mode: 0644]
drivers/misc/eeprom/at25.c
drivers/misc/eeprom/eeprom_93xx46.c
drivers/misc/ep93xx_pwm.c
drivers/misc/lattice-ecp3-config.c
drivers/misc/mei/Kconfig
drivers/misc/mei/Makefile
drivers/misc/mei/amthif.c
drivers/misc/mei/bus.c [new file with mode: 0644]
drivers/misc/mei/client.c
drivers/misc/mei/debugfs.c [new file with mode: 0644]
drivers/misc/mei/hw-me.c
drivers/misc/mei/hw-me.h
drivers/misc/mei/init.c
drivers/misc/mei/interrupt.c
drivers/misc/mei/main.c
drivers/misc/mei/mei_dev.h
drivers/misc/mei/pci-me.c
drivers/misc/vmw_vmci/Kconfig
drivers/mmc/host/sdricoh_cs.c
drivers/net/arcnet/com20020_cs.c
drivers/net/can/sja1000/ems_pcmcia.c
drivers/net/can/sja1000/peak_pcmcia.c
drivers/net/can/softing/softing_cs.c
drivers/net/ethernet/3com/3c574_cs.c
drivers/net/ethernet/3com/3c589_cs.c
drivers/net/ethernet/8390/axnet_cs.c
drivers/net/ethernet/8390/pcnet_cs.c
drivers/net/ethernet/amd/nmclan_cs.c
drivers/net/ethernet/fujitsu/fmvj18x_cs.c
drivers/net/ethernet/smsc/smc91c92_cs.c
drivers/net/ethernet/xircom/xirc2ps_cs.c
drivers/net/wireless/airo_cs.c
drivers/net/wireless/atmel_cs.c
drivers/net/wireless/b43/pcmcia.c
drivers/net/wireless/hostap/hostap_cs.c
drivers/net/wireless/libertas/if_cs.c
drivers/net/wireless/orinoco/orinoco_cs.c
drivers/net/wireless/orinoco/spectrum_cs.c
drivers/net/wireless/wl3501_cs.c
drivers/parport/parport_amiga.c
drivers/parport/parport_cs.c
drivers/parport/parport_gsc.c
drivers/parport/parport_sunbpp.c
drivers/parport/procfs.c
drivers/scsi/pcmcia/aha152x_stub.c
drivers/scsi/pcmcia/fdomain_stub.c
drivers/scsi/pcmcia/nsp_cs.c
drivers/scsi/pcmcia/qlogic_stub.c
drivers/scsi/pcmcia/sym53c500_cs.c
drivers/ssbi/Kconfig [new file with mode: 0644]
drivers/ssbi/Makefile [new file with mode: 0644]
drivers/ssbi/ssbi.c [new file with mode: 0644]
drivers/tty/serial/8250/serial_cs.c
drivers/uio/uio.c
drivers/usb/host/sl811_cs.c
drivers/w1/masters/mxc_w1.c
drivers/w1/slaves/Kconfig
drivers/w1/slaves/w1_ds2408.c
include/linux/hyperv.h
include/linux/ipack.h
include/linux/mei_cl_bus.h [new file with mode: 0644]
include/linux/mfd/arizona/core.h
include/linux/mfd/arizona/pdata.h
include/linux/mfd/arizona/registers.h
include/linux/mod_devicetable.h
include/linux/platform_data/emif_plat.h
include/linux/ssbi.h [new file with mode: 0644]
include/pcmcia/ds.h
include/uapi/linux/connector.h
mm/page_alloc.c
scripts/mod/devicetable-offsets.c
scripts/mod/file2alias.c
sound/pcmcia/pdaudiocf/pdaudiocf.c
sound/pcmcia/vx/vxpocket.c
sound/soc/codecs/arizona.c
sound/soc/codecs/arizona.h
sound/soc/codecs/wm5102.c
sound/soc/codecs/wm5110.c
tools/hv/hv_kvp_daemon.c
tools/hv/hv_vss_daemon.c [new file with mode: 0644]

diff --git a/Documentation/ABI/testing/sysfs-bus-mei b/Documentation/ABI/testing/sysfs-bus-mei
new file mode 100644 (file)
index 0000000..2066f0b
--- /dev/null
@@ -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>
diff --git a/Documentation/devicetree/bindings/arm/msm/ssbi.txt b/Documentation/devicetree/bindings/arm/msm/ssbi.txt
new file mode 100644 (file)
index 0000000..54fd5ce
--- /dev/null
@@ -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.
diff --git a/Documentation/misc-devices/mei/mei-client-bus.txt b/Documentation/misc-devices/mei/mei-client-bus.txt
new file mode 100644 (file)
index 0000000..9dc5ebf
--- /dev/null
@@ -0,0 +1,135 @@
+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_register_event_cb(dev, contact_event_cb, contact);
+
+       return 0;
+ }
+
+In the probe routine the driver basically 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);
+       }
+}
index 74e58a4..d8e6e8c 100644 (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
index 31f2157..67f8670 100644 (file)
                      <0x19c00000 0x1000>;
                interrupts = <0 195 0x0>;
        };
+
+       qcom,ssbi@500000 {
+               compatible = "qcom,ssbi";
+               reg = <0x500000 0x1000>;
+               qcom,controller-type = "pmic-arbiter";
+       };
 };
index 9e621b5..c9b09a8 100644 (file)
                      <0x16400000 0x1000>;
                interrupts = <0 154 0x0>;
        };
+
+       qcom,ssbi@500000 {
+               compatible = "qcom,ssbi";
+               reg = <0x500000 0x1000>;
+               qcom,controller-type = "pmic-arbiter";
+       };
 };
index 202fa6d..78a956e 100644 (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"
index dce39a9..4865ed2 100644 (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
index 958238d..40254f4 100644 (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);
index 0d26851..6c3e3d4 100644 (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);
index 7ffd3f4..a1aaa3b 100644 (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);
index 35a553a..beb262f 100644 (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);
index 036cb36..33f3a69 100644 (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);
index 25373df..974321a 100644 (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);
 
 
index f05d857..895d0b8 100644 (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");
index 3099198..d34a24a 100644 (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");
index 3b22a60..2e2036e 100644 (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. */
index dc357a4..7a1b4a7 100644 (file)
 #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_extcon_info *info = container_of(work,
+                                                       struct arizona_extcon_info,
+                                                       micd_timeout_work.work);
+
+       mutex_lock(&info->lock);
+
+       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, lvl;
-       int ret, i;
+       unsigned int val = 0, lvl;
+       int ret, i, key;
+
+       cancel_delayed_work_sync(&info->micd_timeout_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;
-       }
+       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);
+               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;
+               }
+       }
 
-       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);
index e6abfa0..0a74b56 100644 (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
index ff1be16..bad8128 100644 (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)
index 7311589..ae49237 100644 (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;
index 3787321..4c605c7 100644 (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;
+
+       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));
 
-               pr_info("Memory hot add not supported\n");
+               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,13 +1414,13 @@ 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;
+       cap_msg.caps.cap_bits.hot_add = 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.
+        * Specify our alignment requirements as it relates
+        * memory hot-add. Specify 128MB alignment.
         */
-       cap_msg.caps.cap_bits.hot_add = 1;
+       cap_msg.caps.cap_bits.hot_add_alignment = 7;
 
        /*
         * Currently the host does not use these
@@ -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);
diff --git a/drivers/hv/hv_snapshot.c b/drivers/hv/hv_snapshot.c
new file mode 100644 (file)
index 0000000..8ad5653
--- /dev/null
@@ -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);
+}
index 1d4cbd8..2f561c5 100644 (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
+       },
        { },
 };
 
index cafa72f..d6fbb57 100644 (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;
 
index 0246b1f..c276fde 100644 (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,
index 7ec6b20..6e066c5 100644 (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 0;
+}
+EXPORT_SYMBOL_GPL(ipack_device_init);
 
-       return ret;
+int ipack_device_add(struct ipack_device *dev)
+{
+       return device_add(&dev->dev);
+}
+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_device_register);
+EXPORT_SYMBOL_GPL(ipack_get_device);
 
-void ipack_device_unregister(struct ipack_device *dev)
+void ipack_put_device(struct ipack_device *dev)
 {
-       device_unregister(&dev->dev);
+       put_device(&dev->dev);
 }
-EXPORT_SYMBOL_GPL(ipack_device_unregister);
+EXPORT_SYMBOL_GPL(ipack_put_device);
 
 static int __init ipack_init(void)
 {
index c21353d..62b8030 100644 (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);
index 4e676bc..baad94e 100644 (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);
index ebe5691..40f6fad 100644 (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);
index 90f8129..92ef62d 100644 (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);
index f2476ff..b8dd149 100644 (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);
index df08736..cadf1cc 100644 (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);
-}
-
-static void __exit emif_unregister(void)
-{
-       platform_driver_unregister(&emif_driver);
-}
+module_platform_driver_probe(emif_driver, emif_probe);
 
-module_init(emif_register);
-module_exit(emif_unregister);
 MODULE_DESCRIPTION("TI EMIF SDRAM Controller Driver");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:emif");
index 0b97598..f4ae074 100644 (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,
index c346941..ca86581 100644 (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
index d4b297c..ecc137f 100644 (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);
index a433f58..ca2aed6 100644 (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:
index e83fdfe..69bb79d 100644 (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
index 35a1463..865cbc6 100644 (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
index 0e67f82..98f9bb2 100644 (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
index fe8616a..48651ef 100644 (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");
index 28f5aaa..494d050 100644 (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");
index 2ed8fc3..f4975f7 100644 (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
index cf03d0a..818f3a0 100644 (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,
        },
 };
 
index 9858f36..effd8c6 100644 (file)
 
 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
diff --git a/drivers/misc/dummy-irq.c b/drivers/misc/dummy-irq.c
new file mode 100644 (file)
index 0000000..7014167
--- /dev/null
@@ -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");
index b08cf8a..ad8fd8e 100644 (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;
index a6b5d5e..94cfc12 100644 (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;
 }
index 16d7179..96787ec 100644 (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>");
index 155700b..bb26f08 100644 (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];
index d21b4d0..c76fa31 100644 (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.
 
index 040af6c..3612d57 100644 (file)
@@ -10,5 +10,9 @@ 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-$(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
index c86d7e3..9a5e8c7 100644 (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;
@@ -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;
diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c
new file mode 100644 (file)
index 0000000..8dbcb15
--- /dev/null
@@ -0,0 +1,496 @@
+/*
+ * 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_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->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_msg_hdr mei_hdr;
+       struct mei_cl_cb *cb;
+       int me_cl_id, err;
+
+       if (WARN_ON(!cl || !cl->dev))
+               return -ENODEV;
+
+       if (cl->state != MEI_FILE_CONNECTED)
+               return -ENODEV;
+
+       cb = mei_io_cb_init(cl, NULL);
+       if (!cb)
+               return -ENOMEM;
+
+       err = mei_io_cb_alloc_req_buf(cb, length);
+       if (err < 0) {
+               mei_io_cb_free(cb);
+               return err;
+       }
+
+       memcpy(cb->request_buffer.data, buf, length);
+       cb->fop_type = MEI_FOP_WRITE;
+
+       dev = cl->dev;
+
+       mutex_lock(&dev->device_lock);
+
+       /* Check if we have an ME client device */
+       me_cl_id = mei_me_cl_by_id(dev, cl->me_client_id);
+       if (me_cl_id == dev->me_clients_num) {
+               err = -ENODEV;
+               goto out_err;
+       }
+
+       if (length > dev->me_clients[me_cl_id].props.max_msg_length) {
+               err = -EINVAL;
+               goto out_err;
+       }
+
+       err = mei_cl_flow_ctrl_creds(cl);
+       if (err < 0)
+               goto out_err;
+
+       /* Host buffer is not ready, we queue the request */
+       if (err == 0 || !dev->hbuf_is_ready) {
+               cb->buf_idx = 0;
+               mei_hdr.msg_complete = 0;
+               cl->writing_state = MEI_WRITING;
+
+               goto out;
+       }
+
+       dev->hbuf_is_ready = false;
+
+       /* Check for a maximum length */
+       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;
+
+       if (mei_write_message(dev, &mei_hdr, buf)) {
+               err = -EIO;
+               goto out_err;
+       }
+
+       cl->writing_state = MEI_WRITING;
+       cb->buf_idx = mei_hdr.length;
+
+out:
+       if (mei_hdr.msg_complete) {
+               if (mei_cl_flow_ctrl_reduce(cl)) {
+                       err = -ENODEV;
+                       goto out_err;
+               }
+               list_add_tail(&cb->list, &dev->write_waiting_list.list);
+       } else {
+               list_add_tail(&cb->list, &dev->write_list.list);
+       }
+
+       mutex_unlock(&dev->device_lock);
+
+       if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) {
+               if (wait_event_interruptible(cl->tx_wait,
+                       cl->writing_state == MEI_WRITE_COMPLETE)) {
+                               if (signal_pending(current))
+                                       err = -EINTR;
+                       err = -ERESTARTSYS;
+                       mutex_lock(&dev->device_lock);
+                       goto out_err;
+               }
+       }
+
+       return mei_hdr.length;
+
+out_err:
+       mutex_unlock(&dev->device_lock);
+       mei_io_cb_free(cb);
+
+       return err;
+}
+
+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);
+               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);
+}
+
+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);
+
+       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);
+
+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);
+}
index 1569afe..e14397b 100644 (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;
diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c
new file mode 100644 (file)
index 0000000..7135a71
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ *
+ * 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 int mei_dbgfs_open(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+       return 0;
+}
+
+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 = mei_dbgfs_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 = mei_dbgfs_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;
+}
+
index 642c622..5ad53eb 100644 (file)
 
 
 /**
- * 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,8 +466,6 @@ 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;
@@ -465,14 +496,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_dbg(&dev->pdev->dev, "link is established start sending messages.\n");
-                       /* link is established * start sending messages.  */
-
-                       dev->dev_state = MEI_DEV_INIT_CLIENTS;
+                       dev->recvd_hw_ready = true;
+                       wake_up_interruptible(&dev->wait_hw_ready);
 
-                       mei_hbm_start_req(dev);
                        mutex_unlock(&dev->device_lock);
                        return IRQ_HANDLED;
                } else {
@@ -510,33 +536,19 @@ end:
                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 +583,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;
index 8518d3e..80bd829 100644 (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);
 
index 3561799..54b51c0 100644 (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)
@@ -33,7 +35,6 @@ const char *mei_dev_state_str(int state)
        MEI_DEV_STATE(ENABLED);
        MEI_DEV_STATE(RESETING);
        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,16 +59,26 @@ 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;
 
@@ -126,6 +139,7 @@ err:
        mutex_unlock(&dev->device_lock);
        return -ENODEV;
 }
+EXPORT_SYMBOL_GPL(mei_start);
 
 /**
  * mei_reset - resets host and fw.
@@ -137,9 +151,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 &&
@@ -176,12 +187,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)
 {
@@ -199,8 +225,10 @@ void mei_stop(struct mei_device *dev)
        mutex_unlock(&dev->device_lock);
 
        flush_scheduled_work();
-}
 
+       mei_watchdog_unregister(dev);
+}
+EXPORT_SYMBOL_GPL(mei_stop);
 
 
 
index 3535b26..255e085 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 
+#include <linux/export.h>
 #include <linux/pci.h>
 #include <linux/kthread.h>
 #include <linux/interrupt.h>
 
 
 /**
- * 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_compl_handler - dispatch complete handelers
+ *     for the completed callbacks
+ *
+ * @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_irq_thread_state_ok - checks if mei header matches file private data
  *
@@ -153,25 +182,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));
+
+       if (*slots < msg_slots)
+               return -EMSGSIZE;
 
-       *slots -= mei_data2slots(sizeof(struct hbm_client_connect_request));
+       *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 +223,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 +261,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 +302,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;
@@ -408,6 +444,7 @@ int mei_irq_read_handler(struct mei_device *dev,
 end:
        return ret;
 }
+EXPORT_SYMBOL_GPL(mei_irq_read_handler);
 
 
 /**
@@ -419,8 +456,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 +595,7 @@ int mei_irq_write_handler(struct mei_device *dev,
        }
        return 0;
 }
+EXPORT_SYMBOL_GPL(mei_irq_write_handler);
 
 
 
index 903f809..329fb86 100644 (file)
@@ -753,17 +753,44 @@ static struct miscdevice  mei_misc_device = {
                .minor = MISC_DYNAMIC_MINOR,
 };
 
-int mei_register(struct device *dev)
+
+int mei_register(struct mei_device *dev)
 {
-       mei_misc_device.parent = dev;
-       return misc_register(&mei_misc_device);
+       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(void)
+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");
 
index 9787381..8806be4 100644 (file)
@@ -21,6 +21,7 @@
 #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"
@@ -97,7 +98,6 @@ enum mei_dev_state {
        MEI_DEV_ENABLED,
        MEI_DEV_RESETING,
        MEI_DEV_DISABLED,
-       MEI_DEV_RECOVERING_FROM_RESET,
        MEI_DEV_POWER_DOWN,
        MEI_DEV_POWER_UP
 };
@@ -209,15 +209,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 +242,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,6 +268,67 @@ struct mei_hw_ops {
                     unsigned char *buf, unsigned long len);
 };
 
+/* MEI bus API*/
+struct mei_cl_device *mei_cl_add_device(struct mei_device *dev,
+                                 uuid_le uuid, char *name);
+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);
+
+/**
+ * struct mei_cl_transport_ops - MEI CL device transport ops
+ * This structure allows ME host clients to implement technology
+ * specific transport layers.
+ *
+ * @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_transport_ops {
+       int (*send)(struct mei_cl_device *device, u8 *buf, size_t length);
+       int (*recv)(struct mei_cl_device *device, 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_transport_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
 
@@ -296,11 +362,14 @@ struct mei_device {
         */
        struct mutex device_lock; /* device lock */
        struct delayed_work timer_work; /* MEI timer delayed work (timeouts) */
+
+       bool recvd_hw_ready;
        bool recvd_msg;
 
        /*
         * 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;
 
@@ -365,6 +434,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 +451,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 +479,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
@@ -455,6 +541,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 +561,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 +608,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)                  \
index b8b5c9c..88aec6a 100644 (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");
index 39c2eca..ea98f7e 100644 (file)
@@ -4,7 +4,7 @@
 
 config VMWARE_VMCI
        tristate "VMware VMCI Driver"
-       depends on X86 && PCI
+       depends on X86 && PCI && NET
        help
          This is VMware's Virtual Machine Communication Interface.  It enables
          high-speed communication between host and guest in a virtual
index 7009f17..50adbd1 100644 (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);
 
index 5bed4c4..74dc187 100644 (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);
index 5c2f3fb..321c27e 100644 (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);
index 1a7020b..0a707f7 100644 (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);
index c2c0a5b..498605f 100644 (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");
index ffd8de2..6fc994f 100644 (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);
index a556c01..078480a 100644 (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);
index e1b3941..d801c14 100644 (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);
 
 /*====================================================================*/
 
index de1af0b..46c5aad 100644 (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);
index 9f59bf6..d4ed891 100644 (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);
index 2418faf..ab98b77 100644 (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);
 
 /*====================================================================*/
 
index 04393b5..656d2e2 100644 (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);
index 98e09d0..1025b4e 100644 (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)
index 956024a..14128fd 100644 (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);
index b42930f..5225722 100644 (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);
index f2ea2ce..55f2bd7 100644 (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);
index 89e9d3a..56cd01c 100644 (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);
index 16beaf3..c94dd68 100644 (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);
index d7dbc00..d21d959 100644 (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);
index 6e28ee4..e2264bc 100644 (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);
index 730186d..38d2089 100644 (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>,"
index ee78e0e..09503b8 100644 (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");
index 067ad51..e9b52e4 100644 (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);
index 050773c..a5251cb 100644 (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;
index 5c4b6a1..dffd6d0 100644 (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;
index 3f56bc0..92ed045 100644 (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;
index 7d1609f..df82a34 100644 (file)
@@ -220,16 +220,4 @@ static struct pcmcia_driver aha152x_cs_driver = {
        .id_table       = aha152x_ids,
        .resume         = aha152x_resume,
 };
-
-static int __init init_aha152x_cs(void)
-{
-       return pcmcia_register_driver(&aha152x_cs_driver);
-}
-
-static void __exit exit_aha152x_cs(void)
-{
-       pcmcia_unregister_driver(&aha152x_cs_driver);
-}
-
-module_init(init_aha152x_cs);
-module_exit(exit_aha152x_cs);
+module_pcmcia_driver(aha152x_cs_driver);
index 714b248..ba84769 100644 (file)
@@ -194,16 +194,4 @@ static struct pcmcia_driver fdomain_cs_driver = {
        .id_table       = fdomain_ids,
        .resume         = fdomain_resume,
 };
-
-static int __init init_fdomain_cs(void)
-{
-       return pcmcia_register_driver(&fdomain_cs_driver);
-}
-
-static void __exit exit_fdomain_cs(void)
-{
-       pcmcia_unregister_driver(&fdomain_cs_driver);
-}
-
-module_init(init_fdomain_cs);
-module_exit(exit_fdomain_cs);
+module_pcmcia_driver(fdomain_cs_driver);
index b61a753..76ca00c 100644 (file)
@@ -1773,19 +1773,4 @@ static struct pcmcia_driver nsp_driver = {
        .suspend        = nsp_cs_suspend,
        .resume         = nsp_cs_resume,
 };
-
-static int __init nsp_cs_init(void)
-{
-       return pcmcia_register_driver(&nsp_driver);
-}
-
-static void __exit nsp_cs_exit(void)
-{
-       pcmcia_unregister_driver(&nsp_driver);
-}
-
-
-module_init(nsp_cs_init)
-module_exit(nsp_cs_exit)
-
-/* end */
+module_pcmcia_driver(nsp_driver);
index bcaf89f..8d4fdc2 100644 (file)
@@ -300,19 +300,8 @@ static struct pcmcia_driver qlogic_cs_driver = {
        .id_table       = qlogic_ids,
        .resume         = qlogic_resume,
 };
-
-static int __init init_qlogic_cs(void)
-{
-       return pcmcia_register_driver(&qlogic_cs_driver);
-}
-
-static void __exit exit_qlogic_cs(void)
-{
-       pcmcia_unregister_driver(&qlogic_cs_driver);
-}
+module_pcmcia_driver(qlogic_cs_driver);
 
 MODULE_AUTHOR("Tom Zerucha, Michael Griffith");
 MODULE_DESCRIPTION("Driver for the PCMCIA Qlogic FAS SCSI controllers");
 MODULE_LICENSE("GPL");
-module_init(init_qlogic_cs);
-module_exit(exit_qlogic_cs);
index f5b5273..55b0b2b 100644 (file)
@@ -881,18 +881,4 @@ static struct pcmcia_driver sym53c500_cs_driver = {
        .id_table       = sym53c500_ids,
        .resume         = sym53c500_resume,
 };
-
-static int __init
-init_sym53c500_cs(void)
-{
-       return pcmcia_register_driver(&sym53c500_cs_driver);
-}
-
-static void __exit
-exit_sym53c500_cs(void)
-{
-       pcmcia_unregister_driver(&sym53c500_cs_driver);
-}
-
-module_init(init_sym53c500_cs);
-module_exit(exit_sym53c500_cs);
+module_pcmcia_driver(sym53c500_cs_driver);
diff --git a/drivers/ssbi/Kconfig b/drivers/ssbi/Kconfig
new file mode 100644 (file)
index 0000000..1ae4040
--- /dev/null
@@ -0,0 +1,16 @@
+#
+# SSBI bus support
+#
+
+menu "Qualcomm MSM SSBI bus support"
+
+config SSBI
+       tristate "Qualcomm Single-wire Serial Bus Interface (SSBI)"
+       help
+         If you say yes to this option, support will be included for the
+         built-in SSBI interface on Qualcomm MSM family processors.
+
+         This is required for communicating with Qualcomm PMICs and
+         other devices that have the SSBI interface.
+
+endmenu
diff --git a/drivers/ssbi/Makefile b/drivers/ssbi/Makefile
new file mode 100644 (file)
index 0000000..38fb70c
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_SSBI) += ssbi.o
diff --git a/drivers/ssbi/ssbi.c b/drivers/ssbi/ssbi.c
new file mode 100644 (file)
index 0000000..f32da02
--- /dev/null
@@ -0,0 +1,379 @@
+/* Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2010, Google Inc.
+ *
+ * Original authors: Code Aurora Forum
+ *
+ * Author: Dima Zavin <dima@android.com>
+ *  - Largely rewritten from original to not be an i2c driver.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/ssbi.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+/* SSBI 2.0 controller registers */
+#define SSBI2_CMD                      0x0008
+#define SSBI2_RD                       0x0010
+#define SSBI2_STATUS                   0x0014
+#define SSBI2_MODE2                    0x001C
+
+/* SSBI_CMD fields */
+#define SSBI_CMD_RDWRN                 (1 << 24)
+
+/* SSBI_STATUS fields */
+#define SSBI_STATUS_RD_READY           (1 << 2)
+#define SSBI_STATUS_READY              (1 << 1)
+#define SSBI_STATUS_MCHN_BUSY          (1 << 0)
+
+/* SSBI_MODE2 fields */
+#define SSBI_MODE2_REG_ADDR_15_8_SHFT  0x04
+#define SSBI_MODE2_REG_ADDR_15_8_MASK  (0x7f << SSBI_MODE2_REG_ADDR_15_8_SHFT)
+
+#define SET_SSBI_MODE2_REG_ADDR_15_8(MD, AD) \
+       (((MD) & 0x0F) | ((((AD) >> 8) << SSBI_MODE2_REG_ADDR_15_8_SHFT) & \
+       SSBI_MODE2_REG_ADDR_15_8_MASK))
+
+/* SSBI PMIC Arbiter command registers */
+#define SSBI_PA_CMD                    0x0000
+#define SSBI_PA_RD_STATUS              0x0004
+
+/* SSBI_PA_CMD fields */
+#define SSBI_PA_CMD_RDWRN              (1 << 24)
+#define SSBI_PA_CMD_ADDR_MASK          0x7fff /* REG_ADDR_7_0, REG_ADDR_8_14*/
+
+/* SSBI_PA_RD_STATUS fields */
+#define SSBI_PA_RD_STATUS_TRANS_DONE   (1 << 27)
+#define SSBI_PA_RD_STATUS_TRANS_DENIED (1 << 26)
+
+#define SSBI_TIMEOUT_US                        100
+
+struct ssbi {
+       struct device           *slave;
+       void __iomem            *base;
+       spinlock_t              lock;
+       enum ssbi_controller_type controller_type;
+       int (*read)(struct ssbi *, u16 addr, u8 *buf, int len);
+       int (*write)(struct ssbi *, u16 addr, u8 *buf, int len);
+};
+
+#define to_ssbi(dev)   platform_get_drvdata(to_platform_device(dev))
+
+static inline u32 ssbi_readl(struct ssbi *ssbi, u32 reg)
+{
+       return readl(ssbi->base + reg);
+}
+
+static inline void ssbi_writel(struct ssbi *ssbi, u32 val, u32 reg)
+{
+       writel(val, ssbi->base + reg);
+}
+
+/*
+ * Via private exchange with one of the original authors, the hardware
+ * should generally finish a transaction in about 5us.  The worst
+ * case, is when using the arbiter and both other CPUs have just
+ * started trying to use the SSBI bus will result in a time of about
+ * 20us.  It should never take longer than this.
+ *
+ * As such, this wait merely spins, with a udelay.
+ */
+static int ssbi_wait_mask(struct ssbi *ssbi, u32 set_mask, u32 clr_mask)
+{
+       u32 timeout = SSBI_TIMEOUT_US;
+       u32 val;
+
+       while (timeout--) {
+               val = ssbi_readl(ssbi, SSBI2_STATUS);
+               if (((val & set_mask) == set_mask) && ((val & clr_mask) == 0))
+                       return 0;
+               udelay(1);
+       }
+
+       return -ETIMEDOUT;
+}
+
+static int
+ssbi_read_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len)
+{
+       u32 cmd = SSBI_CMD_RDWRN | ((addr & 0xff) << 16);
+       int ret = 0;
+
+       if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) {
+               u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2);
+               mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr);
+               ssbi_writel(ssbi, mode2, SSBI2_MODE2);
+       }
+
+       while (len) {
+               ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0);
+               if (ret)
+                       goto err;
+
+               ssbi_writel(ssbi, cmd, SSBI2_CMD);
+               ret = ssbi_wait_mask(ssbi, SSBI_STATUS_RD_READY, 0);
+               if (ret)
+                       goto err;
+               *buf++ = ssbi_readl(ssbi, SSBI2_RD) & 0xff;
+               len--;
+       }
+
+err:
+       return ret;
+}
+
+static int
+ssbi_write_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len)
+{
+       int ret = 0;
+
+       if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) {
+               u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2);
+               mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr);
+               ssbi_writel(ssbi, mode2, SSBI2_MODE2);
+       }
+
+       while (len) {
+               ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0);
+               if (ret)
+                       goto err;
+
+               ssbi_writel(ssbi, ((addr & 0xff) << 16) | *buf, SSBI2_CMD);
+               ret = ssbi_wait_mask(ssbi, 0, SSBI_STATUS_MCHN_BUSY);
+               if (ret)
+                       goto err;
+               buf++;
+               len--;
+       }
+
+err:
+       return ret;
+}
+
+/*
+ * See ssbi_wait_mask for an explanation of the time and the
+ * busywait.
+ */
+static inline int
+ssbi_pa_transfer(struct ssbi *ssbi, u32 cmd, u8 *data)
+{
+       u32 timeout = SSBI_TIMEOUT_US;
+       u32 rd_status = 0;
+
+       ssbi_writel(ssbi, cmd, SSBI_PA_CMD);
+
+       while (timeout--) {
+               rd_status = ssbi_readl(ssbi, SSBI_PA_RD_STATUS);
+
+               if (rd_status & SSBI_PA_RD_STATUS_TRANS_DENIED)
+                       return -EPERM;
+
+               if (rd_status & SSBI_PA_RD_STATUS_TRANS_DONE) {
+                       if (data)
+                               *data = rd_status & 0xff;
+                       return 0;
+               }
+               udelay(1);
+       }
+
+       return -ETIMEDOUT;
+}
+
+static int
+ssbi_pa_read_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len)
+{
+       u32 cmd;
+       int ret = 0;
+
+       cmd = SSBI_PA_CMD_RDWRN | (addr & SSBI_PA_CMD_ADDR_MASK) << 8;
+
+       while (len) {
+               ret = ssbi_pa_transfer(ssbi, cmd, buf);
+               if (ret)
+                       goto err;
+               buf++;
+               len--;
+       }
+
+err:
+       return ret;
+}
+
+static int
+ssbi_pa_write_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len)
+{
+       u32 cmd;
+       int ret = 0;
+
+       while (len) {
+               cmd = (addr & SSBI_PA_CMD_ADDR_MASK) << 8 | *buf;
+               ret = ssbi_pa_transfer(ssbi, cmd, NULL);
+               if (ret)
+                       goto err;
+               buf++;
+               len--;
+       }
+
+err:
+       return ret;
+}
+
+int ssbi_read(struct device *dev, u16 addr, u8 *buf, int len)
+{
+       struct ssbi *ssbi = to_ssbi(dev);
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&ssbi->lock, flags);
+       ret = ssbi->read(ssbi, addr, buf, len);
+       spin_unlock_irqrestore(&ssbi->lock, flags);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(ssbi_read);
+
+int ssbi_write(struct device *dev, u16 addr, u8 *buf, int len)
+{
+       struct ssbi *ssbi = to_ssbi(dev);
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&ssbi->lock, flags);
+       ret = ssbi->write(ssbi, addr, buf, len);
+       spin_unlock_irqrestore(&ssbi->lock, flags);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(ssbi_write);
+
+static int ssbi_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct resource *mem_res;
+       struct ssbi *ssbi;
+       int ret = 0;
+       const char *type;
+
+       ssbi = kzalloc(sizeof(struct ssbi), GFP_KERNEL);
+       if (!ssbi) {
+               pr_err("can not allocate ssbi_data\n");
+               return -ENOMEM;
+       }
+
+       mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!mem_res) {
+               pr_err("missing mem resource\n");
+               ret = -EINVAL;
+               goto err_get_mem_res;
+       }
+
+       ssbi->base = ioremap(mem_res->start, resource_size(mem_res));
+       if (!ssbi->base) {
+               pr_err("ioremap of 0x%p failed\n", (void *)mem_res->start);
+               ret = -EINVAL;
+               goto err_ioremap;
+       }
+       platform_set_drvdata(pdev, ssbi);
+
+       type = of_get_property(np, "qcom,controller-type", NULL);
+       if (type == NULL) {
+               pr_err("Missing qcom,controller-type property\n");
+               ret = -EINVAL;
+               goto err_ssbi_controller;
+       }
+       dev_info(&pdev->dev, "SSBI controller type: '%s'\n", type);
+       if (strcmp(type, "ssbi") == 0)
+               ssbi->controller_type = MSM_SBI_CTRL_SSBI;
+       else if (strcmp(type, "ssbi2") == 0)
+               ssbi->controller_type = MSM_SBI_CTRL_SSBI2;
+       else if (strcmp(type, "pmic-arbiter") == 0)
+               ssbi->controller_type = MSM_SBI_CTRL_PMIC_ARBITER;
+       else {
+               pr_err("Unknown qcom,controller-type\n");
+               ret = -EINVAL;
+               goto err_ssbi_controller;
+       }
+
+       if (ssbi->controller_type == MSM_SBI_CTRL_PMIC_ARBITER) {
+               ssbi->read = ssbi_pa_read_bytes;
+               ssbi->write = ssbi_pa_write_bytes;
+       } else {
+               ssbi->read = ssbi_read_bytes;
+               ssbi->write = ssbi_write_bytes;
+       }
+
+       spin_lock_init(&ssbi->lock);
+
+       ret = of_platform_populate(np, NULL, NULL, &pdev->dev);
+       if (ret)
+               goto err_ssbi_controller;
+
+       return 0;
+
+err_ssbi_controller:
+       platform_set_drvdata(pdev, NULL);
+       iounmap(ssbi->base);
+err_ioremap:
+err_get_mem_res:
+       kfree(ssbi);
+       return ret;
+}
+
+static int ssbi_remove(struct platform_device *pdev)
+{
+       struct ssbi *ssbi = platform_get_drvdata(pdev);
+
+       platform_set_drvdata(pdev, NULL);
+       iounmap(ssbi->base);
+       kfree(ssbi);
+       return 0;
+}
+
+static struct of_device_id ssbi_match_table[] = {
+       { .compatible = "qcom,ssbi" },
+       {}
+};
+
+static struct platform_driver ssbi_driver = {
+       .probe          = ssbi_probe,
+       .remove         = ssbi_remove,
+       .driver         = {
+               .name   = "ssbi",
+               .owner  = THIS_MODULE,
+               .of_match_table = ssbi_match_table,
+       },
+};
+
+static int __init ssbi_init(void)
+{
+       return platform_driver_register(&ssbi_driver);
+}
+module_init(ssbi_init);
+
+static void __exit ssbi_exit(void)
+{
+       platform_driver_unregister(&ssbi_driver);
+}
+module_exit(ssbi_exit)
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:ssbi");
+MODULE_AUTHOR("Dima Zavin <dima@android.com>");
index b7d48b3..1b74b88 100644 (file)
@@ -852,18 +852,6 @@ static struct pcmcia_driver serial_cs_driver = {
        .suspend        = serial_suspend,
        .resume         = serial_resume,
 };
-
-static int __init init_serial_cs(void)
-{
-       return pcmcia_register_driver(&serial_cs_driver);
-}
-
-static void __exit exit_serial_cs(void)
-{
-       pcmcia_unregister_driver(&serial_cs_driver);
-}
-
-module_init(init_serial_cs);
-module_exit(exit_serial_cs);
+module_pcmcia_driver(serial_cs_driver);
 
 MODULE_LICENSE("GPL");
index c8b9262..b645c47 100644 (file)
@@ -374,6 +374,7 @@ static int uio_get_minor(struct uio_device *idev)
        retval = idr_alloc(&uio_idr, idev, 0, UIO_MAX_DEVICES, GFP_KERNEL);
        if (retval >= 0) {
                idev->minor = retval;
+               retval = 0;
        } else if (retval == -ENOSPC) {
                dev_err(idev->dev, "too many uio devices\n");
                retval = -EINVAL;
index 3b6f50e..469564e 100644 (file)
@@ -200,17 +200,4 @@ static struct pcmcia_driver sl811_cs_driver = {
        .remove         = sl811_cs_detach,
        .id_table       = sl811_ids,
 };
-
-/*====================================================================*/
-
-static int __init init_sl811_cs(void)
-{
-       return pcmcia_register_driver(&sl811_cs_driver);
-}
-module_init(init_sl811_cs);
-
-static void __exit exit_sl811_cs(void)
-{
-       pcmcia_unregister_driver(&sl811_cs_driver);
-}
-module_exit(exit_sl811_cs);
+module_pcmcia_driver(sl811_cs_driver);
index 950d354..47e12cf 100644 (file)
@@ -121,9 +121,9 @@ static int mxc_w1_probe(struct platform_device *pdev)
        mdev->clkdiv = (clk_get_rate(mdev->clk) / 1000000) - 1;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       mdev->regs = devm_request_and_ioremap(&pdev->dev, res);
-       if (!mdev->regs)
-               return -EBUSY;
+       mdev->regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(mdev->regs))
+               return PTR_ERR(mdev->regs);
 
        clk_prepare_enable(mdev->clk);
        __raw_writeb(mdev->clkdiv, mdev->regs + MXC_W1_TIME_DIVIDER);
index 762561f..5e6a3c9 100644 (file)
@@ -22,6 +22,16 @@ config W1_SLAVE_DS2408
          Say Y here if you want to use a 1-wire
          DS2408 8-Channel Addressable Switch device support
 
+config W1_SLAVE_DS2408_READBACK
+       bool "Read-back values written to DS2408's output register"
+       depends on W1_SLAVE_DS2408
+       default y
+       help
+         Enabling this will cause the driver to read back the values written
+         to the chip's output register in order to detect errors.
+
+         This is slower but useful when debugging chips and/or busses.
+
 config W1_SLAVE_DS2413
        tristate "Dual Channel Addressable Switch 0x3a family support (DS2413)"
        help
index 441ad3a..e45eca1 100644 (file)
@@ -178,6 +178,15 @@ static ssize_t w1_f29_write_output(
                w1_write_block(sl->master, w1_buf, 3);
 
                readBack = w1_read_8(sl->master);
+
+               if (readBack != W1_F29_SUCCESS_CONFIRM_BYTE) {
+                       if (w1_reset_resume_command(sl->master))
+                               goto error;
+                       /* try again, the slave is ready for a command */
+                       continue;
+               }
+
+#ifdef CONFIG_W1_SLAVE_DS2408_READBACK
                /* here the master could read another byte which
                   would be the PIO reg (the actual pin logic state)
                   since in this driver we don't know which pins are
@@ -186,11 +195,6 @@ static ssize_t w1_f29_write_output(
                if (w1_reset_resume_command(sl->master))
                        goto error;
 
-               if (readBack != 0xAA) {
-                       /* try again, the slave is ready for a command */
-                       continue;
-               }
-
                /* go read back the output latches */
                /* (the direct effect of the write above) */
                w1_buf[0] = W1_F29_FUNC_READ_PIO_REGS;
@@ -198,7 +202,9 @@ static ssize_t w1_f29_write_output(
                w1_buf[2] = 0;
                w1_write_block(sl->master, w1_buf, 3);
                /* read the result of the READ_PIO_REGS command */
-               if (w1_read_8(sl->master) == *buf) {
+               if (w1_read_8(sl->master) == *buf)
+#endif
+               {
                        /* success! */
                        mutex_unlock(&sl->master->bus_mutex);
                        dev_dbg(&sl->dev,
@@ -297,8 +303,7 @@ error:
 
 
 
-#define NB_SYSFS_BIN_FILES 6
-static struct bin_attribute w1_f29_sysfs_bin_files[NB_SYSFS_BIN_FILES] = {
+static struct bin_attribute w1_f29_sysfs_bin_files[] = {
        {
                .attr = {
                        .name = "state",
@@ -357,7 +362,7 @@ static int w1_f29_add_slave(struct w1_slave *sl)
        int err = 0;
        int i;
 
-       for (i = 0; i < NB_SYSFS_BIN_FILES && !err; ++i)
+       for (i = 0; i < ARRAY_SIZE(w1_f29_sysfs_bin_files) && !err; ++i)
                err = sysfs_create_bin_file(
                        &sl->dev.kobj,
                        &(w1_f29_sysfs_bin_files[i]));
@@ -371,7 +376,7 @@ static int w1_f29_add_slave(struct w1_slave *sl)
 static void w1_f29_remove_slave(struct w1_slave *sl)
 {
        int i;
-       for (i = NB_SYSFS_BIN_FILES - 1; i >= 0; --i)
+       for (i = ARRAY_SIZE(w1_f29_sysfs_bin_files) - 1; i >= 0; --i)
                sysfs_remove_bin_file(&sl->dev.kobj,
                        &(w1_f29_sysfs_bin_files[i]));
 }
index df77ba9..95d0850 100644 (file)
 
 #include <linux/types.h>
 
+
+/*
+ * Implementation of host controlled snapshot of the guest.
+ */
+
+#define VSS_OP_REGISTER 128
+
+enum hv_vss_op {
+       VSS_OP_CREATE = 0,
+       VSS_OP_DELETE,
+       VSS_OP_HOT_BACKUP,
+       VSS_OP_GET_DM_INFO,
+       VSS_OP_BU_COMPLETE,
+       /*
+        * Following operations are only supported with IC version >= 5.0
+        */
+       VSS_OP_FREEZE, /* Freeze the file systems in the VM */
+       VSS_OP_THAW, /* Unfreeze the file systems */
+       VSS_OP_AUTO_RECOVER,
+       VSS_OP_COUNT /* Number of operations, must be last */
+};
+
+
+/*
+ * Header for all VSS messages.
+ */
+struct hv_vss_hdr {
+       __u8 operation;
+       __u8 reserved[7];
+} __attribute__((packed));
+
+
+/*
+ * Flag values for the hv_vss_check_feature. Linux supports only
+ * one value.
+ */
+#define VSS_HBU_NO_AUTO_RECOVERY       0x00000005
+
+struct hv_vss_check_feature {
+       __u32 flags;
+} __attribute__((packed));
+
+struct hv_vss_check_dm_info {
+       __u32 flags;
+} __attribute__((packed));
+
+struct hv_vss_msg {
+       union {
+               struct hv_vss_hdr vss_hdr;
+               int error;
+       };
+       union {
+               struct hv_vss_check_feature vss_cf;
+               struct hv_vss_check_dm_info dm_info;
+       };
+} __attribute__((packed));
+
 /*
  * An implementation of HyperV key value pair (KVP) functionality for Linux.
  *
@@ -1252,6 +1309,14 @@ void vmbus_driver_unregister(struct hv_driver *hv_driver);
                        0xb9, 0x8b, 0x8b, 0xa1, 0xa1, 0xf3, 0xf9, 0x5a \
                }
 
+/*
+ * VSS (Backup/Restore) GUID
+ */
+#define HV_VSS_GUID \
+       .guid = { \
+                       0x29, 0x2e, 0xfa, 0x35, 0x23, 0xea, 0x36, 0x42, \
+                       0x96, 0xae, 0x3a, 0x6e, 0xba, 0xcb, 0xa4,  0x40 \
+               }
 /*
  * Common header for Hyper-V ICs
  */
@@ -1356,6 +1421,10 @@ int hv_kvp_init(struct hv_util_service *);
 void hv_kvp_deinit(void);
 void hv_kvp_onchannelcallback(void *);
 
+int hv_vss_init(struct hv_util_service *);
+void hv_vss_deinit(void);
+void hv_vss_onchannelcallback(void *);
+
 /*
  * Negotiated version with the Host.
  */
index fea12cb..1888e06 100644 (file)
@@ -207,19 +207,41 @@ int ipack_driver_register(struct ipack_driver *edrv, struct module *owner,
 void ipack_driver_unregister(struct ipack_driver *edrv);
 
 /**
- *     ipack_device_register -- register an IPack device with the kernel
- *     @dev: the new device to register.
+ *     ipack_device_init -- initialize an IPack device
+ * @dev: the new device to initialize.
  *
- *     Register a new IPack device ("module" in IndustryPack jargon). The call
- *     is done by the carrier driver.  The carrier should populate the fields
- *     bus and slot as well as the region array of @dev prior to calling this
- *     function.  The rest of the fields will be allocated and populated
- *     during registration.
+ * Initialize a new IPack device ("module" in IndustryPack jargon). The call
+ * is done by the carrier driver.  The carrier should populate the fields
+ * bus and slot as well as the region array of @dev prior to calling this
+ * function.  The rest of the fields will be allocated and populated
+ * during initalization.
  *
- *     Return zero on success or error code on failure.
+ * Return zero on success or error code on failure.
+ *
+ * NOTE: _Never_ directly free @dev after calling this function, even
+ * if it returned an error! Always use ipack_put_device() to give up the
+ * reference initialized in this function instead.
+ */
+int ipack_device_init(struct ipack_device *dev);
+
+/**
+ *     ipack_device_add -- Add an IPack device
+ * @dev: the new device to add.
+ *
+ * Add a new IPack device. The call is done by the carrier driver
+ * after calling ipack_device_init().
+ *
+ * Return zero on success or error code on failure.
+ *
+ * NOTE: _Never_ directly free @dev after calling this function, even
+ * if it returned an error! Always use ipack_put_device() to give up the
+ * reference initialized in this function instead.
  */
-int ipack_device_register(struct ipack_device *dev);
-void ipack_device_unregister(struct ipack_device *dev);
+int ipack_device_add(struct ipack_device *dev);
+void ipack_device_del(struct ipack_device *dev);
+
+void ipack_get_device(struct ipack_device *dev);
+void ipack_put_device(struct ipack_device *dev);
 
 /**
  * DEFINE_IPACK_DEVICE_TABLE - macro used to describe a IndustryPack table
diff --git a/include/linux/mei_cl_bus.h b/include/linux/mei_cl_bus.h
new file mode 100644 (file)
index 0000000..1bece18
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef _LINUX_MEI_CL_BUS_H
+#define _LINUX_MEI_CL_BUS_H
+
+#include <linux/device.h>
+#include <linux/uuid.h>
+
+struct mei_cl_device;
+
+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_device_id *id);
+       int (*remove)(struct mei_cl_device *dev);
+};
+
+int __mei_cl_driver_register(struct mei_cl_driver *driver,
+                               struct module *owner);
+#define mei_cl_driver_register(driver)             \
+       __mei_cl_driver_register(driver, THIS_MODULE)
+
+void mei_cl_driver_unregister(struct mei_cl_driver *driver);
+
+int mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length);
+int mei_cl_recv(struct mei_cl_device *device, u8 *buf, size_t length);
+
+typedef void (*mei_cl_event_cb_t)(struct mei_cl_device *device,
+                              u32 events, void *context);
+int mei_cl_register_event_cb(struct mei_cl_device *device,
+                         mei_cl_event_cb_t read_cb, void *context);
+
+#define MEI_CL_EVENT_RX 0
+#define MEI_CL_EVENT_TX 1
+
+void *mei_cl_get_drvdata(const struct mei_cl_device *device);
+void mei_cl_set_drvdata(struct mei_cl_device *device, void *data);
+
+#endif /* _LINUX_MEI_CL_BUS_H */
index a710255..cc28136 100644 (file)
@@ -100,6 +100,9 @@ struct arizona {
        struct regmap_irq_chip_data *aod_irq_chip;
        struct regmap_irq_chip_data *irq_chip;
 
+       bool hpdet_magic;
+       unsigned int hp_ena;
+
        struct mutex clk_lock;
        int clk32k_ref;
 
index 455c51d..a0f9409 100644 (file)
@@ -86,6 +86,11 @@ struct arizona_micd_config {
        bool gpio;
 };
 
+struct arizona_micd_range {
+       int max;  /** Ohms */
+       int key;  /** Key to report to input layer */
+};
+
 struct arizona_pdata {
        int reset;      /** GPIO controlling /RESET, if any */
        int ldoena;     /** GPIO controlling LODENA, if any */
@@ -117,12 +122,21 @@ struct arizona_pdata {
        /** GPIO5 is used for jack detection */
        bool jd_gpio5;
 
+       /** Internal pull on GPIO5 is disabled when used for jack detection */
+       bool jd_gpio5_nopull;
+
        /** Use the headphone detect circuit to identify the accessory */
        bool hpdet_acc_id;
 
+       /** Check for line output with HPDET method */
+       bool hpdet_acc_id_line;
+
        /** GPIO used for mic isolation with HPDET */
        int hpdet_id_gpio;
 
+       /** Extra debounce timeout used during initial mic detection (ms) */
+       int micd_detect_debounce;
+
        /** GPIO for mic detection polarity */
        int micd_pol_gpio;
 
@@ -135,9 +149,16 @@ struct arizona_pdata {
        /** Mic detect debounce level */
        int micd_dbtime;
 
+       /** Mic detect timeout (ms) */
+       int micd_timeout;
+
        /** Force MICBIAS on for mic detect */
        bool micd_force_micbias;
 
+       /** Mic detect level parameters */
+       const struct arizona_micd_range *micd_ranges;
+       int num_micd_ranges;
+
        /** Headset polarity configurations */
        struct arizona_micd_config *micd_configs;
        int num_micd_configs;
index 3403551..f43aa7c 100644 (file)
 #define ARIZONA_MIC_DETECT_1                     0x2A3
 #define ARIZONA_MIC_DETECT_2                     0x2A4
 #define ARIZONA_MIC_DETECT_3                     0x2A5
+#define ARIZONA_MIC_DETECT_LEVEL_1              0x2A6
+#define ARIZONA_MIC_DETECT_LEVEL_2              0x2A7
+#define ARIZONA_MIC_DETECT_LEVEL_3              0x2A8
+#define ARIZONA_MIC_DETECT_LEVEL_4              0x2A9
 #define ARIZONA_MIC_NOISE_MIX_CONTROL_1          0x2C3
 #define ARIZONA_ISOLATION_CONTROL                0x2CB
 #define ARIZONA_JACK_DETECT_ANALOGUE             0x2D3
index 779cf7c..b508016 100644 (file)
@@ -9,6 +9,7 @@
 
 #ifdef __KERNEL__
 #include <linux/types.h>
+#include <linux/uuid.h>
 typedef unsigned long kernel_ulong_t;
 #endif
 
@@ -568,4 +569,12 @@ struct ipack_device_id {
        __u32 device;                   /* Device ID or IPACK_ANY_ID */
 };
 
+#define MEI_CL_MODULE_PREFIX "mei:"
+#define MEI_CL_NAME_SIZE 32
+
+struct mei_cl_device_id {
+       char name[MEI_CL_NAME_SIZE];
+       kernel_ulong_t driver_info;
+};
+
 #endif /* LINUX_MOD_DEVICETABLE_H */
index 03378ca..5c19a2a 100644 (file)
@@ -40,6 +40,7 @@
 /* Custom config requests */
 #define EMIF_CUSTOM_CONFIG_LPMODE                      0x00000001
 #define EMIF_CUSTOM_CONFIG_TEMP_ALERT_POLL_INTERVAL    0x00000002
+#define EMIF_CUSTOM_CONFIG_EXTENDED_TEMP_PART          0x00000004
 
 #ifndef __ASSEMBLY__
 /**
diff --git a/include/linux/ssbi.h b/include/linux/ssbi.h
new file mode 100644 (file)
index 0000000..44ef5da
--- /dev/null
@@ -0,0 +1,38 @@
+/* Copyright (C) 2010 Google, Inc.
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ * Author: Dima Zavin <dima@android.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 and
+ * only 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.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _LINUX_SSBI_H
+#define _LINUX_SSBI_H
+
+#include <linux/types.h>
+
+struct ssbi_slave_info {
+       const char      *name;
+       void            *platform_data;
+};
+
+enum ssbi_controller_type {
+       MSM_SBI_CTRL_SSBI = 0,
+       MSM_SBI_CTRL_SSBI2,
+       MSM_SBI_CTRL_PMIC_ARBITER,
+};
+
+struct ssbi_platform_data {
+       struct ssbi_slave_info  slave;
+       enum ssbi_controller_type controller_type;
+};
+
+int ssbi_write(struct device *dev, u16 addr, u8 *buf, int len);
+int ssbi_read(struct device *dev, u16 addr, u8 *buf, int len);
+#endif
index 3bbbd78..2d56e42 100644 (file)
@@ -65,6 +65,18 @@ struct pcmcia_driver {
 int pcmcia_register_driver(struct pcmcia_driver *driver);
 void pcmcia_unregister_driver(struct pcmcia_driver *driver);
 
+/**
+ * module_pcmcia_driver() - Helper macro for registering a pcmcia driver
+ * @__pcmcia_driver: pcmcia_driver struct
+ *
+ * Helper macro for pcmcia drivers which do not do anything special in module
+ * init/exit. This eliminates a lot of boilerplate. Each module may only use
+ * this macro once, and calling it replaces module_init() and module_exit().
+ */
+#define module_pcmcia_driver(__pcmcia_driver) \
+       module_driver(__pcmcia_driver, pcmcia_register_driver, \
+                       pcmcia_unregister_driver)
+
 /* for struct resource * array embedded in struct pcmcia_device */
 enum {
        PCMCIA_IOPORT_0,
index 8761a03..4cb2835 100644 (file)
 #define CN_VAL_DRBD                    0x1
 #define CN_KVP_IDX                     0x9     /* HyperV KVP */
 #define CN_KVP_VAL                     0x1     /* queries from the kernel */
+#define CN_VSS_IDX                     0xA     /* HyperV VSS */
+#define CN_VSS_VAL                     0x1     /* queries from the kernel */
 
-#define CN_NETLINK_USERS               10      /* Highest index + 1 */
+
+#define CN_NETLINK_USERS               11      /* Highest index + 1 */
 
 /*
  * Maximum connector's message size.
index 8fcced7..7ff1536 100644 (file)
@@ -1397,6 +1397,7 @@ void split_page(struct page *page, unsigned int order)
        for (i = 1; i < (1 << order); i++)
                set_page_refcounted(page + i);
 }
+EXPORT_SYMBOL_GPL(split_page);
 
 static int __isolate_free_page(struct page *page, unsigned int order)
 {
index b45260b..e66d4d2 100644 (file)
@@ -174,5 +174,8 @@ int main(void)
        DEVID_FIELD(x86_cpu_id, model);
        DEVID_FIELD(x86_cpu_id, vendor);
 
+       DEVID(mei_cl_device_id);
+       DEVID_FIELD(mei_cl_device_id, name);
+
        return 0;
 }
index 771ac17..45f9a33 100644 (file)
@@ -1133,6 +1133,18 @@ static int do_x86cpu_entry(const char *filename, void *symval,
 }
 ADD_TO_DEVTABLE("x86cpu", x86_cpu_id, do_x86cpu_entry);
 
+/* Looks like: mei:S */
+static int do_mei_entry(const char *filename, void *symval,
+                       char *alias)
+{
+       DEF_FIELD_ADDR(symval, mei_cl_device_id, name);
+
+       sprintf(alias, MEI_CL_MODULE_PREFIX "%s", *name);
+
+       return 1;
+}
+ADD_TO_DEVTABLE("mei", mei_cl_device_id, do_mei_entry);
+
 /* Does namelen bytes of name exactly match the symbol? */
 static bool sym_is(const char *name, unsigned namelen, const char *symbol)
 {
index f9b5229..8f489de 100644 (file)
@@ -295,18 +295,5 @@ static struct pcmcia_driver pdacf_cs_driver = {
        .suspend        = pdacf_suspend,
        .resume         = pdacf_resume,
 #endif
-
 };
-
-static int __init init_pdacf(void)
-{
-       return pcmcia_register_driver(&pdacf_cs_driver);
-}
-
-static void __exit exit_pdacf(void)
-{
-       pcmcia_unregister_driver(&pdacf_cs_driver);
-}
-
-module_init(init_pdacf);
-module_exit(exit_pdacf);
+module_pcmcia_driver(pdacf_cs_driver);
index 8f93504..d4db7ec 100644 (file)
@@ -367,16 +367,4 @@ static struct pcmcia_driver vxp_cs_driver = {
        .resume         = vxp_resume,
 #endif
 };
-
-static int __init init_vxpocket(void)
-{
-       return pcmcia_register_driver(&vxp_cs_driver);
-}
-
-static void __exit exit_vxpocket(void)
-{
-       pcmcia_unregister_driver(&vxp_cs_driver);
-}
-
-module_init(init_vxpocket);
-module_exit(exit_vxpocket);
+module_pcmcia_driver(vxp_cs_driver);
index ac948a6..e7d3471 100644 (file)
@@ -364,6 +364,39 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
 }
 EXPORT_SYMBOL_GPL(arizona_out_ev);
 
+int arizona_hp_ev(struct snd_soc_dapm_widget *w,
+                  struct snd_kcontrol *kcontrol,
+                  int event)
+{
+       struct arizona_priv *priv = snd_soc_codec_get_drvdata(w->codec);
+       unsigned int mask = 1 << w->shift;
+       unsigned int val;
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               val = mask;
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               val = 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* Store the desired state for the HP outputs */
+       priv->arizona->hp_ena &= ~mask;
+       priv->arizona->hp_ena |= val;
+
+       /* Force off if HPDET magic is active */
+       if (priv->arizona->hpdet_magic)
+               val = 0;
+
+       snd_soc_update_bits(w->codec, ARIZONA_OUTPUT_ENABLES_1, mask, val);
+
+       return arizona_out_ev(w, kcontrol, event);
+}
+EXPORT_SYMBOL_GPL(arizona_hp_ev);
+
 static unsigned int arizona_sysclk_48k_rates[] = {
        6144000,
        12288000,
index 116372c..13dd291 100644 (file)
@@ -184,6 +184,9 @@ extern int arizona_in_ev(struct snd_soc_dapm_widget *w,
 extern int arizona_out_ev(struct snd_soc_dapm_widget *w,
                          struct snd_kcontrol *kcontrol,
                          int event);
+extern int arizona_hp_ev(struct snd_soc_dapm_widget *w,
+                        struct snd_kcontrol *kcontrol,
+                        int event);
 
 extern int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
                              int source, unsigned int freq, int dir);
index b82bbf5..2657aad 100644 (file)
@@ -1131,11 +1131,11 @@ ARIZONA_DSP_WIDGETS(DSP1, "DSP1"),
 SND_SOC_DAPM_VALUE_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
                       ARIZONA_AEC_LOOPBACK_ENA, 0, &wm5102_aec_loopback_mux),
 
-SND_SOC_DAPM_PGA_E("OUT1L", ARIZONA_OUTPUT_ENABLES_1,
-                  ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM,
+                  ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
-SND_SOC_DAPM_PGA_E("OUT1R", ARIZONA_OUTPUT_ENABLES_1,
-                  ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM,
+                  ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
 SND_SOC_DAPM_PGA_E("OUT2L", ARIZONA_OUTPUT_ENABLES_1,
                   ARIZONA_OUT2L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
index cdeb301..7841b42 100644 (file)
@@ -551,11 +551,11 @@ SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0,
 SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0,
                    ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0),
 
-SND_SOC_DAPM_PGA_E("OUT1L", ARIZONA_OUTPUT_ENABLES_1,
-                  ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM,
+                  ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
-SND_SOC_DAPM_PGA_E("OUT1R", ARIZONA_OUTPUT_ENABLES_1,
-                  ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM,
+                  ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
 SND_SOC_DAPM_PGA_E("OUT2L", ARIZONA_OUTPUT_ENABLES_1,
                   ARIZONA_OUT2L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
index c800ea4..5a1f648 100644 (file)
@@ -102,6 +102,10 @@ static struct utsname uts_buf;
 #define MAX_FILE_NAME 100
 #define ENTRIES_PER_BLOCK 50
 
+#ifndef SOL_NETLINK
+#define SOL_NETLINK 270
+#endif
+
 struct kvp_record {
        char key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
        char value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
@@ -1407,7 +1411,7 @@ netlink_send(int fd, struct cn_msg *msg)
 
 int main(void)
 {
-       int fd, len, sock_opt;
+       int fd, len, nl_group;
        int error;
        struct cn_msg *message;
        struct pollfd pfd;
@@ -1443,7 +1447,7 @@ int main(void)
        addr.nl_family = AF_NETLINK;
        addr.nl_pad = 0;
        addr.nl_pid = 0;
-       addr.nl_groups = CN_KVP_IDX;
+       addr.nl_groups = 0;
 
 
        error = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
@@ -1452,8 +1456,8 @@ int main(void)
                close(fd);
                exit(EXIT_FAILURE);
        }
-       sock_opt = addr.nl_groups;
-       setsockopt(fd, 270, 1, &sock_opt, sizeof(sock_opt));
+       nl_group = CN_KVP_IDX;
+       setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &nl_group, sizeof(nl_group));
        /*
         * Register ourselves with the kernel.
         */
@@ -1499,6 +1503,10 @@ int main(void)
                }
 
                incoming_msg = (struct nlmsghdr *)kvp_recv_buffer;
+
+               if (incoming_msg->nlmsg_type != NLMSG_DONE)
+                       continue;
+
                incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg);
                hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data;
 
diff --git a/tools/hv/hv_vss_daemon.c b/tools/hv/hv_vss_daemon.c
new file mode 100644 (file)
index 0000000..9526995
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * An implementation of the host initiated guest snapshot for Hyper-V.
+ *
+ *
+ * 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.
+ *
+ */
+
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <linux/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <linux/connector.h>
+#include <linux/hyperv.h>
+#include <linux/netlink.h>
+#include <syslog.h>
+
+static char vss_recv_buffer[4096];
+static char vss_send_buffer[4096];
+static struct sockaddr_nl addr;
+
+#ifndef SOL_NETLINK
+#define SOL_NETLINK 270
+#endif
+
+
+static int vss_operate(int operation)
+{
+       char *fs_op;
+       char cmd[512];
+       char buf[512];
+       FILE *file;
+       char *p;
+       char *x;
+       int error;
+
+       switch (operation) {
+       case VSS_OP_FREEZE:
+               fs_op = "-f ";
+               break;
+       case VSS_OP_THAW:
+               fs_op = "-u ";
+               break;
+       }
+
+       file = popen("mount | awk '/^\/dev\// { print $3}'", "r");
+       if (file == NULL)
+               return;
+
+       while ((p = fgets(buf, sizeof(buf), file)) != NULL) {
+               x = strchr(p, '\n');
+               *x = '\0';
+               if (!strncmp(p, "/", sizeof("/")))
+                       continue;
+
+               sprintf(cmd, "%s %s %s", "fsfreeze ", fs_op, p);
+               syslog(LOG_INFO, "VSS cmd is %s\n", cmd);
+               error = system(cmd);
+       }
+       pclose(file);
+
+       sprintf(cmd, "%s %s %s", "fsfreeze ", fs_op, "/");
+       syslog(LOG_INFO, "VSS cmd is %s\n", cmd);
+       error = system(cmd);
+
+       return error;
+}
+
+static int netlink_send(int fd, struct cn_msg *msg)
+{
+       struct nlmsghdr *nlh;
+       unsigned int size;
+       struct msghdr message;
+       char buffer[64];
+       struct iovec iov[2];
+
+       size = NLMSG_SPACE(sizeof(struct cn_msg) + msg->len);
+
+       nlh = (struct nlmsghdr *)buffer;
+       nlh->nlmsg_seq = 0;
+       nlh->nlmsg_pid = getpid();
+       nlh->nlmsg_type = NLMSG_DONE;
+       nlh->nlmsg_len = NLMSG_LENGTH(size - sizeof(*nlh));
+       nlh->nlmsg_flags = 0;
+
+       iov[0].iov_base = nlh;
+       iov[0].iov_len = sizeof(*nlh);
+
+       iov[1].iov_base = msg;
+       iov[1].iov_len = size;
+
+       memset(&message, 0, sizeof(message));
+       message.msg_name = &addr;
+       message.msg_namelen = sizeof(addr);
+       message.msg_iov = iov;
+       message.msg_iovlen = 2;
+
+       return sendmsg(fd, &message, 0);
+}
+
+int main(void)
+{
+       int fd, len, nl_group;
+       int error;
+       struct cn_msg *message;
+       struct pollfd pfd;
+       struct nlmsghdr *incoming_msg;
+       struct cn_msg   *incoming_cn_msg;
+       int     op;
+       struct hv_vss_msg *vss_msg;
+
+       daemon(1, 0);
+       openlog("Hyper-V VSS", 0, LOG_USER);
+       syslog(LOG_INFO, "VSS starting; pid is:%d", getpid());
+
+       fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
+       if (fd < 0) {
+               syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd);
+               exit(EXIT_FAILURE);
+       }
+       addr.nl_family = AF_NETLINK;
+       addr.nl_pad = 0;
+       addr.nl_pid = 0;
+       addr.nl_groups = 0;
+
+
+       error = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
+       if (error < 0) {
+               syslog(LOG_ERR, "bind failed; error:%d", error);
+               close(fd);
+               exit(EXIT_FAILURE);
+       }
+       nl_group = CN_VSS_IDX;
+       setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &nl_group, sizeof(nl_group));
+       /*
+        * Register ourselves with the kernel.
+        */
+       message = (struct cn_msg *)vss_send_buffer;
+       message->id.idx = CN_VSS_IDX;
+       message->id.val = CN_VSS_VAL;
+       message->ack = 0;
+       vss_msg = (struct hv_vss_msg *)message->data;
+       vss_msg->vss_hdr.operation = VSS_OP_REGISTER;
+
+       message->len = sizeof(struct hv_vss_msg);
+
+       len = netlink_send(fd, message);
+       if (len < 0) {
+               syslog(LOG_ERR, "netlink_send failed; error:%d", len);
+               close(fd);
+               exit(EXIT_FAILURE);
+       }
+
+       pfd.fd = fd;
+
+       while (1) {
+               struct sockaddr *addr_p = (struct sockaddr *) &addr;
+               socklen_t addr_l = sizeof(addr);
+               pfd.events = POLLIN;
+               pfd.revents = 0;
+               poll(&pfd, 1, -1);
+
+               len = recvfrom(fd, vss_recv_buffer, sizeof(vss_recv_buffer), 0,
+                               addr_p, &addr_l);
+
+               if (len < 0 || addr.nl_pid) {
+                       syslog(LOG_ERR, "recvfrom failed; pid:%u error:%d %s",
+                                       addr.nl_pid, errno, strerror(errno));
+                       close(fd);
+                       return -1;
+               }
+
+               incoming_msg = (struct nlmsghdr *)vss_recv_buffer;
+
+               if (incoming_msg->nlmsg_type != NLMSG_DONE)
+                       continue;
+
+               incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg);
+               vss_msg = (struct hv_vss_msg *)incoming_cn_msg->data;
+               op = vss_msg->vss_hdr.operation;
+               error =  HV_S_OK;
+
+               switch (op) {
+               case VSS_OP_FREEZE:
+               case VSS_OP_THAW:
+                       error = vss_operate(op);
+                       if (error)
+                               error = HV_E_FAIL;
+                       break;
+               default:
+                       syslog(LOG_ERR, "Illegal op:%d\n", op);
+               }
+               vss_msg->error = error;
+               len = netlink_send(fd, incoming_cn_msg);
+               if (len < 0) {
+                       syslog(LOG_ERR, "net_link send failed; error:%d", len);
+                       exit(EXIT_FAILURE);
+               }
+       }
+
+}