Merge tag 'arizona-extcon-asoc' of git://git.kernel.org/pub/scm/linux/kernel/git...
Greg Kroah-Hartman [Tue, 26 Mar 2013 16:19:02 +0000 (09:19 -0700)]
Mark writes:

ASoC/extcon: arizona: Fix interaction between HPDET and headphone outputs

This patch series covers both ASoC and extcon subsystems and fixes an
interaction between the HPDET function and the headphone outputs - we
really shouldn't run HPDET while the headphone is active.  The first
patch is a refactoring to make the extcon side easier.

95 files changed:
Documentation/devicetree/bindings/arm/msm/ssbi.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/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/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/misc/Kconfig
drivers/misc/Makefile
drivers/misc/apds990x.c
drivers/misc/arm-charlcd.c
drivers/misc/atmel_pwm.c
drivers/misc/dummy-irq.c [new file with mode: 0644]
drivers/misc/ep93xx_pwm.c
drivers/misc/mei/amthif.c
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/mei_dev.h
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/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/platform_data/emif_plat.h
include/linux/ssbi.h [new file with mode: 0644]
include/pcmcia/ds.h
include/uapi/linux/connector.h
sound/pcmcia/pdaudiocf/pdaudiocf.c
sound/pcmcia/vx/vxpocket.c
tools/hv/hv_kvp_daemon.c
tools/hv/hv_vss_daemon.c [new file with mode: 0644]

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.
index 4cf5fd3..8b9cbc7 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 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..d522526 100644 (file)
@@ -412,13 +412,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 +478,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,7 +492,28 @@ 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.
@@ -467,6 +521,11 @@ struct hv_dynmem_device {
        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
         * next version to try.
@@ -476,35 +535,329 @@ 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);
+       }
+}
+
+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));
+
+               if (ret) {
+                       pr_info("hot_add memory failed error is %d\n", ret);
+                       has->ha_end_pfn -= HA_CHUNK;
+                       has->covered_end_pfn -=  processed_pfn;
+                       break;
+               }
+
+               /*
+                * 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 (do_hot_add) {
+               if (has->covered_end_pfn != start_pfn) {
+                       has->covered_end_pfn = start_pfn;
+                       has->covered_start_pfn = start_pfn;
+               }
+               return true;
+
+       }
+
+       return false;
+}
 
-               pr_info("Memory hot add not supported\n");
+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);
 
                /*
-                * Currently we do not support hot add.
-                * Just fail the request.
+                * 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) && (!dm_device.host_specified_ha_region)) {
+               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;
+       }
+
+       resp.page_count = process_hot_add(pg_start, pfn_cnt,
+                                       rg_start, rg_sz);
+#endif
+       if (resp.page_count > 0)
+               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 +876,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))
@@ -657,9 +1010,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;
@@ -684,14 +1037,14 @@ 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) || (num_ballooned == num_pages)) {
                        bl_resp->more_pages = 0;
                        done = true;
-                       dm->state = DM_INITIALIZED;
+                       dm_device.state = DM_INITIALIZED;
                }
 
                /*
@@ -719,7 +1072,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 +1114,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 +1125,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 +1197,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 +1222,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 +1237,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 +1304,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 +1316,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 +1337,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,12 +1364,6 @@ static int balloon_probe(struct hv_device *dev,
        cap_msg.hdr.trans_id = atomic_inc_return(&trans_id);
 
        cap_msg.caps.cap_bits.balloon = 1;
-       /*
-        * While we currently don't support hot-add,
-        * we still advertise this capability since the
-        * host requires that guests partcipating in the
-        * dynamic memory protocol support hot add.
-        */
        cap_msg.caps.cap_bits.hot_add = 1;
 
        /*
@@ -1009,8 +1377,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 +1401,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 +1416,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 +1462,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 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..72933c7 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
        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 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..1efb6a4 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;
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");
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 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 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;
index 45ea718..11a2a65 100644 (file)
@@ -222,6 +222,38 @@ static bool mei_me_hw_is_ready(struct mei_device *dev)
        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
  *
@@ -295,10 +327,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;
 
@@ -423,8 +456,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;
@@ -455,14 +486,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->recvd_hw_ready = true;
+                       wake_up_interruptible(&dev->wait_hw_ready);
 
-                       dev->dev_state = MEI_DEV_INIT_CLIENTS;
-
-                       mei_hbm_start_req(dev);
                        mutex_unlock(&dev->device_lock);
                        return IRQ_HANDLED;
                } else {
@@ -499,33 +525,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,
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 6ec5301..fc3d97c 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/mei.h>
 
 #include "mei_dev.h"
+#include "hbm.h"
 #include "client.h"
 
 const char *mei_dev_state_str(int state)
@@ -47,6 +48,7 @@ void mei_device_init(struct mei_device *dev)
        /* setup our list array */
        INIT_LIST_HEAD(&dev->file_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;
@@ -176,6 +178,20 @@ 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);
 
index 3535b26..73fbce3 100644 (file)
 
 
 /**
- * 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))
@@ -54,6 +54,31 @@ void mei_irq_complete_handler(struct mei_cl *cl, struct mei_cl_cb *cb_pos)
 }
 
 /**
+ * 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);
+       }
+}
+/**
  * _mei_irq_thread_state_ok - checks if mei header matches file private data
  *
  * @cl: private data of the file object
@@ -153,25 +178,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 +219,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 +257,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 +298,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;
@@ -419,8 +451,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;
index cb80166..1a4b50c 100644 (file)
@@ -213,11 +213,11 @@ struct mei_cl {
 
 /** 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 +237,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);
@@ -296,11 +296,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;
 
@@ -374,6 +377,17 @@ 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
@@ -391,8 +405,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
@@ -454,6 +467,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);
@@ -469,10 +487,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);
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 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.
  *
@@ -1253,6 +1310,14 @@ void vmbus_driver_unregister(struct hv_driver *hv_driver);
                }
 
 /*
+ * 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
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 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 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);
+               }
+       }
+
+}