Merge tag 'pull_req_20121122' of git://git.kernel.org/pub/scm/linux/kernel/git/mzx...
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 27 Nov 2012 14:44:10 +0000 (06:44 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 27 Nov 2012 14:44:10 +0000 (06:44 -0800)
MyungJoo writes:
 "extcon pull request targetting Linux 3.8 for Greg KH on 2012.11.22
  This is based on Linux 3.7 rc6"

135 files changed:
Documentation/DocBook/uio-howto.tmpl
MAINTAINERS
arch/arm/include/asm/io.h
arch/arm/mm/mmap.c
arch/ia64/include/asm/io.h
arch/ia64/kernel/efi.c
arch/sh/include/asm/io.h
arch/sh/mm/mmap.c
drivers/char/agp/ali-agp.c
drivers/char/agp/amd-k7-agp.c
drivers/char/agp/amd64-agp.c
drivers/char/agp/ati-agp.c
drivers/char/agp/efficeon-agp.c
drivers/char/agp/i460-agp.c
drivers/char/agp/intel-agp.c
drivers/char/agp/nvidia-agp.c
drivers/char/agp/sgi-agp.c
drivers/char/agp/sis-agp.c
drivers/char/agp/sworks-agp.c
drivers/char/agp/uninorth-agp.c
drivers/char/agp/via-agp.c
drivers/char/hpet.c
drivers/char/hw_random/atmel-rng.c
drivers/char/hw_random/bcm63xx-rng.c
drivers/char/hw_random/exynos-rng.c
drivers/char/hw_random/n2-drv.c
drivers/char/hw_random/pasemi-rng.c
drivers/char/hw_random/picoxcell-rng.c
drivers/char/hw_random/ppc4xx-rng.c
drivers/char/hw_random/timeriomem-rng.c
drivers/char/hw_random/virtio-rng.c
drivers/char/ipmi/ipmi_si_intf.c
drivers/char/mbcs.c
drivers/char/mem.c
drivers/char/pc8736x_gpio.c
drivers/char/ps3flash.c
drivers/char/sonypi.c
drivers/char/tb0219.c
drivers/char/tpm/tpm_i2c_infineon.c
drivers/char/tpm/tpm_ibmvtpm.c
drivers/char/tpm/tpm_infineon.c
drivers/char/tpm/tpm_tis.c
drivers/char/virtio_console.c
drivers/char/xilinx_hwicap/xilinx_hwicap.c
drivers/extcon/extcon-adc-jack.c
drivers/extcon/extcon-arizona.c
drivers/extcon/extcon-gpio.c
drivers/extcon/extcon-max77693.c
drivers/extcon/extcon-max8997.c
drivers/hv/Kconfig
drivers/hv/Makefile
drivers/hv/channel.c
drivers/hv/channel_mgmt.c
drivers/hv/hv_balloon.c [new file with mode: 0644]
drivers/memstick/host/Kconfig
drivers/memstick/host/Makefile
drivers/memstick/host/rtsx_pci_ms.c [new file with mode: 0644]
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/rtl8411.c [new file with mode: 0644]
drivers/mfd/rts5209.c [new file with mode: 0644]
drivers/mfd/rts5229.c [new file with mode: 0644]
drivers/mfd/rtsx_pcr.c [new file with mode: 0644]
drivers/mfd/rtsx_pcr.h [new file with mode: 0644]
drivers/misc/ad525x_dpot-i2c.c
drivers/misc/ad525x_dpot-spi.c
drivers/misc/ad525x_dpot.c
drivers/misc/apds9802als.c
drivers/misc/apds990x.c
drivers/misc/atmel-ssc.c
drivers/misc/bh1770glc.c
drivers/misc/bh1780gli.c
drivers/misc/bmp085-i2c.c
drivers/misc/bmp085-spi.c
drivers/misc/bmp085.c
drivers/misc/cb710/core.c
drivers/misc/cs5535-mfgpt.c
drivers/misc/eeprom/at24.c
drivers/misc/eeprom/at25.c
drivers/misc/eeprom/eeprom_93xx46.c
drivers/misc/fsa9480.c
drivers/misc/hpilo.c
drivers/misc/ibmasm/module.c
drivers/misc/ioc4.c
drivers/misc/isl29003.c
drivers/misc/lis3lv02d/lis3lv02d_i2c.c
drivers/misc/lis3lv02d/lis3lv02d_spi.c
drivers/misc/mei/Makefile
drivers/misc/mei/amthif.c [new file with mode: 0644]
drivers/misc/mei/hw.h
drivers/misc/mei/init.c
drivers/misc/mei/interface.c
drivers/misc/mei/interrupt.c
drivers/misc/mei/iorw.c
drivers/misc/mei/main.c
drivers/misc/mei/mei_dev.h
drivers/misc/mei/wd.c
drivers/misc/pch_phub.c
drivers/misc/phantom.c
drivers/misc/pti.c
drivers/misc/spear13xx_pcie_gadget.c
drivers/misc/ti-st/st_core.c
drivers/misc/ti-st/st_kim.c
drivers/misc/ti_dac7512.c
drivers/misc/tsl2550.c
drivers/mmc/host/Kconfig
drivers/mmc/host/Makefile
drivers/mmc/host/rtsx_pci_sdmmc.c [new file with mode: 0644]
drivers/parport/Kconfig
drivers/uio/Kconfig
drivers/uio/Makefile
drivers/uio/uio_aec.c
drivers/uio/uio_cif.c
drivers/uio/uio_dmem_genirq.c [new file with mode: 0644]
drivers/uio/uio_netx.c
drivers/uio/uio_pci_generic.c
drivers/uio/uio_pdrv.c
drivers/uio/uio_pdrv_genirq.c
drivers/uio/uio_pruss.c
drivers/uio/uio_sercos3.c
drivers/w1/masters/Kconfig
drivers/w1/masters/ds2482.c
drivers/w1/masters/matrox_w1.c
drivers/w1/masters/mxc_w1.c
drivers/w1/masters/omap_hdq.c
drivers/w1/masters/w1-gpio.c
drivers/w1/w1.c
drivers/xen/xen-selfballoon.c
include/linux/mfd/rtsx_common.h [new file with mode: 0644]
include/linux/mfd/rtsx_pci.h [new file with mode: 0644]
include/linux/mman.h
include/linux/platform_data/uio_dmem_genirq.h [new file with mode: 0644]
mm/mmap.c
mm/nommu.c
tools/hv/hv_kvp_daemon.c

index ac3d0018140cd34cd7dd242334de64713406c6b6..ddb05e98af0df99459c0735605c7208dfdd6892b 100644 (file)
@@ -719,6 +719,62 @@ framework to set up sysfs files for this region. Simply leave it alone.
        </para>
 </sect1>
 
+<sect1 id="using uio_dmem_genirq">
+<title>Using uio_dmem_genirq for platform devices</title>
+       <para>
+       In addition to statically allocated memory ranges, they may also be
+       a desire to use dynamically allocated regions in a user space driver.
+       In particular, being able to access memory made available through the
+       dma-mapping API, may be particularly useful.  The
+       <varname>uio_dmem_genirq</varname> driver provides a way to accomplish
+       this.
+       </para>
+       <para>
+       This driver is used in a similar manner to the
+       <varname>"uio_pdrv_genirq"</varname> driver with respect to interrupt
+       configuration and handling.
+       </para>
+       <para>
+       Set the <varname>.name</varname> element of
+       <varname>struct platform_device</varname> to
+       <varname>"uio_dmem_genirq"</varname> to use this driver.
+       </para>
+       <para>
+       When using this driver, fill in the <varname>.platform_data</varname>
+       element of <varname>struct platform_device</varname>, which is of type
+       <varname>struct uio_dmem_genirq_pdata</varname> and which contains the
+       following elements:
+       </para>
+       <itemizedlist>
+       <listitem><varname>struct uio_info uioinfo</varname>: The same
+       structure used as the  <varname>uio_pdrv_genirq</varname> platform
+       data</listitem>
+       <listitem><varname>unsigned int *dynamic_region_sizes</varname>:
+       Pointer to list of sizes of dynamic memory regions to be mapped into
+       user space.
+       </listitem>
+       <listitem><varname>unsigned int num_dynamic_regions</varname>:
+       Number of elements in <varname>dynamic_region_sizes</varname> array.
+       </listitem>
+       </itemizedlist>
+       <para>
+       The dynamic regions defined in the platform data will be appended to
+       the <varname> mem[] </varname> array after the platform device
+       resources, which implies that the total number of static and dynamic
+       memory regions cannot exceed <varname>MAX_UIO_MAPS</varname>.
+       </para>
+       <para>
+       The dynamic memory regions will be allocated when the UIO device file,
+       <varname>/dev/uioX</varname> is opened.
+       Simiar to static memory resources, the memory region information for
+       dynamic regions is then visible via sysfs at
+       <varname>/sys/class/uio/uioX/maps/mapY/*</varname>.
+       The dynmaic memory regions will be freed when the UIO device file is
+       closed. When no processes are holding the device file open, the address
+       returned to userspace is ~0.
+       </para>
+</sect1>
+
 </chapter>
 
 <chapter id="userspace_driver" xreflabel="Writing a driver in user space">
index bb0b27db673f3c5d0f2cbf4215dc3efbe2c17bd3..9890539013597ed416bce398af5cf8553aeaa661 100644 (file)
@@ -3596,7 +3596,6 @@ S:        Maintained
 F:     drivers/hv/
 F:     drivers/hid/hid-hyperv.c
 F:     drivers/net/hyperv/
-F:     drivers/staging/hv/
 
 I2C OVER PARALLEL PORT
 M:     Jean Delvare <khali@linux-fr.org>
index 42f042ee4ada2563aac52e7449c062011042839c..652b56086de79d1c1922f06d2fb7cb87f5d29da2 100644 (file)
@@ -374,7 +374,7 @@ extern void pci_iounmap(struct pci_dev *dev, void __iomem *addr);
 
 #ifdef CONFIG_MMU
 #define ARCH_HAS_VALID_PHYS_ADDR_RANGE
-extern int valid_phys_addr_range(unsigned long addr, size_t size);
+extern int valid_phys_addr_range(phys_addr_t addr, size_t size);
 extern int valid_mmap_phys_addr_range(unsigned long pfn, size_t size);
 extern int devmem_is_allowed(unsigned long pfn);
 #endif
index ce8cb1970d7ae393da39208c929e8a1d983f91c2..89f2b7f7b042254132d13e226c7465fb338f4303 100644 (file)
@@ -279,7 +279,7 @@ void arch_pick_mmap_layout(struct mm_struct *mm)
  * You really shouldn't be using read() or write() on /dev/mem.  This
  * might go away in the future.
  */
-int valid_phys_addr_range(unsigned long addr, size_t size)
+int valid_phys_addr_range(phys_addr_t addr, size_t size)
 {
        if (addr < PHYS_OFFSET)
                return 0;
index 2c26321c28c3c0f504503c3d64421c95824bd585..74a7cc3293bc3aa279c735e4144a8cd7439d0172 100644 (file)
@@ -90,7 +90,7 @@ phys_to_virt (unsigned long address)
 
 #define ARCH_HAS_VALID_PHYS_ADDR_RANGE
 extern u64 kern_mem_attribute (unsigned long phys_addr, unsigned long size);
-extern int valid_phys_addr_range (unsigned long addr, size_t count); /* efi.c */
+extern int valid_phys_addr_range (phys_addr_t addr, size_t count); /* efi.c */
 extern int valid_mmap_phys_addr_range (unsigned long pfn, size_t count);
 
 /*
index d37bbd48637fd80f52f01b3a0592f341feabb030..f034563aeae547c44b0e3a2a99b4ab535917cee1 100644 (file)
@@ -870,7 +870,7 @@ kern_mem_attribute (unsigned long phys_addr, unsigned long size)
 EXPORT_SYMBOL(kern_mem_attribute);
 
 int
-valid_phys_addr_range (unsigned long phys_addr, unsigned long size)
+valid_phys_addr_range (phys_addr_t phys_addr, unsigned long size)
 {
        u64 attr;
 
index 73a23f4617a37c5a5270d5bc50d9689af45d8f89..629db2ad79162173dec3e903fd4af568e16c8839 100644 (file)
@@ -382,7 +382,7 @@ static inline int iounmap_fixed(void __iomem *addr) { return -EINVAL; }
 #define xlate_dev_kmem_ptr(p)  p
 
 #define ARCH_HAS_VALID_PHYS_ADDR_RANGE
-int valid_phys_addr_range(unsigned long addr, size_t size);
+int valid_phys_addr_range(phys_addr_t addr, size_t size);
 int valid_mmap_phys_addr_range(unsigned long pfn, size_t size);
 
 #endif /* __KERNEL__ */
index afeb710ec5c35d06926717a7f666f373ea366887..80bf494ddbcb4c86c1852d82fadfb973de89b435 100644 (file)
@@ -238,7 +238,7 @@ bottomup:
  * You really shouldn't be using read() or write() on /dev/mem.  This
  * might go away in the future.
  */
-int valid_phys_addr_range(unsigned long addr, size_t count)
+int valid_phys_addr_range(phys_addr_t addr, size_t count)
 {
        if (addr < __MEMORY_START)
                return 0;
index fd793519ea2b1f663f71c093df28b569aef57d2f..478493543b32391a8f520c84c223851c50304cfd 100644 (file)
@@ -249,7 +249,7 @@ static const struct agp_bridge_driver ali_m1541_bridge = {
 };
 
 
-static struct agp_device_ids ali_agp_device_ids[] __devinitdata =
+static struct agp_device_ids ali_agp_device_ids[] =
 {
        {
                .device_id      = PCI_DEVICE_ID_AL_M1541,
@@ -374,7 +374,7 @@ found:
        return agp_add_bridge(bridge);
 }
 
-static void __devexit agp_ali_remove(struct pci_dev *pdev)
+static void agp_ali_remove(struct pci_dev *pdev)
 {
        struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
 
index f7e88787af970947f236b78f68c6ade8c02fab46..1b2101160e98ce9550c7ad833b0a5f25e08b6259 100644 (file)
@@ -388,7 +388,7 @@ static const struct agp_bridge_driver amd_irongate_driver = {
        .agp_type_to_mask_type  = agp_generic_type_to_mask_type,
 };
 
-static struct agp_device_ids amd_agp_device_ids[] __devinitdata =
+static struct agp_device_ids amd_agp_device_ids[] =
 {
        {
                .device_id      = PCI_DEVICE_ID_AMD_FE_GATE_7006,
@@ -480,7 +480,7 @@ static int __devinit agp_amdk7_probe(struct pci_dev *pdev,
        return agp_add_bridge(bridge);
 }
 
-static void __devexit agp_amdk7_remove(struct pci_dev *pdev)
+static void agp_amdk7_remove(struct pci_dev *pdev)
 {
        struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
 
index 444f8b6ab41104003d94e7d510c3f89af02ecfce..061d46209b1a97422449cb4d9d1b65abf3437277 100644 (file)
@@ -579,7 +579,7 @@ static int __devinit agp_amd64_probe(struct pci_dev *pdev,
        return 0;
 }
 
-static void __devexit agp_amd64_remove(struct pci_dev *pdev)
+static void agp_amd64_remove(struct pci_dev *pdev)
 {
        struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
 
index dc30e22434947472c150cff60d00e52fde6ec04f..ed0433576e74e8cde3ce8caff0919bb6e704b58d 100644 (file)
@@ -445,7 +445,7 @@ static const struct agp_bridge_driver ati_generic_bridge = {
 };
 
 
-static struct agp_device_ids ati_agp_device_ids[] __devinitdata =
+static struct agp_device_ids ati_agp_device_ids[] =
 {
        {
                .device_id      = PCI_DEVICE_ID_ATI_RS100,
@@ -533,7 +533,7 @@ found:
        return agp_add_bridge(bridge);
 }
 
-static void __devexit agp_ati_remove(struct pci_dev *pdev)
+static void agp_ati_remove(struct pci_dev *pdev)
 {
        struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
 
index d607f53d8afcc7b3594c2629ab887b4ec5643b72..55f3e33a309fbc03f191ca640fa309dc6b798fa8 100644 (file)
@@ -407,7 +407,7 @@ static int __devinit agp_efficeon_probe(struct pci_dev *pdev,
        return agp_add_bridge(bridge);
 }
 
-static void __devexit agp_efficeon_remove(struct pci_dev *pdev)
+static void agp_efficeon_remove(struct pci_dev *pdev)
 {
        struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
 
index 75b763cb3ea1cb700ff5eab33b23fdc40ec5072d..d328b662e50da33f4c1ed57c10257a55033319af 100644 (file)
@@ -611,7 +611,7 @@ static int __devinit agp_intel_i460_probe(struct pci_dev *pdev,
        return agp_add_bridge(bridge);
 }
 
-static void __devexit agp_intel_i460_remove(struct pci_dev *pdev)
+static void agp_intel_i460_remove(struct pci_dev *pdev)
 {
        struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
 
index b130df0a195820af4e9725d0237be9f59c5b7d3f..f3a8f52b5a00eabebc4f6d338155c7c92c591530 100644 (file)
@@ -819,7 +819,7 @@ found_gmch:
        return err;
 }
 
-static void __devexit agp_intel_remove(struct pci_dev *pdev)
+static void agp_intel_remove(struct pci_dev *pdev)
 {
        struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
 
index b9734a978186382fb624f56b73ed5db02a3e7e3e..66e0868000f4fb1441e80ea5176407941bf21a8d 100644 (file)
@@ -388,7 +388,7 @@ static int __devinit agp_nvidia_probe(struct pci_dev *pdev,
        return agp_add_bridge(bridge);
 }
 
-static void __devexit agp_nvidia_remove(struct pci_dev *pdev)
+static void agp_nvidia_remove(struct pci_dev *pdev)
 {
        struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
 
index 3a5af2f9b0152ad78d40e263cdf38d9276b7744b..a18791d7718a9d1bcc3e5512b161275c481e0b15 100644 (file)
@@ -327,7 +327,7 @@ static int __devinit agp_sgi_init(void)
        return 0;
 }
 
-static void __devexit agp_sgi_cleanup(void)
+static void agp_sgi_cleanup(void)
 {
        kfree(sgi_tioca_agp_bridges);
        sgi_tioca_agp_bridges = NULL;
index 08704ae539566fa8c337b9dbaa34c31654ff0a8c..93d1d31f9d0cf2f7a7811c6994aff22bc5dec9f5 100644 (file)
@@ -17,8 +17,8 @@
 #define PCI_DEVICE_ID_SI_662   0x0662
 #define PCI_DEVICE_ID_SI_671   0x0671
 
-static bool __devinitdata agp_sis_force_delay = 0;
-static int __devinitdata agp_sis_agp_spec = -1;
+static bool agp_sis_force_delay = 0;
+static int agp_sis_agp_spec = -1;
 
 static int sis_fetch_size(void)
 {
@@ -148,7 +148,7 @@ static struct agp_bridge_driver sis_driver = {
 };
 
 // chipsets that require the 'delay hack'
-static int sis_broken_chipsets[] __devinitdata = {
+static int sis_broken_chipsets[] = {
        PCI_DEVICE_ID_SI_648,
        PCI_DEVICE_ID_SI_746,
        0 // terminator
@@ -211,7 +211,7 @@ static int __devinit agp_sis_probe(struct pci_dev *pdev,
        return agp_add_bridge(bridge);
 }
 
-static void __devexit agp_sis_remove(struct pci_dev *pdev)
+static void agp_sis_remove(struct pci_dev *pdev)
 {
        struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
 
index f02f9b07fd4ca71ff6db710f85efb580de8977bb..26020fb8d7a992b3daae19b5bf8b72922bee698a 100644 (file)
@@ -518,7 +518,7 @@ static int __devinit agp_serverworks_probe(struct pci_dev *pdev,
        return agp_add_bridge(bridge);
 }
 
-static void __devexit agp_serverworks_remove(struct pci_dev *pdev)
+static void agp_serverworks_remove(struct pci_dev *pdev)
 {
        struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
 
index a32c492baf5cc15001ad6882dc25a14d1be86419..011967ad3eedfb7a447261ed0c1e9150ab8b8c12 100644 (file)
@@ -557,7 +557,7 @@ const struct agp_bridge_driver u3_agp_driver = {
        .needs_scratch_page     = true,
 };
 
-static struct agp_device_ids uninorth_agp_device_ids[] __devinitdata = {
+static struct agp_device_ids uninorth_agp_device_ids[] = {
        {
                .device_id      = PCI_DEVICE_ID_APPLE_UNI_N_AGP,
                .chipset_name   = "UniNorth",
@@ -663,7 +663,7 @@ static int __devinit agp_uninorth_probe(struct pci_dev *pdev,
        return agp_add_bridge(bridge);
 }
 
-static void __devexit agp_uninorth_remove(struct pci_dev *pdev)
+static void agp_uninorth_remove(struct pci_dev *pdev)
 {
        struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
 
index 8bc384937401d53155f4a5f95107775ed723dc6f..6818595bb863d6f3e8f1a8c2d10d21718fdcc791 100644 (file)
@@ -224,7 +224,7 @@ static const struct agp_bridge_driver via_driver = {
        .agp_type_to_mask_type  = agp_generic_type_to_mask_type,
 };
 
-static struct agp_device_ids via_agp_device_ids[] __devinitdata =
+static struct agp_device_ids via_agp_device_ids[] =
 {
        {
                .device_id      = PCI_DEVICE_ID_VIA_82C597_0,
@@ -485,7 +485,7 @@ static int __devinit agp_via_probe(struct pci_dev *pdev,
        return agp_add_bridge(bridge);
 }
 
-static void __devexit agp_via_remove(struct pci_dev *pdev)
+static void agp_via_remove(struct pci_dev *pdev)
 {
        struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
 
index dfd7876f127c0d23c9d45bc2922ccefe85ff3181..fe6d4be4829600d9c6e44078f95ddb65b84ba957 100644 (file)
@@ -816,7 +816,7 @@ static unsigned long __hpet_calibrate(struct hpets *hpetp)
 
 static unsigned long hpet_calibrate(struct hpets *hpetp)
 {
-       unsigned long ret = -1;
+       unsigned long ret = ~0UL;
        unsigned long tmp;
 
        /*
@@ -1001,6 +1001,9 @@ static acpi_status hpet_resources(struct acpi_resource *res, void *data)
                irqp = &res->data.extended_irq;
 
                for (i = 0; i < irqp->interrupt_count; i++) {
+                       if (hdp->hd_nirqs >= HPET_MAX_TIMERS)
+                               break;
+
                        irq = acpi_register_gsi(NULL, irqp->interrupts[i],
                                      irqp->triggering, irqp->polarity);
                        if (irq < 0)
index 731c9046cf7bf0dbd06d26be41ea1e1b25b87ac8..5a4a6e70478b469418ad5d5da36eebc640de0f2a 100644 (file)
@@ -98,7 +98,7 @@ err_enable:
        return ret;
 }
 
-static int __devexit atmel_trng_remove(struct platform_device *pdev)
+static int atmel_trng_remove(struct platform_device *pdev)
 {
        struct atmel_trng *trng = platform_get_drvdata(pdev);
 
index aec6a4277caafe023be08194e0f2741a3e797f01..ae95bcb18d4a8390d9c0cdd99047ddabb982c097 100644 (file)
@@ -145,7 +145,7 @@ out:
        return ret;
 }
 
-static int __devexit bcm63xx_rng_remove(struct platform_device *pdev)
+static int bcm63xx_rng_remove(struct platform_device *pdev)
 {
        struct hwrng *rng = platform_get_drvdata(pdev);
        struct bcm63xx_rng_priv *priv = to_rng_priv(rng);
index 232ba9ce579cf69ea71844ec3d1bd199d4459852..bdc852ea76323772743fe4972b5bc532dbc23144 100644 (file)
@@ -134,7 +134,7 @@ static int __devinit exynos_rng_probe(struct platform_device *pdev)
        return hwrng_register(&exynos_rng->rng);
 }
 
-static int __devexit exynos_rng_remove(struct platform_device *pdev)
+static int exynos_rng_remove(struct platform_device *pdev)
 {
        struct exynos_rng *exynos_rng = platform_get_drvdata(pdev);
 
index ebd48f0135dae66dfd313ed9024b32f53bb4e125..d68a72a08b5128d9ac175b4338c6bb2aaccd018e 100644 (file)
@@ -25,7 +25,7 @@
 #define DRV_MODULE_VERSION     "0.2"
 #define DRV_MODULE_RELDATE     "July 27, 2011"
 
-static char version[] __devinitdata =
+static char version[] =
        DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
 
 MODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
@@ -719,7 +719,7 @@ out:
        return err;
 }
 
-static int __devexit n2rng_remove(struct platform_device *op)
+static int n2rng_remove(struct platform_device *op)
 {
        struct n2rng *np = dev_get_drvdata(&op->dev);
 
index 3a632673aed5e10b2b574a14f24f6c1637e5ba1f..a1f70407cc9e03070cd627230726b4246602e986 100644 (file)
@@ -122,7 +122,7 @@ static int __devinit rng_probe(struct platform_device *ofdev)
        return err;
 }
 
-static int __devexit rng_remove(struct platform_device *dev)
+static int rng_remove(struct platform_device *dev)
 {
        void __iomem *rng_regs = (void __iomem *)pasemi_rng.priv;
 
index 97bd891422c77511c1c506518bd55584457b76d4..d4b24c1dd48e37e4077ca01b56bf191b35bc6990 100644 (file)
@@ -151,7 +151,7 @@ err_enable:
        return ret;
 }
 
-static int __devexit picoxcell_trng_remove(struct platform_device *pdev)
+static int picoxcell_trng_remove(struct platform_device *pdev)
 {
        hwrng_unregister(&picoxcell_trng);
        clk_disable(rng_clk);
index c51762c13031928d57e307dff56f31cd638aad73..af6506a69cd9473343538d48bf17cb32a9877e92 100644 (file)
@@ -111,7 +111,7 @@ static int __devinit ppc4xx_rng_probe(struct platform_device *dev)
        return err;
 }
 
-static int __devexit ppc4xx_rng_remove(struct platform_device *dev)
+static int ppc4xx_rng_remove(struct platform_device *dev)
 {
        void __iomem *rng_regs = (void __iomem *) ppc4xx_rng.priv;
 
index f1a1618db1fb1e39912d9c59bb7901fddef7f0fd..3a1abc9417e4e8fd3e86628f75d4e3d71521fea7 100644 (file)
@@ -130,7 +130,7 @@ failed:
        return ret;
 }
 
-static int __devexit timeriomem_rng_remove(struct platform_device *pdev)
+static int timeriomem_rng_remove(struct platform_device *pdev)
 {
        del_timer_sync(&timeriomem_rng_timer);
        hwrng_unregister(&timeriomem_rng_ops);
index 5708299507d0e5b59ad401d495f1b0baa80dbe65..621f595f1a98b55b066d6e77a710e7c31a6d91c6 100644 (file)
@@ -119,7 +119,7 @@ static int virtrng_probe(struct virtio_device *vdev)
        return probe_common(vdev);
 }
 
-static void __devexit virtrng_remove(struct virtio_device *vdev)
+static void virtrng_remove(struct virtio_device *vdev)
 {
        remove_common(vdev);
 }
index 32a6c7e256bd82496f7461999a7ea3408342f423..20ab5b3a89150954cab2ec442b4f8b4ce93234a9 100644 (file)
@@ -1836,7 +1836,7 @@ static int hotmod_handler(const char *val, struct kernel_param *kp)
        return rv;
 }
 
-static int __devinit hardcode_find_bmc(void)
+static int hardcode_find_bmc(void)
 {
        int ret = -ENODEV;
        int             i;
@@ -2023,7 +2023,7 @@ struct SPMITable {
        s8      spmi_id[1]; /* A '\0' terminated array starts here. */
 };
 
-static int __devinit try_init_spmi(struct SPMITable *spmi)
+static int try_init_spmi(struct SPMITable *spmi)
 {
        struct smi_info  *info;
 
@@ -2106,7 +2106,7 @@ static int __devinit try_init_spmi(struct SPMITable *spmi)
        return 0;
 }
 
-static void __devinit spmi_find_bmc(void)
+static void spmi_find_bmc(void)
 {
        acpi_status      status;
        struct SPMITable *spmi;
@@ -2128,7 +2128,7 @@ static void __devinit spmi_find_bmc(void)
        }
 }
 
-static int __devinit ipmi_pnp_probe(struct pnp_dev *dev,
+static int ipmi_pnp_probe(struct pnp_dev *dev,
                                    const struct pnp_device_id *dev_id)
 {
        struct acpi_device *acpi_dev;
@@ -2228,7 +2228,7 @@ err_free:
        return -EINVAL;
 }
 
-static void __devexit ipmi_pnp_remove(struct pnp_dev *dev)
+static void ipmi_pnp_remove(struct pnp_dev *dev)
 {
        struct smi_info *info = pnp_get_drvdata(dev);
 
@@ -2258,7 +2258,7 @@ struct dmi_ipmi_data {
        u8              slave_addr;
 };
 
-static int __devinit decode_dmi(const struct dmi_header *dm,
+static int decode_dmi(const struct dmi_header *dm,
                                struct dmi_ipmi_data *dmi)
 {
        const u8        *data = (const u8 *)dm;
@@ -2320,7 +2320,7 @@ static int __devinit decode_dmi(const struct dmi_header *dm,
        return 0;
 }
 
-static void __devinit try_init_dmi(struct dmi_ipmi_data *ipmi_data)
+static void try_init_dmi(struct dmi_ipmi_data *ipmi_data)
 {
        struct smi_info *info;
 
@@ -2388,7 +2388,7 @@ static void __devinit try_init_dmi(struct dmi_ipmi_data *ipmi_data)
                kfree(info);
 }
 
-static void __devinit dmi_find_bmc(void)
+static void dmi_find_bmc(void)
 {
        const struct dmi_device *dev = NULL;
        struct dmi_ipmi_data data;
@@ -2424,7 +2424,7 @@ static void ipmi_pci_cleanup(struct smi_info *info)
        pci_disable_device(pdev);
 }
 
-static int __devinit ipmi_pci_probe_regspacing(struct smi_info *info)
+static int ipmi_pci_probe_regspacing(struct smi_info *info)
 {
        if (info->si_type == SI_KCS) {
                unsigned char   status;
@@ -2456,7 +2456,7 @@ static int __devinit ipmi_pci_probe_regspacing(struct smi_info *info)
        return DEFAULT_REGSPACING;
 }
 
-static int __devinit ipmi_pci_probe(struct pci_dev *pdev,
+static int ipmi_pci_probe(struct pci_dev *pdev,
                                    const struct pci_device_id *ent)
 {
        int rv;
@@ -2529,7 +2529,7 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev,
        return 0;
 }
 
-static void __devexit ipmi_pci_remove(struct pci_dev *pdev)
+static void ipmi_pci_remove(struct pci_dev *pdev)
 {
        struct smi_info *info = pci_get_drvdata(pdev);
        cleanup_one_si(info);
@@ -2551,7 +2551,7 @@ static struct pci_driver ipmi_pci_driver = {
 #endif /* CONFIG_PCI */
 
 static struct of_device_id ipmi_match[];
-static int __devinit ipmi_probe(struct platform_device *dev)
+static int ipmi_probe(struct platform_device *dev)
 {
 #ifdef CONFIG_OF
        const struct of_device_id *match;
@@ -2635,7 +2635,7 @@ static int __devinit ipmi_probe(struct platform_device *dev)
        return 0;
 }
 
-static int __devexit ipmi_remove(struct platform_device *dev)
+static int ipmi_remove(struct platform_device *dev)
 {
 #ifdef CONFIG_OF
        cleanup_one_si(dev_get_drvdata(&dev->dev));
@@ -3047,7 +3047,7 @@ static inline void wait_for_timer_and_thread(struct smi_info *smi_info)
        }
 }
 
-static __devinitdata struct ipmi_default_vals
+static struct ipmi_default_vals
 {
        int type;
        int port;
@@ -3059,7 +3059,7 @@ static __devinitdata struct ipmi_default_vals
        { .port = 0 }
 };
 
-static void __devinit default_find_bmc(void)
+static void default_find_bmc(void)
 {
        struct smi_info *info;
        int             i;
@@ -3359,7 +3359,7 @@ static int try_smi_init(struct smi_info *new_smi)
        return rv;
 }
 
-static int __devinit init_ipmi_si(void)
+static int init_ipmi_si(void)
 {
        int  i;
        char *str;
index f74e892711ddfd1dc89baead7546782f30a3a5f2..e5d3e3f7a49bcdbbb31d8e05045b8125d561b210 100644 (file)
@@ -799,7 +799,7 @@ static int mbcs_remove(struct cx_dev *dev)
        return 0;
 }
 
-static const struct cx_device_id __devinitconst mbcs_id_table[] = {
+static const struct cx_device_id mbcs_id_table[] = {
        {
         .part_num = MBCS_PART_NUM,
         .mfg_num = MBCS_MFG_NUM,
index 0537903c985b653bfd78d3005133267f43bdf538..c6fa3bc2baa89cfec662945dcc9818dadfe0d44c 100644 (file)
@@ -48,7 +48,7 @@ static inline unsigned long size_inside_page(unsigned long start,
 }
 
 #ifndef ARCH_HAS_VALID_PHYS_ADDR_RANGE
-static inline int valid_phys_addr_range(unsigned long addr, size_t count)
+static inline int valid_phys_addr_range(phys_addr_t addr, size_t count)
 {
        return addr + count <= __pa(high_memory);
 }
@@ -96,7 +96,7 @@ void __weak unxlate_dev_mem_ptr(unsigned long phys, void *addr)
 static ssize_t read_mem(struct file *file, char __user *buf,
                        size_t count, loff_t *ppos)
 {
-       unsigned long p = *ppos;
+       phys_addr_t p = *ppos;
        ssize_t read, sz;
        char *ptr;
 
@@ -153,7 +153,7 @@ static ssize_t read_mem(struct file *file, char __user *buf,
 static ssize_t write_mem(struct file *file, const char __user *buf,
                         size_t count, loff_t *ppos)
 {
-       unsigned long p = *ppos;
+       phys_addr_t p = *ppos;
        ssize_t written, sz;
        unsigned long copied;
        void *ptr;
@@ -226,7 +226,7 @@ int __weak phys_mem_access_prot_allowed(struct file *file,
  *
  */
 #ifdef pgprot_noncached
-static int uncached_access(struct file *file, unsigned long addr)
+static int uncached_access(struct file *file, phys_addr_t addr)
 {
 #if defined(CONFIG_IA64)
        /*
@@ -258,7 +258,7 @@ static pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
                                     unsigned long size, pgprot_t vma_prot)
 {
 #ifdef pgprot_noncached
-       unsigned long offset = pfn << PAGE_SHIFT;
+       phys_addr_t offset = pfn << PAGE_SHIFT;
 
        if (uncached_access(file, offset))
                return pgprot_noncached(vma_prot);
index b304ec05250124e55183fe59f55d1f99e9e33356..3f79a9fb6b1b4a1e7489535c30cd6407f2defd44 100644 (file)
@@ -345,8 +345,7 @@ static void __exit pc8736x_gpio_cleanup(void)
        unregister_chrdev_region(MKDEV(major,0), PC8736X_GPIO_CT);
        release_region(pc8736x_gpio_base, PC8736X_GPIO_RANGE);
 
-       platform_device_del(pdev);
-       platform_device_put(pdev);
+       platform_device_unregister(pdev);
 }
 
 module_init(pc8736x_gpio_init);
index 6abdde4da2b7834991a8e4e0b3feb52e0e054993..588063ac9517976cc1952d0411ac0df25226a83f 100644 (file)
@@ -363,7 +363,7 @@ static struct miscdevice ps3flash_misc = {
        .fops   = &ps3flash_fops,
 };
 
-static int __devinit ps3flash_probe(struct ps3_system_bus_device *_dev)
+static int ps3flash_probe(struct ps3_system_bus_device *_dev)
 {
        struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core);
        struct ps3flash_private *priv;
index 9b4f0116ff21c95d73d2ad2da39d7fb40dc24ee8..d780295a147371533e5ad936d8c10e86a85fac57 100644 (file)
@@ -1164,7 +1164,7 @@ static struct acpi_driver sonypi_acpi_driver = {
 };
 #endif
 
-static int __devinit sonypi_create_input_devices(struct platform_device *pdev)
+static int sonypi_create_input_devices(struct platform_device *pdev)
 {
        struct input_dev *jog_dev;
        struct input_dev *key_dev;
@@ -1225,7 +1225,7 @@ static int __devinit sonypi_create_input_devices(struct platform_device *pdev)
        return error;
 }
 
-static int __devinit sonypi_setup_ioports(struct sonypi_device *dev,
+static int sonypi_setup_ioports(struct sonypi_device *dev,
                                const struct sonypi_ioport_list *ioport_list)
 {
        /* try to detect if sony-laptop is being used and thus
@@ -1265,7 +1265,7 @@ static int __devinit sonypi_setup_ioports(struct sonypi_device *dev,
        return -EBUSY;
 }
 
-static int __devinit sonypi_setup_irq(struct sonypi_device *dev,
+static int sonypi_setup_irq(struct sonypi_device *dev,
                                      const struct sonypi_irq_list *irq_list)
 {
        while (irq_list->irq) {
@@ -1282,7 +1282,7 @@ static int __devinit sonypi_setup_irq(struct sonypi_device *dev,
        return -EBUSY;
 }
 
-static void __devinit sonypi_display_info(void)
+static void sonypi_display_info(void)
 {
        printk(KERN_INFO "sonypi: detected type%d model, "
               "verbose = %d, fnkeyinit = %s, camera = %s, "
@@ -1304,7 +1304,7 @@ static void __devinit sonypi_display_info(void)
                       sonypi_misc_device.minor);
 }
 
-static int __devinit sonypi_probe(struct platform_device *dev)
+static int sonypi_probe(struct platform_device *dev)
 {
        const struct sonypi_ioport_list *ioport_list;
        const struct sonypi_irq_list *irq_list;
@@ -1428,7 +1428,7 @@ static int __devinit sonypi_probe(struct platform_device *dev)
        return error;
 }
 
-static int __devexit sonypi_remove(struct platform_device *dev)
+static int sonypi_remove(struct platform_device *dev)
 {
        sonypi_disable();
 
@@ -1491,7 +1491,7 @@ static struct platform_driver sonypi_driver = {
                .pm     = SONYPI_PM,
        },
        .probe          = sonypi_probe,
-       .remove         = __devexit_p(sonypi_remove),
+       .remove         = sonypi_remove,
        .shutdown       = sonypi_shutdown,
 };
 
index ad264185eb10a0fb43d1c67ed86a7cbab9b70381..34c63f85104d30cee29cc64196c2904df8735787 100644 (file)
@@ -284,7 +284,7 @@ static void tb0219_pci_irq_init(void)
        vr41xx_set_irq_level(TB0219_PCI_SLOT3_PIN, IRQ_LEVEL_LOW);
 }
 
-static int __devinit tb0219_probe(struct platform_device *dev)
+static int tb0219_probe(struct platform_device *dev)
 {
        int retval;
 
@@ -318,7 +318,7 @@ static int __devinit tb0219_probe(struct platform_device *dev)
        return 0;
 }
 
-static int __devexit tb0219_remove(struct platform_device *dev)
+static int tb0219_remove(struct platform_device *dev)
 {
        _machine_restart = old_machine_restart;
 
@@ -334,7 +334,7 @@ static struct platform_device *tb0219_platform_device;
 
 static struct platform_driver tb0219_device_driver = {
        .probe          = tb0219_probe,
-       .remove         = __devexit_p(tb0219_remove),
+       .remove         = tb0219_remove,
        .driver         = {
                .name   = "TB0219",
                .owner  = THIS_MODULE,
index 5a831aec9d4b97fc46737170991bc95b3ca8e7e5..78983b77caf4711a62c53a9a9b9201a692f3a967 100644 (file)
@@ -656,7 +656,7 @@ static int __devinit tpm_tis_i2c_probe(struct i2c_client *client,
        return rc;
 }
 
-static int __devexit tpm_tis_i2c_remove(struct i2c_client *client)
+static int tpm_tis_i2c_remove(struct i2c_client *client)
 {
        struct tpm_chip *chip = tpm_dev.chip;
        release_locality(chip, chip->vendor.locality, 1);
index efc4ab36a9d603a226e1870813fced9f27b1e581..5a72f39d962065b3d003673cd1d357f072d1a011 100644 (file)
@@ -32,7 +32,7 @@
 
 static const char tpm_ibmvtpm_driver_name[] = "tpm_ibmvtpm";
 
-static struct vio_device_id tpm_ibmvtpm_device_table[] __devinitdata = {
+static struct vio_device_id tpm_ibmvtpm_device_table[] = {
        { "IBM,vtpm", "IBM,vtpm"},
        { "", "" }
 };
@@ -267,7 +267,7 @@ static int ibmvtpm_crq_send_init(struct ibmvtpm_dev *ibmvtpm)
  * Return value:
  *     0
  */
-static int __devexit tpm_ibmvtpm_remove(struct vio_dev *vdev)
+static int tpm_ibmvtpm_remove(struct vio_dev *vdev)
 {
        struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(&vdev->dev);
        int rc = 0;
index 3251a44e8ceb5f68fa1e68f275095dbb48c925e5..4dd5f8acecdaf994b5d220f6e5d910a54a96557c 100644 (file)
@@ -594,7 +594,7 @@ err_last:
        return rc;
 }
 
-static __devexit void tpm_inf_pnp_remove(struct pnp_dev *dev)
+static void tpm_inf_pnp_remove(struct pnp_dev *dev)
 {
        struct tpm_chip *chip = pnp_get_drvdata(dev);
 
index 6bdf2671254f405e03bfcca83cebba4cbae2e50c..a599cc267fccf1dfba8ffe0d6119bc9b77b57a41 100644 (file)
@@ -769,7 +769,7 @@ static int tpm_tis_pnp_resume(struct pnp_dev *dev)
        return ret;
 }
 
-static struct pnp_device_id tpm_pnp_tbl[] __devinitdata = {
+static struct pnp_device_id tpm_pnp_tbl[] = {
        {"PNP0C31", 0},         /* TPM */
        {"ATM1200", 0},         /* Atmel */
        {"IFX0102", 0},         /* Infineon */
@@ -783,7 +783,7 @@ static struct pnp_device_id tpm_pnp_tbl[] __devinitdata = {
 };
 MODULE_DEVICE_TABLE(pnp, tpm_pnp_tbl);
 
-static __devexit void tpm_tis_pnp_remove(struct pnp_dev *dev)
+static void tpm_tis_pnp_remove(struct pnp_dev *dev)
 {
        struct tpm_chip *chip = pnp_get_drvdata(dev);
 
index 8ab9c3d4bf134c23bda43adafc688485a1c9e764..90493d4ead1fc3479e01d387195e084f0dbbd707 100644 (file)
@@ -1846,7 +1846,7 @@ static void remove_controlq_data(struct ports_device *portdev)
  * config space to see how many ports the host has spawned.  We
  * initialize each port found.
  */
-static int __devinit virtcons_probe(struct virtio_device *vdev)
+static int virtcons_probe(struct virtio_device *vdev)
 {
        struct ports_device *portdev;
        int err;
index 2c5d15beea355c419ca95b9fc0b2a2f1e3e4ffaa..5224da5202d3497589d4d74df171cde3efd7ba67 100644 (file)
@@ -595,7 +595,7 @@ static const struct file_operations hwicap_fops = {
        .llseek = noop_llseek,
 };
 
-static int __devinit hwicap_setup(struct device *dev, int id,
+static int hwicap_setup(struct device *dev, int id,
                const struct resource *regs_res,
                const struct hwicap_driver_config *config,
                const struct config_registers *config_regs)
@@ -717,7 +717,7 @@ static struct hwicap_driver_config fifo_icap_config = {
        .reset = fifo_icap_reset,
 };
 
-static int __devexit hwicap_remove(struct device *dev)
+static int hwicap_remove(struct device *dev)
 {
        struct hwicap_drvdata *drvdata;
 
@@ -740,7 +740,7 @@ static int __devexit hwicap_remove(struct device *dev)
 }
 
 #ifdef CONFIG_OF
-static int __devinit hwicap_of_probe(struct platform_device *op,
+static int hwicap_of_probe(struct platform_device *op,
                                     const struct hwicap_driver_config *config)
 {
        struct resource res;
@@ -785,8 +785,8 @@ static inline int hwicap_of_probe(struct platform_device *op,
 }
 #endif /* CONFIG_OF */
 
-static const struct of_device_id __devinitconst hwicap_of_match[];
-static int __devinit hwicap_drv_probe(struct platform_device *pdev)
+static const struct of_device_id hwicap_of_match[];
+static int hwicap_drv_probe(struct platform_device *pdev)
 {
        const struct of_device_id *match;
        struct resource *res;
@@ -822,14 +822,14 @@ static int __devinit hwicap_drv_probe(struct platform_device *pdev)
                        &buffer_icap_config, regs);
 }
 
-static int __devexit hwicap_drv_remove(struct platform_device *pdev)
+static int hwicap_drv_remove(struct platform_device *pdev)
 {
        return hwicap_remove(&pdev->dev);
 }
 
 #ifdef CONFIG_OF
 /* Match table for device tree binding */
-static const struct of_device_id __devinitconst hwicap_of_match[] = {
+static const struct of_device_id hwicap_of_match[] = {
        { .compatible = "xlnx,opb-hwicap-1.00.b", .data = &buffer_icap_config},
        { .compatible = "xlnx,xps-hwicap-1.00.a", .data = &fifo_icap_config},
        {},
index e87196f6d2d2db43f945e6317b3d14fe09e680f4..eda2a1aa4adb1919a3563d5be2e5d3e773136658 100644 (file)
@@ -91,7 +91,7 @@ static irqreturn_t adc_jack_irq_thread(int irq, void *_data)
        return IRQ_HANDLED;
 }
 
-static int __devinit adc_jack_probe(struct platform_device *pdev)
+static int adc_jack_probe(struct platform_device *pdev)
 {
        struct adc_jack_data *data;
        struct adc_jack_pdata *pdata = pdev->dev.platform_data;
@@ -175,7 +175,7 @@ out:
        return err;
 }
 
-static int __devexit adc_jack_remove(struct platform_device *pdev)
+static int adc_jack_remove(struct platform_device *pdev)
 {
        struct adc_jack_data *data = platform_get_drvdata(pdev);
 
@@ -188,7 +188,7 @@ static int __devexit adc_jack_remove(struct platform_device *pdev)
 
 static struct platform_driver adc_jack_driver = {
        .probe          = adc_jack_probe,
-       .remove         = __devexit_p(adc_jack_remove),
+       .remove         = adc_jack_remove,
        .driver         = {
                .name   = "adc-jack",
                .owner  = THIS_MODULE,
index d876a54a8bd11b4671a6baf0b8be89f4e50f7129..414aed50b1bc0aa52c32a49019eef746f7d63617 100644 (file)
@@ -338,7 +338,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
        return IRQ_HANDLED;
 }
 
-static int __devinit arizona_extcon_probe(struct platform_device *pdev)
+static int arizona_extcon_probe(struct platform_device *pdev)
 {
        struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
        struct arizona_pdata *pdata;
@@ -518,7 +518,7 @@ err:
        return ret;
 }
 
-static int __devexit arizona_extcon_remove(struct platform_device *pdev)
+static int arizona_extcon_remove(struct platform_device *pdev)
 {
        struct arizona_extcon_info *info = platform_get_drvdata(pdev);
        struct arizona *arizona = info->arizona;
@@ -545,7 +545,7 @@ static struct platform_driver arizona_extcon_driver = {
                .owner  = THIS_MODULE,
        },
        .probe          = arizona_extcon_probe,
-       .remove         = __devexit_p(arizona_extcon_remove),
+       .remove         = arizona_extcon_remove,
 };
 
 module_platform_driver(arizona_extcon_driver);
index 71d3ab7b3d8d72c8c477250e25b8ec93ce7842e8..1b14bfcdc176895632b84f6f46a35f851a48067b 100644 (file)
@@ -76,7 +76,7 @@ static ssize_t extcon_gpio_print_state(struct extcon_dev *edev, char *buf)
        return -EINVAL;
 }
 
-static int __devinit gpio_extcon_probe(struct platform_device *pdev)
+static int gpio_extcon_probe(struct platform_device *pdev)
 {
        struct gpio_extcon_platform_data *pdata = pdev->dev.platform_data;
        struct gpio_extcon_data *extcon_data;
@@ -137,7 +137,7 @@ err:
        return ret;
 }
 
-static int __devexit gpio_extcon_remove(struct platform_device *pdev)
+static int gpio_extcon_remove(struct platform_device *pdev)
 {
        struct gpio_extcon_data *extcon_data = platform_get_drvdata(pdev);
 
@@ -150,7 +150,7 @@ static int __devexit gpio_extcon_remove(struct platform_device *pdev)
 
 static struct platform_driver gpio_extcon_driver = {
        .probe          = gpio_extcon_probe,
-       .remove         = __devexit_p(gpio_extcon_remove),
+       .remove         = gpio_extcon_remove,
        .driver         = {
                .name   = "extcon-gpio",
                .owner  = THIS_MODULE,
index 8bf5e4835b4c21a1cf6f2d5dd64534b319429f0e..8c17b65eb74d7d34077479a4726cec30ce68fccd 100644 (file)
@@ -648,7 +648,7 @@ out:
        return ret;
 }
 
-static int __devinit max77693_muic_probe(struct platform_device *pdev)
+static int max77693_muic_probe(struct platform_device *pdev)
 {
        struct max77693_dev *max77693 = dev_get_drvdata(pdev->dev.parent);
        struct max77693_platform_data *pdata = dev_get_platdata(max77693->dev);
@@ -772,7 +772,7 @@ err_irq:
        return ret;
 }
 
-static int __devexit max77693_muic_remove(struct platform_device *pdev)
+static int max77693_muic_remove(struct platform_device *pdev)
 {
        struct max77693_muic_info *info = platform_get_drvdata(pdev);
        int i;
@@ -791,7 +791,7 @@ static struct platform_driver max77693_muic_driver = {
                .owner  = THIS_MODULE,
        },
        .probe          = max77693_muic_probe,
-       .remove         = __devexit_p(max77693_muic_remove),
+       .remove         = max77693_muic_remove,
 };
 
 module_platform_driver(max77693_muic_driver);
index 8059325ce7da24f5a4136b176b876b3fb0e9cd57..93009fe6ef0555908312e7b5f571658d1ff7f1f3 100644 (file)
@@ -426,7 +426,7 @@ static void max8997_muic_detect_dev(struct max8997_muic_info *info)
        max8997_muic_handle_charger_type(info, chg_type);
 }
 
-static int __devinit max8997_muic_probe(struct platform_device *pdev)
+static int max8997_muic_probe(struct platform_device *pdev)
 {
        struct max8997_dev *max8997 = dev_get_drvdata(pdev->dev.parent);
        struct max8997_platform_data *pdata = dev_get_platdata(max8997->dev);
@@ -507,7 +507,7 @@ err_irq:
        return ret;
 }
 
-static int __devexit max8997_muic_remove(struct platform_device *pdev)
+static int max8997_muic_remove(struct platform_device *pdev)
 {
        struct max8997_muic_info *info = platform_get_drvdata(pdev);
        int i;
@@ -527,7 +527,7 @@ static struct platform_driver max8997_muic_driver = {
                .owner  = THIS_MODULE,
        },
        .probe          = max8997_muic_probe,
-       .remove         = __devexit_p(max8997_muic_remove),
+       .remove         = max8997_muic_remove,
 };
 
 module_platform_driver(max8997_muic_driver);
index 70f5dde1cc525e19beeecd9f36ebae7e19eb325e..b38ef6d8d0492d7392814021f7ac30c7813ab361 100644 (file)
@@ -13,4 +13,10 @@ config HYPERV_UTILS
        help
          Select this option to enable the Hyper-V Utilities.
 
+config HYPERV_BALLOON
+       tristate "Microsoft Hyper-V Balloon driver"
+       depends on HYPERV
+       help
+         Select this option to enable Hyper-V Balloon driver.
+
 endmenu
index a23938b991c9a70d72c4d8b4ac8e106569ea3295..e6abfa02d8b7b5c02e68c793e083bc15e3d6c4c0 100644 (file)
@@ -1,5 +1,6 @@
 obj-$(CONFIG_HYPERV)           += hv_vmbus.o
 obj-$(CONFIG_HYPERV_UTILS)     += hv_utils.o
+obj-$(CONFIG_HYPERV_BALLOON)   += hv_balloon.o
 
 hv_vmbus-y := vmbus_drv.o \
                 hv.o connection.o channel.o \
index f4c3d28cd1fcc96ed76720433104aa21bd0b08a6..773a2f25a8f062e794a5ac6b3f4747fab61d0ffb 100644 (file)
 #define NUM_PAGES_SPANNED(addr, len) \
 ((PAGE_ALIGN(addr + len) >> PAGE_SHIFT) - (addr >> PAGE_SHIFT))
 
-/* Internal routines */
-static int create_gpadl_header(
-       void *kbuffer,  /* must be phys and virt contiguous */
-       u32 size,       /* page-size multiple */
-       struct vmbus_channel_msginfo **msginfo,
-       u32 *messagecount);
-static void vmbus_setevent(struct vmbus_channel *channel);
-
 /*
  * vmbus_setevent- Trigger an event notification on the specified
  * channel.
index 2b8b8d4558d247c808a0a8f7f6bfd51601aa31a2..2f84c5cff8d4f95a026a42034eb1fac960ec6e6a 100644 (file)
@@ -265,14 +265,9 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
 {
        struct vmbus_channel_offer_channel *offer;
        struct vmbus_channel *newchannel;
-       uuid_le *guidtype;
-       uuid_le *guidinstance;
 
        offer = (struct vmbus_channel_offer_channel *)hdr;
 
-       guidtype = &offer->offer.if_type;
-       guidinstance = &offer->offer.if_instance;
-
        /* Allocate the channel object and save this offer. */
        newchannel = alloc_channel();
        if (!newchannel) {
@@ -470,7 +465,6 @@ static void vmbus_onversion_response(
 {
        struct vmbus_channel_msginfo *msginfo;
        struct vmbus_channel_message_header *requestheader;
-       struct vmbus_channel_initiate_contact *initiate;
        struct vmbus_channel_version_response *version_response;
        unsigned long flags;
 
@@ -484,8 +478,6 @@ static void vmbus_onversion_response(
 
                if (requestheader->msgtype ==
                    CHANNELMSG_INITIATE_CONTACT) {
-                       initiate =
-                       (struct vmbus_channel_initiate_contact *)requestheader;
                        memcpy(&msginfo->response.version_response,
                              version_response,
                              sizeof(struct vmbus_channel_version_response));
diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c
new file mode 100644 (file)
index 0000000..f6c0011
--- /dev/null
@@ -0,0 +1,1041 @@
+/*
+ * Copyright (c) 2012, Microsoft Corporation.
+ *
+ * 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/kernel.h>
+#include <linux/mman.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/completion.h>
+#include <linux/memory_hotplug.h>
+#include <linux/memory.h>
+#include <linux/notifier.h>
+#include <linux/mman.h>
+#include <linux/percpu_counter.h>
+
+#include <linux/hyperv.h>
+
+/*
+ * We begin with definitions supporting the Dynamic Memory protocol
+ * with the host.
+ *
+ * Begin protocol definitions.
+ */
+
+
+
+/*
+ * Protocol versions. The low word is the minor version, the high word the major
+ * version.
+ *
+ * History:
+ * Initial version 1.0
+ * Changed to 0.1 on 2009/03/25
+ * Changes to 0.2 on 2009/05/14
+ * Changes to 0.3 on 2009/12/03
+ * Changed to 1.0 on 2011/04/05
+ */
+
+#define DYNMEM_MAKE_VERSION(Major, Minor) ((__u32)(((Major) << 16) | (Minor)))
+#define DYNMEM_MAJOR_VERSION(Version) ((__u32)(Version) >> 16)
+#define DYNMEM_MINOR_VERSION(Version) ((__u32)(Version) & 0xff)
+
+enum {
+       DYNMEM_PROTOCOL_VERSION_1 = DYNMEM_MAKE_VERSION(0, 3),
+       DYNMEM_PROTOCOL_VERSION_2 = DYNMEM_MAKE_VERSION(1, 0),
+
+       DYNMEM_PROTOCOL_VERSION_WIN7 = DYNMEM_PROTOCOL_VERSION_1,
+       DYNMEM_PROTOCOL_VERSION_WIN8 = DYNMEM_PROTOCOL_VERSION_2,
+
+       DYNMEM_PROTOCOL_VERSION_CURRENT = DYNMEM_PROTOCOL_VERSION_WIN8
+};
+
+
+
+/*
+ * Message Types
+ */
+
+enum dm_message_type {
+       /*
+        * Version 0.3
+        */
+       DM_ERROR                        = 0,
+       DM_VERSION_REQUEST              = 1,
+       DM_VERSION_RESPONSE             = 2,
+       DM_CAPABILITIES_REPORT          = 3,
+       DM_CAPABILITIES_RESPONSE        = 4,
+       DM_STATUS_REPORT                = 5,
+       DM_BALLOON_REQUEST              = 6,
+       DM_BALLOON_RESPONSE             = 7,
+       DM_UNBALLOON_REQUEST            = 8,
+       DM_UNBALLOON_RESPONSE           = 9,
+       DM_MEM_HOT_ADD_REQUEST          = 10,
+       DM_MEM_HOT_ADD_RESPONSE         = 11,
+       DM_VERSION_03_MAX               = 11,
+       /*
+        * Version 1.0.
+        */
+       DM_INFO_MESSAGE                 = 12,
+       DM_VERSION_1_MAX                = 12
+};
+
+
+/*
+ * Structures defining the dynamic memory management
+ * protocol.
+ */
+
+union dm_version {
+       struct {
+               __u16 minor_version;
+               __u16 major_version;
+       };
+       __u32 version;
+} __packed;
+
+
+union dm_caps {
+       struct {
+               __u64 balloon:1;
+               __u64 hot_add:1;
+               __u64 reservedz:62;
+       } cap_bits;
+       __u64 caps;
+} __packed;
+
+union dm_mem_page_range {
+       struct  {
+               /*
+                * The PFN number of the first page in the range.
+                * 40 bits is the architectural limit of a PFN
+                * number for AMD64.
+                */
+               __u64 start_page:40;
+               /*
+                * The number of pages in the range.
+                */
+               __u64 page_cnt:24;
+       } finfo;
+       __u64  page_range;
+} __packed;
+
+
+
+/*
+ * The header for all dynamic memory messages:
+ *
+ * type: Type of the message.
+ * size: Size of the message in bytes; including the header.
+ * trans_id: The guest is responsible for manufacturing this ID.
+ */
+
+struct dm_header {
+       __u16 type;
+       __u16 size;
+       __u32 trans_id;
+} __packed;
+
+/*
+ * A generic message format for dynamic memory.
+ * Specific message formats are defined later in the file.
+ */
+
+struct dm_message {
+       struct dm_header hdr;
+       __u8 data[]; /* enclosed message */
+} __packed;
+
+
+/*
+ * Specific message types supporting the dynamic memory protocol.
+ */
+
+/*
+ * Version negotiation message. Sent from the guest to the host.
+ * The guest is free to try different versions until the host
+ * accepts the version.
+ *
+ * dm_version: The protocol version requested.
+ * is_last_attempt: If TRUE, this is the last version guest will request.
+ * reservedz: Reserved field, set to zero.
+ */
+
+struct dm_version_request {
+       struct dm_header hdr;
+       union dm_version version;
+       __u32 is_last_attempt:1;
+       __u32 reservedz:31;
+} __packed;
+
+/*
+ * Version response message; Host to Guest and indicates
+ * if the host has accepted the version sent by the guest.
+ *
+ * is_accepted: If TRUE, host has accepted the version and the guest
+ * should proceed to the next stage of the protocol. FALSE indicates that
+ * guest should re-try with a different version.
+ *
+ * reservedz: Reserved field, set to zero.
+ */
+
+struct dm_version_response {
+       struct dm_header hdr;
+       __u64 is_accepted:1;
+       __u64 reservedz:63;
+} __packed;
+
+/*
+ * Message reporting capabilities. This is sent from the guest to the
+ * host.
+ */
+
+struct dm_capabilities {
+       struct dm_header hdr;
+       union dm_caps caps;
+       __u64 min_page_cnt;
+       __u64 max_page_number;
+} __packed;
+
+/*
+ * Response to the capabilities message. This is sent from the host to the
+ * guest. This message notifies if the host has accepted the guest's
+ * capabilities. If the host has not accepted, the guest must shutdown
+ * the service.
+ *
+ * is_accepted: Indicates if the host has accepted guest's capabilities.
+ * reservedz: Must be 0.
+ */
+
+struct dm_capabilities_resp_msg {
+       struct dm_header hdr;
+       __u64 is_accepted:1;
+       __u64 reservedz:63;
+} __packed;
+
+/*
+ * This message is used to report memory pressure from the guest.
+ * This message is not part of any transaction and there is no
+ * response to this message.
+ *
+ * num_avail: Available memory in pages.
+ * num_committed: Committed memory in pages.
+ * page_file_size: The accumulated size of all page files
+ *                in the system in pages.
+ * zero_free: The nunber of zero and free pages.
+ * page_file_writes: The writes to the page file in pages.
+ * io_diff: An indicator of file cache efficiency or page file activity,
+ *         calculated as File Cache Page Fault Count - Page Read Count.
+ *         This value is in pages.
+ *
+ * Some of these metrics are Windows specific and fortunately
+ * the algorithm on the host side that computes the guest memory
+ * pressure only uses num_committed value.
+ */
+
+struct dm_status {
+       struct dm_header hdr;
+       __u64 num_avail;
+       __u64 num_committed;
+       __u64 page_file_size;
+       __u64 zero_free;
+       __u32 page_file_writes;
+       __u32 io_diff;
+} __packed;
+
+
+/*
+ * Message to ask the guest to allocate memory - balloon up message.
+ * This message is sent from the host to the guest. The guest may not be
+ * able to allocate as much memory as requested.
+ *
+ * num_pages: number of pages to allocate.
+ */
+
+struct dm_balloon {
+       struct dm_header hdr;
+       __u32 num_pages;
+       __u32 reservedz;
+} __packed;
+
+
+/*
+ * Balloon response message; this message is sent from the guest
+ * to the host in response to the balloon message.
+ *
+ * reservedz: Reserved; must be set to zero.
+ * more_pages: If FALSE, this is the last message of the transaction.
+ * if TRUE there will atleast one more message from the guest.
+ *
+ * range_count: The number of ranges in the range array.
+ *
+ * range_array: An array of page ranges returned to the host.
+ *
+ */
+
+struct dm_balloon_response {
+       struct dm_header hdr;
+       __u32 reservedz;
+       __u32 more_pages:1;
+       __u32 range_count:31;
+       union dm_mem_page_range range_array[];
+} __packed;
+
+/*
+ * Un-balloon message; this message is sent from the host
+ * to the guest to give guest more memory.
+ *
+ * more_pages: If FALSE, this is the last message of the transaction.
+ * if TRUE there will atleast one more message from the guest.
+ *
+ * reservedz: Reserved; must be set to zero.
+ *
+ * range_count: The number of ranges in the range array.
+ *
+ * range_array: An array of page ranges returned to the host.
+ *
+ */
+
+struct dm_unballoon_request {
+       struct dm_header hdr;
+       __u32 more_pages:1;
+       __u32 reservedz:31;
+       __u32 range_count;
+       union dm_mem_page_range range_array[];
+} __packed;
+
+/*
+ * Un-balloon response message; this message is sent from the guest
+ * to the host in response to an unballoon request.
+ *
+ */
+
+struct dm_unballoon_response {
+       struct dm_header hdr;
+} __packed;
+
+
+/*
+ * Hot add request message. Message sent from the host to the guest.
+ *
+ * mem_range: Memory range to hot add.
+ *
+ * On Linux we currently don't support this since we cannot hot add
+ * arbitrary granularity of memory.
+ */
+
+struct dm_hot_add {
+       struct dm_header hdr;
+       union dm_mem_page_range range;
+} __packed;
+
+/*
+ * Hot add response message.
+ * This message is sent by the guest to report the status of a hot add request.
+ * If page_count is less than the requested page count, then the host should
+ * assume all further hot add requests will fail, since this indicates that
+ * the guest has hit an upper physical memory barrier.
+ *
+ * Hot adds may also fail due to low resources; in this case, the guest must
+ * not complete this message until the hot add can succeed, and the host must
+ * not send a new hot add request until the response is sent.
+ * If VSC fails to hot add memory DYNMEM_NUMBER_OF_UNSUCCESSFUL_HOTADD_ATTEMPTS
+ * times it fails the request.
+ *
+ *
+ * page_count: number of pages that were successfully hot added.
+ *
+ * result: result of the operation 1: success, 0: failure.
+ *
+ */
+
+struct dm_hot_add_response {
+       struct dm_header hdr;
+       __u32 page_count;
+       __u32 result;
+} __packed;
+
+/*
+ * Types of information sent from host to the guest.
+ */
+
+enum dm_info_type {
+       INFO_TYPE_MAX_PAGE_CNT = 0,
+       MAX_INFO_TYPE
+};
+
+
+/*
+ * Header for the information message.
+ */
+
+struct dm_info_header {
+       enum dm_info_type type;
+       __u32 data_size;
+} __packed;
+
+/*
+ * This message is sent from the host to the guest to pass
+ * some relevant information (win8 addition).
+ *
+ * reserved: no used.
+ * info_size: size of the information blob.
+ * info: information blob.
+ */
+
+struct dm_info_msg {
+       struct dm_info_header header;
+       __u32 reserved;
+       __u32 info_size;
+       __u8  info[];
+};
+
+/*
+ * End protocol definitions.
+ */
+
+static bool hot_add;
+static bool do_hot_add;
+
+module_param(hot_add, bool, (S_IRUGO | S_IWUSR));
+MODULE_PARM_DESC(hot_add, "If set attempt memory hot_add");
+
+static atomic_t trans_id = ATOMIC_INIT(0);
+
+static int dm_ring_size = (5 * PAGE_SIZE);
+
+/*
+ * Driver specific state.
+ */
+
+enum hv_dm_state {
+       DM_INITIALIZING = 0,
+       DM_INITIALIZED,
+       DM_BALLOON_UP,
+       DM_BALLOON_DOWN,
+       DM_HOT_ADD,
+       DM_INIT_ERROR
+};
+
+
+static __u8 recv_buffer[PAGE_SIZE];
+static __u8 *send_buffer;
+#define PAGES_IN_2M    512
+
+struct hv_dynmem_device {
+       struct hv_device *dev;
+       enum hv_dm_state state;
+       struct completion host_event;
+       struct completion config_event;
+
+       /*
+        * Number of pages we have currently ballooned out.
+        */
+       unsigned int num_pages_ballooned;
+
+       /*
+        * This thread handles both balloon/hot-add
+        * requests from the host as well as notifying
+        * the host with regards to memory pressure in
+        * the guest.
+        */
+       struct task_struct *thread;
+
+       /*
+        * We start with the highest version we can support
+        * and downgrade based on the host; we save here the
+        * next version to try.
+        */
+       __u32 next_version;
+};
+
+static struct hv_dynmem_device dm_device;
+
+static void hot_add_req(struct hv_dynmem_device *dm, struct dm_hot_add *msg)
+{
+
+       struct dm_hot_add_response resp;
+
+       if (do_hot_add) {
+
+               pr_info("Memory hot add not supported\n");
+
+               /*
+                * Currently we do not support hot add.
+                * Just fail the request.
+                */
+       }
+
+       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;
+
+       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)
+{
+       switch (msg->header.type) {
+       case INFO_TYPE_MAX_PAGE_CNT:
+               pr_info("Received INFO_TYPE_MAX_PAGE_CNT\n");
+               pr_info("Data Size is %d\n", msg->header.data_size);
+               break;
+       default:
+               pr_info("Received Unknown type: %d\n", msg->header.type);
+       }
+}
+
+/*
+ * Post our status as it relates memory pressure to the
+ * host. Host expects the guests to post this status
+ * periodically at 1 second intervals.
+ *
+ * The metrics specified in this protocol are very Windows
+ * specific and so we cook up numbers here to convey our memory
+ * pressure.
+ */
+
+static void post_status(struct hv_dynmem_device *dm)
+{
+       struct dm_status status;
+
+
+       memset(&status, 0, sizeof(struct dm_status));
+       status.hdr.type = DM_STATUS_REPORT;
+       status.hdr.size = sizeof(struct dm_status);
+       status.hdr.trans_id = atomic_inc_return(&trans_id);
+
+
+       status.num_committed = vm_memory_committed();
+
+       vmbus_sendpacket(dm->dev->channel, &status,
+                               sizeof(struct dm_status),
+                               (unsigned long)NULL,
+                               VM_PKT_DATA_INBAND, 0);
+
+}
+
+
+
+static void free_balloon_pages(struct hv_dynmem_device *dm,
+                        union dm_mem_page_range *range_array)
+{
+       int num_pages = range_array->finfo.page_cnt;
+       __u64 start_frame = range_array->finfo.start_page;
+       struct page *pg;
+       int i;
+
+       for (i = 0; i < num_pages; i++) {
+               pg = pfn_to_page(i + start_frame);
+               __free_page(pg);
+               dm->num_pages_ballooned--;
+       }
+}
+
+
+
+static int  alloc_balloon_pages(struct hv_dynmem_device *dm, int num_pages,
+                        struct dm_balloon_response *bl_resp, int alloc_unit,
+                        bool *alloc_error)
+{
+       int i = 0;
+       struct page *pg;
+
+       if (num_pages < alloc_unit)
+               return 0;
+
+       for (i = 0; (i * alloc_unit) < num_pages; i++) {
+               if (bl_resp->hdr.size + sizeof(union dm_mem_page_range) >
+                       PAGE_SIZE)
+                       return i * alloc_unit;
+
+               /*
+                * We execute this code in a thread context. Furthermore,
+                * we don't want the kernel to try too hard.
+                */
+               pg = alloc_pages(GFP_HIGHUSER | __GFP_NORETRY |
+                               __GFP_NOMEMALLOC | __GFP_NOWARN,
+                               get_order(alloc_unit << PAGE_SHIFT));
+
+               if (!pg) {
+                       *alloc_error = true;
+                       return i * alloc_unit;
+               }
+
+
+               dm->num_pages_ballooned += alloc_unit;
+
+               bl_resp->range_count++;
+               bl_resp->range_array[i].finfo.start_page =
+                       page_to_pfn(pg);
+               bl_resp->range_array[i].finfo.page_cnt = alloc_unit;
+               bl_resp->hdr.size += sizeof(union dm_mem_page_range);
+
+       }
+
+       return num_pages;
+}
+
+
+
+static void balloon_up(struct hv_dynmem_device *dm, struct dm_balloon *req)
+{
+       int num_pages = req->num_pages;
+       int num_ballooned = 0;
+       struct dm_balloon_response *bl_resp;
+       int alloc_unit;
+       int ret;
+       bool alloc_error = false;
+       bool done = false;
+       int i;
+
+
+       /*
+        * Currently, we only support 4k allocations.
+        */
+       alloc_unit = 1;
+
+       while (!done) {
+               bl_resp = (struct dm_balloon_response *)send_buffer;
+               memset(send_buffer, 0, PAGE_SIZE);
+               bl_resp->hdr.type = DM_BALLOON_RESPONSE;
+               bl_resp->hdr.trans_id = atomic_inc_return(&trans_id);
+               bl_resp->hdr.size = sizeof(struct dm_balloon_response);
+               bl_resp->more_pages = 1;
+
+
+               num_pages -= num_ballooned;
+               num_ballooned = alloc_balloon_pages(dm, 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;
+               }
+
+               /*
+                * We are pushing a lot of data through the channel;
+                * deal with transient failures caused because of the
+                * lack of space in the ring buffer.
+                */
+
+               do {
+                       ret = vmbus_sendpacket(dm_device.dev->channel,
+                                               bl_resp,
+                                               bl_resp->hdr.size,
+                                               (unsigned long)NULL,
+                                               VM_PKT_DATA_INBAND, 0);
+
+                       if (ret == -EAGAIN)
+                               msleep(20);
+
+               } while (ret == -EAGAIN);
+
+               if (ret) {
+                       /*
+                        * Free up the memory we allocatted.
+                        */
+                       pr_info("Balloon response failed\n");
+
+                       for (i = 0; i < bl_resp->range_count; i++)
+                               free_balloon_pages(dm,
+                                                &bl_resp->range_array[i]);
+
+                       done = true;
+               }
+       }
+
+}
+
+static void balloon_down(struct hv_dynmem_device *dm,
+                       struct dm_unballoon_request *req)
+{
+       union dm_mem_page_range *range_array = req->range_array;
+       int range_count = req->range_count;
+       struct dm_unballoon_response resp;
+       int i;
+
+       for (i = 0; i < range_count; i++)
+               free_balloon_pages(dm, &range_array[i]);
+
+       if (req->more_pages == 1)
+               return;
+
+       memset(&resp, 0, sizeof(struct dm_unballoon_response));
+       resp.hdr.type = DM_UNBALLOON_RESPONSE;
+       resp.hdr.trans_id = atomic_inc_return(&trans_id);
+       resp.hdr.size = sizeof(struct dm_unballoon_response);
+
+       vmbus_sendpacket(dm_device.dev->channel, &resp,
+                               sizeof(struct dm_unballoon_response),
+                               (unsigned long)NULL,
+                               VM_PKT_DATA_INBAND, 0);
+
+       dm->state = DM_INITIALIZED;
+}
+
+static void balloon_onchannelcallback(void *context);
+
+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);
+               /*
+                * The host expects us to post information on the memory
+                * pressure every second.
+                */
+
+               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;
+}
+
+
+static void version_resp(struct hv_dynmem_device *dm,
+                       struct dm_version_response *vresp)
+{
+       struct dm_version_request version_req;
+       int ret;
+
+       if (vresp->is_accepted) {
+               /*
+                * We are done; wakeup the
+                * context waiting for version
+                * negotiation.
+                */
+               complete(&dm->host_event);
+               return;
+       }
+       /*
+        * If there are more versions to try, continue
+        * with negotiations; if not
+        * shutdown the service since we are not able
+        * to negotiate a suitable version number
+        * with the host.
+        */
+       if (dm->next_version == 0)
+               goto version_error;
+
+       dm->next_version = 0;
+       memset(&version_req, 0, sizeof(struct dm_version_request));
+       version_req.hdr.type = DM_VERSION_REQUEST;
+       version_req.hdr.size = sizeof(struct dm_version_request);
+       version_req.hdr.trans_id = atomic_inc_return(&trans_id);
+       version_req.version.version = DYNMEM_PROTOCOL_VERSION_WIN7;
+       version_req.is_last_attempt = 1;
+
+       ret = vmbus_sendpacket(dm->dev->channel, &version_req,
+                               sizeof(struct dm_version_request),
+                               (unsigned long)NULL,
+                               VM_PKT_DATA_INBAND, 0);
+
+       if (ret)
+               goto version_error;
+
+       return;
+
+version_error:
+       dm->state = DM_INIT_ERROR;
+       complete(&dm->host_event);
+}
+
+static void cap_resp(struct hv_dynmem_device *dm,
+                       struct dm_capabilities_resp_msg *cap_resp)
+{
+       if (!cap_resp->is_accepted) {
+               pr_info("Capabilities not accepted by host\n");
+               dm->state = DM_INIT_ERROR;
+       }
+       complete(&dm->host_event);
+}
+
+static void balloon_onchannelcallback(void *context)
+{
+       struct hv_device *dev = context;
+       u32 recvlen;
+       u64 requestid;
+       struct dm_message *dm_msg;
+       struct dm_header *dm_hdr;
+       struct hv_dynmem_device *dm = hv_get_drvdata(dev);
+
+       memset(recv_buffer, 0, sizeof(recv_buffer));
+       vmbus_recvpacket(dev->channel, recv_buffer,
+                        PAGE_SIZE, &recvlen, &requestid);
+
+       if (recvlen > 0) {
+               dm_msg = (struct dm_message *)recv_buffer;
+               dm_hdr = &dm_msg->hdr;
+
+               switch (dm_hdr->type) {
+               case DM_VERSION_RESPONSE:
+                       version_resp(dm,
+                                (struct dm_version_response *)dm_msg);
+                       break;
+
+               case DM_CAPABILITIES_RESPONSE:
+                       cap_resp(dm,
+                                (struct dm_capabilities_resp_msg *)dm_msg);
+                       break;
+
+               case DM_BALLOON_REQUEST:
+                       dm->state = DM_BALLOON_UP;
+                       complete(&dm->config_event);
+                       break;
+
+               case DM_UNBALLOON_REQUEST:
+                       dm->state = DM_BALLOON_DOWN;
+                       balloon_down(dm,
+                                (struct dm_unballoon_request *)recv_buffer);
+                       break;
+
+               case DM_MEM_HOT_ADD_REQUEST:
+                       dm->state = DM_HOT_ADD;
+                       complete(&dm->config_event);
+                       break;
+
+               case DM_INFO_MESSAGE:
+                       process_info(dm, (struct dm_info_msg *)dm_msg);
+                       break;
+
+               default:
+                       pr_err("Unhandled message: type: %d\n", dm_hdr->type);
+
+               }
+       }
+
+}
+
+static int balloon_probe(struct hv_device *dev,
+                       const struct hv_vmbus_device_id *dev_id)
+{
+       int ret, t;
+       struct dm_version_request version_req;
+       struct dm_capabilities cap_msg;
+
+       do_hot_add = hot_add;
+
+       /*
+        * First allocate a send buffer.
+        */
+
+       send_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
+       if (!send_buffer)
+               return -ENOMEM;
+
+       ret = vmbus_open(dev->channel, dm_ring_size, dm_ring_size, NULL, 0,
+                       balloon_onchannelcallback, dev);
+
+       if (ret)
+               return ret;
+
+       dm_device.dev = dev;
+       dm_device.state = DM_INITIALIZING;
+       dm_device.next_version = DYNMEM_PROTOCOL_VERSION_WIN7;
+       init_completion(&dm_device.host_event);
+       init_completion(&dm_device.config_event);
+
+       dm_device.thread =
+                kthread_run(dm_thread_func, &dm_device, "hv_balloon");
+       if (IS_ERR(dm_device.thread)) {
+               ret = PTR_ERR(dm_device.thread);
+               goto probe_error0;
+       }
+
+       hv_set_drvdata(dev, &dm_device);
+       /*
+        * Initiate the hand shake with the host and negotiate
+        * a version that the host can support. We start with the
+        * highest version number and go down if the host cannot
+        * support it.
+        */
+       memset(&version_req, 0, sizeof(struct dm_version_request));
+       version_req.hdr.type = DM_VERSION_REQUEST;
+       version_req.hdr.size = sizeof(struct dm_version_request);
+       version_req.hdr.trans_id = atomic_inc_return(&trans_id);
+       version_req.version.version = DYNMEM_PROTOCOL_VERSION_WIN8;
+       version_req.is_last_attempt = 0;
+
+       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);
+       if (ret)
+               goto probe_error1;
+
+       t = wait_for_completion_timeout(&dm_device.host_event, 5*HZ);
+       if (t == 0) {
+               ret = -ETIMEDOUT;
+               goto probe_error1;
+       }
+
+       /*
+        * If we could not negotiate a compatible version with the host
+        * fail the probe function.
+        */
+       if (dm_device.state == DM_INIT_ERROR) {
+               ret = -ETIMEDOUT;
+               goto probe_error1;
+       }
+       /*
+        * Now submit our capabilities to the host.
+        */
+       memset(&cap_msg, 0, sizeof(struct dm_capabilities));
+       cap_msg.hdr.type = DM_CAPABILITIES_REPORT;
+       cap_msg.hdr.size = sizeof(struct dm_capabilities);
+       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;
+
+       /*
+        * Currently the host does not use these
+        * values and we set them to what is done in the
+        * Windows driver.
+        */
+       cap_msg.min_page_cnt = 0;
+       cap_msg.max_page_number = -1;
+
+       ret = vmbus_sendpacket(dev->channel, &cap_msg,
+                               sizeof(struct dm_capabilities),
+                               (unsigned long)NULL,
+                               VM_PKT_DATA_INBAND,
+                               VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+       if (ret)
+               goto probe_error1;
+
+       t = wait_for_completion_timeout(&dm_device.host_event, 5*HZ);
+       if (t == 0) {
+               ret = -ETIMEDOUT;
+               goto probe_error1;
+       }
+
+       /*
+        * If the host does not like our capabilities,
+        * fail the probe function.
+        */
+       if (dm_device.state == DM_INIT_ERROR) {
+               ret = -ETIMEDOUT;
+               goto probe_error1;
+       }
+
+       dm_device.state = DM_INITIALIZED;
+
+       return 0;
+
+probe_error1:
+       kthread_stop(dm_device.thread);
+
+probe_error0:
+       vmbus_close(dev->channel);
+       return ret;
+}
+
+static int balloon_remove(struct hv_device *dev)
+{
+       struct hv_dynmem_device *dm = hv_get_drvdata(dev);
+
+       if (dm->num_pages_ballooned != 0)
+               pr_warn("Ballooned pages: %d\n", dm->num_pages_ballooned);
+
+       vmbus_close(dev->channel);
+       kthread_stop(dm->thread);
+
+       return 0;
+}
+
+static const struct hv_vmbus_device_id id_table[] = {
+       /* Dynamic Memory Class ID */
+       /* 525074DC-8985-46e2-8057-A307DC18A502 */
+       { VMBUS_DEVICE(0xdc, 0x74, 0x50, 0X52, 0x85, 0x89, 0xe2, 0x46,
+                      0x80, 0x57, 0xa3, 0x07, 0xdc, 0x18, 0xa5, 0x02)
+       },
+       { },
+};
+
+MODULE_DEVICE_TABLE(vmbus, id_table);
+
+static  struct hv_driver balloon_drv = {
+       .name = "hv_balloon",
+       .id_table = id_table,
+       .probe =  balloon_probe,
+       .remove =  balloon_remove,
+};
+
+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);
+MODULE_LICENSE("GPL");
index cc0997a0517137052c138ed90bd4516594b8e5dc..4f7a17fd1aa72ce2c2a508afea99d7ff44e0aef0 100644 (file)
@@ -42,3 +42,13 @@ config MEMSTICK_R592
 
          To compile this driver as a module, choose M here: the module will
          be called r592.
+
+config MEMSTICK_REALTEK_PCI
+       tristate "Realtek PCI-E Memstick Card Interface Driver"
+       depends on MFD_RTSX_PCI
+       help
+         Say Y here to include driver code to support Memstick card interface
+         of Realtek PCI-E card reader
+
+         To compile this driver as a module, choose M here: the module will
+         be called rtsx_pci_ms.
index 31ba8d378e4660d7a91d61a2ff38adef8f000c1f..af3459d7686ef65ace7bfc1d91cc8623f7418a5d 100644 (file)
@@ -5,3 +5,4 @@
 obj-$(CONFIG_MEMSTICK_TIFM_MS)         += tifm_ms.o
 obj-$(CONFIG_MEMSTICK_JMICRON_38X)     += jmb38x_ms.o
 obj-$(CONFIG_MEMSTICK_R592)            += r592.o
+obj-$(CONFIG_MEMSTICK_REALTEK_PCI)     += rtsx_pci_ms.o
diff --git a/drivers/memstick/host/rtsx_pci_ms.c b/drivers/memstick/host/rtsx_pci_ms.c
new file mode 100644 (file)
index 0000000..f5ddb82
--- /dev/null
@@ -0,0 +1,641 @@
+/* Realtek PCI-Express Memstick Card Interface driver
+ *
+ * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG <wei_wang@realsil.com.cn>
+ *   No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
+ */
+
+#include <linux/module.h>
+#include <linux/highmem.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/memstick.h>
+#include <linux/mfd/rtsx_pci.h>
+#include <asm/unaligned.h>
+
+struct realtek_pci_ms {
+       struct platform_device  *pdev;
+       struct rtsx_pcr         *pcr;
+       struct memstick_host    *msh;
+       struct memstick_request *req;
+
+       struct mutex            host_mutex;
+       struct work_struct      handle_req;
+
+       u8                      ssc_depth;
+       unsigned int            clock;
+       unsigned char           ifmode;
+       bool                    eject;
+};
+
+static inline struct device *ms_dev(struct realtek_pci_ms *host)
+{
+       return &(host->pdev->dev);
+}
+
+static inline void ms_clear_error(struct realtek_pci_ms *host)
+{
+       rtsx_pci_write_register(host->pcr, CARD_STOP,
+                       MS_STOP | MS_CLR_ERR, MS_STOP | MS_CLR_ERR);
+}
+
+#ifdef DEBUG
+
+static void ms_print_debug_regs(struct realtek_pci_ms *host)
+{
+       struct rtsx_pcr *pcr = host->pcr;
+       u16 i;
+       u8 *ptr;
+
+       /* Print MS host internal registers */
+       rtsx_pci_init_cmd(pcr);
+       for (i = 0xFD40; i <= 0xFD44; i++)
+               rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0);
+       for (i = 0xFD52; i <= 0xFD69; i++)
+               rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0);
+       rtsx_pci_send_cmd(pcr, 100);
+
+       ptr = rtsx_pci_get_cmd_data(pcr);
+       for (i = 0xFD40; i <= 0xFD44; i++)
+               dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++));
+       for (i = 0xFD52; i <= 0xFD69; i++)
+               dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++));
+}
+
+#else
+
+#define ms_print_debug_regs(host)
+
+#endif
+
+static int ms_power_on(struct realtek_pci_ms *host)
+{
+       struct rtsx_pcr *pcr = host->pcr;
+       int err;
+
+       rtsx_pci_init_cmd(pcr);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SELECT, 0x07, MS_MOD_SEL);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SHARE_MODE,
+                       CARD_SHARE_MASK, CARD_SHARE_48_MS);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_EN,
+                       MS_CLK_EN, MS_CLK_EN);
+       err = rtsx_pci_send_cmd(pcr, 100);
+       if (err < 0)
+               return err;
+
+       err = rtsx_pci_card_pull_ctl_enable(pcr, RTSX_MS_CARD);
+       if (err < 0)
+               return err;
+
+       err = rtsx_pci_card_power_on(pcr, RTSX_MS_CARD);
+       if (err < 0)
+               return err;
+
+       /* Wait ms power stable */
+       msleep(150);
+
+       err = rtsx_pci_write_register(pcr, CARD_OE,
+                       MS_OUTPUT_EN, MS_OUTPUT_EN);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+static int ms_power_off(struct realtek_pci_ms *host)
+{
+       struct rtsx_pcr *pcr = host->pcr;
+       int err;
+
+       rtsx_pci_init_cmd(pcr);
+
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_EN, MS_CLK_EN, 0);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_OE, MS_OUTPUT_EN, 0);
+
+       err = rtsx_pci_send_cmd(pcr, 100);
+       if (err < 0)
+               return err;
+
+       err = rtsx_pci_card_power_off(pcr, RTSX_MS_CARD);
+       if (err < 0)
+               return err;
+
+       return rtsx_pci_card_pull_ctl_disable(pcr, RTSX_MS_CARD);
+}
+
+static int ms_transfer_data(struct realtek_pci_ms *host, unsigned char data_dir,
+               u8 tpc, u8 cfg, struct scatterlist *sg)
+{
+       struct rtsx_pcr *pcr = host->pcr;
+       int err;
+       unsigned int length = sg->length;
+       u16 sec_cnt = (u16)(length / 512);
+       u8 val, trans_mode, dma_dir;
+
+       dev_dbg(ms_dev(host), "%s: tpc = 0x%02x, data_dir = %s, length = %d\n",
+                       __func__, tpc, (data_dir == READ) ? "READ" : "WRITE",
+                       length);
+
+       if (data_dir == READ) {
+               dma_dir = DMA_DIR_FROM_CARD;
+               trans_mode = MS_TM_AUTO_READ;
+       } else {
+               dma_dir = DMA_DIR_TO_CARD;
+               trans_mode = MS_TM_AUTO_WRITE;
+       }
+
+       rtsx_pci_init_cmd(pcr);
+
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_SECTOR_CNT_H,
+                       0xFF, (u8)(sec_cnt >> 8));
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_SECTOR_CNT_L,
+                       0xFF, (u8)sec_cnt);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg);
+
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, IRQSTAT0,
+                       DMA_DONE_INT, DMA_DONE_INT);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC3, 0xFF, (u8)(length >> 24));
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC2, 0xFF, (u8)(length >> 16));
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC1, 0xFF, (u8)(length >> 8));
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC0, 0xFF, (u8)length);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL,
+                       0x03 | DMA_PACK_SIZE_MASK, dma_dir | DMA_EN | DMA_512);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE,
+                       0x01, RING_BUFFER);
+
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANSFER,
+                       0xFF, MS_TRANSFER_START | trans_mode);
+       rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, MS_TRANSFER,
+                       MS_TRANSFER_END, MS_TRANSFER_END);
+
+       rtsx_pci_send_cmd_no_wait(pcr);
+
+       err = rtsx_pci_transfer_data(pcr, sg, 1, data_dir == READ, 10000);
+       if (err < 0) {
+               ms_clear_error(host);
+               return err;
+       }
+
+       rtsx_pci_read_register(pcr, MS_TRANS_CFG, &val);
+       if (val & (MS_INT_CMDNK | MS_INT_ERR | MS_CRC16_ERR | MS_RDY_TIMEOUT))
+               return -EIO;
+
+       return 0;
+}
+
+static int ms_write_bytes(struct realtek_pci_ms *host, u8 tpc,
+               u8 cfg, u8 cnt, u8 *data, u8 *int_reg)
+{
+       struct rtsx_pcr *pcr = host->pcr;
+       int err, i;
+
+       dev_dbg(ms_dev(host), "%s: tpc = 0x%02x\n", __func__, tpc);
+
+       if (!data)
+               return -EINVAL;
+
+       rtsx_pci_init_cmd(pcr);
+
+       for (i = 0; i < cnt; i++)
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
+                               PPBUF_BASE2 + i, 0xFF, data[i]);
+       if (cnt % 2)
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
+                               PPBUF_BASE2 + i, 0xFF, 0xFF);
+
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE,
+                       0x01, PINGPONG_BUFFER);
+
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANSFER,
+                       0xFF, MS_TRANSFER_START | MS_TM_WRITE_BYTES);
+       rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, MS_TRANSFER,
+                       MS_TRANSFER_END, MS_TRANSFER_END);
+       if (int_reg)
+               rtsx_pci_add_cmd(pcr, READ_REG_CMD, MS_TRANS_CFG, 0, 0);
+
+       err = rtsx_pci_send_cmd(pcr, 5000);
+       if (err < 0) {
+               u8 val;
+
+               rtsx_pci_read_register(pcr, MS_TRANS_CFG, &val);
+               dev_dbg(ms_dev(host), "MS_TRANS_CFG: 0x%02x\n", val);
+
+               if (int_reg)
+                       *int_reg = val & 0x0F;
+
+               ms_print_debug_regs(host);
+
+               ms_clear_error(host);
+
+               if (!(tpc & 0x08)) {
+                       if (val & MS_CRC16_ERR)
+                               return -EIO;
+               } else {
+                       if (!(val & 0x80)) {
+                               if (val & (MS_INT_ERR | MS_INT_CMDNK))
+                                       return -EIO;
+                       }
+               }
+
+               return -ETIMEDOUT;
+       }
+
+       if (int_reg) {
+               u8 *ptr = rtsx_pci_get_cmd_data(pcr) + 1;
+               *int_reg = *ptr & 0x0F;
+       }
+
+       return 0;
+}
+
+static int ms_read_bytes(struct realtek_pci_ms *host, u8 tpc,
+               u8 cfg, u8 cnt, u8 *data, u8 *int_reg)
+{
+       struct rtsx_pcr *pcr = host->pcr;
+       int err, i;
+       u8 *ptr;
+
+       dev_dbg(ms_dev(host), "%s: tpc = 0x%02x\n", __func__, tpc);
+
+       if (!data)
+               return -EINVAL;
+
+       rtsx_pci_init_cmd(pcr);
+
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE,
+                       0x01, PINGPONG_BUFFER);
+
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANSFER,
+                       0xFF, MS_TRANSFER_START | MS_TM_READ_BYTES);
+       rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, MS_TRANSFER,
+                       MS_TRANSFER_END, MS_TRANSFER_END);
+       for (i = 0; i < cnt - 1; i++)
+               rtsx_pci_add_cmd(pcr, READ_REG_CMD, PPBUF_BASE2 + i, 0, 0);
+       if (cnt % 2)
+               rtsx_pci_add_cmd(pcr, READ_REG_CMD, PPBUF_BASE2 + cnt, 0, 0);
+       else
+               rtsx_pci_add_cmd(pcr, READ_REG_CMD,
+                               PPBUF_BASE2 + cnt - 1, 0, 0);
+       if (int_reg)
+               rtsx_pci_add_cmd(pcr, READ_REG_CMD, MS_TRANS_CFG, 0, 0);
+
+       err = rtsx_pci_send_cmd(pcr, 5000);
+       if (err < 0) {
+               u8 val;
+
+               rtsx_pci_read_register(pcr, MS_TRANS_CFG, &val);
+               dev_dbg(ms_dev(host), "MS_TRANS_CFG: 0x%02x\n", val);
+
+               if (int_reg)
+                       *int_reg = val & 0x0F;
+
+               ms_print_debug_regs(host);
+
+               ms_clear_error(host);
+
+               if (!(tpc & 0x08)) {
+                       if (val & MS_CRC16_ERR)
+                               return -EIO;
+               } else {
+                       if (!(val & 0x80)) {
+                               if (val & (MS_INT_ERR | MS_INT_CMDNK))
+                                       return -EIO;
+                       }
+               }
+
+               return -ETIMEDOUT;
+       }
+
+       ptr = rtsx_pci_get_cmd_data(pcr) + 1;
+       for (i = 0; i < cnt; i++)
+               data[i] = *ptr++;
+
+       if (int_reg)
+               *int_reg = *ptr & 0x0F;
+
+       return 0;
+}
+
+static int rtsx_pci_ms_issue_cmd(struct realtek_pci_ms *host)
+{
+       struct memstick_request *req = host->req;
+       int err = 0;
+       u8 cfg = 0, int_reg;
+
+       dev_dbg(ms_dev(host), "%s\n", __func__);
+
+       if (req->need_card_int) {
+               if (host->ifmode != MEMSTICK_SERIAL)
+                       cfg = WAIT_INT;
+       }
+
+       if (req->long_data) {
+               err = ms_transfer_data(host, req->data_dir,
+                               req->tpc, cfg, &(req->sg));
+       } else {
+               if (req->data_dir == READ) {
+                       err = ms_read_bytes(host, req->tpc, cfg,
+                                       req->data_len, req->data, &int_reg);
+               } else {
+                       err = ms_write_bytes(host, req->tpc, cfg,
+                                       req->data_len, req->data, &int_reg);
+               }
+       }
+       if (err < 0)
+               return err;
+
+       if (req->need_card_int && (host->ifmode == MEMSTICK_SERIAL)) {
+               err = ms_read_bytes(host, MS_TPC_GET_INT,
+                               NO_WAIT_INT, 1, &int_reg, NULL);
+               if (err < 0)
+                       return err;
+       }
+
+       if (req->need_card_int) {
+               dev_dbg(ms_dev(host), "int_reg: 0x%02x\n", int_reg);
+
+               if (int_reg & MS_INT_CMDNK)
+                       req->int_reg |= MEMSTICK_INT_CMDNAK;
+               if (int_reg & MS_INT_BREQ)
+                       req->int_reg |= MEMSTICK_INT_BREQ;
+               if (int_reg & MS_INT_ERR)
+                       req->int_reg |= MEMSTICK_INT_ERR;
+               if (int_reg & MS_INT_CED)
+                       req->int_reg |= MEMSTICK_INT_CED;
+       }
+
+       return 0;
+}
+
+static void rtsx_pci_ms_handle_req(struct work_struct *work)
+{
+       struct realtek_pci_ms *host = container_of(work,
+                       struct realtek_pci_ms, handle_req);
+       struct rtsx_pcr *pcr = host->pcr;
+       struct memstick_host *msh = host->msh;
+       int rc;
+
+       mutex_lock(&pcr->pcr_mutex);
+
+       rtsx_pci_start_run(pcr);
+
+       rtsx_pci_switch_clock(host->pcr, host->clock, host->ssc_depth,
+                       false, true, false);
+       rtsx_pci_write_register(pcr, CARD_SELECT, 0x07, MS_MOD_SEL);
+       rtsx_pci_write_register(pcr, CARD_SHARE_MODE,
+                       CARD_SHARE_MASK, CARD_SHARE_48_MS);
+
+       if (!host->req) {
+               do {
+                       rc = memstick_next_req(msh, &host->req);
+                       dev_dbg(ms_dev(host), "next req %d\n", rc);
+
+                       if (!rc)
+                               host->req->error = rtsx_pci_ms_issue_cmd(host);
+               } while (!rc);
+       }
+
+       mutex_unlock(&pcr->pcr_mutex);
+}
+
+static void rtsx_pci_ms_request(struct memstick_host *msh)
+{
+       struct realtek_pci_ms *host = memstick_priv(msh);
+
+       dev_dbg(ms_dev(host), "--> %s\n", __func__);
+
+       schedule_work(&host->handle_req);
+}
+
+static int rtsx_pci_ms_set_param(struct memstick_host *msh,
+               enum memstick_param param, int value)
+{
+       struct realtek_pci_ms *host = memstick_priv(msh);
+       struct rtsx_pcr *pcr = host->pcr;
+       unsigned int clock = 0;
+       u8 ssc_depth = 0;
+       int err;
+
+       dev_dbg(ms_dev(host), "%s: param = %d, value = %d\n",
+                       __func__, param, value);
+
+       switch (param) {
+       case MEMSTICK_POWER:
+               if (value == MEMSTICK_POWER_ON)
+                       err = ms_power_on(host);
+               else if (value == MEMSTICK_POWER_OFF)
+                       err = ms_power_off(host);
+               else
+                       return -EINVAL;
+               break;
+
+       case MEMSTICK_INTERFACE:
+               if (value == MEMSTICK_SERIAL) {
+                       clock = 19000000;
+                       ssc_depth = RTSX_SSC_DEPTH_500K;
+
+                       err = rtsx_pci_write_register(pcr, MS_CFG,
+                                       0x18, MS_BUS_WIDTH_1);
+                       if (err < 0)
+                               return err;
+               } else if (value == MEMSTICK_PAR4) {
+                       clock = 39000000;
+                       ssc_depth = RTSX_SSC_DEPTH_1M;
+
+                       err = rtsx_pci_write_register(pcr, MS_CFG,
+                                       0x58, MS_BUS_WIDTH_4 | PUSH_TIME_ODD);
+                       if (err < 0)
+                               return err;
+               } else {
+                       return -EINVAL;
+               }
+
+               err = rtsx_pci_switch_clock(pcr, clock,
+                               ssc_depth, false, true, false);
+               if (err < 0)
+                       return err;
+
+               host->ssc_depth = ssc_depth;
+               host->clock = clock;
+               host->ifmode = value;
+               break;
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int rtsx_pci_ms_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct realtek_pci_ms *host = platform_get_drvdata(pdev);
+       struct memstick_host *msh = host->msh;
+
+       dev_dbg(ms_dev(host), "--> %s\n", __func__);
+
+       memstick_suspend_host(msh);
+       return 0;
+}
+
+static int rtsx_pci_ms_resume(struct platform_device *pdev)
+{
+       struct realtek_pci_ms *host = platform_get_drvdata(pdev);
+       struct memstick_host *msh = host->msh;
+
+       dev_dbg(ms_dev(host), "--> %s\n", __func__);
+
+       memstick_resume_host(msh);
+       return 0;
+}
+
+#else /* CONFIG_PM */
+
+#define rtsx_pci_ms_suspend NULL
+#define rtsx_pci_ms_resume NULL
+
+#endif /* CONFIG_PM */
+
+static void rtsx_pci_ms_card_event(struct platform_device *pdev)
+{
+       struct realtek_pci_ms *host = platform_get_drvdata(pdev);
+
+       memstick_detect_change(host->msh);
+}
+
+static int rtsx_pci_ms_drv_probe(struct platform_device *pdev)
+{
+       struct memstick_host *msh;
+       struct realtek_pci_ms *host;
+       struct rtsx_pcr *pcr;
+       struct pcr_handle *handle = pdev->dev.platform_data;
+       int rc;
+
+       if (!handle)
+               return -ENXIO;
+
+       pcr = handle->pcr;
+       if (!pcr)
+               return -ENXIO;
+
+       dev_dbg(&(pdev->dev),
+                       ": Realtek PCI-E Memstick controller found\n");
+
+       msh = memstick_alloc_host(sizeof(*host), &pdev->dev);
+       if (!msh)
+               return -ENOMEM;
+
+       host = memstick_priv(msh);
+       host->pcr = pcr;
+       host->msh = msh;
+       host->pdev = pdev;
+       platform_set_drvdata(pdev, host);
+       pcr->slots[RTSX_MS_CARD].p_dev = pdev;
+       pcr->slots[RTSX_MS_CARD].card_event = rtsx_pci_ms_card_event;
+
+       mutex_init(&host->host_mutex);
+
+       INIT_WORK(&host->handle_req, rtsx_pci_ms_handle_req);
+       msh->request = rtsx_pci_ms_request;
+       msh->set_param = rtsx_pci_ms_set_param;
+       msh->caps = MEMSTICK_CAP_PAR4;
+
+       rc = memstick_add_host(msh);
+       if (rc) {
+               memstick_free_host(msh);
+               return rc;
+       }
+
+       return 0;
+}
+
+static int rtsx_pci_ms_drv_remove(struct platform_device *pdev)
+{
+       struct realtek_pci_ms *host = platform_get_drvdata(pdev);
+       struct rtsx_pcr *pcr;
+       struct memstick_host *msh;
+       int rc;
+
+       if (!host)
+               return 0;
+
+       pcr = host->pcr;
+       pcr->slots[RTSX_MS_CARD].p_dev = NULL;
+       pcr->slots[RTSX_MS_CARD].card_event = NULL;
+       msh = host->msh;
+       host->eject = true;
+
+       mutex_lock(&host->host_mutex);
+       if (host->req) {
+               dev_dbg(&(pdev->dev),
+                       "%s: Controller removed during transfer\n",
+                       dev_name(&msh->dev));
+
+               rtsx_pci_complete_unfinished_transfer(pcr);
+
+               host->req->error = -ENOMEDIUM;
+               do {
+                       rc = memstick_next_req(msh, &host->req);
+                       if (!rc)
+                               host->req->error = -ENOMEDIUM;
+               } while (!rc);
+       }
+       mutex_unlock(&host->host_mutex);
+
+       memstick_remove_host(msh);
+       memstick_free_host(msh);
+
+       platform_set_drvdata(pdev, NULL);
+
+       dev_dbg(&(pdev->dev),
+               ": Realtek PCI-E Memstick controller has been removed\n");
+
+       return 0;
+}
+
+static struct platform_device_id rtsx_pci_ms_ids[] = {
+       {
+               .name = DRV_NAME_RTSX_PCI_MS,
+       }, {
+               /* sentinel */
+       }
+};
+MODULE_DEVICE_TABLE(platform, rtsx_pci_ms_ids);
+
+static struct platform_driver rtsx_pci_ms_driver = {
+       .probe          = rtsx_pci_ms_drv_probe,
+       .remove         = rtsx_pci_ms_drv_remove,
+       .id_table       = rtsx_pci_ms_ids,
+       .suspend        = rtsx_pci_ms_suspend,
+       .resume         = rtsx_pci_ms_resume,
+       .driver         = {
+               .owner  = THIS_MODULE,
+               .name   = DRV_NAME_RTSX_PCI_MS,
+       },
+};
+module_platform_driver(rtsx_pci_ms_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Wei WANG <wei_wang@realsil.com.cn>");
+MODULE_DESCRIPTION("Realtek PCI-E Memstick Card Host Driver");
index acab3ef8a310efb611ccb30868c7f85474807d86..2c10938b35677168d694163ce0db8b2fe5cb3299 100644 (file)
@@ -63,6 +63,16 @@ config MFD_SM501_GPIO
         lines on the SM501. The platform data is used to supply the
         base number for the first GPIO line to register.
 
+config MFD_RTSX_PCI
+       tristate "Support for Realtek PCI-E card reader"
+       depends on PCI
+       select MFD_CORE
+       help
+         This supports for Realtek PCI-Express card reader including rts5209,
+         rts5229, rtl8411, etc. Realtek card reader supports access to many
+         types of memory cards, such as Memory Stick, Memory Stick Pro,
+         Secure Digital and MultiMediaCard.
+
 config MFD_ASIC3
        bool "Support for Compaq ASIC3"
        depends on GENERIC_HARDIRQS && GPIOLIB && ARM
index d8ccb630ddb07f6fbdf821661181f2c5fe67bbce..b53db06d1b46775e8fe8f3cf690d2a0a0211c9dd 100644 (file)
@@ -9,6 +9,9 @@ obj-$(CONFIG_MFD_88PM805)       += 88pm805.o 88pm80x.o
 obj-$(CONFIG_MFD_SM501)                += sm501.o
 obj-$(CONFIG_MFD_ASIC3)                += asic3.o tmio_core.o
 
+rtsx_pci-objs                  := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o
+obj-$(CONFIG_MFD_RTSX_PCI)     += rtsx_pci.o
+
 obj-$(CONFIG_HTC_EGPIO)                += htc-egpio.o
 obj-$(CONFIG_HTC_PASIC3)       += htc-pasic3.o
 obj-$(CONFIG_HTC_I2CPLD)       += htc-i2cpld.o
diff --git a/drivers/mfd/rtl8411.c b/drivers/mfd/rtl8411.c
new file mode 100644 (file)
index 0000000..89f046c
--- /dev/null
@@ -0,0 +1,251 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG <wei_wang@realsil.com.cn>
+ *   No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
+ */
+
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/mfd/rtsx_pci.h>
+
+#include "rtsx_pcr.h"
+
+static u8 rtl8411_get_ic_version(struct rtsx_pcr *pcr)
+{
+       u8 val;
+
+       rtsx_pci_read_register(pcr, SYS_VER, &val);
+       return val & 0x0F;
+}
+
+static int rtl8411_extra_init_hw(struct rtsx_pcr *pcr)
+{
+       return rtsx_pci_write_register(pcr, CD_PAD_CTL,
+                       CD_DISABLE_MASK | CD_AUTO_DISABLE, CD_ENABLE);
+}
+
+static int rtl8411_turn_on_led(struct rtsx_pcr *pcr)
+{
+       return rtsx_pci_write_register(pcr, CARD_GPIO, 0x01, 0x00);
+}
+
+static int rtl8411_turn_off_led(struct rtsx_pcr *pcr)
+{
+       return rtsx_pci_write_register(pcr, CARD_GPIO, 0x01, 0x01);
+}
+
+static int rtl8411_enable_auto_blink(struct rtsx_pcr *pcr)
+{
+       return rtsx_pci_write_register(pcr, CARD_AUTO_BLINK, 0xFF, 0x0D);
+}
+
+static int rtl8411_disable_auto_blink(struct rtsx_pcr *pcr)
+{
+       return rtsx_pci_write_register(pcr, CARD_AUTO_BLINK, 0x08, 0x00);
+}
+
+static int rtl8411_card_power_on(struct rtsx_pcr *pcr, int card)
+{
+       int err;
+
+       rtsx_pci_init_cmd(pcr);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL,
+                       BPP_POWER_MASK, BPP_POWER_5_PERCENT_ON);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_CTL,
+                       BPP_LDO_POWB, BPP_LDO_SUSPEND);
+       err = rtsx_pci_send_cmd(pcr, 100);
+       if (err < 0)
+               return err;
+
+       /* To avoid too large in-rush current */
+       udelay(150);
+
+       err = rtsx_pci_write_register(pcr, CARD_PWR_CTL,
+                       BPP_POWER_MASK, BPP_POWER_10_PERCENT_ON);
+       if (err < 0)
+               return err;
+
+       udelay(150);
+
+       err = rtsx_pci_write_register(pcr, CARD_PWR_CTL,
+                       BPP_POWER_MASK, BPP_POWER_15_PERCENT_ON);
+       if (err < 0)
+               return err;
+
+       udelay(150);
+
+       err = rtsx_pci_write_register(pcr, CARD_PWR_CTL,
+                       BPP_POWER_MASK, BPP_POWER_ON);
+       if (err < 0)
+               return err;
+
+       return rtsx_pci_write_register(pcr, LDO_CTL, BPP_LDO_POWB, BPP_LDO_ON);
+}
+
+static int rtl8411_card_power_off(struct rtsx_pcr *pcr, int card)
+{
+       int err;
+
+       err = rtsx_pci_write_register(pcr, CARD_PWR_CTL,
+                       BPP_POWER_MASK, BPP_POWER_OFF);
+       if (err < 0)
+               return err;
+
+       return rtsx_pci_write_register(pcr, LDO_CTL,
+                       BPP_LDO_POWB, BPP_LDO_SUSPEND);
+}
+
+static unsigned int rtl8411_cd_deglitch(struct rtsx_pcr *pcr)
+{
+       unsigned int card_exist;
+
+       card_exist = rtsx_pci_readl(pcr, RTSX_BIPR);
+       card_exist &= CARD_EXIST;
+       if (!card_exist) {
+               /* Enable card CD */
+               rtsx_pci_write_register(pcr, CD_PAD_CTL,
+                               CD_DISABLE_MASK, CD_ENABLE);
+               /* Enable card interrupt */
+               rtsx_pci_write_register(pcr, EFUSE_CONTENT, 0xe0, 0x00);
+               return 0;
+       }
+
+       if (hweight32(card_exist) > 1) {
+               rtsx_pci_write_register(pcr, CARD_PWR_CTL,
+                               BPP_POWER_MASK, BPP_POWER_5_PERCENT_ON);
+               msleep(100);
+
+               card_exist = rtsx_pci_readl(pcr, RTSX_BIPR);
+               if (card_exist & MS_EXIST)
+                       card_exist = MS_EXIST;
+               else if (card_exist & SD_EXIST)
+                       card_exist = SD_EXIST;
+               else
+                       card_exist = 0;
+
+               rtsx_pci_write_register(pcr, CARD_PWR_CTL,
+                               BPP_POWER_MASK, BPP_POWER_OFF);
+
+               dev_dbg(&(pcr->pci->dev),
+                               "After CD deglitch, card_exist = 0x%x\n",
+                               card_exist);
+       }
+
+       if (card_exist & MS_EXIST) {
+               /* Disable SD interrupt */
+               rtsx_pci_write_register(pcr, EFUSE_CONTENT, 0xe0, 0x40);
+               rtsx_pci_write_register(pcr, CD_PAD_CTL,
+                               CD_DISABLE_MASK, MS_CD_EN_ONLY);
+       } else if (card_exist & SD_EXIST) {
+               /* Disable MS interrupt */
+               rtsx_pci_write_register(pcr, EFUSE_CONTENT, 0xe0, 0x80);
+               rtsx_pci_write_register(pcr, CD_PAD_CTL,
+                               CD_DISABLE_MASK, SD_CD_EN_ONLY);
+       }
+
+       return card_exist;
+}
+
+static const struct pcr_ops rtl8411_pcr_ops = {
+       .extra_init_hw = rtl8411_extra_init_hw,
+       .optimize_phy = NULL,
+       .turn_on_led = rtl8411_turn_on_led,
+       .turn_off_led = rtl8411_turn_off_led,
+       .enable_auto_blink = rtl8411_enable_auto_blink,
+       .disable_auto_blink = rtl8411_disable_auto_blink,
+       .card_power_on = rtl8411_card_power_on,
+       .card_power_off = rtl8411_card_power_off,
+       .cd_deglitch = rtl8411_cd_deglitch,
+};
+
+/* SD Pull Control Enable:
+ *     SD_DAT[3:0] ==> pull up
+ *     SD_CD       ==> pull up
+ *     SD_WP       ==> pull up
+ *     SD_CMD      ==> pull up
+ *     SD_CLK      ==> pull down
+ */
+static const u32 rtl8411_sd_pull_ctl_enable_tbl[] = {
+       RTSX_REG_PAIR(CARD_PULL_CTL1, 0xAA),
+       RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA),
+       RTSX_REG_PAIR(CARD_PULL_CTL3, 0xA9),
+       RTSX_REG_PAIR(CARD_PULL_CTL4, 0x09),
+       RTSX_REG_PAIR(CARD_PULL_CTL5, 0x09),
+       RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04),
+       0,
+};
+
+/* SD Pull Control Disable:
+ *     SD_DAT[3:0] ==> pull down
+ *     SD_CD       ==> pull up
+ *     SD_WP       ==> pull down
+ *     SD_CMD      ==> pull down
+ *     SD_CLK      ==> pull down
+ */
+static const u32 rtl8411_sd_pull_ctl_disable_tbl[] = {
+       RTSX_REG_PAIR(CARD_PULL_CTL1, 0x65),
+       RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
+       RTSX_REG_PAIR(CARD_PULL_CTL3, 0x95),
+       RTSX_REG_PAIR(CARD_PULL_CTL4, 0x09),
+       RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05),
+       RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04),
+       0,
+};
+
+/* MS Pull Control Enable:
+ *     MS CD       ==> pull up
+ *     others      ==> pull down
+ */
+static const u32 rtl8411_ms_pull_ctl_enable_tbl[] = {
+       RTSX_REG_PAIR(CARD_PULL_CTL1, 0x65),
+       RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
+       RTSX_REG_PAIR(CARD_PULL_CTL3, 0x95),
+       RTSX_REG_PAIR(CARD_PULL_CTL4, 0x05),
+       RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05),
+       RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04),
+       0,
+};
+
+/* MS Pull Control Disable:
+ *     MS CD       ==> pull up
+ *     others      ==> pull down
+ */
+static const u32 rtl8411_ms_pull_ctl_disable_tbl[] = {
+       RTSX_REG_PAIR(CARD_PULL_CTL1, 0x65),
+       RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
+       RTSX_REG_PAIR(CARD_PULL_CTL3, 0x95),
+       RTSX_REG_PAIR(CARD_PULL_CTL4, 0x09),
+       RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05),
+       RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04),
+       0,
+};
+
+void rtl8411_init_params(struct rtsx_pcr *pcr)
+{
+       pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104;
+       pcr->num_slots = 2;
+       pcr->ops = &rtl8411_pcr_ops;
+
+       pcr->ic_version = rtl8411_get_ic_version(pcr);
+       pcr->sd_pull_ctl_enable_tbl = rtl8411_sd_pull_ctl_enable_tbl;
+       pcr->sd_pull_ctl_disable_tbl = rtl8411_sd_pull_ctl_disable_tbl;
+       pcr->ms_pull_ctl_enable_tbl = rtl8411_ms_pull_ctl_enable_tbl;
+       pcr->ms_pull_ctl_disable_tbl = rtl8411_ms_pull_ctl_disable_tbl;
+}
diff --git a/drivers/mfd/rts5209.c b/drivers/mfd/rts5209.c
new file mode 100644 (file)
index 0000000..283a4f1
--- /dev/null
@@ -0,0 +1,223 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG <wei_wang@realsil.com.cn>
+ *   No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mfd/rtsx_pci.h>
+
+#include "rtsx_pcr.h"
+
+static u8 rts5209_get_ic_version(struct rtsx_pcr *pcr)
+{
+       u8 val;
+
+       val = rtsx_pci_readb(pcr, 0x1C);
+       return val & 0x0F;
+}
+
+static void rts5209_init_vendor_cfg(struct rtsx_pcr *pcr)
+{
+       u32 val;
+
+       rtsx_pci_read_config_dword(pcr, 0x724, &val);
+       dev_dbg(&(pcr->pci->dev), "Cfg 0x724: 0x%x\n", val);
+
+       if (!(val & 0x80)) {
+               if (val & 0x08)
+                       pcr->ms_pmos = false;
+               else
+                       pcr->ms_pmos = true;
+       }
+}
+
+static int rts5209_extra_init_hw(struct rtsx_pcr *pcr)
+{
+       rtsx_pci_init_cmd(pcr);
+
+       /* Turn off LED */
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_GPIO, 0xFF, 0x03);
+       /* Configure GPIO as output */
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_GPIO_DIR, 0xFF, 0x03);
+
+       return rtsx_pci_send_cmd(pcr, 100);
+}
+
+static int rts5209_optimize_phy(struct rtsx_pcr *pcr)
+{
+       return rtsx_pci_write_phy_register(pcr, 0x00, 0xB966);
+}
+
+static int rts5209_turn_on_led(struct rtsx_pcr *pcr)
+{
+       return rtsx_pci_write_register(pcr, CARD_GPIO, 0x01, 0x00);
+}
+
+static int rts5209_turn_off_led(struct rtsx_pcr *pcr)
+{
+       return rtsx_pci_write_register(pcr, CARD_GPIO, 0x01, 0x01);
+}
+
+static int rts5209_enable_auto_blink(struct rtsx_pcr *pcr)
+{
+       return rtsx_pci_write_register(pcr, CARD_AUTO_BLINK, 0xFF, 0x0D);
+}
+
+static int rts5209_disable_auto_blink(struct rtsx_pcr *pcr)
+{
+       return rtsx_pci_write_register(pcr, CARD_AUTO_BLINK, 0x08, 0x00);
+}
+
+static int rts5209_card_power_on(struct rtsx_pcr *pcr, int card)
+{
+       int err;
+       u8 pwr_mask, partial_pwr_on, pwr_on;
+
+       pwr_mask = SD_POWER_MASK;
+       partial_pwr_on = SD_PARTIAL_POWER_ON;
+       pwr_on = SD_POWER_ON;
+
+       if (pcr->ms_pmos && (card == RTSX_MS_CARD)) {
+               pwr_mask = MS_POWER_MASK;
+               partial_pwr_on = MS_PARTIAL_POWER_ON;
+               pwr_on = MS_POWER_ON;
+       }
+
+       rtsx_pci_init_cmd(pcr);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL,
+                       pwr_mask, partial_pwr_on);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL,
+                       LDO3318_PWR_MASK, 0x04);
+       err = rtsx_pci_send_cmd(pcr, 100);
+       if (err < 0)
+               return err;
+
+       /* To avoid too large in-rush current */
+       udelay(150);
+
+       rtsx_pci_init_cmd(pcr);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, pwr_mask, pwr_on);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL,
+                       LDO3318_PWR_MASK, 0x00);
+       err = rtsx_pci_send_cmd(pcr, 100);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+static int rts5209_card_power_off(struct rtsx_pcr *pcr, int card)
+{
+       u8 pwr_mask, pwr_off;
+
+       pwr_mask = SD_POWER_MASK;
+       pwr_off = SD_POWER_OFF;
+
+       if (pcr->ms_pmos && (card == RTSX_MS_CARD)) {
+               pwr_mask = MS_POWER_MASK;
+               pwr_off = MS_POWER_OFF;
+       }
+
+       rtsx_pci_init_cmd(pcr);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL,
+                       pwr_mask | PMOS_STRG_MASK, pwr_off | PMOS_STRG_400mA);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL,
+                       LDO3318_PWR_MASK, 0X06);
+       return rtsx_pci_send_cmd(pcr, 100);
+}
+
+static const struct pcr_ops rts5209_pcr_ops = {
+       .extra_init_hw = rts5209_extra_init_hw,
+       .optimize_phy = rts5209_optimize_phy,
+       .turn_on_led = rts5209_turn_on_led,
+       .turn_off_led = rts5209_turn_off_led,
+       .enable_auto_blink = rts5209_enable_auto_blink,
+       .disable_auto_blink = rts5209_disable_auto_blink,
+       .card_power_on = rts5209_card_power_on,
+       .card_power_off = rts5209_card_power_off,
+       .cd_deglitch = NULL,
+};
+
+/* SD Pull Control Enable:
+ *     SD_DAT[3:0] ==> pull up
+ *     SD_CD       ==> pull up
+ *     SD_WP       ==> pull up
+ *     SD_CMD      ==> pull up
+ *     SD_CLK      ==> pull down
+ */
+static const u32 rts5209_sd_pull_ctl_enable_tbl[] = {
+       RTSX_REG_PAIR(CARD_PULL_CTL1, 0xAA),
+       RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA),
+       RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE9),
+       0,
+};
+
+/* SD Pull Control Disable:
+ *     SD_DAT[3:0] ==> pull down
+ *     SD_CD       ==> pull up
+ *     SD_WP       ==> pull down
+ *     SD_CMD      ==> pull down
+ *     SD_CLK      ==> pull down
+ */
+static const u32 rts5209_sd_pull_ctl_disable_tbl[] = {
+       RTSX_REG_PAIR(CARD_PULL_CTL1, 0x55),
+       RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
+       RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD5),
+       0,
+};
+
+/* MS Pull Control Enable:
+ *     MS CD       ==> pull up
+ *     others      ==> pull down
+ */
+static const u32 rts5209_ms_pull_ctl_enable_tbl[] = {
+       RTSX_REG_PAIR(CARD_PULL_CTL4, 0x55),
+       RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55),
+       RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15),
+       0,
+};
+
+/* MS Pull Control Disable:
+ *     MS CD       ==> pull up
+ *     others      ==> pull down
+ */
+static const u32 rts5209_ms_pull_ctl_disable_tbl[] = {
+       RTSX_REG_PAIR(CARD_PULL_CTL4, 0x55),
+       RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55),
+       RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15),
+       0,
+};
+
+void rts5209_init_params(struct rtsx_pcr *pcr)
+{
+       pcr->extra_caps = EXTRA_CAPS_SD_SDR50 |
+               EXTRA_CAPS_SD_SDR104 | EXTRA_CAPS_MMC_8BIT;
+       pcr->num_slots = 2;
+       pcr->ops = &rts5209_pcr_ops;
+
+       rts5209_init_vendor_cfg(pcr);
+
+       pcr->ic_version = rts5209_get_ic_version(pcr);
+       pcr->sd_pull_ctl_enable_tbl = rts5209_sd_pull_ctl_enable_tbl;
+       pcr->sd_pull_ctl_disable_tbl = rts5209_sd_pull_ctl_disable_tbl;
+       pcr->ms_pull_ctl_enable_tbl = rts5209_ms_pull_ctl_enable_tbl;
+       pcr->ms_pull_ctl_disable_tbl = rts5209_ms_pull_ctl_disable_tbl;
+}
diff --git a/drivers/mfd/rts5229.c b/drivers/mfd/rts5229.c
new file mode 100644 (file)
index 0000000..b9dbab2
--- /dev/null
@@ -0,0 +1,205 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG <wei_wang@realsil.com.cn>
+ *   No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mfd/rtsx_pci.h>
+
+#include "rtsx_pcr.h"
+
+static u8 rts5229_get_ic_version(struct rtsx_pcr *pcr)
+{
+       u8 val;
+
+       rtsx_pci_read_register(pcr, DUMMY_REG_RESET_0, &val);
+       return val & 0x0F;
+}
+
+static int rts5229_extra_init_hw(struct rtsx_pcr *pcr)
+{
+       rtsx_pci_init_cmd(pcr);
+
+       /* Configure GPIO as output */
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, GPIO_CTL, 0x02, 0x02);
+       /* Switch LDO3318 source from DV33 to card_3v3 */
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x00);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x01);
+       /* LED shine disabled, set initial shine cycle period */
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, OLT_LED_CTL, 0x0F, 0x02);
+
+       return rtsx_pci_send_cmd(pcr, 100);
+}
+
+static int rts5229_optimize_phy(struct rtsx_pcr *pcr)
+{
+       /* Optimize RX sensitivity */
+       return rtsx_pci_write_phy_register(pcr, 0x00, 0xBA42);
+}
+
+static int rts5229_turn_on_led(struct rtsx_pcr *pcr)
+{
+       return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x02);
+}
+
+static int rts5229_turn_off_led(struct rtsx_pcr *pcr)
+{
+       return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x00);
+}
+
+static int rts5229_enable_auto_blink(struct rtsx_pcr *pcr)
+{
+       return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x08);
+}
+
+static int rts5229_disable_auto_blink(struct rtsx_pcr *pcr)
+{
+       return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x00);
+}
+
+static int rts5229_card_power_on(struct rtsx_pcr *pcr, int card)
+{
+       int err;
+
+       rtsx_pci_init_cmd(pcr);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL,
+                       SD_POWER_MASK, SD_PARTIAL_POWER_ON);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL,
+                       LDO3318_PWR_MASK, 0x02);
+       err = rtsx_pci_send_cmd(pcr, 100);
+       if (err < 0)
+               return err;
+
+       /* To avoid too large in-rush current */
+       udelay(150);
+
+       rtsx_pci_init_cmd(pcr);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL,
+                       SD_POWER_MASK, SD_POWER_ON);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL,
+                       LDO3318_PWR_MASK, 0x06);
+       err = rtsx_pci_send_cmd(pcr, 100);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+static int rts5229_card_power_off(struct rtsx_pcr *pcr, int card)
+{
+       rtsx_pci_init_cmd(pcr);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL,
+                       SD_POWER_MASK | PMOS_STRG_MASK,
+                       SD_POWER_OFF | PMOS_STRG_400mA);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL,
+                       LDO3318_PWR_MASK, 0X00);
+       return rtsx_pci_send_cmd(pcr, 100);
+}
+
+static const struct pcr_ops rts5229_pcr_ops = {
+       .extra_init_hw = rts5229_extra_init_hw,
+       .optimize_phy = rts5229_optimize_phy,
+       .turn_on_led = rts5229_turn_on_led,
+       .turn_off_led = rts5229_turn_off_led,
+       .enable_auto_blink = rts5229_enable_auto_blink,
+       .disable_auto_blink = rts5229_disable_auto_blink,
+       .card_power_on = rts5229_card_power_on,
+       .card_power_off = rts5229_card_power_off,
+       .cd_deglitch = NULL,
+};
+
+/* SD Pull Control Enable:
+ *     SD_DAT[3:0] ==> pull up
+ *     SD_CD       ==> pull up
+ *     SD_WP       ==> pull up
+ *     SD_CMD      ==> pull up
+ *     SD_CLK      ==> pull down
+ */
+static const u32 rts5229_sd_pull_ctl_enable_tbl1[] = {
+       RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA),
+       RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE9),
+       0,
+};
+
+/* For RTS5229 version C */
+static const u32 rts5229_sd_pull_ctl_enable_tbl2[] = {
+       RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA),
+       RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD9),
+       0,
+};
+
+/* SD Pull Control Disable:
+ *     SD_DAT[3:0] ==> pull down
+ *     SD_CD       ==> pull up
+ *     SD_WP       ==> pull down
+ *     SD_CMD      ==> pull down
+ *     SD_CLK      ==> pull down
+ */
+static const u32 rts5229_sd_pull_ctl_disable_tbl1[] = {
+       RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
+       RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD5),
+       0,
+};
+
+/* For RTS5229 version C */
+static const u32 rts5229_sd_pull_ctl_disable_tbl2[] = {
+       RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55),
+       RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE5),
+       0,
+};
+
+/* MS Pull Control Enable:
+ *     MS CD       ==> pull up
+ *     others      ==> pull down
+ */
+static const u32 rts5229_ms_pull_ctl_enable_tbl[] = {
+       RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55),
+       RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15),
+       0,
+};
+
+/* MS Pull Control Disable:
+ *     MS CD       ==> pull up
+ *     others      ==> pull down
+ */
+static const u32 rts5229_ms_pull_ctl_disable_tbl[] = {
+       RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55),
+       RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15),
+       0,
+};
+
+void rts5229_init_params(struct rtsx_pcr *pcr)
+{
+       pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104;
+       pcr->num_slots = 2;
+       pcr->ops = &rts5229_pcr_ops;
+
+       pcr->ic_version = rts5229_get_ic_version(pcr);
+       if (pcr->ic_version == IC_VER_C) {
+               pcr->sd_pull_ctl_enable_tbl = rts5229_sd_pull_ctl_enable_tbl2;
+               pcr->sd_pull_ctl_disable_tbl = rts5229_sd_pull_ctl_disable_tbl2;
+       } else {
+               pcr->sd_pull_ctl_enable_tbl = rts5229_sd_pull_ctl_enable_tbl1;
+               pcr->sd_pull_ctl_disable_tbl = rts5229_sd_pull_ctl_disable_tbl1;
+       }
+       pcr->ms_pull_ctl_enable_tbl = rts5229_ms_pull_ctl_enable_tbl;
+       pcr->ms_pull_ctl_disable_tbl = rts5229_ms_pull_ctl_disable_tbl;
+}
diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c
new file mode 100644 (file)
index 0000000..56d4377
--- /dev/null
@@ -0,0 +1,1251 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG <wei_wang@realsil.com.cn>
+ *   No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
+ */
+
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/highmem.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/idr.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/rtsx_pci.h>
+#include <asm/unaligned.h>
+
+#include "rtsx_pcr.h"
+
+static bool msi_en = true;
+module_param(msi_en, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(msi_en, "Enable MSI");
+
+static DEFINE_IDR(rtsx_pci_idr);
+static DEFINE_SPINLOCK(rtsx_pci_lock);
+
+static struct mfd_cell rtsx_pcr_cells[] = {
+       [RTSX_SD_CARD] = {
+               .name = DRV_NAME_RTSX_PCI_SDMMC,
+       },
+       [RTSX_MS_CARD] = {
+               .name = DRV_NAME_RTSX_PCI_MS,
+       },
+};
+
+static DEFINE_PCI_DEVICE_TABLE(rtsx_pci_ids) = {
+       { PCI_DEVICE(0x10EC, 0x5209), PCI_CLASS_OTHERS << 16, 0xFF0000 },
+       { PCI_DEVICE(0x10EC, 0x5229), PCI_CLASS_OTHERS << 16, 0xFF0000 },
+       { PCI_DEVICE(0x10EC, 0x5289), PCI_CLASS_OTHERS << 16, 0xFF0000 },
+       { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, rtsx_pci_ids);
+
+void rtsx_pci_start_run(struct rtsx_pcr *pcr)
+{
+       /* If pci device removed, don't queue idle work any more */
+       if (pcr->remove_pci)
+               return;
+
+       if (pcr->state != PDEV_STAT_RUN) {
+               pcr->state = PDEV_STAT_RUN;
+               if (pcr->ops->enable_auto_blink)
+                       pcr->ops->enable_auto_blink(pcr);
+       }
+
+       mod_delayed_work(system_wq, &pcr->idle_work, msecs_to_jiffies(200));
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_start_run);
+
+int rtsx_pci_write_register(struct rtsx_pcr *pcr, u16 addr, u8 mask, u8 data)
+{
+       int i;
+       u32 val = HAIMR_WRITE_START;
+
+       val |= (u32)(addr & 0x3FFF) << 16;
+       val |= (u32)mask << 8;
+       val |= (u32)data;
+
+       rtsx_pci_writel(pcr, RTSX_HAIMR, val);
+
+       for (i = 0; i < MAX_RW_REG_CNT; i++) {
+               val = rtsx_pci_readl(pcr, RTSX_HAIMR);
+               if ((val & HAIMR_TRANS_END) == 0) {
+                       if (data != (u8)val)
+                               return -EIO;
+                       return 0;
+               }
+       }
+
+       return -ETIMEDOUT;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_write_register);
+
+int rtsx_pci_read_register(struct rtsx_pcr *pcr, u16 addr, u8 *data)
+{
+       u32 val = HAIMR_READ_START;
+       int i;
+
+       val |= (u32)(addr & 0x3FFF) << 16;
+       rtsx_pci_writel(pcr, RTSX_HAIMR, val);
+
+       for (i = 0; i < MAX_RW_REG_CNT; i++) {
+               val = rtsx_pci_readl(pcr, RTSX_HAIMR);
+               if ((val & HAIMR_TRANS_END) == 0)
+                       break;
+       }
+
+       if (i >= MAX_RW_REG_CNT)
+               return -ETIMEDOUT;
+
+       if (data)
+               *data = (u8)(val & 0xFF);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_read_register);
+
+int rtsx_pci_write_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 val)
+{
+       int err, i, finished = 0;
+       u8 tmp;
+
+       rtsx_pci_init_cmd(pcr);
+
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYDATA0, 0xFF, (u8)val);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYDATA1, 0xFF, (u8)(val >> 8));
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYADDR, 0xFF, addr);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYRWCTL, 0xFF, 0x81);
+
+       err = rtsx_pci_send_cmd(pcr, 100);
+       if (err < 0)
+               return err;
+
+       for (i = 0; i < 100000; i++) {
+               err = rtsx_pci_read_register(pcr, PHYRWCTL, &tmp);
+               if (err < 0)
+                       return err;
+
+               if (!(tmp & 0x80)) {
+                       finished = 1;
+                       break;
+               }
+       }
+
+       if (!finished)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_write_phy_register);
+
+int rtsx_pci_read_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 *val)
+{
+       int err, i, finished = 0;
+       u16 data;
+       u8 *ptr, tmp;
+
+       rtsx_pci_init_cmd(pcr);
+
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYADDR, 0xFF, addr);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYRWCTL, 0xFF, 0x80);
+
+       err = rtsx_pci_send_cmd(pcr, 100);
+       if (err < 0)
+               return err;
+
+       for (i = 0; i < 100000; i++) {
+               err = rtsx_pci_read_register(pcr, PHYRWCTL, &tmp);
+               if (err < 0)
+                       return err;
+
+               if (!(tmp & 0x80)) {
+                       finished = 1;
+                       break;
+               }
+       }
+
+       if (!finished)
+               return -ETIMEDOUT;
+
+       rtsx_pci_init_cmd(pcr);
+
+       rtsx_pci_add_cmd(pcr, READ_REG_CMD, PHYDATA0, 0, 0);
+       rtsx_pci_add_cmd(pcr, READ_REG_CMD, PHYDATA1, 0, 0);
+
+       err = rtsx_pci_send_cmd(pcr, 100);
+       if (err < 0)
+               return err;
+
+       ptr = rtsx_pci_get_cmd_data(pcr);
+       data = ((u16)ptr[1] << 8) | ptr[0];
+
+       if (val)
+               *val = data;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_read_phy_register);
+
+void rtsx_pci_stop_cmd(struct rtsx_pcr *pcr)
+{
+       rtsx_pci_writel(pcr, RTSX_HCBCTLR, STOP_CMD);
+       rtsx_pci_writel(pcr, RTSX_HDBCTLR, STOP_DMA);
+
+       rtsx_pci_write_register(pcr, DMACTL, 0x80, 0x80);
+       rtsx_pci_write_register(pcr, RBCTL, 0x80, 0x80);
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_stop_cmd);
+
+void rtsx_pci_add_cmd(struct rtsx_pcr *pcr,
+               u8 cmd_type, u16 reg_addr, u8 mask, u8 data)
+{
+       unsigned long flags;
+       u32 val = 0;
+       u32 *ptr = (u32 *)(pcr->host_cmds_ptr);
+
+       val |= (u32)(cmd_type & 0x03) << 30;
+       val |= (u32)(reg_addr & 0x3FFF) << 16;
+       val |= (u32)mask << 8;
+       val |= (u32)data;
+
+       spin_lock_irqsave(&pcr->lock, flags);
+       ptr += pcr->ci;
+       if (pcr->ci < (HOST_CMDS_BUF_LEN / 4)) {
+               put_unaligned_le32(val, ptr);
+               ptr++;
+               pcr->ci++;
+       }
+       spin_unlock_irqrestore(&pcr->lock, flags);
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_add_cmd);
+
+void rtsx_pci_send_cmd_no_wait(struct rtsx_pcr *pcr)
+{
+       u32 val = 1 << 31;
+
+       rtsx_pci_writel(pcr, RTSX_HCBAR, pcr->host_cmds_addr);
+
+       val |= (u32)(pcr->ci * 4) & 0x00FFFFFF;
+       /* Hardware Auto Response */
+       val |= 0x40000000;
+       rtsx_pci_writel(pcr, RTSX_HCBCTLR, val);
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_send_cmd_no_wait);
+
+int rtsx_pci_send_cmd(struct rtsx_pcr *pcr, int timeout)
+{
+       struct completion trans_done;
+       u32 val = 1 << 31;
+       long timeleft;
+       unsigned long flags;
+       int err = 0;
+
+       spin_lock_irqsave(&pcr->lock, flags);
+
+       /* set up data structures for the wakeup system */
+       pcr->done = &trans_done;
+       pcr->trans_result = TRANS_NOT_READY;
+       init_completion(&trans_done);
+
+       rtsx_pci_writel(pcr, RTSX_HCBAR, pcr->host_cmds_addr);
+
+       val |= (u32)(pcr->ci * 4) & 0x00FFFFFF;
+       /* Hardware Auto Response */
+       val |= 0x40000000;
+       rtsx_pci_writel(pcr, RTSX_HCBCTLR, val);
+
+       spin_unlock_irqrestore(&pcr->lock, flags);
+
+       /* Wait for TRANS_OK_INT */
+       timeleft = wait_for_completion_interruptible_timeout(
+                       &trans_done, msecs_to_jiffies(timeout));
+       if (timeleft <= 0) {
+               dev_dbg(&(pcr->pci->dev), "Timeout (%s %d)\n",
+                               __func__, __LINE__);
+               err = -ETIMEDOUT;
+               goto finish_send_cmd;
+       }
+
+       spin_lock_irqsave(&pcr->lock, flags);
+       if (pcr->trans_result == TRANS_RESULT_FAIL)
+               err = -EINVAL;
+       else if (pcr->trans_result == TRANS_RESULT_OK)
+               err = 0;
+       else if (pcr->trans_result == TRANS_NO_DEVICE)
+               err = -ENODEV;
+       spin_unlock_irqrestore(&pcr->lock, flags);
+
+finish_send_cmd:
+       spin_lock_irqsave(&pcr->lock, flags);
+       pcr->done = NULL;
+       spin_unlock_irqrestore(&pcr->lock, flags);
+
+       if ((err < 0) && (err != -ENODEV))
+               rtsx_pci_stop_cmd(pcr);
+
+       if (pcr->finish_me)
+               complete(pcr->finish_me);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_send_cmd);
+
+static void rtsx_pci_add_sg_tbl(struct rtsx_pcr *pcr,
+               dma_addr_t addr, unsigned int len, int end)
+{
+       u64 *ptr = (u64 *)(pcr->host_sg_tbl_ptr) + pcr->sgi;
+       u64 val;
+       u8 option = SG_VALID | SG_TRANS_DATA;
+
+       dev_dbg(&(pcr->pci->dev), "DMA addr: 0x%x, Len: 0x%x\n",
+                       (unsigned int)addr, len);
+
+       if (end)
+               option |= SG_END;
+       val = ((u64)addr << 32) | ((u64)len << 12) | option;
+
+       put_unaligned_le64(val, ptr);
+       ptr++;
+       pcr->sgi++;
+}
+
+int rtsx_pci_transfer_data(struct rtsx_pcr *pcr, struct scatterlist *sglist,
+               int num_sg, bool read, int timeout)
+{
+       struct completion trans_done;
+       u8 dir;
+       int err = 0, i, count;
+       long timeleft;
+       unsigned long flags;
+       struct scatterlist *sg;
+       enum dma_data_direction dma_dir;
+       u32 val;
+       dma_addr_t addr;
+       unsigned int len;
+
+       dev_dbg(&(pcr->pci->dev), "--> %s: num_sg = %d\n", __func__, num_sg);
+
+       /* don't transfer data during abort processing */
+       if (pcr->remove_pci)
+               return -EINVAL;
+
+       if ((sglist == NULL) || (num_sg <= 0))
+               return -EINVAL;
+
+       if (read) {
+               dir = DEVICE_TO_HOST;
+               dma_dir = DMA_FROM_DEVICE;
+       } else {
+               dir = HOST_TO_DEVICE;
+               dma_dir = DMA_TO_DEVICE;
+       }
+
+       count = dma_map_sg(&(pcr->pci->dev), sglist, num_sg, dma_dir);
+       if (count < 1) {
+               dev_err(&(pcr->pci->dev), "scatterlist map failed\n");
+               return -EINVAL;
+       }
+       dev_dbg(&(pcr->pci->dev), "DMA mapping count: %d\n", count);
+
+       val = ((u32)(dir & 0x01) << 29) | TRIG_DMA | ADMA_MODE;
+       pcr->sgi = 0;
+       for_each_sg(sglist, sg, count, i) {
+               addr = sg_dma_address(sg);
+               len = sg_dma_len(sg);
+               rtsx_pci_add_sg_tbl(pcr, addr, len, i == count - 1);
+       }
+
+       spin_lock_irqsave(&pcr->lock, flags);
+
+       pcr->done = &trans_done;
+       pcr->trans_result = TRANS_NOT_READY;
+       init_completion(&trans_done);
+       rtsx_pci_writel(pcr, RTSX_HDBAR, pcr->host_sg_tbl_addr);
+       rtsx_pci_writel(pcr, RTSX_HDBCTLR, val);
+
+       spin_unlock_irqrestore(&pcr->lock, flags);
+
+       timeleft = wait_for_completion_interruptible_timeout(
+                       &trans_done, msecs_to_jiffies(timeout));
+       if (timeleft <= 0) {
+               dev_dbg(&(pcr->pci->dev), "Timeout (%s %d)\n",
+                               __func__, __LINE__);
+               err = -ETIMEDOUT;
+               goto out;
+       }
+
+       spin_lock_irqsave(&pcr->lock, flags);
+
+       if (pcr->trans_result == TRANS_RESULT_FAIL)
+               err = -EINVAL;
+       else if (pcr->trans_result == TRANS_NO_DEVICE)
+               err = -ENODEV;
+
+       spin_unlock_irqrestore(&pcr->lock, flags);
+
+out:
+       spin_lock_irqsave(&pcr->lock, flags);
+       pcr->done = NULL;
+       spin_unlock_irqrestore(&pcr->lock, flags);
+
+       dma_unmap_sg(&(pcr->pci->dev), sglist, num_sg, dma_dir);
+
+       if ((err < 0) && (err != -ENODEV))
+               rtsx_pci_stop_cmd(pcr);
+
+       if (pcr->finish_me)
+               complete(pcr->finish_me);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_transfer_data);
+
+int rtsx_pci_read_ppbuf(struct rtsx_pcr *pcr, u8 *buf, int buf_len)
+{
+       int err;
+       int i, j;
+       u16 reg;
+       u8 *ptr;
+
+       if (buf_len > 512)
+               buf_len = 512;
+
+       ptr = buf;
+       reg = PPBUF_BASE2;
+       for (i = 0; i < buf_len / 256; i++) {
+               rtsx_pci_init_cmd(pcr);
+
+               for (j = 0; j < 256; j++)
+                       rtsx_pci_add_cmd(pcr, READ_REG_CMD, reg++, 0, 0);
+
+               err = rtsx_pci_send_cmd(pcr, 250);
+               if (err < 0)
+                       return err;
+
+               memcpy(ptr, rtsx_pci_get_cmd_data(pcr), 256);
+               ptr += 256;
+       }
+
+       if (buf_len % 256) {
+               rtsx_pci_init_cmd(pcr);
+
+               for (j = 0; j < buf_len % 256; j++)
+                       rtsx_pci_add_cmd(pcr, READ_REG_CMD, reg++, 0, 0);
+
+               err = rtsx_pci_send_cmd(pcr, 250);
+               if (err < 0)
+                       return err;
+       }
+
+       memcpy(ptr, rtsx_pci_get_cmd_data(pcr), buf_len % 256);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_read_ppbuf);
+
+int rtsx_pci_write_ppbuf(struct rtsx_pcr *pcr, u8 *buf, int buf_len)
+{
+       int err;
+       int i, j;
+       u16 reg;
+       u8 *ptr;
+
+       if (buf_len > 512)
+               buf_len = 512;
+
+       ptr = buf;
+       reg = PPBUF_BASE2;
+       for (i = 0; i < buf_len / 256; i++) {
+               rtsx_pci_init_cmd(pcr);
+
+               for (j = 0; j < 256; j++) {
+                       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
+                                       reg++, 0xFF, *ptr);
+                       ptr++;
+               }
+
+               err = rtsx_pci_send_cmd(pcr, 250);
+               if (err < 0)
+                       return err;
+       }
+
+       if (buf_len % 256) {
+               rtsx_pci_init_cmd(pcr);
+
+               for (j = 0; j < buf_len % 256; j++) {
+                       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
+                                       reg++, 0xFF, *ptr);
+                       ptr++;
+               }
+
+               err = rtsx_pci_send_cmd(pcr, 250);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_write_ppbuf);
+
+static int rtsx_pci_set_pull_ctl(struct rtsx_pcr *pcr, const u32 *tbl)
+{
+       int err;
+
+       rtsx_pci_init_cmd(pcr);
+
+       while (*tbl & 0xFFFF0000) {
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
+                               (u16)(*tbl >> 16), 0xFF, (u8)(*tbl));
+               tbl++;
+       }
+
+       err = rtsx_pci_send_cmd(pcr, 100);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+int rtsx_pci_card_pull_ctl_enable(struct rtsx_pcr *pcr, int card)
+{
+       const u32 *tbl;
+
+       if (card == RTSX_SD_CARD)
+               tbl = pcr->sd_pull_ctl_enable_tbl;
+       else if (card == RTSX_MS_CARD)
+               tbl = pcr->ms_pull_ctl_enable_tbl;
+       else
+               return -EINVAL;
+
+       return rtsx_pci_set_pull_ctl(pcr, tbl);
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_card_pull_ctl_enable);
+
+int rtsx_pci_card_pull_ctl_disable(struct rtsx_pcr *pcr, int card)
+{
+       const u32 *tbl;
+
+       if (card == RTSX_SD_CARD)
+               tbl = pcr->sd_pull_ctl_disable_tbl;
+       else if (card == RTSX_MS_CARD)
+               tbl = pcr->ms_pull_ctl_disable_tbl;
+       else
+               return -EINVAL;
+
+
+       return rtsx_pci_set_pull_ctl(pcr, tbl);
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_card_pull_ctl_disable);
+
+static void rtsx_pci_enable_bus_int(struct rtsx_pcr *pcr)
+{
+       pcr->bier = TRANS_OK_INT_EN | TRANS_FAIL_INT_EN | SD_INT_EN;
+
+       if (pcr->num_slots > 1)
+               pcr->bier |= MS_INT_EN;
+
+       /* Enable Bus Interrupt */
+       rtsx_pci_writel(pcr, RTSX_BIER, pcr->bier);
+
+       dev_dbg(&(pcr->pci->dev), "RTSX_BIER: 0x%08x\n", pcr->bier);
+}
+
+static inline u8 double_ssc_depth(u8 depth)
+{
+       return ((depth > 1) ? (depth - 1) : depth);
+}
+
+static u8 revise_ssc_depth(u8 ssc_depth, u8 div)
+{
+       if (div > CLK_DIV_1) {
+               if (ssc_depth > (div - 1))
+                       ssc_depth -= (div - 1);
+               else
+                       ssc_depth = SSC_DEPTH_4M;
+       }
+
+       return ssc_depth;
+}
+
+int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock,
+               u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk)
+{
+       int err, clk;
+       u8 N, min_N, max_N, clk_divider;
+       u8 mcu_cnt, div, max_div;
+       u8 depth[] = {
+               [RTSX_SSC_DEPTH_4M] = SSC_DEPTH_4M,
+               [RTSX_SSC_DEPTH_2M] = SSC_DEPTH_2M,
+               [RTSX_SSC_DEPTH_1M] = SSC_DEPTH_1M,
+               [RTSX_SSC_DEPTH_500K] = SSC_DEPTH_500K,
+               [RTSX_SSC_DEPTH_250K] = SSC_DEPTH_250K,
+       };
+
+       if (initial_mode) {
+               /* We use 250k(around) here, in initial stage */
+               clk_divider = SD_CLK_DIVIDE_128;
+               card_clock = 30000000;
+       } else {
+               clk_divider = SD_CLK_DIVIDE_0;
+       }
+       err = rtsx_pci_write_register(pcr, SD_CFG1,
+                       SD_CLK_DIVIDE_MASK, clk_divider);
+       if (err < 0)
+               return err;
+
+       card_clock /= 1000000;
+       dev_dbg(&(pcr->pci->dev), "Switch card clock to %dMHz\n", card_clock);
+
+       min_N = 80;
+       max_N = 208;
+       max_div = CLK_DIV_8;
+
+       clk = card_clock;
+       if (!initial_mode && double_clk)
+               clk = card_clock * 2;
+       dev_dbg(&(pcr->pci->dev),
+                       "Internal SSC clock: %dMHz (cur_clock = %d)\n",
+                       clk, pcr->cur_clock);
+
+       if (clk == pcr->cur_clock)
+               return 0;
+
+       N = (u8)(clk - 2);
+       if ((clk <= 2) || (N > max_N))
+               return -EINVAL;
+
+       mcu_cnt = (u8)(125/clk + 3);
+       if (mcu_cnt > 15)
+               mcu_cnt = 15;
+
+       /* Make sure that the SSC clock div_n is equal or greater than min_N */
+       div = CLK_DIV_1;
+       while ((N < min_N) && (div < max_div)) {
+               N = (N + 2) * 2 - 2;
+               div++;
+       }
+       dev_dbg(&(pcr->pci->dev), "N = %d, div = %d\n", N, div);
+
+       ssc_depth = depth[ssc_depth];
+       if (double_clk)
+               ssc_depth = double_ssc_depth(ssc_depth);
+
+       ssc_depth = revise_ssc_depth(ssc_depth, div);
+       dev_dbg(&(pcr->pci->dev), "ssc_depth = %d\n", ssc_depth);
+
+       rtsx_pci_init_cmd(pcr);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL,
+                       CLK_LOW_FREQ, CLK_LOW_FREQ);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_DIV,
+                       0xFF, (div << 4) | mcu_cnt);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2,
+                       SSC_DEPTH_MASK, ssc_depth);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, N);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, SSC_RSTB);
+       if (vpclk) {
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL,
+                               PHASE_NOT_RESET, 0);
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL,
+                               PHASE_NOT_RESET, PHASE_NOT_RESET);
+       }
+
+       err = rtsx_pci_send_cmd(pcr, 2000);
+       if (err < 0)
+               return err;
+
+       /* Wait SSC clock stable */
+       udelay(10);
+       err = rtsx_pci_write_register(pcr, CLK_CTL, CLK_LOW_FREQ, 0);
+       if (err < 0)
+               return err;
+
+       pcr->cur_clock = clk;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_switch_clock);
+
+int rtsx_pci_card_power_on(struct rtsx_pcr *pcr, int card)
+{
+       if (pcr->ops->card_power_on)
+               return pcr->ops->card_power_on(pcr, card);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_card_power_on);
+
+int rtsx_pci_card_power_off(struct rtsx_pcr *pcr, int card)
+{
+       if (pcr->ops->card_power_off)
+               return pcr->ops->card_power_off(pcr, card);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_card_power_off);
+
+unsigned int rtsx_pci_card_exist(struct rtsx_pcr *pcr)
+{
+       unsigned int val;
+
+       val = rtsx_pci_readl(pcr, RTSX_BIPR);
+       if (pcr->ops->cd_deglitch)
+               val = pcr->ops->cd_deglitch(pcr);
+
+       return val;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_card_exist);
+
+void rtsx_pci_complete_unfinished_transfer(struct rtsx_pcr *pcr)
+{
+       struct completion finish;
+
+       pcr->finish_me = &finish;
+       init_completion(&finish);
+
+       if (pcr->done)
+               complete(pcr->done);
+
+       if (!pcr->remove_pci)
+               rtsx_pci_stop_cmd(pcr);
+
+       wait_for_completion_interruptible_timeout(&finish,
+                       msecs_to_jiffies(2));
+       pcr->finish_me = NULL;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_complete_unfinished_transfer);
+
+static void rtsx_pci_card_detect(struct work_struct *work)
+{
+       struct delayed_work *dwork;
+       struct rtsx_pcr *pcr;
+       unsigned long flags;
+       unsigned int card_detect = 0;
+       u32 irq_status;
+
+       dwork = to_delayed_work(work);
+       pcr = container_of(dwork, struct rtsx_pcr, carddet_work);
+
+       dev_dbg(&(pcr->pci->dev), "--> %s\n", __func__);
+
+       spin_lock_irqsave(&pcr->lock, flags);
+
+       irq_status = rtsx_pci_readl(pcr, RTSX_BIPR);
+       dev_dbg(&(pcr->pci->dev), "irq_status: 0x%08x\n", irq_status);
+
+       if (pcr->card_inserted || pcr->card_removed) {
+               dev_dbg(&(pcr->pci->dev),
+                               "card_inserted: 0x%x, card_removed: 0x%x\n",
+                               pcr->card_inserted, pcr->card_removed);
+
+               if (pcr->ops->cd_deglitch)
+                       pcr->card_inserted = pcr->ops->cd_deglitch(pcr);
+
+               card_detect = pcr->card_inserted | pcr->card_removed;
+               pcr->card_inserted = 0;
+               pcr->card_removed = 0;
+       }
+
+       spin_unlock_irqrestore(&pcr->lock, flags);
+
+       if (card_detect & SD_EXIST)
+               pcr->slots[RTSX_SD_CARD].card_event(
+                               pcr->slots[RTSX_SD_CARD].p_dev);
+       if (card_detect & MS_EXIST)
+               pcr->slots[RTSX_MS_CARD].card_event(
+                               pcr->slots[RTSX_MS_CARD].p_dev);
+}
+
+static irqreturn_t rtsx_pci_isr(int irq, void *dev_id)
+{
+       struct rtsx_pcr *pcr = dev_id;
+       u32 int_reg;
+
+       if (!pcr)
+               return IRQ_NONE;
+
+       spin_lock(&pcr->lock);
+
+       int_reg = rtsx_pci_readl(pcr, RTSX_BIPR);
+       /* Clear interrupt flag */
+       rtsx_pci_writel(pcr, RTSX_BIPR, int_reg);
+       if ((int_reg & pcr->bier) == 0) {
+               spin_unlock(&pcr->lock);
+               return IRQ_NONE;
+       }
+       if (int_reg == 0xFFFFFFFF) {
+               spin_unlock(&pcr->lock);
+               return IRQ_HANDLED;
+       }
+
+       int_reg &= (pcr->bier | 0x7FFFFF);
+
+       if (int_reg & SD_INT) {
+               if (int_reg & SD_EXIST) {
+                       pcr->card_inserted |= SD_EXIST;
+               } else {
+                       pcr->card_removed |= SD_EXIST;
+                       pcr->card_inserted &= ~SD_EXIST;
+               }
+       }
+
+       if (int_reg & MS_INT) {
+               if (int_reg & MS_EXIST) {
+                       pcr->card_inserted |= MS_EXIST;
+               } else {
+                       pcr->card_removed |= MS_EXIST;
+                       pcr->card_inserted &= ~MS_EXIST;
+               }
+       }
+
+       if (pcr->card_inserted || pcr->card_removed)
+               schedule_delayed_work(&pcr->carddet_work,
+                               msecs_to_jiffies(200));
+
+       if (int_reg & (NEED_COMPLETE_INT | DELINK_INT)) {
+               if (int_reg & (TRANS_FAIL_INT | DELINK_INT)) {
+                       pcr->trans_result = TRANS_RESULT_FAIL;
+                       if (pcr->done)
+                               complete(pcr->done);
+               } else if (int_reg & TRANS_OK_INT) {
+                       pcr->trans_result = TRANS_RESULT_OK;
+                       if (pcr->done)
+                               complete(pcr->done);
+               }
+       }
+
+       spin_unlock(&pcr->lock);
+       return IRQ_HANDLED;
+}
+
+static int rtsx_pci_acquire_irq(struct rtsx_pcr *pcr)
+{
+       dev_info(&(pcr->pci->dev), "%s: pcr->msi_en = %d, pci->irq = %d\n",
+                       __func__, pcr->msi_en, pcr->pci->irq);
+
+       if (request_irq(pcr->pci->irq, rtsx_pci_isr,
+                       pcr->msi_en ? 0 : IRQF_SHARED,
+                       DRV_NAME_RTSX_PCI, pcr)) {
+               dev_err(&(pcr->pci->dev),
+                       "rtsx_sdmmc: unable to grab IRQ %d, disabling device\n",
+                       pcr->pci->irq);
+               return -1;
+       }
+
+       pcr->irq = pcr->pci->irq;
+       pci_intx(pcr->pci, !pcr->msi_en);
+
+       return 0;
+}
+
+static void rtsx_pci_idle_work(struct work_struct *work)
+{
+       struct delayed_work *dwork = to_delayed_work(work);
+       struct rtsx_pcr *pcr = container_of(dwork, struct rtsx_pcr, idle_work);
+
+       dev_dbg(&(pcr->pci->dev), "--> %s\n", __func__);
+
+       mutex_lock(&pcr->pcr_mutex);
+
+       pcr->state = PDEV_STAT_IDLE;
+
+       if (pcr->ops->disable_auto_blink)
+               pcr->ops->disable_auto_blink(pcr);
+       if (pcr->ops->turn_off_led)
+               pcr->ops->turn_off_led(pcr);
+
+       mutex_unlock(&pcr->pcr_mutex);
+}
+
+static int rtsx_pci_init_hw(struct rtsx_pcr *pcr)
+{
+       int err;
+
+       rtsx_pci_writel(pcr, RTSX_HCBAR, pcr->host_cmds_addr);
+
+       rtsx_pci_enable_bus_int(pcr);
+
+       /* Power on SSC */
+       err = rtsx_pci_write_register(pcr, FPDCTL, SSC_POWER_DOWN, 0);
+       if (err < 0)
+               return err;
+
+       /* Wait SSC power stable */
+       udelay(200);
+
+       if (pcr->ops->optimize_phy) {
+               err = pcr->ops->optimize_phy(pcr);
+               if (err < 0)
+                       return err;
+       }
+
+       rtsx_pci_init_cmd(pcr);
+
+       /* Set mcu_cnt to 7 to ensure data can be sampled properly */
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_DIV, 0x07, 0x07);
+
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, HOST_SLEEP_STATE, 0x03, 0x00);
+       /* Disable card clock */
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_EN, 0x1E, 0);
+       /* Reset ASPM state to default value */
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, ASPM_FORCE_CTL, 0x3F, 0);
+       /* Reset delink mode */
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CHANGE_LINK_STATE, 0x0A, 0);
+       /* Card driving select */
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DRIVE_SEL,
+                       0x07, DRIVER_TYPE_D);
+       /* Enable SSC Clock */
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1,
+                       0xFF, SSC_8X_EN | SSC_SEL_4M);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2, 0xFF, 0x12);
+       /* Disable cd_pwr_save */
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CHANGE_LINK_STATE, 0x16, 0x10);
+       /* Clear Link Ready Interrupt */
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, IRQSTAT0,
+                       LINK_RDY_INT, LINK_RDY_INT);
+       /* Enlarge the estimation window of PERST# glitch
+        * to reduce the chance of invalid card interrupt
+        */
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PERST_GLITCH_WIDTH, 0xFF, 0x80);
+       /* Update RC oscillator to 400k
+        * bit[0] F_HIGH: for RC oscillator, Rst_value is 1'b1
+        *                1: 2M  0: 400k
+        */
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, RCCTL, 0x01, 0x00);
+       /* Set interrupt write clear
+        * bit 1: U_elbi_if_rd_clr_en
+        *      1: Enable ELBI interrupt[31:22] & [7:0] flag read clear
+        *      0: ELBI interrupt flag[31:22] & [7:0] only can be write clear
+        */
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, NFTS_TX_CTRL, 0x02, 0);
+       /* Force CLKREQ# PIN to drive 0 to request clock */
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, 0x08, 0x08);
+
+       err = rtsx_pci_send_cmd(pcr, 100);
+       if (err < 0)
+               return err;
+
+       /* Enable clk_request_n to enable clock power management */
+       rtsx_pci_write_config_byte(pcr, 0x81, 1);
+       /* Enter L1 when host tx idle */
+       rtsx_pci_write_config_byte(pcr, 0x70F, 0x5B);
+
+       if (pcr->ops->extra_init_hw) {
+               err = pcr->ops->extra_init_hw(pcr);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+static int rtsx_pci_init_chip(struct rtsx_pcr *pcr)
+{
+       int err;
+
+       spin_lock_init(&pcr->lock);
+       mutex_init(&pcr->pcr_mutex);
+
+       switch (PCI_PID(pcr)) {
+       default:
+       case 0x5209:
+               rts5209_init_params(pcr);
+               break;
+
+       case 0x5229:
+               rts5229_init_params(pcr);
+               break;
+
+       case 0x5289:
+               rtl8411_init_params(pcr);
+               break;
+       }
+
+       dev_dbg(&(pcr->pci->dev), "PID: 0x%04x, IC version: 0x%02x\n",
+                       PCI_PID(pcr), pcr->ic_version);
+
+       pcr->slots = kcalloc(pcr->num_slots, sizeof(struct rtsx_slot),
+                       GFP_KERNEL);
+       if (!pcr->slots)
+               return -ENOMEM;
+
+       pcr->state = PDEV_STAT_IDLE;
+       err = rtsx_pci_init_hw(pcr);
+       if (err < 0) {
+               kfree(pcr->slots);
+               return err;
+       }
+
+       return 0;
+}
+
+static int __devinit rtsx_pci_probe(struct pci_dev *pcidev,
+                                   const struct pci_device_id *id)
+{
+       struct rtsx_pcr *pcr;
+       struct pcr_handle *handle;
+       u32 base, len;
+       int ret, i;
+
+       dev_dbg(&(pcidev->dev),
+               ": Realtek PCI-E Card Reader found at %s [%04x:%04x] (rev %x)\n",
+               pci_name(pcidev), (int)pcidev->vendor, (int)pcidev->device,
+               (int)pcidev->revision);
+
+       ret = pci_enable_device(pcidev);
+       if (ret)
+               return ret;
+
+       ret = pci_request_regions(pcidev, DRV_NAME_RTSX_PCI);
+       if (ret)
+               goto disable;
+
+       pcr = kzalloc(sizeof(*pcr), GFP_KERNEL);
+       if (!pcr) {
+               ret = -ENOMEM;
+               goto release_pci;
+       }
+
+       handle = kzalloc(sizeof(*handle), GFP_KERNEL);
+       if (!handle) {
+               ret = -ENOMEM;
+               goto free_pcr;
+       }
+       handle->pcr = pcr;
+
+       if (!idr_pre_get(&rtsx_pci_idr, GFP_KERNEL)) {
+               ret = -ENOMEM;
+               goto free_handle;
+       }
+
+       spin_lock(&rtsx_pci_lock);
+       ret = idr_get_new(&rtsx_pci_idr, pcr, &pcr->id);
+       spin_unlock(&rtsx_pci_lock);
+       if (ret)
+               goto free_handle;
+
+       pcr->pci = pcidev;
+       dev_set_drvdata(&pcidev->dev, handle);
+
+       len = pci_resource_len(pcidev, 0);
+       base = pci_resource_start(pcidev, 0);
+       pcr->remap_addr = ioremap_nocache(base, len);
+       if (!pcr->remap_addr) {
+               ret = -ENOMEM;
+               goto free_host;
+       }
+
+       pcr->rtsx_resv_buf = dma_alloc_coherent(&(pcidev->dev),
+                       RTSX_RESV_BUF_LEN, &(pcr->rtsx_resv_buf_addr),
+                       GFP_KERNEL);
+       if (pcr->rtsx_resv_buf == NULL) {
+               ret = -ENXIO;
+               goto unmap;
+       }
+       pcr->host_cmds_ptr = pcr->rtsx_resv_buf;
+       pcr->host_cmds_addr = pcr->rtsx_resv_buf_addr;
+       pcr->host_sg_tbl_ptr = pcr->rtsx_resv_buf + HOST_CMDS_BUF_LEN;
+       pcr->host_sg_tbl_addr = pcr->rtsx_resv_buf_addr + HOST_CMDS_BUF_LEN;
+
+       pcr->card_inserted = 0;
+       pcr->card_removed = 0;
+       INIT_DELAYED_WORK(&pcr->carddet_work, rtsx_pci_card_detect);
+       INIT_DELAYED_WORK(&pcr->idle_work, rtsx_pci_idle_work);
+
+       pcr->msi_en = msi_en;
+       if (pcr->msi_en) {
+               ret = pci_enable_msi(pcidev);
+               if (ret < 0)
+                       pcr->msi_en = false;
+       }
+
+       ret = rtsx_pci_acquire_irq(pcr);
+       if (ret < 0)
+               goto free_dma;
+
+       pci_set_master(pcidev);
+       synchronize_irq(pcr->irq);
+
+       ret = rtsx_pci_init_chip(pcr);
+       if (ret < 0)
+               goto disable_irq;
+
+       for (i = 0; i < ARRAY_SIZE(rtsx_pcr_cells); i++) {
+               rtsx_pcr_cells[i].platform_data = handle;
+               rtsx_pcr_cells[i].pdata_size = sizeof(*handle);
+       }
+       ret = mfd_add_devices(&pcidev->dev, pcr->id, rtsx_pcr_cells,
+                       ARRAY_SIZE(rtsx_pcr_cells), NULL, 0, NULL);
+       if (ret < 0)
+               goto disable_irq;
+
+       schedule_delayed_work(&pcr->idle_work, msecs_to_jiffies(200));
+
+       return 0;
+
+disable_irq:
+       free_irq(pcr->irq, (void *)pcr);
+free_dma:
+       dma_free_coherent(&(pcr->pci->dev), RTSX_RESV_BUF_LEN,
+                       pcr->rtsx_resv_buf, pcr->rtsx_resv_buf_addr);
+unmap:
+       iounmap(pcr->remap_addr);
+free_host:
+       dev_set_drvdata(&pcidev->dev, NULL);
+free_handle:
+       kfree(handle);
+free_pcr:
+       kfree(pcr);
+release_pci:
+       pci_release_regions(pcidev);
+disable:
+       pci_disable_device(pcidev);
+
+       return ret;
+}
+
+static void __devexit rtsx_pci_remove(struct pci_dev *pcidev)
+{
+       struct pcr_handle *handle = pci_get_drvdata(pcidev);
+       struct rtsx_pcr *pcr = handle->pcr;
+
+       pcr->remove_pci = true;
+
+       cancel_delayed_work(&pcr->carddet_work);
+       cancel_delayed_work(&pcr->idle_work);
+
+       mfd_remove_devices(&pcidev->dev);
+
+       dma_free_coherent(&(pcr->pci->dev), RTSX_RESV_BUF_LEN,
+                       pcr->rtsx_resv_buf, pcr->rtsx_resv_buf_addr);
+       free_irq(pcr->irq, (void *)pcr);
+       if (pcr->msi_en)
+               pci_disable_msi(pcr->pci);
+       iounmap(pcr->remap_addr);
+
+       dev_set_drvdata(&pcidev->dev, NULL);
+       pci_release_regions(pcidev);
+       pci_disable_device(pcidev);
+
+       spin_lock(&rtsx_pci_lock);
+       idr_remove(&rtsx_pci_idr, pcr->id);
+       spin_unlock(&rtsx_pci_lock);
+
+       kfree(pcr->slots);
+       kfree(pcr);
+       kfree(handle);
+
+       dev_dbg(&(pcidev->dev),
+               ": Realtek PCI-E Card Reader at %s [%04x:%04x] has been removed\n",
+               pci_name(pcidev), (int)pcidev->vendor, (int)pcidev->device);
+}
+
+#ifdef CONFIG_PM
+
+static int rtsx_pci_suspend(struct pci_dev *pcidev, pm_message_t state)
+{
+       struct pcr_handle *handle;
+       struct rtsx_pcr *pcr;
+       int ret = 0;
+
+       dev_dbg(&(pcidev->dev), "--> %s\n", __func__);
+
+       handle = pci_get_drvdata(pcidev);
+       pcr = handle->pcr;
+
+       cancel_delayed_work(&pcr->carddet_work);
+       cancel_delayed_work(&pcr->idle_work);
+
+       mutex_lock(&pcr->pcr_mutex);
+
+       if (pcr->ops->turn_off_led)
+               pcr->ops->turn_off_led(pcr);
+
+       rtsx_pci_writel(pcr, RTSX_BIER, 0);
+       pcr->bier = 0;
+
+       rtsx_pci_write_register(pcr, PETXCFG, 0x08, 0x08);
+       rtsx_pci_write_register(pcr, HOST_SLEEP_STATE, 0x03, 0x02);
+
+       pci_save_state(pcidev);
+       pci_enable_wake(pcidev, pci_choose_state(pcidev, state), 0);
+       pci_disable_device(pcidev);
+       pci_set_power_state(pcidev, pci_choose_state(pcidev, state));
+
+       mutex_unlock(&pcr->pcr_mutex);
+       return ret;
+}
+
+static int rtsx_pci_resume(struct pci_dev *pcidev)
+{
+       struct pcr_handle *handle;
+       struct rtsx_pcr *pcr;
+       int ret = 0;
+
+       dev_dbg(&(pcidev->dev), "--> %s\n", __func__);
+
+       handle = pci_get_drvdata(pcidev);
+       pcr = handle->pcr;
+
+       mutex_lock(&pcr->pcr_mutex);
+
+       pci_set_power_state(pcidev, PCI_D0);
+       pci_restore_state(pcidev);
+       ret = pci_enable_device(pcidev);
+       if (ret)
+               goto out;
+       pci_set_master(pcidev);
+
+       ret = rtsx_pci_write_register(pcr, HOST_SLEEP_STATE, 0x03, 0x00);
+       if (ret)
+               goto out;
+
+       ret = rtsx_pci_init_hw(pcr);
+       if (ret)
+               goto out;
+
+       schedule_delayed_work(&pcr->idle_work, msecs_to_jiffies(200));
+
+out:
+       mutex_unlock(&pcr->pcr_mutex);
+       return ret;
+}
+
+#else /* CONFIG_PM */
+
+#define rtsx_pci_suspend NULL
+#define rtsx_pci_resume NULL
+
+#endif /* CONFIG_PM */
+
+static struct pci_driver rtsx_pci_driver = {
+       .name = DRV_NAME_RTSX_PCI,
+       .id_table = rtsx_pci_ids,
+       .probe = rtsx_pci_probe,
+       .remove = __devexit_p(rtsx_pci_remove),
+       .suspend = rtsx_pci_suspend,
+       .resume = rtsx_pci_resume,
+};
+module_pci_driver(rtsx_pci_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Wei WANG <wei_wang@realsil.com.cn>");
+MODULE_DESCRIPTION("Realtek PCI-E Card Reader Driver");
diff --git a/drivers/mfd/rtsx_pcr.h b/drivers/mfd/rtsx_pcr.h
new file mode 100644 (file)
index 0000000..12462c1
--- /dev/null
@@ -0,0 +1,32 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG <wei_wang@realsil.com.cn>
+ *   No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
+ */
+
+#ifndef __RTSX_PCR_H
+#define __RTSX_PCR_H
+
+#include <linux/mfd/rtsx_pci.h>
+
+void rts5209_init_params(struct rtsx_pcr *pcr);
+void rts5229_init_params(struct rtsx_pcr *pcr);
+void rtl8411_init_params(struct rtsx_pcr *pcr);
+
+#endif
index 820826270b62af400eb8528071b5e9db5ce6b272..705b881e186d536ccb2bc97c392d108b3ecf765c 100644 (file)
@@ -51,7 +51,7 @@ static const struct ad_dpot_bus_ops bops = {
        .write_r8d16    = write_r8d16,
 };
 
-static int __devinit ad_dpot_i2c_probe(struct i2c_client *client,
+static int ad_dpot_i2c_probe(struct i2c_client *client,
                                      const struct i2c_device_id *id)
 {
        struct ad_dpot_bus_data bdata = {
@@ -68,7 +68,7 @@ static int __devinit ad_dpot_i2c_probe(struct i2c_client *client,
        return ad_dpot_probe(&client->dev, &bdata, id->driver_data, id->name);
 }
 
-static int __devexit ad_dpot_i2c_remove(struct i2c_client *client)
+static int ad_dpot_i2c_remove(struct i2c_client *client)
 {
        return ad_dpot_remove(&client->dev);
 }
@@ -109,7 +109,7 @@ static struct i2c_driver ad_dpot_i2c_driver = {
                .owner  = THIS_MODULE,
        },
        .probe          = ad_dpot_i2c_probe,
-       .remove         = __devexit_p(ad_dpot_i2c_remove),
+       .remove         = ad_dpot_i2c_remove,
        .id_table       = ad_dpot_id,
 };
 
index f62317540d002c9f2ce943537cb9f131e549ef7d..9da04ede04f356aaef1495bd0cf01408e6556da5 100644 (file)
@@ -75,7 +75,7 @@ static const struct ad_dpot_bus_ops bops = {
        .write_r8d8     = write16,
        .write_r8d16    = write24,
 };
-static int __devinit ad_dpot_spi_probe(struct spi_device *spi)
+static int ad_dpot_spi_probe(struct spi_device *spi)
 {
        struct ad_dpot_bus_data bdata = {
                .client = spi,
@@ -87,7 +87,7 @@ static int __devinit ad_dpot_spi_probe(struct spi_device *spi)
                             spi_get_device_id(spi)->name);
 }
 
-static int __devexit ad_dpot_spi_remove(struct spi_device *spi)
+static int ad_dpot_spi_remove(struct spi_device *spi)
 {
        return ad_dpot_remove(&spi->dev);
 }
@@ -131,7 +131,7 @@ static struct spi_driver ad_dpot_spi_driver = {
                .owner  = THIS_MODULE,
        },
        .probe          = ad_dpot_spi_probe,
-       .remove         = __devexit_p(ad_dpot_spi_remove),
+       .remove         = ad_dpot_spi_remove,
        .id_table       = ad_dpot_spi_id,
 };
 
index 6938f1be664dc8e865df5d50a2616b278dc261a8..8f99e8e3f0ac1b68e189977d2386279ca8dd67c7 100644 (file)
@@ -641,7 +641,7 @@ static const struct attribute_group ad525x_group_commands = {
        .attrs = ad525x_attributes_commands,
 };
 
-__devinit int ad_dpot_add_files(struct device *dev,
+int ad_dpot_add_files(struct device *dev,
                unsigned features, unsigned rdac)
 {
        int err = sysfs_create_file(&dev->kobj,
@@ -685,7 +685,7 @@ inline void ad_dpot_remove_files(struct device *dev,
        }
 }
 
-int __devinit ad_dpot_probe(struct device *dev,
+int ad_dpot_probe(struct device *dev,
                struct ad_dpot_bus_data *bdata, unsigned long devid,
                            const char *name)
 {
index 0314773f6db382692f509471ab49ea27208d40d8..d648b089302761596bb06a844b09e939bb065e87 100644 (file)
@@ -68,7 +68,7 @@ static int als_wait_for_data_ready(struct device *dev)
                ret = i2c_smbus_read_byte_data(client, 0x86);
        } while (!(ret & 0x80) && retry--);
 
-       if (!retry) {
+       if (retry < 0) {
                dev_warn(dev, "timeout waiting for data ready\n");
                return -ETIMEDOUT;
        }
@@ -254,7 +254,7 @@ als_error1:
        return res;
 }
 
-static int __devexit apds9802als_remove(struct i2c_client *client)
+static int apds9802als_remove(struct i2c_client *client)
 {
        struct als_data *data = i2c_get_clientdata(client);
 
@@ -326,7 +326,7 @@ static struct i2c_driver apds9802als_driver = {
                .pm = APDS9802ALS_PM_OPS,
        },
        .probe = apds9802als_probe,
-       .remove = __devexit_p(apds9802als_remove),
+       .remove = apds9802als_remove,
        .suspend = apds9802als_suspend,
        .resume = apds9802als_resume,
        .id_table = apds9802als_id,
index ee74244aa03b775493f9221172bf02417f1e9a35..0e67f8263cd8bf421c939ae8870f5e4503af2961 100644 (file)
@@ -1047,7 +1047,7 @@ static struct attribute_group apds990x_attribute_group[] = {
        {.attrs = sysfs_attrs_ctrl },
 };
 
-static int __devinit apds990x_probe(struct i2c_client *client,
+static int apds990x_probe(struct i2c_client *client,
                                const struct i2c_device_id *id)
 {
        struct apds990x_chip *chip;
@@ -1181,7 +1181,7 @@ fail1:
        return err;
 }
 
-static int __devexit apds990x_remove(struct i2c_client *client)
+static int apds990x_remove(struct i2c_client *client)
 {
        struct apds990x_chip *chip = i2c_get_clientdata(client);
 
@@ -1275,7 +1275,7 @@ static struct i2c_driver apds990x_driver = {
                .pm     = &apds990x_pm_ops,
        },
        .probe    = apds990x_probe,
-       .remove   = __devexit_p(apds990x_remove),
+       .remove   = apds990x_remove,
        .id_table = apds990x_id,
 };
 
index 5bb1877810749da2581aff03c6d7d50c18b875c3..c58f9abcb35659f58ee9fbdbaad5c387d366ad06 100644 (file)
@@ -137,7 +137,7 @@ out:
        return retval;
 }
 
-static int __devexit ssc_remove(struct platform_device *pdev)
+static int ssc_remove(struct platform_device *pdev)
 {
        struct ssc_device *ssc = platform_get_drvdata(pdev);
 
@@ -152,7 +152,7 @@ static int __devexit ssc_remove(struct platform_device *pdev)
 }
 
 static struct platform_driver ssc_driver = {
-       .remove         = __devexit_p(ssc_remove),
+       .remove         = ssc_remove,
        .driver         = {
                .name           = "ssc",
                .owner          = THIS_MODULE,
index 3d56ae7ef8def5c507bbd2eb95241d32e2e10832..2ed8fc3be7e66e5e6b4972560c223ba48542b337 100644 (file)
@@ -1162,7 +1162,7 @@ static struct attribute_group bh1770_attribute_group = {
        .attrs = sysfs_attrs
 };
 
-static int __devinit bh1770_probe(struct i2c_client *client,
+static int bh1770_probe(struct i2c_client *client,
                                const struct i2c_device_id *id)
 {
        struct bh1770_chip *chip;
@@ -1285,7 +1285,7 @@ fail1:
        return err;
 }
 
-static int __devexit bh1770_remove(struct i2c_client *client)
+static int bh1770_remove(struct i2c_client *client)
 {
        struct bh1770_chip *chip = i2c_get_clientdata(client);
 
@@ -1395,7 +1395,7 @@ static struct i2c_driver bh1770_driver = {
                .pm     = &bh1770_pm_ops,
        },
        .probe    = bh1770_probe,
-       .remove   = __devexit_p(bh1770_remove),
+       .remove   = bh1770_remove,
        .id_table = bh1770_id,
 };
 
index f1f9877f3fdf5e9ea3ca41e41eeb8d98a9b96e47..cf03d0abf33ef346f8b9d84143f6a07818e304c3 100644 (file)
@@ -144,7 +144,7 @@ static const struct attribute_group bh1780_attr_group = {
        .attrs = bh1780_attributes,
 };
 
-static int __devinit bh1780_probe(struct i2c_client *client,
+static int bh1780_probe(struct i2c_client *client,
                                                const struct i2c_device_id *id)
 {
        int ret;
@@ -185,7 +185,7 @@ err_op_failed:
        return ret;
 }
 
-static int __devexit bh1780_remove(struct i2c_client *client)
+static int bh1780_remove(struct i2c_client *client)
 {
        struct bh1780_data *ddata;
 
@@ -248,7 +248,7 @@ static const struct i2c_device_id bh1780_id[] = {
 
 static struct i2c_driver bh1780_driver = {
        .probe          = bh1780_probe,
-       .remove         = __devexit_p(bh1780_remove),
+       .remove         = bh1780_remove,
        .id_table       = bh1780_id,
        .driver = {
                .name = "bh1780",
index a4f33c995ea17c6eabe257ae92c4fc950e49f0a5..3abfcecf84240b57b32f7253d1742dec178561d1 100644 (file)
@@ -36,7 +36,7 @@ static int bmp085_i2c_detect(struct i2c_client *client,
        return bmp085_detect(&client->dev);
 }
 
-static int __devinit bmp085_i2c_probe(struct i2c_client *client,
+static int bmp085_i2c_probe(struct i2c_client *client,
                                      const struct i2c_device_id *id)
 {
        int err;
@@ -71,7 +71,7 @@ static struct i2c_driver bmp085_i2c_driver = {
        },
        .id_table       = bmp085_id,
        .probe          = bmp085_i2c_probe,
-       .remove         = __devexit_p(bmp085_i2c_remove),
+       .remove         = bmp085_i2c_remove,
 
        .detect         = bmp085_i2c_detect,
        .address_list   = normal_i2c
index 5e982af9973054e8b4fb94524c303552db18c555..d6a52659cf24a7d15642545c7fab79915775fcff 100644 (file)
@@ -22,7 +22,7 @@
 #include <linux/err.h>
 #include "bmp085.h"
 
-static int __devinit bmp085_spi_probe(struct spi_device *client)
+static int bmp085_spi_probe(struct spi_device *client)
 {
        int err;
        struct regmap *regmap;
@@ -70,7 +70,7 @@ static struct spi_driver bmp085_spi_driver = {
        },
        .id_table       = bmp085_id,
        .probe          = bmp085_spi_probe,
-       .remove         = __devexit_p(bmp085_spi_remove)
+       .remove         = bmp085_spi_remove
 };
 
 module_spi_driver(bmp085_spi_driver);
index 62e418293b7e8a1ddccbf2c6330b63458608e76e..849e2fed4da2841310ee9a5cd80430cb29f6d950 100644 (file)
@@ -420,7 +420,7 @@ struct regmap_config bmp085_regmap_config = {
 };
 EXPORT_SYMBOL_GPL(bmp085_regmap_config);
 
-__devinit int bmp085_probe(struct device *dev, struct regmap *regmap)
+int bmp085_probe(struct device *dev, struct regmap *regmap)
 {
        struct bmp085_data *data;
        int err = 0;
index 9d5eed754666d907b566a7e44264c118c3402dd7..2e50f811ff599205aade6f1b877bc7877e3917bb 100644 (file)
@@ -30,7 +30,7 @@ void cb710_pci_update_config_reg(struct pci_dev *pdev,
 EXPORT_SYMBOL_GPL(cb710_pci_update_config_reg);
 
 /* Some magic writes based on Windows driver init code */
-static int __devinit cb710_pci_configure(struct pci_dev *pdev)
+static int cb710_pci_configure(struct pci_dev *pdev)
 {
        unsigned int devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0);
        struct pci_dev *pdev0;
@@ -96,7 +96,7 @@ static void cb710_release_slot(struct device *dev)
 #endif
 }
 
-static int __devinit cb710_register_slot(struct cb710_chip *chip,
+static int cb710_register_slot(struct cb710_chip *chip,
        unsigned slot_mask, unsigned io_offset, const char *name)
 {
        int nr = chip->slots;
@@ -201,7 +201,7 @@ static int cb710_resume(struct pci_dev *pdev)
 
 #endif /* CONFIG_PM */
 
-static int __devinit cb710_probe(struct pci_dev *pdev,
+static int cb710_probe(struct pci_dev *pdev,
        const struct pci_device_id *ent)
 {
        struct cb710_chip *chip;
@@ -305,7 +305,7 @@ unreg_mmc:
        return err;
 }
 
-static void __devexit cb710_remove_one(struct pci_dev *pdev)
+static void cb710_remove_one(struct pci_dev *pdev)
 {
        struct cb710_chip *chip = pci_get_drvdata(pdev);
        unsigned long flags;
@@ -332,7 +332,7 @@ static struct pci_driver cb710_driver = {
        .name = KBUILD_MODNAME,
        .id_table = cb710_pci_tbl,
        .probe = cb710_probe,
-       .remove = __devexit_p(cb710_remove_one),
+       .remove = cb710_remove_one,
 #ifdef CONFIG_PM
        .suspend = cb710_suspend,
        .resume = cb710_resume,
index f505a40a8f492735793b594b89c6fc1cceddc5f8..9858f36dad8b4fe3cc468b5e090b8e163262ddac 100644 (file)
@@ -246,7 +246,7 @@ EXPORT_SYMBOL_GPL(cs5535_mfgpt_write);
  * Jordan tells me that he and Mitch once played w/ it, but it's unclear
  * what the results of that were (and they experienced some instability).
  */
-static void __devinit reset_all_timers(void)
+static void reset_all_timers(void)
 {
        uint32_t val, dummy;
 
@@ -262,7 +262,7 @@ static void __devinit reset_all_timers(void)
  * In other cases (such as with VSAless OpenFirmware), the system firmware
  * leaves timers available for us to use.
  */
-static int __devinit scan_timers(struct cs5535_mfgpt_chip *mfgpt)
+static int scan_timers(struct cs5535_mfgpt_chip *mfgpt)
 {
        struct cs5535_mfgpt_timer timer = { .chip = mfgpt };
        unsigned long flags;
@@ -289,7 +289,7 @@ static int __devinit scan_timers(struct cs5535_mfgpt_chip *mfgpt)
        return timers;
 }
 
-static int __devinit cs5535_mfgpt_probe(struct platform_device *pdev)
+static int cs5535_mfgpt_probe(struct platform_device *pdev)
 {
        struct resource *res;
        int err = -EIO, t;
index ab1ad41786d118327a5cb7210486454430de8f8c..2baeec56edfe9432494d67814c9a57b4da9b916d 100644 (file)
@@ -656,7 +656,7 @@ err_out:
        return err;
 }
 
-static int __devexit at24_remove(struct i2c_client *client)
+static int at24_remove(struct i2c_client *client)
 {
        struct at24_data *at24;
        int i;
@@ -680,7 +680,7 @@ static struct i2c_driver at24_driver = {
                .owner = THIS_MODULE,
        },
        .probe = at24_probe,
-       .remove = __devexit_p(at24_remove),
+       .remove = at24_remove,
        .id_table = at24_ids,
 };
 
index 4ed93dd541164d2a1763688caab1b351d3a799c9..b08cf8a087891c47f17995598ad205626d588bad 100644 (file)
@@ -459,7 +459,7 @@ fail:
        return err;
 }
 
-static int __devexit at25_remove(struct spi_device *spi)
+static int at25_remove(struct spi_device *spi)
 {
        struct at25_data        *at25;
 
@@ -477,7 +477,7 @@ static struct spi_driver at25_driver = {
                .owner          = THIS_MODULE,
        },
        .probe          = at25_probe,
-       .remove         = __devexit_p(at25_remove),
+       .remove         = at25_remove,
 };
 
 module_spi_driver(at25_driver);
index ce3fe3633dd771c724be75870d6712f16971e96c..a6b5d5e73485cc5a83ee6ae8c8d5257019dc3d2e 100644 (file)
@@ -309,7 +309,7 @@ static ssize_t eeprom_93xx46_store_erase(struct device *dev,
 }
 static DEVICE_ATTR(erase, S_IWUSR, NULL, eeprom_93xx46_store_erase);
 
-static int __devinit eeprom_93xx46_probe(struct spi_device *spi)
+static int eeprom_93xx46_probe(struct spi_device *spi)
 {
        struct eeprom_93xx46_platform_data *pd;
        struct eeprom_93xx46_dev *edev;
@@ -370,7 +370,7 @@ fail:
        return err;
 }
 
-static int __devexit eeprom_93xx46_remove(struct spi_device *spi)
+static int eeprom_93xx46_remove(struct spi_device *spi)
 {
        struct eeprom_93xx46_dev *edev = dev_get_drvdata(&spi->dev);
 
@@ -389,7 +389,7 @@ static struct spi_driver eeprom_93xx46_driver = {
                .owner  = THIS_MODULE,
        },
        .probe          = eeprom_93xx46_probe,
-       .remove         = __devexit_p(eeprom_93xx46_remove),
+       .remove         = eeprom_93xx46_remove,
 };
 
 module_spi_driver(eeprom_93xx46_driver);
index ac96c3a4034acfb6aa98be0e1b4fa5b8f8605bac..e8cbb1c59f4c27456dd726d2bb4f32ef360867e4 100644 (file)
@@ -407,7 +407,7 @@ static int fsa9480_irq_init(struct fsa9480_usbsw *usbsw)
        return 0;
 }
 
-static int __devinit fsa9480_probe(struct i2c_client *client,
+static int fsa9480_probe(struct i2c_client *client,
                         const struct i2c_device_id *id)
 {
        struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
@@ -462,7 +462,7 @@ fail1:
        return ret;
 }
 
-static int __devexit fsa9480_remove(struct i2c_client *client)
+static int fsa9480_remove(struct i2c_client *client)
 {
        struct fsa9480_usbsw *usbsw = i2c_get_clientdata(client);
        if (client->irq)
@@ -533,7 +533,7 @@ static struct i2c_driver fsa9480_i2c_driver = {
                .name = "fsa9480",
        },
        .probe = fsa9480_probe,
-       .remove = __devexit_p(fsa9480_remove),
+       .remove = fsa9480_remove,
        .resume = fsa9480_resume,
        .suspend = fsa9480_suspend,
        .id_table = fsa9480_id,
index 12ccdf94e4fa1371889873c0e305669f0f30f7f4..621c7a3733901abc6cf7da5170f1380a3a376ab9 100644 (file)
@@ -30,7 +30,7 @@
 
 static struct class *ilo_class;
 static unsigned int ilo_major;
-static unsigned int max_ccb = MIN_CCB;
+static unsigned int max_ccb = 16;
 static char ilo_hwdev[MAX_ILO_DEV];
 
 static inline int get_entry_id(int entry)
@@ -686,7 +686,7 @@ static void ilo_unmap_device(struct pci_dev *pdev, struct ilo_hwinfo *hw)
        pci_iounmap(pdev, hw->mmio_vaddr);
 }
 
-static int __devinit ilo_map_device(struct pci_dev *pdev, struct ilo_hwinfo *hw)
+static int ilo_map_device(struct pci_dev *pdev, struct ilo_hwinfo *hw)
 {
        int error = -ENOMEM;
 
@@ -725,6 +725,9 @@ static void ilo_remove(struct pci_dev *pdev)
        int i, minor;
        struct ilo_hwinfo *ilo_hw = pci_get_drvdata(pdev);
 
+       if (!ilo_hw)
+               return;
+
        clear_device(ilo_hw);
 
        minor = MINOR(ilo_hw->cdev.dev);
@@ -748,12 +751,16 @@ static void ilo_remove(struct pci_dev *pdev)
        ilo_hwdev[(minor / max_ccb)] = 0;
 }
 
-static int __devinit ilo_probe(struct pci_dev *pdev,
+static int ilo_probe(struct pci_dev *pdev,
                               const struct pci_device_id *ent)
 {
-       int devnum, minor, start, error;
+       int devnum, minor, start, error = 0;
        struct ilo_hwinfo *ilo_hw;
 
+       /* Ignore subsystem_device = 0x1979 (set by BIOS)  */
+       if (pdev->subsystem_device == 0x1979)
+               goto out;
+
        if (max_ccb > MAX_CCB)
                max_ccb = MAX_CCB;
        else if (max_ccb < MIN_CCB)
@@ -852,7 +859,7 @@ static struct pci_driver ilo_driver = {
        .name     = ILO_NAME,
        .id_table = ilo_devices,
        .probe    = ilo_probe,
-       .remove   = __devexit_p(ilo_remove),
+       .remove   = ilo_remove,
 };
 
 static int __init ilo_init(void)
@@ -892,14 +899,14 @@ static void __exit ilo_exit(void)
        class_destroy(ilo_class);
 }
 
-MODULE_VERSION("1.3");
+MODULE_VERSION("1.4");
 MODULE_ALIAS(ILO_NAME);
 MODULE_DESCRIPTION(ILO_NAME);
 MODULE_AUTHOR("David Altobelli <david.altobelli@hp.com>");
 MODULE_LICENSE("GPL v2");
 
 module_param(max_ccb, uint, 0444);
-MODULE_PARM_DESC(max_ccb, "Maximum number of HP iLO channels to attach (8)");
+MODULE_PARM_DESC(max_ccb, "Maximum number of HP iLO channels to attach (16)");
 
 module_init(ilo_init);
 module_exit(ilo_exit);
index 168d8008f460ce3bcea322915e214989f15e22bb..0346d87c5fed9f20fffa622269b4102bbdeb516f 100644 (file)
@@ -62,7 +62,7 @@ module_param(ibmasm_debug, int , S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(ibmasm_debug, " Set debug mode on or off");
 
 
-static int __devinit ibmasm_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
+static int ibmasm_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
 {
        int result;
        struct service_processor *sp;
@@ -163,7 +163,7 @@ error_resources:
        return result;
 }
 
-static void __devexit ibmasm_remove_one(struct pci_dev *pdev)
+static void ibmasm_remove_one(struct pci_dev *pdev)
 {
        struct service_processor *sp = (struct service_processor *)pci_get_drvdata(pdev);
 
@@ -198,7 +198,7 @@ static struct pci_driver ibmasm_driver = {
        .name           = DRIVER_NAME,
        .id_table       = ibmasm_pci_table,
        .probe          = ibmasm_init_one,
-       .remove         = __devexit_p(ibmasm_remove_one),
+       .remove         = ibmasm_remove_one,
 };
 
 static void __exit ibmasm_exit (void)
index 6a7710603a904dcb2522738c18ab8e62d789324c..06f6ad29ceff294a67cb34c10f88b2a1f1e6e78a 100644 (file)
@@ -139,7 +139,7 @@ ioc4_unregister_submodule(struct ioc4_submodule *is)
  * even though the following code utilizes external interrupt registers
  * to perform the speed calculation.
  */
-static void __devinit
+static void
 ioc4_clock_calibrate(struct ioc4_driver_data *idd)
 {
        union ioc4_int_out int_out;
@@ -231,7 +231,7 @@ ioc4_clock_calibrate(struct ioc4_driver_data *idd)
  * on the same PCI bus at slot number 3 to differentiate IO9 from IO10.
  * If neither is present, it's a PCI-RT.
  */
-static unsigned int __devinit
+static unsigned int
 ioc4_variant(struct ioc4_driver_data *idd)
 {
        struct pci_dev *pdev = NULL;
@@ -279,7 +279,7 @@ ioc4_load_modules(struct work_struct *work)
 static DECLARE_WORK(ioc4_load_modules_work, ioc4_load_modules);
 
 /* Adds a new instance of an IOC4 card */
-static int __devinit
+static int
 ioc4_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
 {
        struct ioc4_driver_data *idd;
@@ -415,7 +415,7 @@ out:
 }
 
 /* Removes a particular instance of an IOC4 card. */
-static void __devexit
+static void
 ioc4_remove(struct pci_dev *pdev)
 {
        struct ioc4_submodule *is;
@@ -466,7 +466,7 @@ static struct pci_driver ioc4_driver = {
        .name = "IOC4",
        .id_table = ioc4_id_table,
        .probe = ioc4_probe,
-       .remove = __devexit_p(ioc4_remove),
+       .remove = ioc4_remove,
 };
 
 MODULE_DEVICE_TABLE(pci, ioc4_id_table);
index eb5de2e210d742ff1d7902c4fd7c8b2156a4f555..29b306c6bdb3bbf3d5e4c2b225984d9572acdd10 100644 (file)
@@ -365,7 +365,7 @@ static int isl29003_init_client(struct i2c_client *client)
  * I2C layer
  */
 
-static int __devinit isl29003_probe(struct i2c_client *client,
+static int isl29003_probe(struct i2c_client *client,
                                    const struct i2c_device_id *id)
 {
        struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
@@ -401,7 +401,7 @@ exit_kfree:
        return err;
 }
 
-static int __devexit isl29003_remove(struct i2c_client *client)
+static int isl29003_remove(struct i2c_client *client)
 {
        sysfs_remove_group(&client->dev.kobj, &isl29003_attr_group);
        isl29003_set_power_state(client, 0);
@@ -451,7 +451,7 @@ static struct i2c_driver isl29003_driver = {
        .suspend = isl29003_suspend,
        .resume = isl29003_resume,
        .probe  = isl29003_probe,
-       .remove = __devexit_p(isl29003_remove),
+       .remove = isl29003_remove,
        .id_table = isl29003_id,
 };
 
index 60ec8689d6e3dd94403041456de3379246996508..7c97550240f19db127c56b1eb1322f0fe8de803d 100644 (file)
@@ -114,7 +114,7 @@ static struct of_device_id lis3lv02d_i2c_dt_ids[] = {
 MODULE_DEVICE_TABLE(of, lis3lv02d_i2c_dt_ids);
 #endif
 
-static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client,
+static int lis3lv02d_i2c_probe(struct i2c_client *client,
                                        const struct i2c_device_id *id)
 {
        int ret = 0;
@@ -191,7 +191,7 @@ fail:
        return ret;
 }
 
-static int __devexit lis3lv02d_i2c_remove(struct i2c_client *client)
+static int lis3lv02d_i2c_remove(struct i2c_client *client)
 {
        struct lis3lv02d *lis3 = i2c_get_clientdata(client);
        struct lis3lv02d_platform_data *pdata = client->dev.platform_data;
@@ -280,7 +280,7 @@ static struct i2c_driver lis3lv02d_i2c_driver = {
                .of_match_table = of_match_ptr(lis3lv02d_i2c_dt_ids),
        },
        .probe  = lis3lv02d_i2c_probe,
-       .remove = __devexit_p(lis3lv02d_i2c_remove),
+       .remove = lis3lv02d_i2c_remove,
        .id_table = lis3lv02d_id,
 };
 
index ccb6475fa05966eb81888c191ea97a592d3a9c99..9aa2bd2a71ae4fc268f4831d2c9622e890f48b54 100644 (file)
@@ -69,7 +69,7 @@ static struct of_device_id lis302dl_spi_dt_ids[] = {
 MODULE_DEVICE_TABLE(of, lis302dl_spi_dt_ids);
 #endif
 
-static int __devinit lis302dl_spi_probe(struct spi_device *spi)
+static int lis302dl_spi_probe(struct spi_device *spi)
 {
        int ret;
 
@@ -100,7 +100,7 @@ static int __devinit lis302dl_spi_probe(struct spi_device *spi)
        return lis3lv02d_init_device(&lis3_dev);
 }
 
-static int __devexit lis302dl_spi_remove(struct spi_device *spi)
+static int lis302dl_spi_remove(struct spi_device *spi)
 {
        struct lis3lv02d *lis3 = spi_get_drvdata(spi);
        lis3lv02d_joystick_disable(lis3);
@@ -144,7 +144,7 @@ static struct spi_driver lis302dl_spi_driver = {
                .of_match_table = of_match_ptr(lis302dl_spi_dt_ids),
        },
        .probe  = lis302dl_spi_probe,
-       .remove = __devexit_p(lis302dl_spi_remove),
+       .remove = lis302dl_spi_remove,
 };
 
 module_spi_driver(lis302dl_spi_driver);
index 57168db6c7e5b2ea71041486bdf9ec1b31e8b21a..0017842e166c2f0c72d0dd6c86181eba852ed1b3 100644 (file)
@@ -8,4 +8,5 @@ mei-objs += interrupt.o
 mei-objs += interface.o
 mei-objs += iorw.o
 mei-objs += main.o
+mei-objs += amthif.o
 mei-objs += wd.o
diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c
new file mode 100644 (file)
index 0000000..18794ae
--- /dev/null
@@ -0,0 +1,722 @@
+/*
+ *
+ * Intel Management Engine Interface (Intel MEI) Linux driver
+ * Copyright (c) 2003-2012, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/aio.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ioctl.h>
+#include <linux/cdev.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/uuid.h>
+#include <linux/jiffies.h>
+#include <linux/uaccess.h>
+
+
+#include "mei_dev.h"
+#include "hw.h"
+#include <linux/mei.h>
+#include "interface.h"
+
+const uuid_le mei_amthi_guid  = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d, 0xac,
+                                               0xa8, 0x46, 0xe0, 0xff, 0x65,
+                                               0x81, 0x4c);
+
+/**
+ * mei_amthif_reset_params - initializes mei device iamthif
+ *
+ * @dev: the device structure
+ */
+void mei_amthif_reset_params(struct mei_device *dev)
+{
+       /* reset iamthif parameters. */
+       dev->iamthif_current_cb = NULL;
+       dev->iamthif_msg_buf_size = 0;
+       dev->iamthif_msg_buf_index = 0;
+       dev->iamthif_canceled = false;
+       dev->iamthif_ioctl = false;
+       dev->iamthif_state = MEI_IAMTHIF_IDLE;
+       dev->iamthif_timer = 0;
+}
+
+/**
+ * mei_amthif_host_init_ - mei initialization amthif client.
+ *
+ * @dev: the device structure
+ *
+ */
+void mei_amthif_host_init(struct mei_device *dev)
+{
+       int i;
+       unsigned char *msg_buf;
+
+       mei_cl_init(&dev->iamthif_cl, dev);
+       dev->iamthif_cl.state = MEI_FILE_DISCONNECTED;
+
+       /* find ME amthi client */
+       i = mei_me_cl_link(dev, &dev->iamthif_cl,
+                           &mei_amthi_guid, MEI_IAMTHIF_HOST_CLIENT_ID);
+       if (i < 0) {
+               dev_info(&dev->pdev->dev, "failed to find iamthif client.\n");
+               return;
+       }
+
+       /* Assign iamthif_mtu to the value received from ME  */
+
+       dev->iamthif_mtu = dev->me_clients[i].props.max_msg_length;
+       dev_dbg(&dev->pdev->dev, "IAMTHIF_MTU = %d\n",
+                       dev->me_clients[i].props.max_msg_length);
+
+       kfree(dev->iamthif_msg_buf);
+       dev->iamthif_msg_buf = NULL;
+
+       /* allocate storage for ME message buffer */
+       msg_buf = kcalloc(dev->iamthif_mtu,
+                       sizeof(unsigned char), GFP_KERNEL);
+       if (!msg_buf) {
+               dev_dbg(&dev->pdev->dev, "memory allocation for ME message buffer failed.\n");
+               return;
+       }
+
+       dev->iamthif_msg_buf = msg_buf;
+
+       if (mei_connect(dev, &dev->iamthif_cl)) {
+               dev_dbg(&dev->pdev->dev, "Failed to connect to AMTHI client\n");
+               dev->iamthif_cl.state = MEI_FILE_DISCONNECTED;
+               dev->iamthif_cl.host_client_id = 0;
+       } else {
+               dev->iamthif_cl.timer_count = MEI_CONNECT_TIMEOUT;
+       }
+}
+
+/**
+ * mei_amthif_find_read_list_entry - finds a amthilist entry for current file
+ *
+ * @dev: the device structure
+ * @file: pointer to file object
+ *
+ * returns   returned a list entry on success, NULL on failure.
+ */
+struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev,
+                                               struct file *file)
+{
+       struct mei_cl_cb *pos = NULL;
+       struct mei_cl_cb *next = NULL;
+
+       list_for_each_entry_safe(pos, next,
+                               &dev->amthif_rd_complete_list.list, list) {
+               if (pos->cl && pos->cl == &dev->iamthif_cl &&
+                       pos->file_object == file)
+                       return pos;
+       }
+       return NULL;
+}
+
+
+/**
+ * mei_amthif_read - read data from AMTHIF client
+ *
+ * @dev: the device structure
+ * @if_num:  minor number
+ * @file: pointer to file object
+ * @*ubuf: pointer to user data in user space
+ * @length: data length to read
+ * @offset: data read offset
+ *
+ * Locking: called under "dev->device_lock" lock
+ *
+ * returns
+ *  returned data length on success,
+ *  zero if no data to read,
+ *  negative on failure.
+ */
+int mei_amthif_read(struct mei_device *dev, struct file *file,
+              char __user *ubuf, size_t length, loff_t *offset)
+{
+       int rets;
+       int wait_ret;
+       struct mei_cl_cb *cb = NULL;
+       struct mei_cl *cl = file->private_data;
+       unsigned long timeout;
+       int i;
+
+       /* Only Posible if we are in timeout */
+       if (!cl || cl != &dev->iamthif_cl) {
+               dev_dbg(&dev->pdev->dev, "bad file ext.\n");
+               return -ETIMEDOUT;
+       }
+
+       i = mei_me_cl_by_id(dev, dev->iamthif_cl.me_client_id);
+
+       if (i < 0) {
+               dev_dbg(&dev->pdev->dev, "amthi client not found.\n");
+               return -ENODEV;
+       }
+       dev_dbg(&dev->pdev->dev, "checking amthi data\n");
+       cb = mei_amthif_find_read_list_entry(dev, file);
+
+       /* Check for if we can block or not*/
+       if (cb == NULL && file->f_flags & O_NONBLOCK)
+               return -EAGAIN;
+
+
+       dev_dbg(&dev->pdev->dev, "waiting for amthi data\n");
+       while (cb == NULL) {
+               /* unlock the Mutex */
+               mutex_unlock(&dev->device_lock);
+
+               wait_ret = wait_event_interruptible(dev->iamthif_cl.wait,
+                       (cb = mei_amthif_find_read_list_entry(dev, file)));
+
+               if (wait_ret)
+                       return -ERESTARTSYS;
+
+               dev_dbg(&dev->pdev->dev, "woke up from sleep\n");
+
+               /* Locking again the Mutex */
+               mutex_lock(&dev->device_lock);
+       }
+
+
+       dev_dbg(&dev->pdev->dev, "Got amthi data\n");
+       dev->iamthif_timer = 0;
+
+       if (cb) {
+               timeout = cb->read_time +
+                       mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER);
+               dev_dbg(&dev->pdev->dev, "amthi timeout = %lud\n",
+                               timeout);
+
+               if  (time_after(jiffies, timeout)) {
+                       dev_dbg(&dev->pdev->dev, "amthi Time out\n");
+                       /* 15 sec for the message has expired */
+                       list_del(&cb->list);
+                       rets = -ETIMEDOUT;
+                       goto free;
+               }
+       }
+       /* if the whole message will fit remove it from the list */
+       if (cb->buf_idx >= *offset && length >= (cb->buf_idx - *offset))
+               list_del(&cb->list);
+       else if (cb->buf_idx > 0 && cb->buf_idx <= *offset) {
+               /* end of the message has been reached */
+               list_del(&cb->list);
+               rets = 0;
+               goto free;
+       }
+               /* else means that not full buffer will be read and do not
+                * remove message from deletion list
+                */
+
+       dev_dbg(&dev->pdev->dev, "amthi cb->response_buffer size - %d\n",
+           cb->response_buffer.size);
+       dev_dbg(&dev->pdev->dev, "amthi cb->buf_idx - %lu\n", cb->buf_idx);
+
+       /* length is being turncated to PAGE_SIZE, however,
+        * the buf_idx may point beyond */
+       length = min_t(size_t, length, (cb->buf_idx - *offset));
+
+       if (copy_to_user(ubuf, cb->response_buffer.data + *offset, length))
+               rets = -EFAULT;
+       else {
+               rets = length;
+               if ((*offset + length) < cb->buf_idx) {
+                       *offset += length;
+                       goto out;
+               }
+       }
+free:
+       dev_dbg(&dev->pdev->dev, "free amthi cb memory.\n");
+       *offset = 0;
+       mei_io_cb_free(cb);
+out:
+       return rets;
+}
+
+/**
+ * mei_amthif_send_cmd - send amthif command to the ME
+ *
+ * @dev: the device structure
+ * @cb: mei call back struct
+ *
+ * returns 0 on success, <0 on failure.
+ *
+ */
+static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb)
+{
+       struct mei_msg_hdr mei_hdr;
+       int ret;
+
+       if (!dev || !cb)
+               return -ENODEV;
+
+       dev_dbg(&dev->pdev->dev, "write data to amthi client.\n");
+
+       dev->iamthif_state = MEI_IAMTHIF_WRITING;
+       dev->iamthif_current_cb = cb;
+       dev->iamthif_file_object = cb->file_object;
+       dev->iamthif_canceled = false;
+       dev->iamthif_ioctl = true;
+       dev->iamthif_msg_buf_size = cb->request_buffer.size;
+       memcpy(dev->iamthif_msg_buf, cb->request_buffer.data,
+              cb->request_buffer.size);
+
+       ret = mei_flow_ctrl_creds(dev, &dev->iamthif_cl);
+       if (ret < 0)
+               return ret;
+
+       if (ret && dev->mei_host_buffer_is_empty) {
+               ret = 0;
+               dev->mei_host_buffer_is_empty = false;
+               if (cb->request_buffer.size > mei_hbuf_max_data(dev)) {
+                       mei_hdr.length = mei_hbuf_max_data(dev);
+                       mei_hdr.msg_complete = 0;
+               } else {
+                       mei_hdr.length = cb->request_buffer.size;
+                       mei_hdr.msg_complete = 1;
+               }
+
+               mei_hdr.host_addr = dev->iamthif_cl.host_client_id;
+               mei_hdr.me_addr = dev->iamthif_cl.me_client_id;
+               mei_hdr.reserved = 0;
+               dev->iamthif_msg_buf_index += mei_hdr.length;
+               if (mei_write_message(dev, &mei_hdr,
+                                       (unsigned char *)(dev->iamthif_msg_buf),
+                                       mei_hdr.length))
+                       return -ENODEV;
+
+               if (mei_hdr.msg_complete) {
+                       if (mei_flow_ctrl_reduce(dev, &dev->iamthif_cl))
+                               return -ENODEV;
+                       dev->iamthif_flow_control_pending = true;
+                       dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL;
+                       dev_dbg(&dev->pdev->dev, "add amthi cb to write waiting list\n");
+                       dev->iamthif_current_cb = cb;
+                       dev->iamthif_file_object = cb->file_object;
+                       list_add_tail(&cb->list, &dev->write_waiting_list.list);
+               } else {
+                       dev_dbg(&dev->pdev->dev, "message does not complete, so add amthi cb to write list.\n");
+                       list_add_tail(&cb->list, &dev->write_list.list);
+               }
+       } else {
+               if (!(dev->mei_host_buffer_is_empty))
+                       dev_dbg(&dev->pdev->dev, "host buffer is not empty");
+
+               dev_dbg(&dev->pdev->dev, "No flow control credentials, so add iamthif cb to write list.\n");
+               list_add_tail(&cb->list, &dev->write_list.list);
+       }
+       return 0;
+}
+
+/**
+ * mei_amthif_write - write amthif data to amthif client
+ *
+ * @dev: the device structure
+ * @cb: mei call back struct
+ *
+ * returns 0 on success, <0 on failure.
+ *
+ */
+int mei_amthif_write(struct mei_device *dev, struct mei_cl_cb *cb)
+{
+       int ret;
+
+       if (!dev || !cb)
+               return -ENODEV;
+
+       ret = mei_io_cb_alloc_resp_buf(cb, dev->iamthif_mtu);
+       if (ret)
+               return ret;
+
+       cb->fop_type = MEI_FOP_IOCTL;
+
+       if (!list_empty(&dev->amthif_cmd_list.list) ||
+           dev->iamthif_state != MEI_IAMTHIF_IDLE) {
+               dev_dbg(&dev->pdev->dev,
+                       "amthif state = %d\n", dev->iamthif_state);
+               dev_dbg(&dev->pdev->dev, "AMTHIF: add cb to the wait list\n");
+               list_add_tail(&cb->list, &dev->amthif_cmd_list.list);
+               return 0;
+       }
+       return mei_amthif_send_cmd(dev, cb);
+}
+/**
+ * mei_amthif_run_next_cmd
+ *
+ * @dev: the device structure
+ *
+ * returns 0 on success, <0 on failure.
+ */
+void mei_amthif_run_next_cmd(struct mei_device *dev)
+{
+       struct mei_cl_cb *pos = NULL;
+       struct mei_cl_cb *next = NULL;
+       int status;
+
+       if (!dev)
+               return;
+
+       dev->iamthif_msg_buf_size = 0;
+       dev->iamthif_msg_buf_index = 0;
+       dev->iamthif_canceled = false;
+       dev->iamthif_ioctl = true;
+       dev->iamthif_state = MEI_IAMTHIF_IDLE;
+       dev->iamthif_timer = 0;
+       dev->iamthif_file_object = NULL;
+
+       dev_dbg(&dev->pdev->dev, "complete amthi cmd_list cb.\n");
+
+       list_for_each_entry_safe(pos, next, &dev->amthif_cmd_list.list, list) {
+               list_del(&pos->list);
+
+               if (pos->cl && pos->cl == &dev->iamthif_cl) {
+                       status = mei_amthif_send_cmd(dev, pos);
+                       if (status) {
+                               dev_dbg(&dev->pdev->dev,
+                                       "amthi write failed status = %d\n",
+                                               status);
+                               return;
+                       }
+                       break;
+               }
+       }
+}
+
+
+unsigned int mei_amthif_poll(struct mei_device *dev,
+               struct file *file, poll_table *wait)
+{
+       unsigned int mask = 0;
+       mutex_unlock(&dev->device_lock);
+       poll_wait(file, &dev->iamthif_cl.wait, wait);
+       mutex_lock(&dev->device_lock);
+       if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE &&
+               dev->iamthif_file_object == file) {
+               mask |= (POLLIN | POLLRDNORM);
+               dev_dbg(&dev->pdev->dev, "run next amthi cb\n");
+               mei_amthif_run_next_cmd(dev);
+       }
+       return mask;
+}
+
+
+
+/**
+ * mei_amthif_irq_process_completed - processes completed iamthif operation.
+ *
+ * @dev: the device structure.
+ * @slots: free slots.
+ * @cb_pos: callback block.
+ * @cl: private data of the file object.
+ * @cmpl_list: complete list.
+ *
+ * returns 0, OK; otherwise, error.
+ */
+int mei_amthif_irq_write_complete(struct mei_device *dev, s32 *slots,
+                       struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list)
+{
+       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);
+
+       mei_hdr = (struct mei_msg_hdr *)&dev->wr_msg_buf[0];
+       mei_hdr->host_addr = cl->host_client_id;
+       mei_hdr->me_addr = cl->me_client_id;
+       mei_hdr->reserved = 0;
+
+       if (*slots >= msg_slots) {
+               mei_hdr->length = len;
+               mei_hdr->msg_complete = 1;
+       /* Split the message only if we can write the whole host buffer */
+       } else if (*slots == dev->hbuf_depth) {
+               msg_slots = *slots;
+               len = (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
+               mei_hdr->length = len;
+               mei_hdr->msg_complete = 0;
+       } else {
+               /* wait for next time the host buffer is empty */
+               return 0;
+       }
+
+       dev_dbg(&dev->pdev->dev, "msg: len = %d complete = %d\n",
+                       mei_hdr->length, mei_hdr->msg_complete);
+
+       *slots -=  msg_slots;
+       if (mei_write_message(dev, mei_hdr,
+               dev->iamthif_msg_buf + dev->iamthif_msg_buf_index,
+               mei_hdr->length)) {
+                       dev->iamthif_state = MEI_IAMTHIF_IDLE;
+                       cl->status = -ENODEV;
+                       list_del(&cb->list);
+                       return -ENODEV;
+       }
+
+       if (mei_flow_ctrl_reduce(dev, cl))
+               return -ENODEV;
+
+       dev->iamthif_msg_buf_index += mei_hdr->length;
+       cl->status = 0;
+
+       if (mei_hdr->msg_complete) {
+               dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL;
+               dev->iamthif_flow_control_pending = true;
+
+               /* save iamthif cb sent to amthi client */
+               cb->buf_idx = dev->iamthif_msg_buf_index;
+               dev->iamthif_current_cb = cb;
+
+               list_move_tail(&cb->list, &dev->write_waiting_list.list);
+       }
+
+
+       return 0;
+}
+
+/**
+ * mei_amthif_irq_read_message - read routine after ISR to
+ *                     handle the read amthi message
+ *
+ * @complete_list: An instance of our list structure
+ * @dev: the device structure
+ * @mei_hdr: header of amthi message
+ *
+ * returns 0 on success, <0 on failure.
+ */
+int mei_amthif_irq_read_message(struct mei_cl_cb *complete_list,
+               struct mei_device *dev, struct mei_msg_hdr *mei_hdr)
+{
+       struct mei_cl_cb *cb;
+       unsigned char *buffer;
+
+       BUG_ON(mei_hdr->me_addr != dev->iamthif_cl.me_client_id);
+       BUG_ON(dev->iamthif_state != MEI_IAMTHIF_READING);
+
+       buffer = dev->iamthif_msg_buf + dev->iamthif_msg_buf_index;
+       BUG_ON(dev->iamthif_mtu < dev->iamthif_msg_buf_index + mei_hdr->length);
+
+       mei_read_slots(dev, buffer, mei_hdr->length);
+
+       dev->iamthif_msg_buf_index += mei_hdr->length;
+
+       if (!mei_hdr->msg_complete)
+               return 0;
+
+       dev_dbg(&dev->pdev->dev,
+                       "amthi_message_buffer_index =%d\n",
+                       mei_hdr->length);
+
+       dev_dbg(&dev->pdev->dev, "completed amthi read.\n ");
+       if (!dev->iamthif_current_cb)
+               return -ENODEV;
+
+       cb = dev->iamthif_current_cb;
+       dev->iamthif_current_cb = NULL;
+
+       if (!cb->cl)
+               return -ENODEV;
+
+       dev->iamthif_stall_timer = 0;
+       cb->buf_idx = dev->iamthif_msg_buf_index;
+       cb->read_time = jiffies;
+       if (dev->iamthif_ioctl && cb->cl == &dev->iamthif_cl) {
+               /* found the iamthif cb */
+               dev_dbg(&dev->pdev->dev, "complete the amthi read cb.\n ");
+               dev_dbg(&dev->pdev->dev, "add the amthi read cb to complete.\n ");
+               list_add_tail(&cb->list, &complete_list->list);
+       }
+       return 0;
+}
+
+/**
+ * mei_amthif_irq_read - prepares to read amthif data.
+ *
+ * @dev: the device structure.
+ * @slots: free slots.
+ *
+ * returns 0, OK; otherwise, error.
+ */
+int mei_amthif_irq_read(struct mei_device *dev, s32 *slots)
+{
+
+       if (((*slots) * sizeof(u32)) < (sizeof(struct mei_msg_hdr)
+                       + sizeof(struct hbm_flow_control))) {
+               return -EMSGSIZE;
+       }
+       *slots -= mei_data2slots(sizeof(struct hbm_flow_control));
+       if (mei_send_flow_control(dev, &dev->iamthif_cl)) {
+               dev_dbg(&dev->pdev->dev, "iamthif flow control failed\n");
+               return -EIO;
+       }
+
+       dev_dbg(&dev->pdev->dev, "iamthif flow control success\n");
+       dev->iamthif_state = MEI_IAMTHIF_READING;
+       dev->iamthif_flow_control_pending = false;
+       dev->iamthif_msg_buf_index = 0;
+       dev->iamthif_msg_buf_size = 0;
+       dev->iamthif_stall_timer = MEI_IAMTHIF_STALL_TIMER;
+       dev->mei_host_buffer_is_empty = mei_hbuf_is_empty(dev);
+       return 0;
+}
+
+/**
+ * mei_amthif_complete - complete amthif callback.
+ *
+ * @dev: the device structure.
+ * @cb_pos: callback block.
+ */
+void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb)
+{
+       if (dev->iamthif_canceled != 1) {
+               dev->iamthif_state = MEI_IAMTHIF_READ_COMPLETE;
+               dev->iamthif_stall_timer = 0;
+               memcpy(cb->response_buffer.data,
+                               dev->iamthif_msg_buf,
+                               dev->iamthif_msg_buf_index);
+               list_add_tail(&cb->list, &dev->amthif_rd_complete_list.list);
+               dev_dbg(&dev->pdev->dev, "amthi read completed\n");
+               dev->iamthif_timer = jiffies;
+               dev_dbg(&dev->pdev->dev, "dev->iamthif_timer = %ld\n",
+                               dev->iamthif_timer);
+       } else {
+               mei_amthif_run_next_cmd(dev);
+       }
+
+       dev_dbg(&dev->pdev->dev, "completing amthi call back.\n");
+       wake_up_interruptible(&dev->iamthif_cl.wait);
+}
+
+/**
+ * mei_clear_list - removes all callbacks associated with file
+ *             from mei_cb_list
+ *
+ * @dev: device structure.
+ * @file: file structure
+ * @mei_cb_list: callbacks list
+ *
+ * mei_clear_list is called to clear resources associated with file
+ * when application calls close function or Ctrl-C was pressed
+ *
+ * returns true if callback removed from the list, false otherwise
+ */
+static bool mei_clear_list(struct mei_device *dev,
+               const struct file *file, struct list_head *mei_cb_list)
+{
+       struct mei_cl_cb *cb_pos = NULL;
+       struct mei_cl_cb *cb_next = NULL;
+       bool removed = false;
+
+       /* list all list member */
+       list_for_each_entry_safe(cb_pos, cb_next, mei_cb_list, list) {
+               /* check if list member associated with a file */
+               if (file == cb_pos->file_object) {
+                       /* remove member from the list */
+                       list_del(&cb_pos->list);
+                       /* check if cb equal to current iamthif cb */
+                       if (dev->iamthif_current_cb == cb_pos) {
+                               dev->iamthif_current_cb = NULL;
+                               /* send flow control to iamthif client */
+                               mei_send_flow_control(dev, &dev->iamthif_cl);
+                       }
+                       /* free all allocated buffers */
+                       mei_io_cb_free(cb_pos);
+                       cb_pos = NULL;
+                       removed = true;
+               }
+       }
+       return removed;
+}
+
+/**
+ * mei_clear_lists - removes all callbacks associated with file
+ *
+ * @dev: device structure
+ * @file: file structure
+ *
+ * mei_clear_lists is called to clear resources associated with file
+ * when application calls close function or Ctrl-C was pressed
+ *
+ * returns true if callback removed from the list, false otherwise
+ */
+static bool mei_clear_lists(struct mei_device *dev, struct file *file)
+{
+       bool removed = false;
+
+       /* remove callbacks associated with a file */
+       mei_clear_list(dev, file, &dev->amthif_cmd_list.list);
+       if (mei_clear_list(dev, file, &dev->amthif_rd_complete_list.list))
+               removed = true;
+
+       mei_clear_list(dev, file, &dev->ctrl_rd_list.list);
+
+       if (mei_clear_list(dev, file, &dev->ctrl_wr_list.list))
+               removed = true;
+
+       if (mei_clear_list(dev, file, &dev->write_waiting_list.list))
+               removed = true;
+
+       if (mei_clear_list(dev, file, &dev->write_list.list))
+               removed = true;
+
+       /* check if iamthif_current_cb not NULL */
+       if (dev->iamthif_current_cb && !removed) {
+               /* check file and iamthif current cb association */
+               if (dev->iamthif_current_cb->file_object == file) {
+                       /* remove cb */
+                       mei_io_cb_free(dev->iamthif_current_cb);
+                       dev->iamthif_current_cb = NULL;
+                       removed = true;
+               }
+       }
+       return removed;
+}
+
+/**
+* mei_amthif_release - the release function
+*
+*  @inode: pointer to inode structure
+*  @file: pointer to file structure
+*
+*  returns 0 on success, <0 on error
+*/
+int mei_amthif_release(struct mei_device *dev, struct file *file)
+{
+       if (dev->open_handle_count > 0)
+               dev->open_handle_count--;
+
+       if (dev->iamthif_file_object == file &&
+           dev->iamthif_state != MEI_IAMTHIF_IDLE) {
+
+               dev_dbg(&dev->pdev->dev, "amthi canceled iamthif state %d\n",
+                   dev->iamthif_state);
+               dev->iamthif_canceled = true;
+               if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE) {
+                       dev_dbg(&dev->pdev->dev, "run next amthi iamthif cb\n");
+                       mei_amthif_run_next_cmd(dev);
+               }
+       }
+
+       if (mei_clear_lists(dev, file))
+               dev->iamthif_state = MEI_IAMTHIF_IDLE;
+
+       return 0;
+}
index 9700532f02f6802ecb5aedc0465421c4aae80b79..be8ca6b333caaf3688fd9025f1081db77af83ee2 100644 (file)
 #include <linux/uuid.h>
 
 /*
- * Timeouts
+ * Timeouts in Seconds
  */
-#define MEI_INTEROP_TIMEOUT    (HZ * 7)
-#define MEI_CONNECT_TIMEOUT            3       /* at least 2 seconds */
+#define MEI_INTEROP_TIMEOUT         7  /* Timeout on ready message */
+#define MEI_CONNECT_TIMEOUT         3  /* HPS: at least 2 seconds */
 
-#define CONNECT_TIMEOUT        15      /* HPS definition */
-#define INIT_CLIENTS_TIMEOUT   15      /* HPS definition */
+#define MEI_CL_CONNECT_TIMEOUT     15  /* HPS: Client Connect Timeout */
+#define MEI_CLIENTS_INIT_TIMEOUT   15  /* HPS: Clients Enumeration Timeout */
 
-#define IAMTHIF_STALL_TIMER            12      /* seconds */
-#define IAMTHIF_READ_TIMER             10000   /* ms */
+#define MEI_IAMTHIF_STALL_TIMER    12  /* HPS */
+#define MEI_IAMTHIF_READ_TIMER     10  /* HPS */
 
 /*
  * Internal Clients Number
@@ -293,6 +293,14 @@ struct hbm_props_response {
        struct mei_client_properties client_properties;
 } __packed;
 
+/**
+ * struct hbm_client_connect_request - connect/disconnect request
+ *
+ * @hbm_cmd - bus message command header
+ * @me_addr - address of the client in ME
+ * @host_addr - address of the client in the driver
+ * @reserved
+ */
 struct hbm_client_connect_request {
        u8 hbm_cmd;
        u8 me_addr;
@@ -300,6 +308,14 @@ struct hbm_client_connect_request {
        u8 reserved;
 } __packed;
 
+/**
+ * struct hbm_client_connect_response - connect/disconnect response
+ *
+ * @hbm_cmd - bus message command header
+ * @me_addr - address of the client in ME
+ * @host_addr - address of the client in the driver
+ * @status - status of the request
+ */
 struct hbm_client_connect_response {
        u8 hbm_cmd;
        u8 me_addr;
@@ -307,12 +323,6 @@ struct hbm_client_connect_response {
        u8 status;
 } __packed;
 
-struct hbm_client_disconnect_request {
-       u8 hbm_cmd;
-       u8 me_addr;
-       u8 host_addr;
-       u8 reserved[1];
-} __packed;
 
 #define MEI_FC_MESSAGE_RESERVED_LENGTH           5
 
index 98f1430e3e1446b272725bffbf25da609c0aa3e7..a54cd5567ca20bc78a9a823fb63b227a4592264a 100644 (file)
@@ -43,21 +43,6 @@ const char *mei_dev_state_str(int state)
 }
 
 
-const uuid_le mei_amthi_guid  = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d, 0xac,
-                                               0xa8, 0x46, 0xe0, 0xff, 0x65,
-                                               0x81, 0x4c);
-
-/**
- * mei_io_list_init - Sets up a queue list.
- *
- * @list: An instance io list structure
- * @dev: the device structure
- */
-void mei_io_list_init(struct mei_io_list *list)
-{
-       /* initialize our queue list */
-       INIT_LIST_HEAD(&list->mei_cb.cb_list);
-}
 
 /**
  * mei_io_list_flush - removes list entry belonging to cl.
@@ -65,17 +50,15 @@ void mei_io_list_init(struct mei_io_list *list)
  * @list:  An instance of our list structure
  * @cl: private data of the file object
  */
-void mei_io_list_flush(struct mei_io_list *list, struct mei_cl *cl)
+void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl)
 {
        struct mei_cl_cb *pos;
        struct mei_cl_cb *next;
 
-       list_for_each_entry_safe(pos, next, &list->mei_cb.cb_list, cb_list) {
-               if (pos->file_private) {
-                       struct mei_cl *cl_tmp;
-                       cl_tmp = (struct mei_cl *)pos->file_private;
-                       if (mei_cl_cmp_id(cl, cl_tmp))
-                               list_del(&pos->cb_list);
+       list_for_each_entry_safe(pos, next, &list->list, list) {
+               if (pos->cl) {
+                       if (mei_cl_cmp_id(cl, pos->cl))
+                               list_del(&pos->list);
                }
        }
 }
@@ -96,30 +79,13 @@ int mei_cl_flush_queues(struct mei_cl *cl)
        mei_io_list_flush(&cl->dev->write_waiting_list, cl);
        mei_io_list_flush(&cl->dev->ctrl_wr_list, cl);
        mei_io_list_flush(&cl->dev->ctrl_rd_list, cl);
-       mei_io_list_flush(&cl->dev->amthi_cmd_list, cl);
-       mei_io_list_flush(&cl->dev->amthi_read_complete_list, cl);
+       mei_io_list_flush(&cl->dev->amthif_cmd_list, cl);
+       mei_io_list_flush(&cl->dev->amthif_rd_complete_list, cl);
        return 0;
 }
 
 
 
-/**
- * mei_reset_iamthif_params - initializes mei device iamthif
- *
- * @dev: the device structure
- */
-static void mei_reset_iamthif_params(struct mei_device *dev)
-{
-       /* reset iamthif parameters. */
-       dev->iamthif_current_cb = NULL;
-       dev->iamthif_msg_buf_size = 0;
-       dev->iamthif_msg_buf_index = 0;
-       dev->iamthif_canceled = false;
-       dev->iamthif_ioctl = false;
-       dev->iamthif_state = MEI_IAMTHIF_IDLE;
-       dev->iamthif_timer = 0;
-}
-
 /**
  * init_mei_device - allocates and initializes the mei device structure
  *
@@ -144,16 +110,14 @@ struct mei_device *mei_device_init(struct pci_dev *pdev)
        init_waitqueue_head(&dev->wait_stop_wd);
        dev->dev_state = MEI_DEV_INITIALIZING;
        dev->iamthif_state = MEI_IAMTHIF_IDLE;
-       dev->wd_interface_reg = false;
-
 
        mei_io_list_init(&dev->read_list);
        mei_io_list_init(&dev->write_list);
        mei_io_list_init(&dev->write_waiting_list);
        mei_io_list_init(&dev->ctrl_wr_list);
        mei_io_list_init(&dev->ctrl_rd_list);
-       mei_io_list_init(&dev->amthi_cmd_list);
-       mei_io_list_init(&dev->amthi_read_complete_list);
+       mei_io_list_init(&dev->amthif_cmd_list);
+       mei_io_list_init(&dev->amthif_rd_complete_list);
        dev->pdev = pdev;
        return dev;
 }
@@ -196,7 +160,8 @@ int mei_hw_init(struct mei_device *dev)
        if (!dev->recvd_msg) {
                mutex_unlock(&dev->device_lock);
                err = wait_event_interruptible_timeout(dev->wait_recvd_msg,
-                       dev->recvd_msg, MEI_INTEROP_TIMEOUT);
+                       dev->recvd_msg,
+                       mei_secs_to_jiffies(MEI_INTEROP_TIMEOUT));
                mutex_lock(&dev->device_lock);
        }
 
@@ -317,15 +282,13 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
                        cl_pos->timer_count = 0;
                }
                /* remove entry if already in list */
-               dev_dbg(&dev->pdev->dev, "list del iamthif and wd file list.\n");
-               mei_remove_client_from_file_list(dev,
-                               dev->wd_cl.host_client_id);
+               dev_dbg(&dev->pdev->dev, "remove iamthif and wd from the file list.\n");
+               mei_me_cl_unlink(dev, &dev->wd_cl);
 
-               mei_remove_client_from_file_list(dev,
-                               dev->iamthif_cl.host_client_id);
+               mei_me_cl_unlink(dev, &dev->iamthif_cl);
 
-               mei_reset_iamthif_params(dev);
-               dev->extra_write_index = 0;
+               mei_amthif_reset_params(dev);
+               memset(&dev->wr_ext_msg, 0, sizeof(dev->wr_ext_msg));
        }
 
        dev->me_clients_num = 0;
@@ -351,10 +314,9 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
                }
        }
        /* remove all waiting requests */
-       list_for_each_entry_safe(cb_pos, cb_next,
-                       &dev->write_list.mei_cb.cb_list, cb_list) {
-               list_del(&cb_pos->cb_list);
-               mei_free_cb_private(cb_pos);
+       list_for_each_entry_safe(cb_pos, cb_next, &dev->write_list.list, list) {
+               list_del(&cb_pos->list);
+               mei_io_cb_free(cb_pos);
        }
 }
 
@@ -370,31 +332,26 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
 void mei_host_start_message(struct mei_device *dev)
 {
        struct mei_msg_hdr *mei_hdr;
-       struct hbm_host_version_request *host_start_req;
+       struct hbm_host_version_request *start_req;
+       const size_t len = sizeof(struct hbm_host_version_request);
+
+       mei_hdr = mei_hbm_hdr(&dev->wr_msg_buf[0], len);
 
        /* host start message */
-       mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
-       mei_hdr->host_addr = 0;
-       mei_hdr->me_addr = 0;
-       mei_hdr->length = sizeof(struct hbm_host_version_request);
-       mei_hdr->msg_complete = 1;
-       mei_hdr->reserved = 0;
-
-       host_start_req =
-           (struct hbm_host_version_request *) &dev->wr_msg_buf[1];
-       memset(host_start_req, 0, sizeof(struct hbm_host_version_request));
-       host_start_req->hbm_cmd = HOST_START_REQ_CMD;
-       host_start_req->host_version.major_version = HBM_MAJOR_VERSION;
-       host_start_req->host_version.minor_version = HBM_MINOR_VERSION;
+       start_req = (struct hbm_host_version_request *)&dev->wr_msg_buf[1];
+       memset(start_req, 0, len);
+       start_req->hbm_cmd = HOST_START_REQ_CMD;
+       start_req->host_version.major_version = HBM_MAJOR_VERSION;
+       start_req->host_version.minor_version = HBM_MINOR_VERSION;
+
        dev->recvd_msg = false;
-       if (mei_write_message(dev, mei_hdr, (unsigned char *)host_start_req,
-                                      mei_hdr->length)) {
+       if (mei_write_message(dev, mei_hdr, (unsigned char *)start_req, len)) {
                dev_dbg(&dev->pdev->dev, "write send version message to FW fail.\n");
                dev->dev_state = MEI_DEV_RESETING;
                mei_reset(dev, 1);
        }
        dev->init_clients_state = MEI_START_MESSAGE;
-       dev->init_clients_timer = INIT_CLIENTS_TIMEOUT;
+       dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
        return ;
 }
 
@@ -408,26 +365,22 @@ void mei_host_start_message(struct mei_device *dev)
 void mei_host_enum_clients_message(struct mei_device *dev)
 {
        struct mei_msg_hdr *mei_hdr;
-       struct hbm_host_enum_request *host_enum_req;
-       mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
+       struct hbm_host_enum_request *enum_req;
+       const size_t len = sizeof(struct hbm_host_enum_request);
        /* enumerate clients */
-       mei_hdr->host_addr = 0;
-       mei_hdr->me_addr = 0;
-       mei_hdr->length = sizeof(struct hbm_host_enum_request);
-       mei_hdr->msg_complete = 1;
-       mei_hdr->reserved = 0;
-
-       host_enum_req = (struct hbm_host_enum_request *) &dev->wr_msg_buf[1];
-       memset(host_enum_req, 0, sizeof(struct hbm_host_enum_request));
-       host_enum_req->hbm_cmd = HOST_ENUM_REQ_CMD;
-       if (mei_write_message(dev, mei_hdr, (unsigned char *)host_enum_req,
-                               mei_hdr->length)) {
+       mei_hdr = mei_hbm_hdr(&dev->wr_msg_buf[0], len);
+
+       enum_req = (struct hbm_host_enum_request *) &dev->wr_msg_buf[1];
+       memset(enum_req, 0, sizeof(struct hbm_host_enum_request));
+       enum_req->hbm_cmd = HOST_ENUM_REQ_CMD;
+
+       if (mei_write_message(dev, mei_hdr, (unsigned char *)enum_req, len)) {
                dev->dev_state = MEI_DEV_RESETING;
                dev_dbg(&dev->pdev->dev, "write send enumeration request message to FW fail.\n");
                mei_reset(dev, 1);
        }
        dev->init_clients_state = MEI_ENUM_CLIENTS_MESSAGE;
-       dev->init_clients_timer = INIT_CLIENTS_TIMEOUT;
+       dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
        return;
 }
 
@@ -470,56 +423,87 @@ void mei_allocate_me_clients_storage(struct mei_device *dev)
        dev->me_clients = clients;
        return ;
 }
-/**
- * host_client_properties - reads properties for client
- *
- * @dev: the device structure
- *
- * returns:
- *     < 0 - Error.
- *  = 0 - no more clients.
- *  = 1 - still have clients to send properties request.
- */
-int mei_host_client_properties(struct mei_device *dev)
+
+void mei_host_client_init(struct work_struct *work)
 {
-       struct mei_msg_hdr *mei_header;
-       struct hbm_props_request *host_cli_req;
-       int b;
-       u8 client_num = dev->me_client_presentation_num;
-
-       b = dev->me_client_index;
-       b = find_next_bit(dev->me_clients_map, MEI_CLIENTS_MAX, b);
-       if (b < MEI_CLIENTS_MAX) {
-               dev->me_clients[client_num].client_id = b;
-               dev->me_clients[client_num].mei_flow_ctrl_creds = 0;
-               mei_header = (struct mei_msg_hdr *)&dev->wr_msg_buf[0];
-               mei_header->host_addr = 0;
-               mei_header->me_addr = 0;
-               mei_header->length = sizeof(struct hbm_props_request);
-               mei_header->msg_complete = 1;
-               mei_header->reserved = 0;
-
-               host_cli_req = (struct hbm_props_request *)&dev->wr_msg_buf[1];
-
-               memset(host_cli_req, 0, sizeof(struct hbm_props_request));
-
-               host_cli_req->hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD;
-               host_cli_req->address = b;
-
-               if (mei_write_message(dev, mei_header,
-                               (unsigned char *)host_cli_req,
-                               mei_header->length)) {
-                       dev->dev_state = MEI_DEV_RESETING;
-                       dev_dbg(&dev->pdev->dev, "write send enumeration request message to FW fail.\n");
-                       mei_reset(dev, 1);
-                       return -EIO;
-               }
+       struct mei_device *dev = container_of(work,
+                                             struct mei_device, init_work);
+       struct mei_client_properties *client_props;
+       int i;
+
+       mutex_lock(&dev->device_lock);
+
+       bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX);
+       dev->open_handle_count = 0;
+
+       /*
+        * Reserving the first three client IDs
+        * 0: Reserved for MEI Bus Message communications
+        * 1: Reserved for Watchdog
+        * 2: Reserved for AMTHI
+        */
+       bitmap_set(dev->host_clients_map, 0, 3);
+
+       for (i = 0; i < dev->me_clients_num; i++) {
+               client_props = &dev->me_clients[i].props;
+
+               if (!uuid_le_cmp(client_props->protocol_name, mei_amthi_guid))
+                       mei_amthif_host_init(dev);
+               else if (!uuid_le_cmp(client_props->protocol_name, mei_wd_guid))
+                       mei_wd_host_init(dev);
+       }
+
+       dev->dev_state = MEI_DEV_ENABLED;
 
-               dev->init_clients_timer = INIT_CLIENTS_TIMEOUT;
-               dev->me_client_index = b;
-               return 1;
+       mutex_unlock(&dev->device_lock);
+}
+
+int mei_host_client_enumerate(struct mei_device *dev)
+{
+
+       struct mei_msg_hdr *mei_hdr;
+       struct hbm_props_request *prop_req;
+       const size_t len = sizeof(struct hbm_props_request);
+       unsigned long next_client_index;
+       u8 client_num;
+
+
+       client_num = dev->me_client_presentation_num;
+
+       next_client_index = find_next_bit(dev->me_clients_map, MEI_CLIENTS_MAX,
+                                         dev->me_client_index);
+
+       /* We got all client properties */
+       if (next_client_index == MEI_CLIENTS_MAX) {
+               schedule_work(&dev->init_work);
+
+               return 0;
        }
 
+       dev->me_clients[client_num].client_id = next_client_index;
+       dev->me_clients[client_num].mei_flow_ctrl_creds = 0;
+
+       mei_hdr = mei_hbm_hdr(&dev->wr_msg_buf[0], len);
+       prop_req = (struct hbm_props_request *)&dev->wr_msg_buf[1];
+
+       memset(prop_req, 0, sizeof(struct hbm_props_request));
+
+
+       prop_req->hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD;
+       prop_req->address = next_client_index;
+
+       if (mei_write_message(dev, mei_hdr, (unsigned char *) prop_req,
+                             mei_hdr->length)) {
+               dev->dev_state = MEI_DEV_RESETING;
+               dev_err(&dev->pdev->dev, "Properties request command failed\n");
+               mei_reset(dev, 1);
+
+               return -EIO;
+       }
+
+       dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
+       dev->me_client_index = next_client_index;
+
        return 0;
 }
 
@@ -557,17 +541,20 @@ int mei_me_cl_by_uuid(const struct mei_device *dev, const uuid_le *cuuid)
 
 
 /**
- * mei_me_cl_update_filext - searches for ME client guid
- *                       sets client_id in mei_file_private if found
+ * mei_me_cl_link - create link between host and me clinet and add
+ *   me_cl to the list
+ *
  * @dev: the device structure
- * @cl: private file structure to set client_id in
- * @cuuid: searched uuid of ME client
- * @client_id: id of host client to be set in file private structure
+ * @cl: link between me and host client assocated with opened file descriptor
+ * @cuuid: uuid of ME client
+ * @client_id: id of the host client
  *
- * returns ME client index
+ * returns ME client index if ME client
+ *     -EINVAL on incorrect values
+ *     -ENONET if client not found
  */
-int mei_me_cl_update_filext(struct mei_device *dev, struct mei_cl *cl,
-                               const uuid_le *cuuid, u8 host_cl_id)
+int mei_me_cl_link(struct mei_device *dev, struct mei_cl *cl,
+                       const uuid_le *cuuid, u8 host_cl_id)
 {
        int i;
 
@@ -587,54 +574,22 @@ int mei_me_cl_update_filext(struct mei_device *dev, struct mei_cl *cl,
 
        return -ENOENT;
 }
-
 /**
- * host_init_iamthif - mei initialization iamthif client.
+ * mei_me_cl_unlink - remove me_cl from the list
  *
  * @dev: the device structure
- *
+ * @host_client_id: host client id to be removed
  */
-void mei_host_init_iamthif(struct mei_device *dev)
+void mei_me_cl_unlink(struct mei_device *dev, struct mei_cl *cl)
 {
-       int i;
-       unsigned char *msg_buf;
-
-       mei_cl_init(&dev->iamthif_cl, dev);
-       dev->iamthif_cl.state = MEI_FILE_DISCONNECTED;
-
-       /* find ME amthi client */
-       i = mei_me_cl_update_filext(dev, &dev->iamthif_cl,
-                           &mei_amthi_guid, MEI_IAMTHIF_HOST_CLIENT_ID);
-       if (i < 0) {
-               dev_dbg(&dev->pdev->dev, "failed to find iamthif client.\n");
-               return;
-       }
-
-       /* Assign iamthif_mtu to the value received from ME  */
-
-       dev->iamthif_mtu = dev->me_clients[i].props.max_msg_length;
-       dev_dbg(&dev->pdev->dev, "IAMTHIF_MTU = %d\n",
-                       dev->me_clients[i].props.max_msg_length);
-
-       kfree(dev->iamthif_msg_buf);
-       dev->iamthif_msg_buf = NULL;
-
-       /* allocate storage for ME message buffer */
-       msg_buf = kcalloc(dev->iamthif_mtu,
-                       sizeof(unsigned char), GFP_KERNEL);
-       if (!msg_buf) {
-               dev_dbg(&dev->pdev->dev, "memory allocation for ME message buffer failed.\n");
-               return;
-       }
-
-       dev->iamthif_msg_buf = msg_buf;
-
-       if (mei_connect(dev, &dev->iamthif_cl)) {
-               dev_dbg(&dev->pdev->dev, "Failed to connect to AMTHI client\n");
-               dev->iamthif_cl.state = MEI_FILE_DISCONNECTED;
-               dev->iamthif_cl.host_client_id = 0;
-       } else {
-               dev->iamthif_cl.timer_count = CONNECT_TIMEOUT;
+       struct mei_cl *pos, *next;
+       list_for_each_entry_safe(pos, next, &dev->file_list, link) {
+               if (cl->host_client_id == pos->host_client_id) {
+                       dev_dbg(&dev->pdev->dev, "remove host client = %d, ME client = %d\n",
+                                       pos->host_client_id, pos->me_client_id);
+                       list_del_init(&pos->link);
+                       break;
+               }
        }
 }
 
@@ -671,9 +626,8 @@ struct mei_cl *mei_cl_allocate(struct mei_device *dev)
  */
 int mei_disconnect_host_client(struct mei_device *dev, struct mei_cl *cl)
 {
-       int rets, err;
-       long timeout = 15;      /* 15 seconds */
        struct mei_cl_cb *cb;
+       int rets, err;
 
        if (!dev || !cl)
                return -ENODEV;
@@ -681,13 +635,11 @@ int mei_disconnect_host_client(struct mei_device *dev, struct mei_cl *cl)
        if (cl->state != MEI_FILE_DISCONNECTING)
                return 0;
 
-       cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL);
+       cb = mei_io_cb_init(cl, NULL);
        if (!cb)
                return -ENOMEM;
 
-       INIT_LIST_HEAD(&cb->cb_list);
-       cb->file_private = cl;
-       cb->major_file_operations = MEI_CLOSE;
+       cb->fop_type = MEI_FOP_CLOSE;
        if (dev->mei_host_buffer_is_empty) {
                dev->mei_host_buffer_is_empty = false;
                if (mei_disconnect(dev, cl)) {
@@ -696,17 +648,17 @@ int mei_disconnect_host_client(struct mei_device *dev, struct mei_cl *cl)
                        goto free;
                }
                mdelay(10); /* Wait for hardware disconnection ready */
-               list_add_tail(&cb->cb_list, &dev->ctrl_rd_list.mei_cb.cb_list);
+               list_add_tail(&cb->list, &dev->ctrl_rd_list.list);
        } else {
                dev_dbg(&dev->pdev->dev, "add disconnect cb to control write list\n");
-               list_add_tail(&cb->cb_list,
-                               &dev->ctrl_wr_list.mei_cb.cb_list);
+               list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
+
        }
        mutex_unlock(&dev->device_lock);
 
        err = wait_event_timeout(dev->wait_recvd_msg,
-                (MEI_FILE_DISCONNECTED == cl->state),
-                timeout * HZ);
+                       MEI_FILE_DISCONNECTED == cl->state,
+                       mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
 
        mutex_lock(&dev->device_lock);
        if (MEI_FILE_DISCONNECTED == cl->state) {
@@ -728,29 +680,7 @@ int mei_disconnect_host_client(struct mei_device *dev, struct mei_cl *cl)
        mei_io_list_flush(&dev->ctrl_rd_list, cl);
        mei_io_list_flush(&dev->ctrl_wr_list, cl);
 free:
-       mei_free_cb_private(cb);
+       mei_io_cb_free(cb);
        return rets;
 }
 
-/**
- * mei_remove_client_from_file_list -
- *     removes file private data from device file list
- *
- * @dev: the device structure
- * @host_client_id: host client id to be removed
- */
-void mei_remove_client_from_file_list(struct mei_device *dev,
-                                      u8 host_client_id)
-{
-       struct mei_cl *cl_pos = NULL;
-       struct mei_cl *cl_next = NULL;
-       list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
-               if (host_client_id == cl_pos->host_client_id) {
-                       dev_dbg(&dev->pdev->dev, "remove host client = %d, ME client = %d\n",
-                                       cl_pos->host_client_id,
-                                       cl_pos->me_client_id);
-                       list_del_init(&cl_pos->link);
-                       break;
-               }
-       }
-}
index 509c3957ff4558cdfbd80f79362ee5d43e409943..8de8547859607c9c9440e6e8fac691547db4f864 100644 (file)
@@ -292,28 +292,23 @@ int mei_flow_ctrl_reduce(struct mei_device *dev, struct mei_cl *cl)
 int mei_send_flow_control(struct mei_device *dev, struct mei_cl *cl)
 {
        struct mei_msg_hdr *mei_hdr;
-       struct hbm_flow_control *mei_flow_control;
-
-       mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
-       mei_hdr->host_addr = 0;
-       mei_hdr->me_addr = 0;
-       mei_hdr->length = sizeof(struct hbm_flow_control);
-       mei_hdr->msg_complete = 1;
-       mei_hdr->reserved = 0;
-
-       mei_flow_control = (struct hbm_flow_control *) &dev->wr_msg_buf[1];
-       memset(mei_flow_control, 0, sizeof(*mei_flow_control));
-       mei_flow_control->host_addr = cl->host_client_id;
-       mei_flow_control->me_addr = cl->me_client_id;
-       mei_flow_control->hbm_cmd = MEI_FLOW_CONTROL_CMD;
-       memset(mei_flow_control->reserved, 0,
-                       sizeof(mei_flow_control->reserved));
+       struct hbm_flow_control *flow_ctrl;
+       const size_t len = sizeof(struct hbm_flow_control);
+
+       mei_hdr = mei_hbm_hdr(&dev->wr_msg_buf[0], len);
+
+       flow_ctrl = (struct hbm_flow_control *)&dev->wr_msg_buf[1];
+       memset(flow_ctrl, 0, len);
+       flow_ctrl->hbm_cmd = MEI_FLOW_CONTROL_CMD;
+       flow_ctrl->host_addr = cl->host_client_id;
+       flow_ctrl->me_addr = cl->me_client_id;
+       /* FIXME: reserved !? */
+       memset(flow_ctrl->reserved, 0, sizeof(flow_ctrl->reserved));
        dev_dbg(&dev->pdev->dev, "sending flow control host client = %d, ME client = %d\n",
                cl->host_client_id, cl->me_client_id);
 
        return mei_write_message(dev, mei_hdr,
-                               (unsigned char *) mei_flow_control,
-                               sizeof(struct hbm_flow_control));
+                       (unsigned char *) flow_ctrl, len);
 }
 
 /**
@@ -352,26 +347,19 @@ int mei_other_client_is_connecting(struct mei_device *dev,
 int mei_disconnect(struct mei_device *dev, struct mei_cl *cl)
 {
        struct mei_msg_hdr *mei_hdr;
-       struct hbm_client_disconnect_request *mei_cli_disconnect;
-
-       mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
-       mei_hdr->host_addr = 0;
-       mei_hdr->me_addr = 0;
-       mei_hdr->length = sizeof(struct hbm_client_disconnect_request);
-       mei_hdr->msg_complete = 1;
-       mei_hdr->reserved = 0;
-
-       mei_cli_disconnect =
-           (struct hbm_client_disconnect_request *) &dev->wr_msg_buf[1];
-       memset(mei_cli_disconnect, 0, sizeof(*mei_cli_disconnect));
-       mei_cli_disconnect->host_addr = cl->host_client_id;
-       mei_cli_disconnect->me_addr = cl->me_client_id;
-       mei_cli_disconnect->hbm_cmd = CLIENT_DISCONNECT_REQ_CMD;
-       mei_cli_disconnect->reserved[0] = 0;
+       struct hbm_client_connect_request *req;
+       const size_t len = sizeof(struct hbm_client_connect_request);
 
-       return mei_write_message(dev, mei_hdr,
-                               (unsigned char *) mei_cli_disconnect,
-                               sizeof(struct hbm_client_disconnect_request));
+       mei_hdr = mei_hbm_hdr(&dev->wr_msg_buf[0], len);
+
+       req = (struct hbm_client_connect_request *)&dev->wr_msg_buf[1];
+       memset(req, 0, len);
+       req->hbm_cmd = CLIENT_DISCONNECT_REQ_CMD;
+       req->host_addr = cl->host_client_id;
+       req->me_addr = cl->me_client_id;
+       req->reserved = 0;
+
+       return mei_write_message(dev, mei_hdr, (unsigned char *)req, len);
 }
 
 /**
@@ -385,23 +373,16 @@ int mei_disconnect(struct mei_device *dev, struct mei_cl *cl)
 int mei_connect(struct mei_device *dev, struct mei_cl *cl)
 {
        struct mei_msg_hdr *mei_hdr;
-       struct hbm_client_connect_request *mei_cli_connect;
-
-       mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
-       mei_hdr->host_addr = 0;
-       mei_hdr->me_addr = 0;
-       mei_hdr->length = sizeof(struct hbm_client_connect_request);
-       mei_hdr->msg_complete = 1;
-       mei_hdr->reserved = 0;
-
-       mei_cli_connect =
-           (struct hbm_client_connect_request *) &dev->wr_msg_buf[1];
-       mei_cli_connect->host_addr = cl->host_client_id;
-       mei_cli_connect->me_addr = cl->me_client_id;
-       mei_cli_connect->hbm_cmd = CLIENT_CONNECT_REQ_CMD;
-       mei_cli_connect->reserved = 0;
+       struct hbm_client_connect_request *req;
+       const size_t len = sizeof(struct hbm_client_connect_request);
 
-       return mei_write_message(dev, mei_hdr,
-                               (unsigned char *) mei_cli_connect,
-                               sizeof(struct hbm_client_connect_request));
+       mei_hdr = mei_hbm_hdr(&dev->wr_msg_buf[0], len);
+
+       req = (struct hbm_client_connect_request *) &dev->wr_msg_buf[1];
+       req->hbm_cmd = CLIENT_CONNECT_REQ_CMD;
+       req->host_addr = cl->host_client_id;
+       req->me_addr = cl->me_client_id;
+       req->reserved = 0;
+
+       return mei_write_message(dev, mei_hdr, (unsigned char *) req, len);
 }
index 3533edde04a525518624111b86e3b4c76c1c699d..04fa2134615eb490be4e2c20a50bccfc39ad5f75 100644 (file)
@@ -57,14 +57,14 @@ irqreturn_t mei_interrupt_quick_handler(int irq, void *dev_id)
  */
 static void _mei_cmpl(struct mei_cl *cl, struct mei_cl_cb *cb_pos)
 {
-       if (cb_pos->major_file_operations == MEI_WRITE) {
-               mei_free_cb_private(cb_pos);
+       if (cb_pos->fop_type == MEI_FOP_WRITE) {
+               mei_io_cb_free(cb_pos);
                cb_pos = NULL;
                cl->writing_state = MEI_WRITE_COMPLETE;
                if (waitqueue_active(&cl->tx_wait))
                        wake_up_interruptible(&cl->tx_wait);
 
-       } else if (cb_pos->major_file_operations == MEI_READ &&
+       } else if (cb_pos->fop_type == MEI_FOP_READ &&
                        MEI_READING == cl->reading_state) {
                cl->reading_state = MEI_READ_COMPLETE;
                if (waitqueue_active(&cl->rx_wait))
@@ -73,94 +73,6 @@ static void _mei_cmpl(struct mei_cl *cl, struct mei_cl_cb *cb_pos)
        }
 }
 
-/**
- * _mei_cmpl_iamthif - processes completed iamthif operation.
- *
- * @dev: the device structure.
- * @cb_pos: callback block.
- */
-static void _mei_cmpl_iamthif(struct mei_device *dev, struct mei_cl_cb *cb_pos)
-{
-       if (dev->iamthif_canceled != 1) {
-               dev->iamthif_state = MEI_IAMTHIF_READ_COMPLETE;
-               dev->iamthif_stall_timer = 0;
-               memcpy(cb_pos->response_buffer.data,
-                               dev->iamthif_msg_buf,
-                               dev->iamthif_msg_buf_index);
-               list_add_tail(&cb_pos->cb_list,
-                               &dev->amthi_read_complete_list.mei_cb.cb_list);
-               dev_dbg(&dev->pdev->dev, "amthi read completed.\n");
-               dev->iamthif_timer = jiffies;
-               dev_dbg(&dev->pdev->dev, "dev->iamthif_timer = %ld\n",
-                               dev->iamthif_timer);
-       } else {
-               mei_run_next_iamthif_cmd(dev);
-       }
-
-       dev_dbg(&dev->pdev->dev, "completing amthi call back.\n");
-       wake_up_interruptible(&dev->iamthif_cl.wait);
-}
-
-
-/**
- * mei_irq_thread_read_amthi_message - bottom half read routine after ISR to
- * handle the read amthi message data processing.
- *
- * @complete_list: An instance of our list structure
- * @dev: the device structure
- * @mei_hdr: header of amthi message
- *
- * returns 0 on success, <0 on failure.
- */
-static int mei_irq_thread_read_amthi_message(struct mei_io_list *complete_list,
-               struct mei_device *dev,
-               struct mei_msg_hdr *mei_hdr)
-{
-       struct mei_cl *cl;
-       struct mei_cl_cb *cb;
-       unsigned char *buffer;
-
-       BUG_ON(mei_hdr->me_addr != dev->iamthif_cl.me_client_id);
-       BUG_ON(dev->iamthif_state != MEI_IAMTHIF_READING);
-
-       buffer = dev->iamthif_msg_buf + dev->iamthif_msg_buf_index;
-       BUG_ON(dev->iamthif_mtu < dev->iamthif_msg_buf_index + mei_hdr->length);
-
-       mei_read_slots(dev, buffer, mei_hdr->length);
-
-       dev->iamthif_msg_buf_index += mei_hdr->length;
-
-       if (!mei_hdr->msg_complete)
-               return 0;
-
-       dev_dbg(&dev->pdev->dev,
-                       "amthi_message_buffer_index =%d\n",
-                       mei_hdr->length);
-
-       dev_dbg(&dev->pdev->dev, "completed amthi read.\n ");
-       if (!dev->iamthif_current_cb)
-               return -ENODEV;
-
-       cb = dev->iamthif_current_cb;
-       dev->iamthif_current_cb = NULL;
-
-       cl = (struct mei_cl *)cb->file_private;
-       if (!cl)
-               return -ENODEV;
-
-       dev->iamthif_stall_timer = 0;
-       cb->information =       dev->iamthif_msg_buf_index;
-       cb->read_time = jiffies;
-       if (dev->iamthif_ioctl && cl == &dev->iamthif_cl) {
-               /* found the iamthif cb */
-               dev_dbg(&dev->pdev->dev, "complete the amthi read cb.\n ");
-               dev_dbg(&dev->pdev->dev, "add the amthi read cb to complete.\n ");
-               list_add_tail(&cb->cb_list,
-                                               &complete_list->mei_cb.cb_list);
-       }
-       return 0;
-}
-
 /**
  * _mei_irq_thread_state_ok - checks if mei header matches file private data
  *
@@ -188,7 +100,7 @@ static int _mei_irq_thread_state_ok(struct mei_cl *cl,
  *
  * returns 0 on success, <0 on failure.
  */
-static int mei_irq_thread_read_client_message(struct mei_io_list *complete_list,
+static int mei_irq_thread_read_client_message(struct mei_cl_cb *complete_list,
                struct mei_device *dev,
                struct mei_msg_hdr *mei_hdr)
 {
@@ -197,36 +109,36 @@ static int mei_irq_thread_read_client_message(struct mei_io_list *complete_list,
        unsigned char *buffer = NULL;
 
        dev_dbg(&dev->pdev->dev, "start client msg\n");
-       if (list_empty(&dev->read_list.mei_cb.cb_list))
+       if (list_empty(&dev->read_list.list))
                goto quit;
 
-       list_for_each_entry_safe(cb_pos, cb_next,
-                       &dev->read_list.mei_cb.cb_list, cb_list) {
-               cl = (struct mei_cl *)cb_pos->file_private;
+       list_for_each_entry_safe(cb_pos, cb_next, &dev->read_list.list, list) {
+               cl = cb_pos->cl;
                if (cl && _mei_irq_thread_state_ok(cl, mei_hdr)) {
                        cl->reading_state = MEI_READING;
-                       buffer = cb_pos->response_buffer.data + cb_pos->information;
+                       buffer = cb_pos->response_buffer.data + cb_pos->buf_idx;
 
                        if (cb_pos->response_buffer.size <
-                                       mei_hdr->length + cb_pos->information) {
+                                       mei_hdr->length + cb_pos->buf_idx) {
                                dev_dbg(&dev->pdev->dev, "message overflow.\n");
-                               list_del(&cb_pos->cb_list);
+                               list_del(&cb_pos->list);
                                return -ENOMEM;
                        }
                        if (buffer)
                                mei_read_slots(dev, buffer, mei_hdr->length);
 
-                       cb_pos->information += mei_hdr->length;
+                       cb_pos->buf_idx += mei_hdr->length;
                        if (mei_hdr->msg_complete) {
                                cl->status = 0;
-                               list_del(&cb_pos->cb_list);
+                               list_del(&cb_pos->list);
                                dev_dbg(&dev->pdev->dev,
                                        "completed read H cl = %d, ME cl = %d, length = %lu\n",
                                        cl->host_client_id,
                                        cl->me_client_id,
-                                       cb_pos->information);
-                               list_add_tail(&cb_pos->cb_list,
-                                       &complete_list->mei_cb.cb_list);
+                                       cb_pos->buf_idx);
+
+                               list_add_tail(&cb_pos->list,
+                                               &complete_list->list);
                        }
 
                        break;
@@ -245,37 +157,6 @@ quit:
        return 0;
 }
 
-/**
- * _mei_irq_thread_iamthif_read - prepares to read iamthif data.
- *
- * @dev: the device structure.
- * @slots: free slots.
- *
- * returns 0, OK; otherwise, error.
- */
-static int _mei_irq_thread_iamthif_read(struct mei_device *dev, s32 *slots)
-{
-
-       if (((*slots) * sizeof(u32)) < (sizeof(struct mei_msg_hdr)
-                       + sizeof(struct hbm_flow_control))) {
-               return -EMSGSIZE;
-       }
-       *slots -= mei_data2slots(sizeof(struct hbm_flow_control));
-       if (mei_send_flow_control(dev, &dev->iamthif_cl)) {
-               dev_dbg(&dev->pdev->dev, "iamthif flow control failed\n");
-               return -EIO;
-       }
-
-       dev_dbg(&dev->pdev->dev, "iamthif flow control success\n");
-       dev->iamthif_state = MEI_IAMTHIF_READING;
-       dev->iamthif_flow_control_pending = false;
-       dev->iamthif_msg_buf_index = 0;
-       dev->iamthif_msg_buf_size = 0;
-       dev->iamthif_stall_timer = IAMTHIF_STALL_TIMER;
-       dev->mei_host_buffer_is_empty = mei_hbuf_is_empty(dev);
-       return 0;
-}
-
 /**
  * _mei_irq_thread_close - processes close related operation.
  *
@@ -290,26 +171,24 @@ static int _mei_irq_thread_iamthif_read(struct mei_device *dev, s32 *slots)
 static int _mei_irq_thread_close(struct mei_device *dev, s32 *slots,
                                struct mei_cl_cb *cb_pos,
                                struct mei_cl *cl,
-                               struct mei_io_list *cmpl_list)
+                               struct mei_cl_cb *cmpl_list)
 {
        if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) +
-                       sizeof(struct hbm_client_disconnect_request)))
+                       sizeof(struct hbm_client_connect_request)))
                return -EBADMSG;
 
-       *slots -= mei_data2slots(sizeof(struct hbm_client_disconnect_request));
+       *slots -= mei_data2slots(sizeof(struct hbm_client_connect_request));
 
        if (mei_disconnect(dev, cl)) {
                cl->status = 0;
-               cb_pos->information = 0;
-               list_move_tail(&cb_pos->cb_list,
-                               &cmpl_list->mei_cb.cb_list);
+               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->information = 0;
-               list_move_tail(&cb_pos->cb_list,
-                               &dev->ctrl_rd_list.mei_cb.cb_list);
+               cb_pos->buf_idx = 0;
+               list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list);
                cl->timer_count = MEI_CONNECT_TIMEOUT;
        }
 
@@ -356,7 +235,7 @@ static void mei_client_connect_response(struct mei_device *dev,
 {
 
        struct mei_cl *cl;
-       struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL;
+       struct mei_cl_cb *pos = NULL, *next = NULL;
 
        dev_dbg(&dev->pdev->dev,
                        "connect_response:\n"
@@ -373,8 +252,6 @@ static void mei_client_connect_response(struct mei_device *dev,
                dev_dbg(&dev->pdev->dev, "successfully connected to WD client.\n");
                mei_watchdog_register(dev);
 
-               /* next step in the state maching */
-               mei_host_init_iamthif(dev);
                return;
        }
 
@@ -382,17 +259,16 @@ static void mei_client_connect_response(struct mei_device *dev,
                dev->iamthif_state = MEI_IAMTHIF_IDLE;
                return;
        }
-       list_for_each_entry_safe(cb_pos, cb_next,
-                               &dev->ctrl_rd_list.mei_cb.cb_list, cb_list) {
+       list_for_each_entry_safe(pos, next, &dev->ctrl_rd_list.list, list) {
 
-               cl = (struct mei_cl *)cb_pos->file_private;
+               cl = pos->cl;
                if (!cl) {
-                       list_del(&cb_pos->cb_list);
+                       list_del(&pos->list);
                        return;
                }
-               if (MEI_IOCTL == cb_pos->major_file_operations) {
+               if (pos->fop_type == MEI_FOP_IOCTL) {
                        if (is_treat_specially_client(cl, rs)) {
-                               list_del(&cb_pos->cb_list);
+                               list_del(&pos->list);
                                cl->status = 0;
                                cl->timer_count = 0;
                                break;
@@ -411,7 +287,7 @@ static void mei_client_disconnect_response(struct mei_device *dev,
                                        struct hbm_client_connect_response *rs)
 {
        struct mei_cl *cl;
-       struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL;
+       struct mei_cl_cb *pos = NULL, *next = NULL;
 
        dev_dbg(&dev->pdev->dev,
                        "disconnect_response:\n"
@@ -422,12 +298,11 @@ static void mei_client_disconnect_response(struct mei_device *dev,
                        rs->host_addr,
                        rs->status);
 
-       list_for_each_entry_safe(cb_pos, cb_next,
-                       &dev->ctrl_rd_list.mei_cb.cb_list, cb_list) {
-               cl = (struct mei_cl *)cb_pos->file_private;
+       list_for_each_entry_safe(pos, next, &dev->ctrl_rd_list.list, list) {
+               cl = pos->cl;
 
                if (!cl) {
-                       list_del(&cb_pos->cb_list);
+                       list_del(&pos->list);
                        return;
                }
 
@@ -435,7 +310,7 @@ static void mei_client_disconnect_response(struct mei_device *dev,
                if (cl->host_client_id == rs->host_addr &&
                    cl->me_client_id == rs->me_addr) {
 
-                       list_del(&cb_pos->cb_list);
+                       list_del(&pos->list);
                        if (!rs->status)
                                cl->state = MEI_FILE_DISCONNECTED;
 
@@ -537,10 +412,10 @@ static void mei_client_flow_control_response(struct mei_device *dev,
  * returns !=0, same; 0,not.
  */
 static int same_disconn_addr(struct mei_cl *cl,
-                            struct hbm_client_disconnect_request *disconn)
+                            struct hbm_client_connect_request *req)
 {
-       return (cl->host_client_id == disconn->host_addr &&
-               cl->me_client_id == disconn->me_addr);
+       return (cl->host_client_id == req->host_addr &&
+               cl->me_client_id == req->me_addr);
 }
 
 /**
@@ -550,49 +425,38 @@ static int same_disconn_addr(struct mei_cl *cl,
  * @disconnect_req: disconnect request bus message.
  */
 static void mei_client_disconnect_request(struct mei_device *dev,
-               struct hbm_client_disconnect_request *disconnect_req)
+               struct hbm_client_connect_request *disconnect_req)
 {
-       struct mei_msg_hdr *mei_hdr;
        struct hbm_client_connect_response *disconnect_res;
-       struct mei_cl *cl_pos = NULL;
-       struct mei_cl *cl_next = NULL;
+       struct mei_cl *pos, *next;
+       const size_t len = sizeof(struct hbm_client_connect_response);
 
-       list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
-               if (same_disconn_addr(cl_pos, disconnect_req)) {
+       list_for_each_entry_safe(pos, next, &dev->file_list, link) {
+               if (same_disconn_addr(pos, disconnect_req)) {
                        dev_dbg(&dev->pdev->dev, "disconnect request host client %d ME client %d.\n",
                                        disconnect_req->host_addr,
                                        disconnect_req->me_addr);
-                       cl_pos->state = MEI_FILE_DISCONNECTED;
-                       cl_pos->timer_count = 0;
-                       if (cl_pos == &dev->wd_cl)
+                       pos->state = MEI_FILE_DISCONNECTED;
+                       pos->timer_count = 0;
+                       if (pos == &dev->wd_cl)
                                dev->wd_pending = false;
-                       else if (cl_pos == &dev->iamthif_cl)
+                       else if (pos == &dev->iamthif_cl)
                                dev->iamthif_timer = 0;
 
                        /* prepare disconnect response */
-                       mei_hdr =
-                               (struct mei_msg_hdr *) &dev->ext_msg_buf[0];
-                       mei_hdr->host_addr = 0;
-                       mei_hdr->me_addr = 0;
-                       mei_hdr->length =
-                               sizeof(struct hbm_client_connect_response);
-                       mei_hdr->msg_complete = 1;
-                       mei_hdr->reserved = 0;
-
+                       (void)mei_hbm_hdr((u32 *)&dev->wr_ext_msg.hdr, len);
                        disconnect_res =
                                (struct hbm_client_connect_response *)
-                               &dev->ext_msg_buf[1];
-                       disconnect_res->host_addr = cl_pos->host_client_id;
-                       disconnect_res->me_addr = cl_pos->me_client_id;
+                               &dev->wr_ext_msg.data;
                        disconnect_res->hbm_cmd = CLIENT_DISCONNECT_RES_CMD;
+                       disconnect_res->host_addr = pos->host_client_id;
+                       disconnect_res->me_addr = pos->me_client_id;
                        disconnect_res->status = 0;
-                       dev->extra_write_index = 2;
                        break;
                }
        }
 }
 
-
 /**
  * mei_irq_thread_read_bus_message - bottom half read routine after ISR to
  * handle the read bus message cmd processing.
@@ -604,16 +468,15 @@ static void mei_irq_thread_read_bus_message(struct mei_device *dev,
                struct mei_msg_hdr *mei_hdr)
 {
        struct mei_bus_message *mei_msg;
+       struct mei_me_client *me_client;
        struct hbm_host_version_response *version_res;
        struct hbm_client_connect_response *connect_res;
        struct hbm_client_connect_response *disconnect_res;
+       struct hbm_client_connect_request *disconnect_req;
        struct hbm_flow_control *flow_control;
        struct hbm_props_response *props_res;
        struct hbm_host_enum_response *enum_res;
-       struct hbm_client_disconnect_request *disconnect_req;
-       struct hbm_host_stop_request *host_stop_req;
-       int res;
-
+       struct hbm_host_stop_request *stop_req;
 
        /* read the message to our buffer */
        BUG_ON(mei_hdr->length >= sizeof(dev->rd_msg_buf));
@@ -637,26 +500,20 @@ static void mei_irq_thread_read_bus_message(struct mei_device *dev,
                                return;
                        }
                } else {
+                       u32 *buf = dev->wr_msg_buf;
+                       const size_t len = sizeof(struct hbm_host_stop_request);
+
                        dev->version = version_res->me_max_version;
+
                        /* send stop message */
-                       mei_hdr = (struct mei_msg_hdr *)&dev->wr_msg_buf[0];
-                       mei_hdr->host_addr = 0;
-                       mei_hdr->me_addr = 0;
-                       mei_hdr->length = sizeof(struct hbm_host_stop_request);
-                       mei_hdr->msg_complete = 1;
-                       mei_hdr->reserved = 0;
-
-                       host_stop_req = (struct hbm_host_stop_request *)
-                                                       &dev->wr_msg_buf[1];
-
-                       memset(host_stop_req,
-                                       0,
-                                       sizeof(struct hbm_host_stop_request));
-                       host_stop_req->hbm_cmd = HOST_STOP_REQ_CMD;
-                       host_stop_req->reason = DRIVER_STOP_REQUEST;
+                       mei_hdr = mei_hbm_hdr(&buf[0], len);
+                       stop_req = (struct hbm_host_stop_request *)&buf[1];
+                       memset(stop_req, 0, len);
+                       stop_req->hbm_cmd = HOST_STOP_REQ_CMD;
+                       stop_req->reason = DRIVER_STOP_REQUEST;
+
                        mei_write_message(dev, mei_hdr,
-                                          (unsigned char *) (host_stop_req),
-                                          mei_hdr->length);
+                                       (unsigned char *)stop_req, len);
                        dev_dbg(&dev->pdev->dev, "version mismatch.\n");
                        return;
                }
@@ -666,16 +523,14 @@ static void mei_irq_thread_read_bus_message(struct mei_device *dev,
                break;
 
        case CLIENT_CONNECT_RES_CMD:
-               connect_res =
-                       (struct hbm_client_connect_response *) mei_msg;
+               connect_res = (struct hbm_client_connect_response *) mei_msg;
                mei_client_connect_response(dev, connect_res);
                dev_dbg(&dev->pdev->dev, "client connect response message received.\n");
                wake_up(&dev->wait_recvd_msg);
                break;
 
        case CLIENT_DISCONNECT_RES_CMD:
-               disconnect_res =
-                       (struct hbm_client_connect_response *) mei_msg;
+               disconnect_res = (struct hbm_client_connect_response *) mei_msg;
                mei_client_disconnect_response(dev, disconnect_res);
                dev_dbg(&dev->pdev->dev, "client disconnect response message received.\n");
                wake_up(&dev->wait_recvd_msg);
@@ -689,64 +544,37 @@ static void mei_irq_thread_read_bus_message(struct mei_device *dev,
 
        case HOST_CLIENT_PROPERTIES_RES_CMD:
                props_res = (struct hbm_props_response *)mei_msg;
+               me_client = &dev->me_clients[dev->me_client_presentation_num];
+
                if (props_res->status || !dev->me_clients) {
                        dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message wrong status.\n");
                        mei_reset(dev, 1);
                        return;
                }
-               if (dev->me_clients[dev->me_client_presentation_num]
-                                       .client_id == props_res->address) {
 
-                       dev->me_clients[dev->me_client_presentation_num].props
-                                               = props_res->client_properties;
+               if (me_client->client_id != props_res->address) {
+                       dev_err(&dev->pdev->dev,
+                               "Host client properties reply mismatch\n");
+                       mei_reset(dev, 1);
 
-                       if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
-                           dev->init_clients_state ==
-                                       MEI_CLIENT_PROPERTIES_MESSAGE) {
-                               dev->me_client_index++;
-                               dev->me_client_presentation_num++;
-
-                               /** Send Client Properties request **/
-                               res = mei_host_client_properties(dev);
-                               if (res < 0) {
-                                       dev_dbg(&dev->pdev->dev, "mei_host_client_properties() failed");
-                                       return;
-                               } else if (!res) {
-                                       /*
-                                        * No more clients to send to.
-                                        * Clear Map for indicating now ME clients
-                                        * with associated host client
-                                        */
-                                       bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX);
-                                       dev->open_handle_count = 0;
-
-                                       /*
-                                        * Reserving the first three client IDs
-                                        * Client Id 0 - Reserved for MEI Bus Message communications
-                                        * Client Id 1 - Reserved for Watchdog
-                                        * Client ID 2 - Reserved for AMTHI
-                                        */
-                                       bitmap_set(dev->host_clients_map, 0, 3);
-                                       dev->dev_state = MEI_DEV_ENABLED;
-
-                                       /* if wd initialization fails, initialization the AMTHI client,
-                                        * otherwise the AMTHI client will be initialized after the WD client connect response
-                                        * will be received
-                                        */
-                                       if (mei_wd_host_init(dev))
-                                               mei_host_init_iamthif(dev);
-                               }
+                       return;
+               }
 
-                       } else {
-                               dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message");
-                               mei_reset(dev, 1);
-                               return;
-                       }
-               } else {
-                       dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message for wrong client ID\n");
+               if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
+                   dev->init_clients_state != MEI_CLIENT_PROPERTIES_MESSAGE) {
+                       dev_err(&dev->pdev->dev,
+                               "Unexpected client properties reply\n");
                        mei_reset(dev, 1);
+
                        return;
                }
+
+               me_client->props = props_res->client_properties;
+               dev->me_client_index++;
+               dev->me_client_presentation_num++;
+
+               mei_host_client_enumerate(dev);
+
                break;
 
        case HOST_ENUM_RES_CMD:
@@ -760,7 +588,8 @@ static void mei_irq_thread_read_bus_message(struct mei_device *dev,
                                mei_allocate_me_clients_storage(dev);
                                dev->init_clients_state =
                                        MEI_CLIENT_PROPERTIES_MESSAGE;
-                               mei_host_client_properties(dev);
+
+                               mei_host_client_enumerate(dev);
                } else {
                        dev_dbg(&dev->pdev->dev, "reset due to received host enumeration clients response bus message.\n");
                        mei_reset(dev, 1);
@@ -776,29 +605,23 @@ static void mei_irq_thread_read_bus_message(struct mei_device *dev,
 
        case CLIENT_DISCONNECT_REQ_CMD:
                /* search for client */
-               disconnect_req =
-                       (struct hbm_client_disconnect_request *) mei_msg;
+               disconnect_req = (struct hbm_client_connect_request *)mei_msg;
                mei_client_disconnect_request(dev, disconnect_req);
                break;
 
        case ME_STOP_REQ_CMD:
-               /* prepare stop request */
-               mei_hdr = (struct mei_msg_hdr *) &dev->ext_msg_buf[0];
-               mei_hdr->host_addr = 0;
-               mei_hdr->me_addr = 0;
-               mei_hdr->length = sizeof(struct hbm_host_stop_request);
-               mei_hdr->msg_complete = 1;
-               mei_hdr->reserved = 0;
-               host_stop_req =
-                       (struct hbm_host_stop_request *) &dev->ext_msg_buf[1];
-               memset(host_stop_req, 0, sizeof(struct hbm_host_stop_request));
-               host_stop_req->hbm_cmd = HOST_STOP_REQ_CMD;
-               host_stop_req->reason = DRIVER_STOP_REQUEST;
-               host_stop_req->reserved[0] = 0;
-               host_stop_req->reserved[1] = 0;
-               dev->extra_write_index = 2;
-               break;
+       {
+               /* prepare stop request: sent in next interrupt event */
+
+               const size_t len = sizeof(struct hbm_host_stop_request);
 
+               mei_hdr = mei_hbm_hdr((u32 *)&dev->wr_ext_msg.hdr, len);
+               stop_req = (struct hbm_host_stop_request *)&dev->wr_ext_msg.data;
+               memset(stop_req, 0, len);
+               stop_req->hbm_cmd = HOST_STOP_REQ_CMD;
+               stop_req->reason = DRIVER_STOP_REQUEST;
+               break;
+       }
        default:
                BUG();
                break;
@@ -821,12 +644,12 @@ static void mei_irq_thread_read_bus_message(struct mei_device *dev,
 static int _mei_irq_thread_read(struct mei_device *dev,        s32 *slots,
                        struct mei_cl_cb *cb_pos,
                        struct mei_cl *cl,
-                       struct mei_io_list *cmpl_list)
+                       struct mei_cl_cb *cmpl_list)
 {
        if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) +
                        sizeof(struct hbm_flow_control))) {
                /* return the cancel routine */
-               list_del(&cb_pos->cb_list);
+               list_del(&cb_pos->list);
                return -EBADMSG;
        }
 
@@ -834,11 +657,11 @@ static int _mei_irq_thread_read(struct mei_device *dev,   s32 *slots,
 
        if (mei_send_flow_control(dev, cl)) {
                cl->status = -ENODEV;
-               cb_pos->information = 0;
-               list_move_tail(&cb_pos->cb_list, &cmpl_list->mei_cb.cb_list);
+               cb_pos->buf_idx = 0;
+               list_move_tail(&cb_pos->list, &cmpl_list->list);
                return -ENODEV;
        }
-       list_move_tail(&cb_pos->cb_list, &dev->read_list.mei_cb.cb_list);
+       list_move_tail(&cb_pos->list, &dev->read_list.list);
 
        return 0;
 }
@@ -858,12 +681,12 @@ static int _mei_irq_thread_read(struct mei_device *dev,   s32 *slots,
 static int _mei_irq_thread_ioctl(struct mei_device *dev, s32 *slots,
                        struct mei_cl_cb *cb_pos,
                        struct mei_cl *cl,
-                       struct mei_io_list *cmpl_list)
+                       struct mei_cl_cb *cmpl_list)
 {
        if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) +
                        sizeof(struct hbm_client_connect_request))) {
                /* return the cancel routine */
-               list_del(&cb_pos->cb_list);
+               list_del(&cb_pos->list);
                return -EBADMSG;
        }
 
@@ -871,188 +694,73 @@ static int _mei_irq_thread_ioctl(struct mei_device *dev, s32 *slots,
         *slots -= mei_data2slots(sizeof(struct hbm_client_connect_request));
        if (mei_connect(dev, cl)) {
                cl->status = -ENODEV;
-               cb_pos->information = 0;
-               list_del(&cb_pos->cb_list);
+               cb_pos->buf_idx = 0;
+               list_del(&cb_pos->list);
                return -ENODEV;
        } else {
-               list_move_tail(&cb_pos->cb_list,
-                       &dev->ctrl_rd_list.mei_cb.cb_list);
+               list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list);
                cl->timer_count = MEI_CONNECT_TIMEOUT;
        }
        return 0;
 }
 
 /**
- * _mei_irq_thread_cmpl - processes completed and no-iamthif operation.
+ * mei_irq_thread_write_complete - write messages to device.
  *
  * @dev: the device structure.
  * @slots: free slots.
- * @cb_pos: callback block.
- * @cl: private data of the file object.
+ * @cb: callback block.
  * @cmpl_list: complete list.
  *
  * returns 0, OK; otherwise, error.
  */
-static int _mei_irq_thread_cmpl(struct mei_device *dev,        s32 *slots,
-                       struct mei_cl_cb *cb_pos,
-                       struct mei_cl *cl,
-                       struct mei_io_list *cmpl_list)
+static int mei_irq_thread_write_complete(struct mei_device *dev, s32 *slots,
+                       struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list)
 {
        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);
+
+       mei_hdr = (struct mei_msg_hdr *)&dev->wr_msg_buf[0];
+       mei_hdr->host_addr = cl->host_client_id;
+       mei_hdr->me_addr = cl->me_client_id;
+       mei_hdr->reserved = 0;
 
-       if ((*slots * sizeof(u32)) >= (sizeof(struct mei_msg_hdr) +
-                       (cb_pos->request_buffer.size -
-                       cb_pos->information))) {
-               mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
-               mei_hdr->host_addr = cl->host_client_id;
-               mei_hdr->me_addr = cl->me_client_id;
-               mei_hdr->length = cb_pos->request_buffer.size -
-                                       cb_pos->information;
+       if (*slots >= msg_slots) {
+               mei_hdr->length = len;
                mei_hdr->msg_complete = 1;
-               mei_hdr->reserved = 0;
-               dev_dbg(&dev->pdev->dev, "cb_pos->request_buffer.size =%d"
-                       "mei_hdr->msg_complete = %d\n",
-                               cb_pos->request_buffer.size,
-                               mei_hdr->msg_complete);
-               dev_dbg(&dev->pdev->dev, "cb_pos->information  =%lu\n",
-                               cb_pos->information);
-               dev_dbg(&dev->pdev->dev, "mei_hdr->length  =%d\n",
-                               mei_hdr->length);
-               *slots -= mei_data2slots(mei_hdr->length);
-               if (mei_write_message(dev, mei_hdr,
-                               (unsigned char *)
-                               (cb_pos->request_buffer.data +
-                               cb_pos->information),
-                               mei_hdr->length)) {
-                       cl->status = -ENODEV;
-                       list_move_tail(&cb_pos->cb_list,
-                               &cmpl_list->mei_cb.cb_list);
-                       return -ENODEV;
-               } else {
-                       if (mei_flow_ctrl_reduce(dev, cl))
-                               return -ENODEV;
-                       cl->status = 0;
-                       cb_pos->information += mei_hdr->length;
-                       list_move_tail(&cb_pos->cb_list,
-                               &dev->write_waiting_list.mei_cb.cb_list);
-               }
+       /* Split the message only if we can write the whole host buffer */
        } else if (*slots == dev->hbuf_depth) {
-               /* buffer is still empty */
-               mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
-               mei_hdr->host_addr = cl->host_client_id;
-               mei_hdr->me_addr = cl->me_client_id;
-               mei_hdr->length =
-                       (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
+               msg_slots = *slots;
+               len = (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
+               mei_hdr->length = len;
                mei_hdr->msg_complete = 0;
-               mei_hdr->reserved = 0;
-               *slots -= mei_data2slots(mei_hdr->length);
-               if (mei_write_message(dev, mei_hdr,
-                                       (unsigned char *)
-                                       (cb_pos->request_buffer.data +
-                                       cb_pos->information),
-                                       mei_hdr->length)) {
-                       cl->status = -ENODEV;
-                       list_move_tail(&cb_pos->cb_list,
-                               &cmpl_list->mei_cb.cb_list);
-                       return -ENODEV;
-               } else {
-                       cb_pos->information += mei_hdr->length;
-                       dev_dbg(&dev->pdev->dev,
-                                       "cb_pos->request_buffer.size =%d"
-                                       " mei_hdr->msg_complete = %d\n",
-                                       cb_pos->request_buffer.size,
-                                       mei_hdr->msg_complete);
-                       dev_dbg(&dev->pdev->dev, "cb_pos->information  =%lu\n",
-                                       cb_pos->information);
-                       dev_dbg(&dev->pdev->dev, "mei_hdr->length  =%d\n",
-                                       mei_hdr->length);
-               }
-               return -EMSGSIZE;
        } else {
-               return -EBADMSG;
+               /* wait for next time the host buffer is empty */
+               return 0;
        }
 
-       return 0;
-}
-
-/**
- * _mei_irq_thread_cmpl_iamthif - processes completed iamthif operation.
- *
- * @dev: the device structure.
- * @slots: free slots.
- * @cb_pos: callback block.
- * @cl: private data of the file object.
- * @cmpl_list: complete list.
- *
- * returns 0, OK; otherwise, error.
- */
-static int _mei_irq_thread_cmpl_iamthif(struct mei_device *dev, s32 *slots,
-                       struct mei_cl_cb *cb_pos,
-                       struct mei_cl *cl,
-                       struct mei_io_list *cmpl_list)
-{
-       struct mei_msg_hdr *mei_hdr;
-
-       if ((*slots * sizeof(u32)) >= (sizeof(struct mei_msg_hdr) +
-                       dev->iamthif_msg_buf_size -
-                       dev->iamthif_msg_buf_index)) {
-               mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
-               mei_hdr->host_addr = cl->host_client_id;
-               mei_hdr->me_addr = cl->me_client_id;
-               mei_hdr->length = dev->iamthif_msg_buf_size -
-                       dev->iamthif_msg_buf_index;
-               mei_hdr->msg_complete = 1;
-               mei_hdr->reserved = 0;
-
-               *slots -= mei_data2slots(mei_hdr->length);
-
-               if (mei_write_message(dev, mei_hdr,
-                                       (dev->iamthif_msg_buf +
-                                       dev->iamthif_msg_buf_index),
-                                       mei_hdr->length)) {
-                       dev->iamthif_state = MEI_IAMTHIF_IDLE;
-                       cl->status = -ENODEV;
-                       list_del(&cb_pos->cb_list);
-                       return -ENODEV;
-               } else {
-                       if (mei_flow_ctrl_reduce(dev, cl))
-                               return -ENODEV;
-                       dev->iamthif_msg_buf_index += mei_hdr->length;
-                       cb_pos->information = dev->iamthif_msg_buf_index;
-                       cl->status = 0;
-                       dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL;
-                       dev->iamthif_flow_control_pending = true;
-                       /* save iamthif cb sent to amthi client */
-                       dev->iamthif_current_cb = cb_pos;
-                       list_move_tail(&cb_pos->cb_list,
-                               &dev->write_waiting_list.mei_cb.cb_list);
+       dev_dbg(&dev->pdev->dev, "buf: size = %d idx = %lu\n",
+                       cb->request_buffer.size, cb->buf_idx);
+       dev_dbg(&dev->pdev->dev, "msg: len = %d complete = %d\n",
+                       mei_hdr->length, mei_hdr->msg_complete);
 
-               }
-       } else if (*slots == dev->hbuf_depth) {
-               /* buffer is still empty */
-               mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
-               mei_hdr->host_addr = cl->host_client_id;
-               mei_hdr->me_addr = cl->me_client_id;
-               mei_hdr->length =
-                       (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
-               mei_hdr->msg_complete = 0;
-               mei_hdr->reserved = 0;
+       *slots -=  msg_slots;
+       if (mei_write_message(dev, mei_hdr,
+               cb->request_buffer.data + cb->buf_idx, len)) {
+               cl->status = -ENODEV;
+               list_move_tail(&cb->list, &cmpl_list->list);
+               return -ENODEV;
+       }
 
-               *slots -= mei_data2slots(mei_hdr->length);
+       if (mei_flow_ctrl_reduce(dev, cl))
+               return -ENODEV;
 
-               if (mei_write_message(dev, mei_hdr,
-                                       (dev->iamthif_msg_buf +
-                                       dev->iamthif_msg_buf_index),
-                                       mei_hdr->length)) {
-                       cl->status = -ENODEV;
-                       list_del(&cb_pos->cb_list);
-               } else {
-                       dev->iamthif_msg_buf_index += mei_hdr->length;
-               }
-               return -EMSGSIZE;
-       } else {
-               return -EBADMSG;
-       }
+       cl->status = 0;
+       cb->buf_idx += mei_hdr->length;
+       if (mei_hdr->msg_complete)
+               list_move_tail(&cb->list, &dev->write_waiting_list.list);
 
        return 0;
 }
@@ -1067,7 +775,7 @@ static int _mei_irq_thread_cmpl_iamthif(struct mei_device *dev, s32 *slots,
  *
  * returns 0 on success, <0 on failure.
  */
-static int mei_irq_thread_read_handler(struct mei_io_list *cmpl_list,
+static int mei_irq_thread_read_handler(struct mei_cl_cb *cmpl_list,
                struct mei_device *dev,
                s32 *slots)
 {
@@ -1130,8 +838,8 @@ static int mei_irq_thread_read_handler(struct mei_io_list *cmpl_list,
                dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_iamthif_message.\n");
                dev_dbg(&dev->pdev->dev, "mei_hdr->length =%d\n",
                                mei_hdr->length);
-               ret = mei_irq_thread_read_amthi_message(cmpl_list,
-                                                       dev, mei_hdr);
+
+               ret = mei_amthif_irq_read_message(cmpl_list, dev, mei_hdr);
                if (ret)
                        goto end;
 
@@ -1164,53 +872,51 @@ end:
  * mei_irq_thread_write_handler - bottom half write routine after
  * ISR to handle the write processing.
  *
- * @cmpl_list: An instance of our list structure
  * @dev: the device structure
- * @slots: slots to write.
+ * @cmpl_list: An instance of our list structure
  *
  * returns 0 on success, <0 on failure.
  */
-static int mei_irq_thread_write_handler(struct mei_io_list *cmpl_list,
-               struct mei_device *dev,
-               s32 *slots)
+static int mei_irq_thread_write_handler(struct mei_device *dev,
+                               struct mei_cl_cb *cmpl_list)
 {
 
        struct mei_cl *cl;
        struct mei_cl_cb *pos = NULL, *next = NULL;
-       struct mei_io_list *list;
+       struct mei_cl_cb *list;
+       s32 slots;
        int ret;
 
        if (!mei_hbuf_is_empty(dev)) {
                dev_dbg(&dev->pdev->dev, "host buffer is not empty.\n");
                return 0;
        }
-       *slots = mei_hbuf_empty_slots(dev);
-       if (*slots <= 0)
+       slots = mei_hbuf_empty_slots(dev);
+       if (slots <= 0)
                return -EMSGSIZE;
 
        /* complete all waiting for write CB */
        dev_dbg(&dev->pdev->dev, "complete all waiting for write cb.\n");
 
        list = &dev->write_waiting_list;
-       list_for_each_entry_safe(pos, next, &list->mei_cb.cb_list, cb_list) {
-               cl = (struct mei_cl *)pos->file_private;
+       list_for_each_entry_safe(pos, next, &list->list, list) {
+               cl = pos->cl;
                if (cl == NULL)
                        continue;
 
                cl->status = 0;
-               list_del(&pos->cb_list);
+               list_del(&pos->list);
                if (MEI_WRITING == cl->writing_state &&
-                  (pos->major_file_operations == MEI_WRITE) &&
-                  (cl != &dev->iamthif_cl)) {
+                   pos->fop_type == MEI_FOP_WRITE &&
+                   cl != &dev->iamthif_cl) {
                        dev_dbg(&dev->pdev->dev, "MEI WRITE COMPLETE\n");
                        cl->writing_state = MEI_WRITE_COMPLETE;
-                       list_add_tail(&pos->cb_list,
-                                     &cmpl_list->mei_cb.cb_list);
+                       list_add_tail(&pos->list, &cmpl_list->list);
                }
                if (cl == &dev->iamthif_cl) {
                        dev_dbg(&dev->pdev->dev, "check iamthif flow control.\n");
                        if (dev->iamthif_flow_control_pending) {
-                               ret = _mei_irq_thread_iamthif_read(dev, slots);
+                               ret = mei_amthif_irq_read(dev, &slots);
                                if (ret)
                                        return ret;
                        }
@@ -1222,15 +928,11 @@ static int mei_irq_thread_write_handler(struct mei_io_list *cmpl_list,
                wake_up_interruptible(&dev->wait_stop_wd);
        }
 
-       if (dev->extra_write_index) {
-               dev_dbg(&dev->pdev->dev, "extra_write_index =%d.\n",
-                               dev->extra_write_index);
-               mei_write_message(dev,
-                               (struct mei_msg_hdr *) &dev->ext_msg_buf[0],
-                               (unsigned char *) &dev->ext_msg_buf[1],
-                               (dev->extra_write_index - 1) * sizeof(u32));
-               *slots -= dev->extra_write_index;
-               dev->extra_write_index = 0;
+       if (dev->wr_ext_msg.hdr.length) {
+               mei_write_message(dev, &dev->wr_ext_msg.hdr,
+                       dev->wr_ext_msg.data, dev->wr_ext_msg.hdr.length);
+               slots -= mei_data2slots(dev->wr_ext_msg.hdr.length);
+               dev->wr_ext_msg.hdr.length = 0;
        }
        if (dev->dev_state == MEI_DEV_ENABLED) {
                if (dev->wd_pending &&
@@ -1243,41 +945,43 @@ static int mei_irq_thread_write_handler(struct mei_io_list *cmpl_list,
                        dev->wd_pending = false;
 
                        if (dev->wd_state == MEI_WD_RUNNING)
-                               *slots -= mei_data2slots(MEI_WD_START_MSG_SIZE);
+                               slots -= mei_data2slots(MEI_WD_START_MSG_SIZE);
                        else
-                               *slots -= mei_data2slots(MEI_WD_STOP_MSG_SIZE);
+                               slots -= mei_data2slots(MEI_WD_STOP_MSG_SIZE);
                }
        }
 
        /* complete control write list CB */
        dev_dbg(&dev->pdev->dev, "complete control write list cb.\n");
-       list_for_each_entry_safe(pos, next,
-                               &dev->ctrl_wr_list.mei_cb.cb_list, cb_list) {
-               cl = (struct mei_cl *) pos->file_private;
+       list_for_each_entry_safe(pos, next, &dev->ctrl_wr_list.list, list) {
+               cl = pos->cl;
                if (!cl) {
-                       list_del(&pos->cb_list);
+                       list_del(&pos->list);
                        return -ENODEV;
                }
-               switch (pos->major_file_operations) {
-               case MEI_CLOSE:
+               switch (pos->fop_type) {
+               case MEI_FOP_CLOSE:
                        /* send disconnect message */
-                       ret = _mei_irq_thread_close(dev, slots, pos, cl, cmpl_list);
+                       ret = _mei_irq_thread_close(dev, &slots, pos,
+                                               cl, cmpl_list);
                        if (ret)
                                return ret;
 
                        break;
-               case MEI_READ:
+               case MEI_FOP_READ:
                        /* send flow control message */
-                       ret = _mei_irq_thread_read(dev, slots, pos, cl, cmpl_list);
+                       ret = _mei_irq_thread_read(dev, &slots, pos,
+                                               cl, cmpl_list);
                        if (ret)
                                return ret;
 
                        break;
-               case MEI_IOCTL:
+               case MEI_FOP_IOCTL:
                        /* connect message */
                        if (mei_other_client_is_connecting(dev, cl))
                                continue;
-                       ret = _mei_irq_thread_ioctl(dev, slots, pos, cl, cmpl_list);
+                       ret = _mei_irq_thread_ioctl(dev, &slots, pos,
+                                               cl, cmpl_list);
                        if (ret)
                                return ret;
 
@@ -1290,40 +994,26 @@ static int mei_irq_thread_write_handler(struct mei_io_list *cmpl_list,
        }
        /* complete  write list CB */
        dev_dbg(&dev->pdev->dev, "complete write list cb.\n");
-       list_for_each_entry_safe(pos, next,
-                               &dev->write_list.mei_cb.cb_list, cb_list) {
-               cl = (struct mei_cl *)pos->file_private;
+       list_for_each_entry_safe(pos, next, &dev->write_list.list, list) {
+               cl = pos->cl;
                if (cl == NULL)
                        continue;
-
-               if (cl != &dev->iamthif_cl) {
-                       if (mei_flow_ctrl_creds(dev, cl) <= 0) {
-                               dev_dbg(&dev->pdev->dev,
-                                       "No flow control credentials for client %d, not sending.\n",
-                                       cl->host_client_id);
-                               continue;
-                       }
-                       ret = _mei_irq_thread_cmpl(dev, slots, pos,
-                                               cl, cmpl_list);
-                       if (ret)
-                               return ret;
-
-               } else if (cl == &dev->iamthif_cl) {
-                       /* IAMTHIF IOCTL */
-                       dev_dbg(&dev->pdev->dev, "complete amthi write cb.\n");
-                       if (mei_flow_ctrl_creds(dev, cl) <= 0) {
-                               dev_dbg(&dev->pdev->dev,
-                                       "No flow control credentials for amthi client %d.\n",
-                                       cl->host_client_id);
-                               continue;
-                       }
-                       ret = _mei_irq_thread_cmpl_iamthif(dev, slots, pos,
-                                               cl, cmpl_list);
-                       if (ret)
-                               return ret;
-
+               if (mei_flow_ctrl_creds(dev, cl) <= 0) {
+                       dev_dbg(&dev->pdev->dev,
+                               "No flow control credentials for client %d, not sending.\n",
+                               cl->host_client_id);
+                       continue;
                }
 
+               if (cl == &dev->iamthif_cl)
+                       ret = mei_amthif_irq_write_complete(dev, &slots,
+                                                       pos, cmpl_list);
+               else
+                       ret = mei_irq_thread_write_complete(dev, &slots, pos,
+                                               cmpl_list);
+               if (ret)
+                       return ret;
+
        }
        return 0;
 }
@@ -1342,7 +1032,6 @@ void mei_timer(struct work_struct *work)
        unsigned long timeout;
        struct mei_cl *cl_pos = NULL;
        struct mei_cl *cl_next = NULL;
-       struct list_head *amthi_complete_list = NULL;
        struct mei_cl_cb  *cb_pos = NULL;
        struct mei_cl_cb  *cb_next = NULL;
 
@@ -1385,19 +1074,18 @@ void mei_timer(struct work_struct *work)
                        dev->iamthif_state = MEI_IAMTHIF_IDLE;
                        dev->iamthif_timer = 0;
 
-                       if (dev->iamthif_current_cb)
-                               mei_free_cb_private(dev->iamthif_current_cb);
+                       mei_io_cb_free(dev->iamthif_current_cb);
+                       dev->iamthif_current_cb = NULL;
 
                        dev->iamthif_file_object = NULL;
-                       dev->iamthif_current_cb = NULL;
-                       mei_run_next_iamthif_cmd(dev);
+                       mei_amthif_run_next_cmd(dev);
                }
        }
 
        if (dev->iamthif_timer) {
 
                timeout = dev->iamthif_timer +
-                               msecs_to_jiffies(IAMTHIF_READ_TIMER);
+                       mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER);
 
                dev_dbg(&dev->pdev->dev, "dev->iamthif_timer = %ld\n",
                                dev->iamthif_timer);
@@ -1411,25 +1099,22 @@ void mei_timer(struct work_struct *work)
 
                        dev_dbg(&dev->pdev->dev, "freeing AMTHI for other requests\n");
 
-                       amthi_complete_list = &dev->amthi_read_complete_list.
-                                       mei_cb.cb_list;
-
-                       list_for_each_entry_safe(cb_pos, cb_next, amthi_complete_list, cb_list) {
+                       list_for_each_entry_safe(cb_pos, cb_next,
+                               &dev->amthif_rd_complete_list.list, list) {
 
                                cl_pos = cb_pos->file_object->private_data;
 
                                /* Finding the AMTHI entry. */
                                if (cl_pos == &dev->iamthif_cl)
-                                       list_del(&cb_pos->cb_list);
+                                       list_del(&cb_pos->list);
                        }
-                       if (dev->iamthif_current_cb)
-                               mei_free_cb_private(dev->iamthif_current_cb);
+                       mei_io_cb_free(dev->iamthif_current_cb);
+                       dev->iamthif_current_cb = NULL;
 
                        dev->iamthif_file_object->private_data = NULL;
                        dev->iamthif_file_object = NULL;
-                       dev->iamthif_current_cb = NULL;
                        dev->iamthif_timer = 0;
-                       mei_run_next_iamthif_cmd(dev);
+                       mei_amthif_run_next_cmd(dev);
 
                }
        }
@@ -1451,7 +1136,7 @@ out:
 irqreturn_t mei_interrupt_thread_handler(int irq, void *dev_id)
 {
        struct mei_device *dev = (struct mei_device *) dev_id;
-       struct mei_io_list complete_list;
+       struct mei_cl_cb complete_list;
        struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL;
        struct mei_cl *cl;
        s32 slots;
@@ -1504,17 +1189,17 @@ irqreturn_t mei_interrupt_thread_handler(int irq, void *dev_id)
        }
        /* check slots available for reading */
        slots = mei_count_full_read_slots(dev);
-       dev_dbg(&dev->pdev->dev, "slots =%08x  extra_write_index =%08x.\n",
-               slots, dev->extra_write_index);
-       while (slots > 0 && !dev->extra_write_index) {
-               dev_dbg(&dev->pdev->dev, "slots =%08x  extra_write_index =%08x.\n",
-                               slots, dev->extra_write_index);
+       while (slots > 0) {
+               /* we have urgent data to send so break the read */
+               if (dev->wr_ext_msg.hdr.length)
+                       break;
+               dev_dbg(&dev->pdev->dev, "slots =%08x\n", slots);
                dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_handler.\n");
                rets = mei_irq_thread_read_handler(&complete_list, dev, &slots);
                if (rets)
                        goto end;
        }
-       rets = mei_irq_thread_write_handler(&complete_list, dev, &slots);
+       rets = mei_irq_thread_write_handler(dev, &complete_list);
 end:
        dev_dbg(&dev->pdev->dev, "end of bottom half function.\n");
        dev->host_hw_state = mei_hcsr_read(dev);
@@ -1531,21 +1216,20 @@ end:
                wake_up_interruptible(&dev->wait_recvd_msg);
                bus_message_received = false;
        }
-       if (list_empty(&complete_list.mei_cb.cb_list))
+       if (list_empty(&complete_list.list))
                return IRQ_HANDLED;
 
 
-       list_for_each_entry_safe(cb_pos, cb_next,
-                       &complete_list.mei_cb.cb_list, cb_list) {
-               cl = (struct mei_cl *)cb_pos->file_private;
-               list_del(&cb_pos->cb_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_cmpl(cl, cb_pos);
                                cb_pos = NULL;
                        } else if (cl == &dev->iamthif_cl) {
-                               _mei_cmpl_iamthif(dev, cb_pos);
+                               mei_amthif_complete(dev, cb_pos);
                        }
                }
        }
index fcba98eb892e1e410d62a4c198d978ab82b667f5..eb93a1b53b9b29e322badffd693a34fc80feb942 100644 (file)
 #include <linux/mei.h>
 #include "interface.h"
 
+/**
+ * mei_io_cb_free - free mei_cb_private related memory
+ *
+ * @cb: mei callback struct
+ */
+void mei_io_cb_free(struct mei_cl_cb *cb)
+{
+       if (cb == NULL)
+               return;
+
+       kfree(cb->request_buffer.data);
+       kfree(cb->response_buffer.data);
+       kfree(cb);
+}
+/**
+ * mei_io_cb_init - allocate and initialize io callback
+ *
+ * @cl - mei client
+ * @file: pointer to file structure
+ *
+ * returns mei_cl_cb pointer or NULL;
+ */
+struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, struct file *fp)
+{
+       struct mei_cl_cb *cb;
+
+       cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL);
+       if (!cb)
+               return NULL;
+
+       mei_io_list_init(cb);
+
+       cb->file_object = fp;
+       cb->cl = cl;
+       cb->buf_idx = 0;
+       return cb;
+}
+
+
+/**
+ * mei_io_cb_alloc_req_buf - allocate request buffer
+ *
+ * @cb -  io callback structure
+ * @size: size of the buffer
+ *
+ * returns 0 on success
+ *         -EINVAL if cb is NULL
+ *         -ENOMEM if allocation failed
+ */
+int mei_io_cb_alloc_req_buf(struct mei_cl_cb *cb, size_t length)
+{
+       if (!cb)
+               return -EINVAL;
+
+       if (length == 0)
+               return 0;
+
+       cb->request_buffer.data = kmalloc(length, GFP_KERNEL);
+       if (!cb->request_buffer.data)
+               return -ENOMEM;
+       cb->request_buffer.size = length;
+       return 0;
+}
+/**
+ * mei_io_cb_alloc_req_buf - allocate respose buffer
+ *
+ * @cb -  io callback structure
+ * @size: size of the buffer
+ *
+ * returns 0 on success
+ *         -EINVAL if cb is NULL
+ *         -ENOMEM if allocation failed
+ */
+int mei_io_cb_alloc_resp_buf(struct mei_cl_cb *cb, size_t length)
+{
+       if (!cb)
+               return -EINVAL;
+
+       if (length == 0)
+               return 0;
+
+       cb->response_buffer.data = kmalloc(length, GFP_KERNEL);
+       if (!cb->response_buffer.data)
+               return -ENOMEM;
+       cb->response_buffer.size = length;
+       return 0;
+}
+
+
 /**
  * mei_me_cl_by_id return index to me_clients for client_id
  *
@@ -82,9 +171,7 @@ int mei_ioctl_connect_client(struct file *file,
        struct mei_cl_cb *cb;
        struct mei_client *client;
        struct mei_cl *cl;
-       struct mei_cl *cl_pos = NULL;
-       struct mei_cl *cl_next = NULL;
-       long timeout = CONNECT_TIMEOUT;
+       long timeout = mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT);
        int i;
        int err;
        int rets;
@@ -97,16 +184,14 @@ int mei_ioctl_connect_client(struct file *file,
 
        dev_dbg(&dev->pdev->dev, "mei_ioctl_connect_client() Entry\n");
 
-
        /* buffered ioctl cb */
-       cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL);
+       cb = mei_io_cb_init(cl, file);
        if (!cb) {
                rets = -ENOMEM;
                goto end;
        }
-       INIT_LIST_HEAD(&cb->cb_list);
 
-       cb->major_file_operations = MEI_IOCTL;
+       cb->fop_type = MEI_FOP_IOCTL;
 
        if (dev->dev_state != MEI_DEV_ENABLED) {
                rets = -ENODEV;
@@ -142,21 +227,9 @@ int mei_ioctl_connect_client(struct file *file,
                        goto end;
                }
                clear_bit(cl->host_client_id, dev->host_clients_map);
-               list_for_each_entry_safe(cl_pos, cl_next,
-                                        &dev->file_list, link) {
-                       if (mei_cl_cmp_id(cl, cl_pos)) {
-                               dev_dbg(&dev->pdev->dev,
-                                       "remove file private data node host"
-                                   " client = %d, ME client = %d.\n",
-                                   cl_pos->host_client_id,
-                                   cl_pos->me_client_id);
-                               list_del(&cl_pos->link);
-                       }
+               mei_me_cl_unlink(dev, cl);
 
-               }
-               dev_dbg(&dev->pdev->dev, "free file private data memory.\n");
                kfree(cl);
-
                cl = NULL;
                file->private_data = &dev->iamthif_cl;
 
@@ -192,25 +265,19 @@ int mei_ioctl_connect_client(struct file *file,
                } else {
                        dev_dbg(&dev->pdev->dev, "Sending connect message - succeeded\n");
                        cl->timer_count = MEI_CONNECT_TIMEOUT;
-                       cb->file_private = cl;
-                       list_add_tail(&cb->cb_list,
-                                     &dev->ctrl_rd_list.mei_cb.
-                                     cb_list);
+                       list_add_tail(&cb->list, &dev->ctrl_rd_list.list);
                }
 
 
        } else {
                dev_dbg(&dev->pdev->dev, "Queuing the connect request due to device busy\n");
-               cb->file_private = cl;
                dev_dbg(&dev->pdev->dev, "add connect cb to control write list.\n");
-               list_add_tail(&cb->cb_list,
-                             &dev->ctrl_wr_list.mei_cb.cb_list);
+               list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
        }
        mutex_unlock(&dev->device_lock);
        err = wait_event_timeout(dev->wait_recvd_msg,
                        (MEI_FILE_CONNECTED == cl->state ||
-                        MEI_FILE_DISCONNECTED == cl->state),
-                       timeout * HZ);
+                        MEI_FILE_DISCONNECTED == cl->state), timeout);
 
        mutex_lock(&dev->device_lock);
        if (MEI_FILE_CONNECTED == cl->state) {
@@ -234,153 +301,7 @@ int mei_ioctl_connect_client(struct file *file,
        rets = 0;
 end:
        dev_dbg(&dev->pdev->dev, "free connect cb memory.");
-       kfree(cb);
-       return rets;
-}
-
-/**
- * find_amthi_read_list_entry - finds a amthilist entry for current file
- *
- * @dev: the device structure
- * @file: pointer to file object
- *
- * returns   returned a list entry on success, NULL on failure.
- */
-struct mei_cl_cb *find_amthi_read_list_entry(
-               struct mei_device *dev,
-               struct file *file)
-{
-       struct mei_cl *cl_temp;
-       struct mei_cl_cb *pos = NULL;
-       struct mei_cl_cb *next = NULL;
-
-       list_for_each_entry_safe(pos, next,
-           &dev->amthi_read_complete_list.mei_cb.cb_list, cb_list) {
-               cl_temp = (struct mei_cl *)pos->file_private;
-               if (cl_temp && cl_temp == &dev->iamthif_cl &&
-                       pos->file_object == file)
-                       return pos;
-       }
-       return NULL;
-}
-
-/**
- * amthi_read - read data from AMTHI client
- *
- * @dev: the device structure
- * @if_num:  minor number
- * @file: pointer to file object
- * @*ubuf: pointer to user data in user space
- * @length: data length to read
- * @offset: data read offset
- *
- * Locking: called under "dev->device_lock" lock
- *
- * returns
- *  returned data length on success,
- *  zero if no data to read,
- *  negative on failure.
- */
-int amthi_read(struct mei_device *dev, struct file *file,
-              char __user *ubuf, size_t length, loff_t *offset)
-{
-       int rets;
-       int wait_ret;
-       struct mei_cl_cb *cb = NULL;
-       struct mei_cl *cl = file->private_data;
-       unsigned long timeout;
-       int i;
-
-       /* Only Posible if we are in timeout */
-       if (!cl || cl != &dev->iamthif_cl) {
-               dev_dbg(&dev->pdev->dev, "bad file ext.\n");
-               return -ETIMEDOUT;
-       }
-
-       i = mei_me_cl_by_id(dev, dev->iamthif_cl.me_client_id);
-
-       if (i < 0) {
-               dev_dbg(&dev->pdev->dev, "amthi client not found.\n");
-               return -ENODEV;
-       }
-       dev_dbg(&dev->pdev->dev, "checking amthi data\n");
-       cb = find_amthi_read_list_entry(dev, file);
-
-       /* Check for if we can block or not*/
-       if (cb == NULL && file->f_flags & O_NONBLOCK)
-               return -EAGAIN;
-
-
-       dev_dbg(&dev->pdev->dev, "waiting for amthi data\n");
-       while (cb == NULL) {
-               /* unlock the Mutex */
-               mutex_unlock(&dev->device_lock);
-
-               wait_ret = wait_event_interruptible(dev->iamthif_cl.wait,
-                       (cb = find_amthi_read_list_entry(dev, file)));
-
-               if (wait_ret)
-                       return -ERESTARTSYS;
-
-               dev_dbg(&dev->pdev->dev, "woke up from sleep\n");
-
-               /* Locking again the Mutex */
-               mutex_lock(&dev->device_lock);
-       }
-
-
-       dev_dbg(&dev->pdev->dev, "Got amthi data\n");
-       dev->iamthif_timer = 0;
-
-       if (cb) {
-               timeout = cb->read_time + msecs_to_jiffies(IAMTHIF_READ_TIMER);
-               dev_dbg(&dev->pdev->dev, "amthi timeout = %lud\n",
-                               timeout);
-
-               if  (time_after(jiffies, timeout)) {
-                       dev_dbg(&dev->pdev->dev, "amthi Time out\n");
-                       /* 15 sec for the message has expired */
-                       list_del(&cb->cb_list);
-                       rets = -ETIMEDOUT;
-                       goto free;
-               }
-       }
-       /* if the whole message will fit remove it from the list */
-       if (cb->information >= *offset && length >= (cb->information - *offset))
-               list_del(&cb->cb_list);
-       else if (cb->information > 0 && cb->information <= *offset) {
-               /* end of the message has been reached */
-               list_del(&cb->cb_list);
-               rets = 0;
-               goto free;
-       }
-               /* else means that not full buffer will be read and do not
-                * remove message from deletion list
-                */
-
-       dev_dbg(&dev->pdev->dev, "amthi cb->response_buffer size - %d\n",
-           cb->response_buffer.size);
-       dev_dbg(&dev->pdev->dev, "amthi cb->information - %lu\n",
-           cb->information);
-
-       /* length is being turncated to PAGE_SIZE, however,
-        * the information may be longer */
-       length = min_t(size_t, length, (cb->information - *offset));
-
-       if (copy_to_user(ubuf, cb->response_buffer.data + *offset, length))
-               rets = -EFAULT;
-       else {
-               rets = length;
-               if ((*offset + length) < cb->information) {
-                       *offset += length;
-                       goto out;
-               }
-       }
-free:
-       dev_dbg(&dev->pdev->dev, "free amthi cb memory.\n");
-       *offset = 0;
-       mei_free_cb_private(cb);
-out:
+       mei_io_cb_free(cb);
        return rets;
 }
 
@@ -396,7 +317,7 @@ out:
 int mei_start_read(struct mei_device *dev, struct mei_cl *cl)
 {
        struct mei_cl_cb *cb;
-       int rets = 0;
+       int rets;
        int i;
 
        if (cl->state != MEI_FILE_CONNECTED)
@@ -405,187 +326,41 @@ int mei_start_read(struct mei_device *dev, struct mei_cl *cl)
        if (dev->dev_state != MEI_DEV_ENABLED)
                return -ENODEV;
 
-       dev_dbg(&dev->pdev->dev, "check if read is pending.\n");
        if (cl->read_pending || cl->read_cb) {
                dev_dbg(&dev->pdev->dev, "read is pending.\n");
                return -EBUSY;
        }
+       i = mei_me_cl_by_id(dev, cl->me_client_id);
+       if (i < 0) {
+               dev_err(&dev->pdev->dev, "no such me client %d\n",
+                       cl->me_client_id);
+               return  -ENODEV;
+       }
 
-       cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL);
+       cb = mei_io_cb_init(cl, NULL);
        if (!cb)
                return -ENOMEM;
 
-       dev_dbg(&dev->pdev->dev, "allocation call back successful. host client = %d, ME client = %d\n",
-               cl->host_client_id, cl->me_client_id);
-       i = mei_me_cl_by_id(dev, cl->me_client_id);
-       if (i < 0) {
-               rets = -ENODEV;
-               goto unlock;
-       }
+       rets = mei_io_cb_alloc_resp_buf(cb,
+                       dev->me_clients[i].props.max_msg_length);
+       if (rets)
+               goto err;
 
-       cb->response_buffer.size = dev->me_clients[i].props.max_msg_length;
-       cb->response_buffer.data =
-                       kmalloc(cb->response_buffer.size, GFP_KERNEL);
-       if (!cb->response_buffer.data) {
-               rets = -ENOMEM;
-               goto unlock;
-       }
-       dev_dbg(&dev->pdev->dev, "allocation call back data success.\n");
-       cb->major_file_operations = MEI_READ;
-       /* make sure information is zero before we start */
-       cb->information = 0;
-       cb->file_private = (void *) cl;
+       cb->fop_type = MEI_FOP_READ;
        cl->read_cb = cb;
        if (dev->mei_host_buffer_is_empty) {
                dev->mei_host_buffer_is_empty = false;
                if (mei_send_flow_control(dev, cl)) {
                        rets = -ENODEV;
-                       goto unlock;
+                       goto err;
                }
-               list_add_tail(&cb->cb_list, &dev->read_list.mei_cb.cb_list);
+               list_add_tail(&cb->list, &dev->read_list.list);
        } else {
-               list_add_tail(&cb->cb_list, &dev->ctrl_wr_list.mei_cb.cb_list);
+               list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
        }
        return rets;
-unlock:
-       mei_free_cb_private(cb);
+err:
+       mei_io_cb_free(cb);
        return rets;
 }
 
-/**
- * amthi_write - write iamthif data to amthi client
- *
- * @dev: the device structure
- * @cb: mei call back struct
- *
- * returns 0 on success, <0 on failure.
- */
-int amthi_write(struct mei_device *dev, struct mei_cl_cb *cb)
-{
-       struct mei_msg_hdr mei_hdr;
-       int ret;
-
-       if (!dev || !cb)
-               return -ENODEV;
-
-       dev_dbg(&dev->pdev->dev, "write data to amthi client.\n");
-
-       dev->iamthif_state = MEI_IAMTHIF_WRITING;
-       dev->iamthif_current_cb = cb;
-       dev->iamthif_file_object = cb->file_object;
-       dev->iamthif_canceled = false;
-       dev->iamthif_ioctl = true;
-       dev->iamthif_msg_buf_size = cb->request_buffer.size;
-       memcpy(dev->iamthif_msg_buf, cb->request_buffer.data,
-              cb->request_buffer.size);
-
-       ret = mei_flow_ctrl_creds(dev, &dev->iamthif_cl);
-       if (ret < 0)
-               return ret;
-
-       if (ret && dev->mei_host_buffer_is_empty) {
-               ret = 0;
-               dev->mei_host_buffer_is_empty = false;
-               if (cb->request_buffer.size > mei_hbuf_max_data(dev)) {
-                       mei_hdr.length = mei_hbuf_max_data(dev);
-                       mei_hdr.msg_complete = 0;
-               } else {
-                       mei_hdr.length = cb->request_buffer.size;
-                       mei_hdr.msg_complete = 1;
-               }
-
-               mei_hdr.host_addr = dev->iamthif_cl.host_client_id;
-               mei_hdr.me_addr = dev->iamthif_cl.me_client_id;
-               mei_hdr.reserved = 0;
-               dev->iamthif_msg_buf_index += mei_hdr.length;
-               if (mei_write_message(dev, &mei_hdr,
-                                       (unsigned char *)(dev->iamthif_msg_buf),
-                                       mei_hdr.length))
-                       return -ENODEV;
-
-               if (mei_hdr.msg_complete) {
-                       if (mei_flow_ctrl_reduce(dev, &dev->iamthif_cl))
-                               return -ENODEV;
-                       dev->iamthif_flow_control_pending = true;
-                       dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL;
-                       dev_dbg(&dev->pdev->dev, "add amthi cb to write waiting list\n");
-                       dev->iamthif_current_cb = cb;
-                       dev->iamthif_file_object = cb->file_object;
-                       list_add_tail(&cb->cb_list,
-                                     &dev->write_waiting_list.mei_cb.cb_list);
-               } else {
-                       dev_dbg(&dev->pdev->dev, "message does not complete, "
-                                       "so add amthi cb to write list.\n");
-                       list_add_tail(&cb->cb_list,
-                                     &dev->write_list.mei_cb.cb_list);
-               }
-       } else {
-               if (!(dev->mei_host_buffer_is_empty))
-                       dev_dbg(&dev->pdev->dev, "host buffer is not empty");
-
-               dev_dbg(&dev->pdev->dev, "No flow control credentials, "
-                               "so add iamthif cb to write list.\n");
-               list_add_tail(&cb->cb_list, &dev->write_list.mei_cb.cb_list);
-       }
-       return 0;
-}
-
-/**
- * iamthif_ioctl_send_msg - send cmd data to amthi client
- *
- * @dev: the device structure
- *
- * returns 0 on success, <0 on failure.
- */
-void mei_run_next_iamthif_cmd(struct mei_device *dev)
-{
-       struct mei_cl *cl_tmp;
-       struct mei_cl_cb *pos = NULL;
-       struct mei_cl_cb *next = NULL;
-       int status;
-
-       if (!dev)
-               return;
-
-       dev->iamthif_msg_buf_size = 0;
-       dev->iamthif_msg_buf_index = 0;
-       dev->iamthif_canceled = false;
-       dev->iamthif_ioctl = true;
-       dev->iamthif_state = MEI_IAMTHIF_IDLE;
-       dev->iamthif_timer = 0;
-       dev->iamthif_file_object = NULL;
-
-       dev_dbg(&dev->pdev->dev, "complete amthi cmd_list cb.\n");
-
-       list_for_each_entry_safe(pos, next,
-                       &dev->amthi_cmd_list.mei_cb.cb_list, cb_list) {
-               list_del(&pos->cb_list);
-               cl_tmp = (struct mei_cl *)pos->file_private;
-
-               if (cl_tmp && cl_tmp == &dev->iamthif_cl) {
-                       status = amthi_write(dev, pos);
-                       if (status) {
-                               dev_dbg(&dev->pdev->dev,
-                                       "amthi write failed status = %d\n",
-                                               status);
-                               return;
-                       }
-                       break;
-               }
-       }
-}
-
-/**
- * mei_free_cb_private - free mei_cb_private related memory
- *
- * @cb: mei callback struct
- */
-void mei_free_cb_private(struct mei_cl_cb *cb)
-{
-       if (cb == NULL)
-               return;
-
-       kfree(cb->request_buffer.data);
-       kfree(cb->response_buffer.data);
-       kfree(cb);
-}
index e8b0858132c1560c7103e3f0b8f8e3d31f1ac424..43fb52ff98ad261aa2578cce00db25720de810d8 100644 (file)
@@ -89,93 +89,6 @@ MODULE_DEVICE_TABLE(pci, mei_pci_tbl);
 static DEFINE_MUTEX(mei_mutex);
 
 
-/**
- * mei_clear_list - removes all callbacks associated with file
- *             from mei_cb_list
- *
- * @dev: device structure.
- * @file: file structure
- * @mei_cb_list: callbacks list
- *
- * mei_clear_list is called to clear resources associated with file
- * when application calls close function or Ctrl-C was pressed
- *
- * returns true if callback removed from the list, false otherwise
- */
-static bool mei_clear_list(struct mei_device *dev,
-               struct file *file, struct list_head *mei_cb_list)
-{
-       struct mei_cl_cb *cb_pos = NULL;
-       struct mei_cl_cb *cb_next = NULL;
-       struct file *file_temp;
-       bool removed = false;
-
-       /* list all list member */
-       list_for_each_entry_safe(cb_pos, cb_next, mei_cb_list, cb_list) {
-               file_temp = (struct file *)cb_pos->file_object;
-               /* check if list member associated with a file */
-               if (file_temp == file) {
-                       /* remove member from the list */
-                       list_del(&cb_pos->cb_list);
-                       /* check if cb equal to current iamthif cb */
-                       if (dev->iamthif_current_cb == cb_pos) {
-                               dev->iamthif_current_cb = NULL;
-                               /* send flow control to iamthif client */
-                               mei_send_flow_control(dev, &dev->iamthif_cl);
-                       }
-                       /* free all allocated buffers */
-                       mei_free_cb_private(cb_pos);
-                       cb_pos = NULL;
-                       removed = true;
-               }
-       }
-       return removed;
-}
-
-/**
- * mei_clear_lists - removes all callbacks associated with file
- *
- * @dev: device structure
- * @file: file structure
- *
- * mei_clear_lists is called to clear resources associated with file
- * when application calls close function or Ctrl-C was pressed
- *
- * returns true if callback removed from the list, false otherwise
- */
-static bool mei_clear_lists(struct mei_device *dev, struct file *file)
-{
-       bool removed = false;
-
-       /* remove callbacks associated with a file */
-       mei_clear_list(dev, file, &dev->amthi_cmd_list.mei_cb.cb_list);
-       if (mei_clear_list(dev, file,
-                           &dev->amthi_read_complete_list.mei_cb.cb_list))
-               removed = true;
-
-       mei_clear_list(dev, file, &dev->ctrl_rd_list.mei_cb.cb_list);
-
-       if (mei_clear_list(dev, file, &dev->ctrl_wr_list.mei_cb.cb_list))
-               removed = true;
-
-       if (mei_clear_list(dev, file, &dev->write_waiting_list.mei_cb.cb_list))
-               removed = true;
-
-       if (mei_clear_list(dev, file, &dev->write_list.mei_cb.cb_list))
-               removed = true;
-
-       /* check if iamthif_current_cb not NULL */
-       if (dev->iamthif_current_cb && !removed) {
-               /* check file and iamthif current cb association */
-               if (dev->iamthif_current_cb->file_object == file) {
-                       /* remove cb */
-                       mei_free_cb_private(dev->iamthif_current_cb);
-                       dev->iamthif_current_cb = NULL;
-                       removed = true;
-               }
-       }
-       return removed;
-}
 /**
  * find_read_list_entry - find read list entry
  *
@@ -192,14 +105,9 @@ static struct mei_cl_cb *find_read_list_entry(
        struct mei_cl_cb *next = NULL;
 
        dev_dbg(&dev->pdev->dev, "remove read_list CB\n");
-       list_for_each_entry_safe(pos, next,
-                       &dev->read_list.mei_cb.cb_list, cb_list) {
-               struct mei_cl *cl_temp;
-               cl_temp = (struct mei_cl *)pos->file_private;
-
-               if (mei_cl_cmp_id(cl, cl_temp))
+       list_for_each_entry_safe(pos, next, &dev->read_list.list, list)
+               if (mei_cl_cmp_id(cl, pos->cl))
                        return pos;
-       }
        return NULL;
 }
 
@@ -297,67 +205,51 @@ static int mei_release(struct inode *inode, struct file *file)
        dev = cl->dev;
 
        mutex_lock(&dev->device_lock);
-       if (cl != &dev->iamthif_cl) {
-               if (cl->state == MEI_FILE_CONNECTED) {
-                       cl->state = MEI_FILE_DISCONNECTING;
-                       dev_dbg(&dev->pdev->dev,
-                               "disconnecting client host client = %d, "
-                           "ME client = %d\n",
-                           cl->host_client_id,
-                           cl->me_client_id);
-                       rets = mei_disconnect_host_client(dev, cl);
-               }
-               mei_cl_flush_queues(cl);
-               dev_dbg(&dev->pdev->dev, "remove client host client = %d, ME client = %d\n",
+       if (cl == &dev->iamthif_cl) {
+               rets = mei_amthif_release(dev, file);
+               goto out;
+       }
+       if (cl->state == MEI_FILE_CONNECTED) {
+               cl->state = MEI_FILE_DISCONNECTING;
+               dev_dbg(&dev->pdev->dev,
+                       "disconnecting client host client = %d, "
+                   "ME client = %d\n",
                    cl->host_client_id,
                    cl->me_client_id);
+               rets = mei_disconnect_host_client(dev, cl);
+       }
+       mei_cl_flush_queues(cl);
+       dev_dbg(&dev->pdev->dev, "remove client host client = %d, ME client = %d\n",
+           cl->host_client_id,
+           cl->me_client_id);
+
+       if (dev->open_handle_count > 0) {
+               clear_bit(cl->host_client_id, dev->host_clients_map);
+               dev->open_handle_count--;
+       }
+       mei_me_cl_unlink(dev, cl);
 
-               if (dev->open_handle_count > 0) {
-                       clear_bit(cl->host_client_id, dev->host_clients_map);
-                       dev->open_handle_count--;
-               }
-               mei_remove_client_from_file_list(dev, cl->host_client_id);
-
-               /* free read cb */
-               cb = NULL;
-               if (cl->read_cb) {
-                       cb = find_read_list_entry(dev, cl);
-                       /* Remove entry from read list */
-                       if (cb)
-                               list_del(&cb->cb_list);
-
-                       cb = cl->read_cb;
-                       cl->read_cb = NULL;
-               }
-
-               file->private_data = NULL;
-
-               if (cb) {
-                       mei_free_cb_private(cb);
-                       cb = NULL;
-               }
+       /* free read cb */
+       cb = NULL;
+       if (cl->read_cb) {
+               cb = find_read_list_entry(dev, cl);
+               /* Remove entry from read list */
+               if (cb)
+                       list_del(&cb->list);
 
-               kfree(cl);
-       } else {
-               if (dev->open_handle_count > 0)
-                       dev->open_handle_count--;
-
-               if (dev->iamthif_file_object == file &&
-                   dev->iamthif_state != MEI_IAMTHIF_IDLE) {
-
-                       dev_dbg(&dev->pdev->dev, "amthi canceled iamthif state %d\n",
-                           dev->iamthif_state);
-                       dev->iamthif_canceled = true;
-                       if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE) {
-                               dev_dbg(&dev->pdev->dev, "run next amthi iamthif cb\n");
-                               mei_run_next_iamthif_cmd(dev);
-                       }
-               }
+               cb = cl->read_cb;
+               cl->read_cb = NULL;
+       }
 
-               if (mei_clear_lists(dev, file))
-                       dev->iamthif_state = MEI_IAMTHIF_IDLE;
+       file->private_data = NULL;
 
+       if (cb) {
+               mei_io_cb_free(cb);
+               cb = NULL;
        }
+
+       kfree(cl);
+out:
        mutex_unlock(&dev->device_lock);
        return rets;
 }
@@ -411,20 +303,19 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
        }
 
        if (cl == &dev->iamthif_cl) {
-               rets = amthi_read(dev, file, ubuf, length, offset);
+               rets = mei_amthif_read(dev, file, ubuf, length, offset);
                goto out;
        }
 
-       if (cl->read_cb && cl->read_cb->information > *offset) {
+       if (cl->read_cb && cl->read_cb->buf_idx > *offset) {
                cb = cl->read_cb;
                goto copy_buffer;
-       } else if (cl->read_cb && cl->read_cb->information > 0 &&
-                  cl->read_cb->information <= *offset) {
+       } else if (cl->read_cb && cl->read_cb->buf_idx > 0 &&
+                  cl->read_cb->buf_idx <= *offset) {
                cb = cl->read_cb;
                rets = 0;
                goto free;
-       } else if ((!cl->read_cb || !cl->read_cb->information) &&
-                   *offset > 0) {
+       } else if ((!cl->read_cb || !cl->read_cb->buf_idx) && *offset > 0) {
                /*Offset needs to be cleaned for contiguous reads*/
                *offset = 0;
                rets = 0;
@@ -481,16 +372,15 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
 copy_buffer:
        dev_dbg(&dev->pdev->dev, "cb->response_buffer size - %d\n",
            cb->response_buffer.size);
-       dev_dbg(&dev->pdev->dev, "cb->information - %lu\n",
-           cb->information);
-       if (length == 0 || ubuf == NULL || *offset > cb->information) {
+       dev_dbg(&dev->pdev->dev, "cb->buf_idx - %lu\n", cb->buf_idx);
+       if (length == 0 || ubuf == NULL || *offset > cb->buf_idx) {
                rets = -EMSGSIZE;
                goto free;
        }
 
-       /* length is being truncated to PAGE_SIZE, however, */
-       /* information size may be longer */
-       length = min_t(size_t, length, (cb->information - *offset));
+       /* length is being truncated to PAGE_SIZE,
+        * however buf_idx may point beyond that */
+       length = min_t(size_t, length, cb->buf_idx - *offset);
 
        if (copy_to_user(ubuf, cb->response_buffer.data + *offset, length)) {
                rets = -EFAULT;
@@ -499,15 +389,15 @@ copy_buffer:
 
        rets = length;
        *offset += length;
-       if ((unsigned long)*offset < cb->information)
+       if ((unsigned long)*offset < cb->buf_idx)
                goto out;
 
 free:
        cb_pos = find_read_list_entry(dev, cl);
        /* Remove entry from read list */
        if (cb_pos)
-               list_del(&cb_pos->cb_list);
-       mei_free_cb_private(cb);
+               list_del(&cb_pos->list);
+       mei_io_cb_free(cb);
        cl->reading_state = MEI_IDLE;
        cl->read_cb = NULL;
        cl->read_pending = 0;
@@ -516,7 +406,6 @@ out:
        mutex_unlock(&dev->device_lock);
        return rets;
 }
-
 /**
  * mei_write - the write function.
  *
@@ -546,23 +435,39 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
        mutex_lock(&dev->device_lock);
 
        if (dev->dev_state != MEI_DEV_ENABLED) {
-               mutex_unlock(&dev->device_lock);
-               return -ENODEV;
+               rets = -ENODEV;
+               goto err;
        }
 
+       i = mei_me_cl_by_id(dev, cl->me_client_id);
+       if (i < 0) {
+               rets = -ENODEV;
+               goto err;
+       }
+       if (length > dev->me_clients[i].props.max_msg_length || length <= 0) {
+               rets = -EMSGSIZE;
+               goto err;
+       }
+
+       if (cl->state != MEI_FILE_CONNECTED) {
+               rets = -ENODEV;
+               dev_err(&dev->pdev->dev, "host client = %d,  is not connected to ME client = %d",
+                       cl->host_client_id, cl->me_client_id);
+               goto err;
+       }
        if (cl == &dev->iamthif_cl) {
-               write_cb = find_amthi_read_list_entry(dev, file);
+               write_cb = mei_amthif_find_read_list_entry(dev, file);
 
                if (write_cb) {
                        timeout = write_cb->read_time +
-                                       msecs_to_jiffies(IAMTHIF_READ_TIMER);
+                               mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER);
 
                        if (time_after(jiffies, timeout) ||
-                                cl->reading_state == MEI_READ_COMPLETE) {
-                                       *offset = 0;
-                                       list_del(&write_cb->cb_list);
-                                       mei_free_cb_private(write_cb);
-                                       write_cb = NULL;
+                           cl->reading_state == MEI_READ_COMPLETE) {
+                               *offset = 0;
+                               list_del(&write_cb->list);
+                               mei_io_cb_free(write_cb);
+                               write_cb = NULL;
                        }
                }
        }
@@ -572,8 +477,8 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
                *offset = 0;
                write_cb = find_read_list_entry(dev, cl);
                if (write_cb) {
-                       list_del(&write_cb->cb_list);
-                       mei_free_cb_private(write_cb);
+                       list_del(&write_cb->list);
+                       mei_io_cb_free(write_cb);
                        write_cb = NULL;
                        cl->reading_state = MEI_IDLE;
                        cl->read_cb = NULL;
@@ -583,24 +488,21 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
                *offset = 0;
 
 
-       write_cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL);
+       write_cb = mei_io_cb_init(cl, file);
        if (!write_cb) {
-               mutex_unlock(&dev->device_lock);
-               return -ENOMEM;
+               dev_err(&dev->pdev->dev, "write cb allocation failed\n");
+               rets = -ENOMEM;
+               goto err;
        }
+       rets = mei_io_cb_alloc_req_buf(write_cb, length);
+       if (rets)
+               goto err;
 
-       write_cb->file_object = file;
-       write_cb->file_private = cl;
-       write_cb->request_buffer.data = kmalloc(length, GFP_KERNEL);
-       rets = -ENOMEM;
-       if (!write_cb->request_buffer.data)
-               goto unlock_dev;
-
-       dev_dbg(&dev->pdev->dev, "length =%d\n", (int) length);
+       dev_dbg(&dev->pdev->dev, "cb request size = %zd\n", length);
 
-       rets = -EFAULT;
-       if (copy_from_user(write_cb->request_buffer.data, ubuf, length))
-               goto unlock_dev;
+       rets = copy_from_user(write_cb->request_buffer.data, ubuf, length);
+       if (rets)
+               goto err;
 
        cl->sm_state = 0;
        if (length == 4 &&
@@ -612,139 +514,71 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
                                 write_cb->request_buffer.data, 4) == 0)))
                cl->sm_state |= MEI_WD_STATE_INDEPENDENCE_MSG_SENT;
 
-       INIT_LIST_HEAD(&write_cb->cb_list);
        if (cl == &dev->iamthif_cl) {
-               write_cb->response_buffer.data =
-                   kmalloc(dev->iamthif_mtu, GFP_KERNEL);
-               if (!write_cb->response_buffer.data) {
-                       rets = -ENOMEM;
-                       goto unlock_dev;
-               }
-               if (dev->dev_state != MEI_DEV_ENABLED) {
-                       rets = -ENODEV;
-                       goto unlock_dev;
-               }
-               i = mei_me_cl_by_id(dev, dev->iamthif_cl.me_client_id);
-               if (i < 0) {
-                       rets = -ENODEV;
-                       goto unlock_dev;
-               }
-               if (length > dev->me_clients[i].props.max_msg_length ||
-                          length <= 0) {
-                       rets = -EMSGSIZE;
-                       goto unlock_dev;
-               }
+               rets = mei_amthif_write(dev, write_cb);
 
-               write_cb->response_buffer.size = dev->iamthif_mtu;
-               write_cb->major_file_operations = MEI_IOCTL;
-               write_cb->information = 0;
-               write_cb->request_buffer.size = length;
-               if (dev->iamthif_cl.state != MEI_FILE_CONNECTED) {
-                       rets = -ENODEV;
-                       goto unlock_dev;
-               }
-
-               if (!list_empty(&dev->amthi_cmd_list.mei_cb.cb_list) ||
-                               dev->iamthif_state != MEI_IAMTHIF_IDLE) {
-                       dev_dbg(&dev->pdev->dev, "amthi_state = %d\n",
-                                       (int) dev->iamthif_state);
-                       dev_dbg(&dev->pdev->dev, "add amthi cb to amthi cmd waiting list\n");
-                       list_add_tail(&write_cb->cb_list,
-                                       &dev->amthi_cmd_list.mei_cb.cb_list);
-                       rets = length;
-               } else {
-                       dev_dbg(&dev->pdev->dev, "call amthi write\n");
-                       rets = amthi_write(dev, write_cb);
-
-                       if (rets) {
-                               dev_dbg(&dev->pdev->dev, "amthi write failed with status = %d\n",
-                                   rets);
-                               goto unlock_dev;
-                       }
-                       rets = length;
+               if (rets) {
+                       dev_err(&dev->pdev->dev,
+                               "amthi write failed with status = %d\n", rets);
+                       goto err;
                }
                mutex_unlock(&dev->device_lock);
-               return rets;
+               return length;
        }
 
-       write_cb->major_file_operations = MEI_WRITE;
-       /* make sure information is zero before we start */
-
-       write_cb->information = 0;
-       write_cb->request_buffer.size = length;
+       write_cb->fop_type = MEI_FOP_WRITE;
 
        dev_dbg(&dev->pdev->dev, "host client = %d, ME client = %d\n",
            cl->host_client_id, cl->me_client_id);
-       if (cl->state != MEI_FILE_CONNECTED) {
-               rets = -ENODEV;
-               dev_dbg(&dev->pdev->dev, "host client = %d,  is not connected to ME client = %d",
-                   cl->host_client_id,
-                   cl->me_client_id);
-               goto unlock_dev;
-       }
-       i = mei_me_cl_by_id(dev, cl->me_client_id);
-       if (i < 0) {
-               rets = -ENODEV;
-               goto unlock_dev;
-       }
-       if (length > dev->me_clients[i].props.max_msg_length || length <= 0) {
-               rets = -EINVAL;
-               goto unlock_dev;
-       }
-       write_cb->file_private = cl;
-
        rets = mei_flow_ctrl_creds(dev, cl);
        if (rets < 0)
-               goto unlock_dev;
+               goto err;
 
-       if (rets && dev->mei_host_buffer_is_empty) {
-               rets = 0;
-               dev->mei_host_buffer_is_empty = false;
-               if (length >  mei_hbuf_max_data(dev)) {
-                       mei_hdr.length = mei_hbuf_max_data(dev);
-                       mei_hdr.msg_complete = 0;
-               } else {
-                       mei_hdr.length = length;
-                       mei_hdr.msg_complete = 1;
-               }
-               mei_hdr.host_addr = cl->host_client_id;
-               mei_hdr.me_addr = cl->me_client_id;
-               mei_hdr.reserved = 0;
-               dev_dbg(&dev->pdev->dev, "call mei_write_message header=%08x.\n",
-                   *((u32 *) &mei_hdr));
-               if (mei_write_message(dev, &mei_hdr,
-                       (unsigned char *) (write_cb->request_buffer.data),
-                       mei_hdr.length)) {
-                       rets = -ENODEV;
-                       goto unlock_dev;
-               }
+       if (rets == 0 || dev->mei_host_buffer_is_empty == false) {
+               write_cb->buf_idx = 0;
+               mei_hdr.msg_complete = 0;
                cl->writing_state = MEI_WRITING;
-               write_cb->information = mei_hdr.length;
-               if (mei_hdr.msg_complete) {
-                       if (mei_flow_ctrl_reduce(dev, cl)) {
-                               rets = -ENODEV;
-                               goto unlock_dev;
-                       }
-                       list_add_tail(&write_cb->cb_list,
-                                     &dev->write_waiting_list.mei_cb.cb_list);
-               } else {
-                       list_add_tail(&write_cb->cb_list,
-                                     &dev->write_list.mei_cb.cb_list);
-               }
+               goto out;
+       }
 
+       dev->mei_host_buffer_is_empty = false;
+       if (length >  mei_hbuf_max_data(dev)) {
+               mei_hdr.length = mei_hbuf_max_data(dev);
+               mei_hdr.msg_complete = 0;
        } else {
+               mei_hdr.length = length;
+               mei_hdr.msg_complete = 1;
+       }
+       mei_hdr.host_addr = cl->host_client_id;
+       mei_hdr.me_addr = cl->me_client_id;
+       mei_hdr.reserved = 0;
+       dev_dbg(&dev->pdev->dev, "call mei_write_message header=%08x.\n",
+           *((u32 *) &mei_hdr));
+       if (mei_write_message(dev, &mei_hdr,
+               write_cb->request_buffer.data, mei_hdr.length)) {
+               rets = -ENODEV;
+               goto err;
+       }
+       cl->writing_state = MEI_WRITING;
+       write_cb->buf_idx = mei_hdr.length;
 
-               write_cb->information = 0;
-               cl->writing_state = MEI_WRITING;
-               list_add_tail(&write_cb->cb_list,
-                             &dev->write_list.mei_cb.cb_list);
+out:
+       if (mei_hdr.msg_complete) {
+               if (mei_flow_ctrl_reduce(dev, cl)) {
+                       rets = -ENODEV;
+                       goto err;
+               }
+               list_add_tail(&write_cb->list, &dev->write_waiting_list.list);
+       } else {
+               list_add_tail(&write_cb->list, &dev->write_list.list);
        }
+
        mutex_unlock(&dev->device_lock);
        return length;
 
-unlock_dev:
+err:
        mutex_unlock(&dev->device_lock);
-       mei_free_cb_private(write_cb);
+       mei_io_cb_free(write_cb);
        return rets;
 }
 
@@ -860,15 +694,7 @@ static unsigned int mei_poll(struct file *file, poll_table *wait)
 
 
        if (cl == &dev->iamthif_cl) {
-               mutex_unlock(&dev->device_lock);
-               poll_wait(file, &dev->iamthif_cl.wait, wait);
-               mutex_lock(&dev->device_lock);
-               if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE &&
-                       dev->iamthif_file_object == file) {
-                       mask |= (POLLIN | POLLRDNORM);
-                       dev_dbg(&dev->pdev->dev, "run next amthi cb\n");
-                       mei_run_next_iamthif_cmd(dev);
-               }
+               mask = mei_amthif_poll(dev, file, wait);
                goto out;
        }
 
@@ -917,7 +743,7 @@ static struct miscdevice  mei_misc_device = {
  *
  * returns true if ME Interface is valid, false otherwise
  */
-static bool __devinit mei_quirk_probe(struct pci_dev *pdev,
+static bool mei_quirk_probe(struct pci_dev *pdev,
                                const struct pci_device_id *ent)
 {
        u32 reg;
@@ -939,7 +765,7 @@ static bool __devinit mei_quirk_probe(struct pci_dev *pdev,
  *
  * returns 0 on success, <0 on failure.
  */
-static int __devinit mei_probe(struct pci_dev *pdev,
+static int mei_probe(struct pci_dev *pdev,
                                const struct pci_device_id *ent)
 {
        struct mei_device *dev;
@@ -1003,6 +829,8 @@ static int __devinit mei_probe(struct pci_dev *pdev,
                goto disable_msi;
        }
        INIT_DELAYED_WORK(&dev->timer_work, mei_timer);
+       INIT_WORK(&dev->init_work, mei_host_client_init);
+
        if (mei_hw_init(dev)) {
                dev_err(&pdev->dev, "init hw failure.\n");
                err = -ENODEV;
@@ -1054,7 +882,7 @@ end:
  * mei_remove is called by the PCI subsystem to alert the driver
  * that it should release a PCI device.
  */
-static void __devexit mei_remove(struct pci_dev *pdev)
+static void mei_remove(struct pci_dev *pdev)
 {
        struct mei_device *dev;
 
@@ -1087,8 +915,8 @@ static void __devexit mei_remove(struct pci_dev *pdev)
 
        /* remove entry if already in list */
        dev_dbg(&pdev->dev, "list del iamthif and wd file list.\n");
-       mei_remove_client_from_file_list(dev, dev->wd_cl.host_client_id);
-       mei_remove_client_from_file_list(dev, dev->iamthif_cl.host_client_id);
+       mei_me_cl_unlink(dev, &dev->wd_cl);
+       mei_me_cl_unlink(dev, &dev->iamthif_cl);
 
        dev->iamthif_current_cb = NULL;
        dev->me_clients_num = 0;
@@ -1195,8 +1023,8 @@ static struct pci_driver mei_driver = {
        .name = KBUILD_MODNAME,
        .id_table = mei_pci_tbl,
        .probe = mei_probe,
-       .remove = __devexit_p(mei_remove),
-       .shutdown = __devexit_p(mei_remove),
+       .remove = mei_remove,
+       .shutdown = mei_remove,
        .driver.pm = MEI_PM_OPS,
 };
 
index adb35fb9281c4ae82ba2a617c062293faa2b2bca..25da04549d04070746463dddabc8f40b71c65e26 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <linux/types.h>
 #include <linux/watchdog.h>
+#include <linux/poll.h>
 #include <linux/mei.h>
 #include "hw.h"
 
@@ -125,13 +126,20 @@ enum mei_wd_states {
        MEI_WD_STOPPING,
 };
 
-/* MEI CB */
-enum mei_cb_major_types {
-       MEI_READ = 0,
-       MEI_WRITE,
-       MEI_IOCTL,
-       MEI_OPEN,
-       MEI_CLOSE
+/**
+ * enum mei_cb_file_ops  - file operation associated with the callback
+ * @MEI_FOP_READ   - read
+ * @MEI_FOP_WRITE  - write
+ * @MEI_FOP_IOCTL  - ioctl
+ * @MEI_FOP_OPEN   - open
+ * @MEI_FOP_CLOSE  - close
+ */
+enum mei_cb_file_ops {
+       MEI_FOP_READ = 0,
+       MEI_FOP_WRITE,
+       MEI_FOP_IOCTL,
+       MEI_FOP_OPEN,
+       MEI_FOP_CLOSE
 };
 
 /*
@@ -143,13 +151,21 @@ struct mei_message_data {
 };
 
 
+struct mei_cl;
+
+/**
+ * struct mei_cl_cb - file operation callback structure
+ *
+ * @cl - file client who is running this operation
+ * @fop_type - file operation type
+ */
 struct mei_cl_cb {
-       struct list_head cb_list;
-       enum mei_cb_major_types major_file_operations;
-       void *file_private;
+       struct list_head list;
+       struct mei_cl *cl;
+       enum mei_cb_file_ops fop_type;
        struct mei_message_data request_buffer;
        struct mei_message_data response_buffer;
-       unsigned long information;
+       unsigned long buf_idx;
        unsigned long read_time;
        struct file *file_object;
 };
@@ -175,29 +191,23 @@ struct mei_cl {
        struct mei_cl_cb *read_cb;
 };
 
-struct mei_io_list {
-       struct mei_cl_cb mei_cb;
-};
-
 /**
- * struct mei_deive -  MEI private device struct
+ * struct mei_device -  MEI private device struct
  * @hbuf_depth - depth of host(write) buffer
+ * @wr_ext_msg - buffer for hbm control responses (set in read cycle)
  */
 struct mei_device {
        struct pci_dev *pdev;   /* pointer to pci device struct */
        /*
         * lists of queues
         */
-        /* array of pointers to aio lists */
-       struct mei_io_list read_list;           /* driver read queue */
-       struct mei_io_list write_list;          /* driver write queue */
-       struct mei_io_list write_waiting_list;  /* write waiting queue */
-       struct mei_io_list ctrl_wr_list;        /* managed write IOCTL list */
-       struct mei_io_list ctrl_rd_list;        /* managed read IOCTL list */
-       struct mei_io_list amthi_cmd_list;      /* amthi list for cmd waiting */
-
-       /* driver managed amthi list for reading completed amthi cmd data */
-       struct mei_io_list amthi_read_complete_list;
+       /* array of pointers to aio lists */
+       struct mei_cl_cb read_list;             /* driver read queue */
+       struct mei_cl_cb write_list;            /* driver write queue */
+       struct mei_cl_cb write_waiting_list;    /* write waiting queue */
+       struct mei_cl_cb ctrl_wr_list;          /* managed write IOCTL list */
+       struct mei_cl_cb ctrl_rd_list;          /* managed read IOCTL list */
+
        /*
         * list of files
         */
@@ -235,11 +245,13 @@ struct mei_device {
        u16 init_clients_timer;
        bool need_reset;
 
-       u32 extra_write_index;
        unsigned char rd_msg_buf[MEI_RD_MSG_BUF_SIZE];  /* control messages */
-       u32 wr_msg_buf[128];    /* used for control messages */
-       u32 ext_msg_buf[8];     /* for control responses */
        u32 rd_msg_hdr;
+       u32 wr_msg_buf[128];    /* used for control messages */
+       struct {
+               struct mei_msg_hdr hdr;
+               unsigned char data[4];  /* All HBM messages are 4 bytes */
+       } wr_ext_msg;           /* for control responses */
 
        struct hbm_version version;
 
@@ -253,12 +265,15 @@ struct mei_device {
 
        struct mei_cl wd_cl;
        enum mei_wd_states wd_state;
-       bool wd_interface_reg;
        bool wd_pending;
        u16 wd_timeout;
        unsigned char wd_data[MEI_WD_START_MSG_SIZE];
 
 
+       /* amthif list for cmd waiting */
+       struct mei_cl_cb amthif_cmd_list;
+       /* driver managed amthif list for reading completed amthif cmd data */
+       struct mei_cl_cb amthif_rd_complete_list;
        struct file *iamthif_file_object;
        struct mei_cl iamthif_cl;
        struct mei_cl_cb *iamthif_current_cb;
@@ -272,8 +287,15 @@ struct mei_device {
        bool iamthif_flow_control_pending;
        bool iamthif_ioctl;
        bool iamthif_canceled;
+
+       struct work_struct init_work;
 };
 
+static inline unsigned long mei_secs_to_jiffies(unsigned long sec)
+{
+       return msecs_to_jiffies(sec * MSEC_PER_SEC);
+}
+
 
 /*
  * mei init function prototypes
@@ -284,21 +306,34 @@ int mei_hw_init(struct mei_device *dev);
 int mei_task_initialize_clients(void *data);
 int mei_initialize_clients(struct mei_device *dev);
 int mei_disconnect_host_client(struct mei_device *dev, struct mei_cl *cl);
-void mei_remove_client_from_file_list(struct mei_device *dev, u8 host_client_id);
-void mei_host_init_iamthif(struct mei_device *dev);
 void mei_allocate_me_clients_storage(struct mei_device *dev);
 
 
-int mei_me_cl_update_filext(struct mei_device *dev, struct mei_cl *cl,
+int mei_me_cl_link(struct mei_device *dev, struct mei_cl *cl,
                        const uuid_le *cguid, u8 host_client_id);
+void mei_me_cl_unlink(struct mei_device *dev, struct mei_cl *cl);
 int mei_me_cl_by_uuid(const struct mei_device *dev, const uuid_le *cuuid);
 int mei_me_cl_by_id(struct mei_device *dev, u8 client_id);
 
 /*
- * MEI IO List Functions
+ * MEI IO Functions
+ */
+struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, struct file *fp);
+void mei_io_cb_free(struct mei_cl_cb *priv_cb);
+int mei_io_cb_alloc_req_buf(struct mei_cl_cb *cb, size_t length);
+int mei_io_cb_alloc_resp_buf(struct mei_cl_cb *cb, size_t length);
+
+
+/**
+ * mei_io_list_init - Sets up a queue list.
+ *
+ * @list: An instance cl callback structure
  */
-void mei_io_list_init(struct mei_io_list *list);
-void mei_io_list_flush(struct mei_io_list *list, struct mei_cl *cl);
+static inline void mei_io_list_init(struct mei_cl_cb *list)
+{
+       INIT_LIST_HEAD(&list->list);
+}
+void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl);
 
 /*
  * MEI ME Client Functions
@@ -330,7 +365,8 @@ static inline bool mei_cl_cmp_id(const struct mei_cl *cl1,
  */
 void mei_host_start_message(struct mei_device *dev);
 void mei_host_enum_clients_message(struct mei_device *dev);
-int mei_host_client_properties(struct mei_device *dev);
+int mei_host_client_enumerate(struct mei_device *dev);
+void mei_host_client_init(struct work_struct *work);
 
 /*
  *  MEI interrupt functions prototype
@@ -347,18 +383,40 @@ int mei_ioctl_connect_client(struct file *file,
 
 int mei_start_read(struct mei_device *dev, struct mei_cl *cl);
 
-int amthi_write(struct mei_device *dev, struct mei_cl_cb *priv_cb);
 
-int amthi_read(struct mei_device *dev, struct file *file,
-             char __user *ubuf, size_t length, loff_t *offset);
+/*
+ * AMTHIF - AMT Host Interface Functions
+ */
+void mei_amthif_reset_params(struct mei_device *dev);
+
+void mei_amthif_host_init(struct mei_device *dev);
+
+int mei_amthif_write(struct mei_device *dev, struct mei_cl_cb *priv_cb);
 
-struct mei_cl_cb *find_amthi_read_list_entry(struct mei_device *dev,
+int mei_amthif_read(struct mei_device *dev, struct file *file,
+               char __user *ubuf, size_t length, loff_t *offset);
+
+unsigned int mei_amthif_poll(struct mei_device *dev,
+               struct file *file, poll_table *wait);
+
+int mei_amthif_release(struct mei_device *dev, struct file *file);
+
+struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev,
                                                struct file *file);
 
-void mei_run_next_iamthif_cmd(struct mei_device *dev);
+void mei_amthif_run_next_cmd(struct mei_device *dev);
+
 
-void mei_free_cb_private(struct mei_cl_cb *priv_cb);
+int mei_amthif_read_message(struct mei_cl_cb *complete_list,
+               struct mei_device *dev, struct mei_msg_hdr *mei_hdr);
 
+int mei_amthif_irq_write_complete(struct mei_device *dev, s32 *slots,
+                       struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list);
+
+void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb);
+int mei_amthif_irq_read_message(struct mei_cl_cb *complete_list,
+               struct mei_device *dev, struct mei_msg_hdr *mei_hdr);
+int mei_amthif_irq_read(struct mei_device *dev, s32 *slots);
 
 /*
  * Register Access Function
@@ -437,4 +495,15 @@ void mei_csr_clear_his(struct mei_device *dev);
 void mei_enable_interrupts(struct mei_device *dev);
 void mei_disable_interrupts(struct mei_device *dev);
 
+static inline struct mei_msg_hdr *mei_hbm_hdr(u32 *buf, size_t length)
+{
+       struct mei_msg_hdr *hdr = (struct mei_msg_hdr *)buf;
+       hdr->host_addr = 0;
+       hdr->me_addr = 0;
+       hdr->length = length;
+       hdr->msg_complete = 1;
+       hdr->reserved = 0;
+       return hdr;
+}
+
 #endif
index d96c537f046ffd4abf58b2a84db5e5dc48690747..636409f9667f397c7ea3ea972fda636cf171e81d 100644 (file)
@@ -62,6 +62,7 @@ static void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout)
  */
 int mei_wd_host_init(struct mei_device *dev)
 {
+       int id;
        mei_cl_init(&dev->wd_cl, dev);
 
        /* look for WD client and connect to it */
@@ -69,12 +70,11 @@ int mei_wd_host_init(struct mei_device *dev)
        dev->wd_timeout = MEI_WD_DEFAULT_TIMEOUT;
        dev->wd_state = MEI_WD_IDLE;
 
-       /* find ME WD client */
-       mei_me_cl_update_filext(dev, &dev->wd_cl,
+       /* Connect WD ME client to the host client */
+       id = mei_me_cl_link(dev, &dev->wd_cl,
                                &mei_wd_guid, MEI_WD_HOST_CLIENT_ID);
 
-       dev_dbg(&dev->pdev->dev, "wd: check client\n");
-       if (MEI_FILE_CONNECTING != dev->wd_cl.state) {
+       if (id < 0) {
                dev_info(&dev->pdev->dev, "wd: failed to find the client\n");
                return -ENOENT;
        }
@@ -85,7 +85,7 @@ int mei_wd_host_init(struct mei_device *dev)
                dev->wd_cl.host_client_id = 0;
                return -EIO;
        }
-       dev->wd_cl.timer_count = CONNECT_TIMEOUT;
+       dev->wd_cl.timer_count = MEI_CONNECT_TIMEOUT;
 
        return 0;
 }
@@ -360,23 +360,20 @@ void mei_watchdog_register(struct mei_device *dev)
        if (watchdog_register_device(&amt_wd_dev)) {
                dev_err(&dev->pdev->dev,
                        "wd: unable to register watchdog device.\n");
-               dev->wd_interface_reg = false;
                return;
        }
 
        dev_dbg(&dev->pdev->dev,
                "wd: successfully register watchdog interface.\n");
-       dev->wd_interface_reg = true;
        watchdog_set_drvdata(&amt_wd_dev, dev);
 }
 
 void mei_watchdog_unregister(struct mei_device *dev)
 {
-       if (!dev->wd_interface_reg)
+       if (test_bit(WDOG_UNREGISTERED, &amt_wd_dev.status))
                return;
 
        watchdog_set_drvdata(&amt_wd_dev, NULL);
        watchdog_unregister_device(&amt_wd_dev);
-       dev->wd_interface_reg = false;
 }
 
index c9f20dae18557deb40f9ed55e9aebae6e4992222..931e635aa4916f324c019d584482c7925e4ed4d6 100644 (file)
@@ -666,7 +666,7 @@ static struct bin_attribute pch_bin_attr = {
        .write = pch_phub_bin_write,
 };
 
-static int __devinit pch_phub_probe(struct pci_dev *pdev,
+static int pch_phub_probe(struct pci_dev *pdev,
                                    const struct pci_device_id *id)
 {
        int retval;
@@ -819,7 +819,7 @@ err_pci_enable_dev:
        return ret;
 }
 
-static void __devexit pch_phub_remove(struct pci_dev *pdev)
+static void pch_phub_remove(struct pci_dev *pdev)
 {
        struct pch_phub_reg *chip = pci_get_drvdata(pdev);
 
@@ -888,7 +888,7 @@ static struct pci_driver pch_phub_driver = {
        .name = "pch_phub",
        .id_table = pch_phub_pcidev_id,
        .probe = pch_phub_probe,
-       .remove = __devexit_p(pch_phub_remove),
+       .remove = pch_phub_remove,
        .suspend = pch_phub_suspend,
        .resume = pch_phub_resume
 };
index 21b28fc6d91272c3191aba4e58c1c61aaefe7ede..68b7c773d2cf472e23395ec563a7ed3abfbb9c9c 100644 (file)
@@ -324,7 +324,7 @@ static irqreturn_t phantom_isr(int irq, void *data)
  * Init and deinit driver
  */
 
-static unsigned int __devinit phantom_get_free(void)
+static unsigned int phantom_get_free(void)
 {
        unsigned int i;
 
@@ -335,7 +335,7 @@ static unsigned int __devinit phantom_get_free(void)
        return i;
 }
 
-static int __devinit phantom_probe(struct pci_dev *pdev,
+static int phantom_probe(struct pci_dev *pdev,
        const struct pci_device_id *pci_id)
 {
        struct phantom_device *pht;
@@ -435,7 +435,7 @@ err:
        return retval;
 }
 
-static void __devexit phantom_remove(struct pci_dev *pdev)
+static void phantom_remove(struct pci_dev *pdev)
 {
        struct phantom_device *pht = pci_get_drvdata(pdev);
        unsigned int minor = MINOR(pht->cdev.dev);
@@ -487,7 +487,7 @@ static int phantom_resume(struct pci_dev *pdev)
 #define phantom_resume NULL
 #endif
 
-static struct pci_device_id phantom_pci_tbl[] __devinitdata = {
+static struct pci_device_id phantom_pci_tbl[] = {
        { .vendor = PCI_VENDOR_ID_PLX, .device = PCI_DEVICE_ID_PLX_9050,
          .subvendor = PCI_VENDOR_ID_PLX, .subdevice = PCI_DEVICE_ID_PLX_9050,
          .class = PCI_CLASS_BRIDGE_OTHER << 8, .class_mask = 0xffff00 },
@@ -499,7 +499,7 @@ static struct pci_driver phantom_pci_driver = {
        .name = "phantom",
        .id_table = phantom_pci_tbl,
        .probe = phantom_probe,
-       .remove = __devexit_p(phantom_remove),
+       .remove = phantom_remove,
        .suspend = phantom_suspend,
        .resume = phantom_resume
 };
index 4999b34b7a6015c7f7cd66347a26037812100553..7003031c9181b4a5a08d25bdba2f788b4a5f092d 100644 (file)
@@ -76,7 +76,7 @@ struct pti_dev {
  */
 static DEFINE_MUTEX(alloclock);
 
-static const struct pci_device_id pci_ids[] __devinitconst = {
+static const struct pci_device_id pci_ids[] = {
                {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x82B)},
                {0}
 };
@@ -796,7 +796,7 @@ static const struct tty_port_operations tty_port_ops = {
  *     0 for success
  *     otherwise, error
  */
-static int __devinit pti_pci_probe(struct pci_dev *pdev,
+static int pti_pci_probe(struct pci_dev *pdev,
                const struct pci_device_id *ent)
 {
        unsigned int a;
@@ -879,7 +879,7 @@ err:
  *                PCI bus.
  * @pdev: variable containing pci info of PTI.
  */
-static void __devexit pti_pci_remove(struct pci_dev *pdev)
+static void pti_pci_remove(struct pci_dev *pdev)
 {
        struct pti_dev *drv_data = pci_get_drvdata(pdev);
 
@@ -901,7 +901,7 @@ static struct pci_driver pti_pci_driver = {
        .name           = PCINAME,
        .id_table       = pci_ids,
        .probe          = pti_pci_probe,
-       .remove         = __devexit_p(pti_pci_remove),
+       .remove         = pti_pci_remove,
 };
 
 /**
index 123ed98eec3e3c1486264e8a0e0ef7dfcedc1909..7deb25dc86a7b2fd101203af1bc5d5ccbd127046 100644 (file)
@@ -711,7 +711,7 @@ static void spear13xx_pcie_device_init(struct spear_pcie_gadget_config *config)
        spear_dbi_write_reg(config, PCI_INTERRUPT_LINE, 1, 1);
 }
 
-static int __devinit spear_pcie_gadget_probe(struct platform_device *pdev)
+static int spear_pcie_gadget_probe(struct platform_device *pdev)
 {
        struct resource *res0, *res1;
        unsigned int status = 0;
@@ -853,7 +853,7 @@ err_rel_res0:
        return status;
 }
 
-static int __devexit spear_pcie_gadget_remove(struct platform_device *pdev)
+static int spear_pcie_gadget_remove(struct platform_device *pdev)
 {
        struct resource *res0, *res1;
        static struct pcie_gadget_target *target;
index 46937b107261df5fddeb39c6dd16d491e898833e..b90a2241d79c41cab2b551736cc9e2a56315a7b3 100644 (file)
@@ -511,7 +511,6 @@ long st_register(struct st_proto_s *new_proto)
        unsigned long flags = 0;
 
        st_kim_ref(&st_gdata, 0);
-       pr_info("%s(%d) ", __func__, new_proto->chnl_id);
        if (st_gdata == NULL || new_proto == NULL || new_proto->recv == NULL
            || new_proto->reg_complete_cb == NULL) {
                pr_err("gdata/new_proto/recv or reg_complete_cb not ready");
index 04a819944f6b4707a50eed443ef17450e9b31b23..9ff942a346edb59ff05d3a3fec52f052ab8849bb 100644 (file)
@@ -705,9 +705,9 @@ static const struct file_operations list_debugfs_fops = {
 static struct dentry *kim_debugfs_dir;
 static int kim_probe(struct platform_device *pdev)
 {
-       long status;
        struct kim_data_s       *kim_gdata;
        struct ti_st_plat_data  *pdata = pdev->dev.platform_data;
+       int err;
 
        if ((pdev->id != -1) && (pdev->id < MAX_ST_DEVICES)) {
                /* multiple devices could exist */
@@ -724,10 +724,11 @@ static int kim_probe(struct platform_device *pdev)
        }
        dev_set_drvdata(&pdev->dev, kim_gdata);
 
-       status = st_core_init(&kim_gdata->core_data);
-       if (status != 0) {
+       err = st_core_init(&kim_gdata->core_data);
+       if (err != 0) {
                pr_err(" ST core init failed");
-               return -EIO;
+               err = -EIO;
+               goto err_core_init;
        }
        /* refer to itself */
        kim_gdata->core_data->kim_data = kim_gdata;
@@ -738,10 +739,10 @@ static int kim_probe(struct platform_device *pdev)
        init_completion(&kim_gdata->kim_rcvd);
        init_completion(&kim_gdata->ldisc_installed);
 
-       status = sysfs_create_group(&pdev->dev.kobj, &uim_attr_grp);
-       if (status) {
+       err = sysfs_create_group(&pdev->dev.kobj, &uim_attr_grp);
+       if (err) {
                pr_err("failed to create sysfs entries");
-               return status;
+               goto err_sysfs_group;
        }
 
        /* copying platform data */
@@ -753,8 +754,8 @@ static int kim_probe(struct platform_device *pdev)
        kim_debugfs_dir = debugfs_create_dir("ti-st", NULL);
        if (IS_ERR(kim_debugfs_dir)) {
                pr_err(" debugfs entries creation failed ");
-               kim_debugfs_dir = NULL;
-               return -EIO;
+               err = -EIO;
+               goto err_debugfs_dir;
        }
 
        debugfs_create_file("version", S_IRUGO, kim_debugfs_dir,
@@ -763,6 +764,17 @@ static int kim_probe(struct platform_device *pdev)
                                kim_gdata, &list_debugfs_fops);
        pr_info(" debugfs entries created ");
        return 0;
+
+err_debugfs_dir:
+       sysfs_remove_group(&pdev->dev.kobj, &uim_attr_grp);
+
+err_sysfs_group:
+       st_core_exit(kim_gdata->core_data);
+
+err_core_init:
+       kfree(kim_gdata);
+
+       return err;
 }
 
 static int kim_remove(struct platform_device *pdev)
index 5acbba120de0ba47f49e8134c9ae1cf20475ca36..1d86407189eb2ee9ac94a8b4464d96f9d7d21393 100644 (file)
@@ -54,7 +54,7 @@ static const struct attribute_group dac7512_attr_group = {
        .attrs = dac7512_attributes,
 };
 
-static int __devinit dac7512_probe(struct spi_device *spi)
+static int dac7512_probe(struct spi_device *spi)
 {
        int ret;
 
@@ -67,7 +67,7 @@ static int __devinit dac7512_probe(struct spi_device *spi)
        return sysfs_create_group(&spi->dev.kobj, &dac7512_attr_group);
 }
 
-static int __devexit dac7512_remove(struct spi_device *spi)
+static int dac7512_remove(struct spi_device *spi)
 {
        sysfs_remove_group(&spi->dev.kobj, &dac7512_attr_group);
        return 0;
@@ -79,7 +79,7 @@ static struct spi_driver dac7512_driver = {
                .owner  = THIS_MODULE,
        },
        .probe  = dac7512_probe,
-       .remove = __devexit_p(dac7512_remove),
+       .remove = dac7512_remove,
 };
 
 module_spi_driver(dac7512_driver);
index 0beb298a17dde8a9b3a54c1f5ddb5bccf5a2c270..1e7bc0eb081ed441a13925e1c7f0a8a4967a2d79 100644 (file)
@@ -347,7 +347,7 @@ static int tsl2550_init_client(struct i2c_client *client)
  */
 
 static struct i2c_driver tsl2550_driver;
-static int __devinit tsl2550_probe(struct i2c_client *client,
+static int tsl2550_probe(struct i2c_client *client,
                                   const struct i2c_device_id *id)
 {
        struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
@@ -405,7 +405,7 @@ exit:
        return err;
 }
 
-static int __devexit tsl2550_remove(struct i2c_client *client)
+static int tsl2550_remove(struct i2c_client *client)
 {
        sysfs_remove_group(&client->dev.kobj, &tsl2550_attr_group);
 
@@ -450,7 +450,7 @@ static struct i2c_driver tsl2550_driver = {
        .suspend = tsl2550_suspend,
        .resume = tsl2550_resume,
        .probe  = tsl2550_probe,
-       .remove = __devexit_p(tsl2550_remove),
+       .remove = tsl2550_remove,
        .id_table = tsl2550_id,
 };
 
index 9bf10e7bbfaffaafed7a373cb320cfb76c5e7fce..dfa6d56ff0ff3db0e7137e0ca19f538923375846 100644 (file)
@@ -621,3 +621,10 @@ config MMC_USHC
 
          Note: These controllers only support SDIO cards and do not
          support MMC or SD memory cards.
+
+config MMC_REALTEK_PCI
+       tristate "Realtek PCI-E SD/MMC Card Interface Driver"
+       depends on MFD_RTSX_PCI
+       help
+         Say Y here to include driver code to support SD/MMC card interface
+         of Realtek PCI-E card reader
index 17ad0a7ba40b4bfcb79c665df49c4798747cece3..8aa592d14c4eac95e18ef61729ca949ad4a02799 100644 (file)
@@ -46,6 +46,8 @@ obj-$(CONFIG_MMC_JZ4740)      += jz4740_mmc.o
 obj-$(CONFIG_MMC_VUB300)       += vub300.o
 obj-$(CONFIG_MMC_USHC)         += ushc.o
 
+obj-$(CONFIG_MMC_REALTEK_PCI)  += rtsx_pci_sdmmc.o
+
 obj-$(CONFIG_MMC_SDHCI_PLTFM)          += sdhci-pltfm.o
 obj-$(CONFIG_MMC_SDHCI_CNS3XXX)                += sdhci-cns3xxx.o
 obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX)      += sdhci-esdhc-imx.o
diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c
new file mode 100644 (file)
index 0000000..12eff6f
--- /dev/null
@@ -0,0 +1,1348 @@
+/* Realtek PCI-Express SD/MMC Card Interface driver
+ *
+ * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG <wei_wang@realsil.com.cn>
+ *   No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
+ */
+
+#include <linux/module.h>
+#include <linux/highmem.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/card.h>
+#include <linux/mfd/rtsx_pci.h>
+#include <asm/unaligned.h>
+
+/* SD Tuning Data Structure
+ * Record continuous timing phase path
+ */
+struct timing_phase_path {
+       int start;
+       int end;
+       int mid;
+       int len;
+};
+
+struct realtek_pci_sdmmc {
+       struct platform_device  *pdev;
+       struct rtsx_pcr         *pcr;
+       struct mmc_host         *mmc;
+       struct mmc_request      *mrq;
+
+       struct mutex            host_mutex;
+
+       u8                      ssc_depth;
+       unsigned int            clock;
+       bool                    vpclk;
+       bool                    double_clk;
+       bool                    eject;
+       bool                    initial_mode;
+       bool                    ddr_mode;
+};
+
+static inline struct device *sdmmc_dev(struct realtek_pci_sdmmc *host)
+{
+       return &(host->pdev->dev);
+}
+
+static inline void sd_clear_error(struct realtek_pci_sdmmc *host)
+{
+       rtsx_pci_write_register(host->pcr, CARD_STOP,
+                       SD_STOP | SD_CLR_ERR, SD_STOP | SD_CLR_ERR);
+}
+
+#ifdef DEBUG
+static void sd_print_debug_regs(struct realtek_pci_sdmmc *host)
+{
+       struct rtsx_pcr *pcr = host->pcr;
+       u16 i;
+       u8 *ptr;
+
+       /* Print SD host internal registers */
+       rtsx_pci_init_cmd(pcr);
+       for (i = 0xFDA0; i <= 0xFDAE; i++)
+               rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0);
+       for (i = 0xFD52; i <= 0xFD69; i++)
+               rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0);
+       rtsx_pci_send_cmd(pcr, 100);
+
+       ptr = rtsx_pci_get_cmd_data(pcr);
+       for (i = 0xFDA0; i <= 0xFDAE; i++)
+               dev_dbg(sdmmc_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++));
+       for (i = 0xFD52; i <= 0xFD69; i++)
+               dev_dbg(sdmmc_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++));
+}
+#else
+#define sd_print_debug_regs(host)
+#endif /* DEBUG */
+
+static int sd_read_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt,
+               u8 *buf, int buf_len, int timeout)
+{
+       struct rtsx_pcr *pcr = host->pcr;
+       int err, i;
+       u8 trans_mode;
+
+       dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD%d\n", __func__, cmd[0] - 0x40);
+
+       if (!buf)
+               buf_len = 0;
+
+       if ((cmd[0] & 0x3F) == MMC_SEND_TUNING_BLOCK)
+               trans_mode = SD_TM_AUTO_TUNING;
+       else
+               trans_mode = SD_TM_NORMAL_READ;
+
+       rtsx_pci_init_cmd(pcr);
+
+       for (i = 0; i < 5; i++)
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD0 + i, 0xFF, cmd[i]);
+
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, (u8)byte_cnt);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_H,
+                       0xFF, (u8)(byte_cnt >> 8));
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_L, 0xFF, 1);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H, 0xFF, 0);
+
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF,
+                       SD_CALCULATE_CRC7 | SD_CHECK_CRC16 |
+                       SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_6);
+       if (trans_mode != SD_TM_AUTO_TUNING)
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
+                               CARD_DATA_SOURCE, 0x01, PINGPONG_BUFFER);
+
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER,
+                       0xFF, trans_mode | SD_TRANSFER_START);
+       rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER,
+                       SD_TRANSFER_END, SD_TRANSFER_END);
+
+       err = rtsx_pci_send_cmd(pcr, timeout);
+       if (err < 0) {
+               sd_print_debug_regs(host);
+               dev_dbg(sdmmc_dev(host),
+                       "rtsx_pci_send_cmd fail (err = %d)\n", err);
+               return err;
+       }
+
+       if (buf && buf_len) {
+               err = rtsx_pci_read_ppbuf(pcr, buf, buf_len);
+               if (err < 0) {
+                       dev_dbg(sdmmc_dev(host),
+                               "rtsx_pci_read_ppbuf fail (err = %d)\n", err);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+static int sd_write_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt,
+               u8 *buf, int buf_len, int timeout)
+{
+       struct rtsx_pcr *pcr = host->pcr;
+       int err, i;
+       u8 trans_mode;
+
+       if (!buf)
+               buf_len = 0;
+
+       if (buf && buf_len) {
+               err = rtsx_pci_write_ppbuf(pcr, buf, buf_len);
+               if (err < 0) {
+                       dev_dbg(sdmmc_dev(host),
+                               "rtsx_pci_write_ppbuf fail (err = %d)\n", err);
+                       return err;
+               }
+       }
+
+       trans_mode = cmd ? SD_TM_AUTO_WRITE_2 : SD_TM_AUTO_WRITE_3;
+       rtsx_pci_init_cmd(pcr);
+
+       if (cmd) {
+               dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d\n", __func__,
+                               cmd[0] - 0x40);
+
+               for (i = 0; i < 5; i++)
+                       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
+                                       SD_CMD0 + i, 0xFF, cmd[i]);
+       }
+
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, (u8)byte_cnt);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_H,
+                       0xFF, (u8)(byte_cnt >> 8));
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_L, 0xFF, 1);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H, 0xFF, 0);
+
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF,
+               SD_CALCULATE_CRC7 | SD_CHECK_CRC16 |
+               SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_6);
+
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, 0xFF,
+                       trans_mode | SD_TRANSFER_START);
+       rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER,
+                       SD_TRANSFER_END, SD_TRANSFER_END);
+
+       err = rtsx_pci_send_cmd(pcr, timeout);
+       if (err < 0) {
+               sd_print_debug_regs(host);
+               dev_dbg(sdmmc_dev(host),
+                       "rtsx_pci_send_cmd fail (err = %d)\n", err);
+               return err;
+       }
+
+       return 0;
+}
+
+static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
+               struct mmc_command *cmd)
+{
+       struct rtsx_pcr *pcr = host->pcr;
+       u8 cmd_idx = (u8)cmd->opcode;
+       u32 arg = cmd->arg;
+       int err = 0;
+       int timeout = 100;
+       int i;
+       u8 *ptr;
+       int stat_idx = 0;
+       u8 rsp_type;
+       int rsp_len = 5;
+
+       dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n",
+                       __func__, cmd_idx, arg);
+
+       /* Response type:
+        * R0
+        * R1, R5, R6, R7
+        * R1b
+        * R2
+        * R3, R4
+        */
+       switch (mmc_resp_type(cmd)) {
+       case MMC_RSP_NONE:
+               rsp_type = SD_RSP_TYPE_R0;
+               rsp_len = 0;
+               break;
+       case MMC_RSP_R1:
+               rsp_type = SD_RSP_TYPE_R1;
+               break;
+       case MMC_RSP_R1B:
+               rsp_type = SD_RSP_TYPE_R1b;
+               break;
+       case MMC_RSP_R2:
+               rsp_type = SD_RSP_TYPE_R2;
+               rsp_len = 16;
+               break;
+       case MMC_RSP_R3:
+               rsp_type = SD_RSP_TYPE_R3;
+               break;
+       default:
+               dev_dbg(sdmmc_dev(host), "cmd->flag is not valid\n");
+               err = -EINVAL;
+               goto out;
+       }
+
+       if (rsp_type == SD_RSP_TYPE_R1b)
+               timeout = 3000;
+
+       if (cmd->opcode == SD_SWITCH_VOLTAGE) {
+               err = rtsx_pci_write_register(pcr, SD_BUS_STAT,
+                               0xFF, SD_CLK_TOGGLE_EN);
+               if (err < 0)
+                       goto out;
+       }
+
+       rtsx_pci_init_cmd(pcr);
+
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD0, 0xFF, 0x40 | cmd_idx);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD1, 0xFF, (u8)(arg >> 24));
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD2, 0xFF, (u8)(arg >> 16));
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD3, 0xFF, (u8)(arg >> 8));
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD4, 0xFF, (u8)arg);
+
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, rsp_type);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE,
+                       0x01, PINGPONG_BUFFER);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER,
+                       0xFF, SD_TM_CMD_RSP | SD_TRANSFER_START);
+       rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER,
+                    SD_TRANSFER_END | SD_STAT_IDLE,
+                    SD_TRANSFER_END | SD_STAT_IDLE);
+
+       if (rsp_type == SD_RSP_TYPE_R2) {
+               /* Read data from ping-pong buffer */
+               for (i = PPBUF_BASE2; i < PPBUF_BASE2 + 16; i++)
+                       rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0);
+               stat_idx = 16;
+       } else if (rsp_type != SD_RSP_TYPE_R0) {
+               /* Read data from SD_CMDx registers */
+               for (i = SD_CMD0; i <= SD_CMD4; i++)
+                       rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0);
+               stat_idx = 5;
+       }
+
+       rtsx_pci_add_cmd(pcr, READ_REG_CMD, SD_STAT1, 0, 0);
+
+       err = rtsx_pci_send_cmd(pcr, timeout);
+       if (err < 0) {
+               sd_print_debug_regs(host);
+               sd_clear_error(host);
+               dev_dbg(sdmmc_dev(host),
+                       "rtsx_pci_send_cmd error (err = %d)\n", err);
+               goto out;
+       }
+
+       if (rsp_type == SD_RSP_TYPE_R0) {
+               err = 0;
+               goto out;
+       }
+
+       /* Eliminate returned value of CHECK_REG_CMD */
+       ptr = rtsx_pci_get_cmd_data(pcr) + 1;
+
+       /* Check (Start,Transmission) bit of Response */
+       if ((ptr[0] & 0xC0) != 0) {
+               err = -EILSEQ;
+               dev_dbg(sdmmc_dev(host), "Invalid response bit\n");
+               goto out;
+       }
+
+       /* Check CRC7 */
+       if (!(rsp_type & SD_NO_CHECK_CRC7)) {
+               if (ptr[stat_idx] & SD_CRC7_ERR) {
+                       err = -EILSEQ;
+                       dev_dbg(sdmmc_dev(host), "CRC7 error\n");
+                       goto out;
+               }
+       }
+
+       if (rsp_type == SD_RSP_TYPE_R2) {
+               for (i = 0; i < 4; i++) {
+                       cmd->resp[i] = get_unaligned_be32(ptr + 1 + i * 4);
+                       dev_dbg(sdmmc_dev(host), "cmd->resp[%d] = 0x%08x\n",
+                                       i, cmd->resp[i]);
+               }
+       } else {
+               cmd->resp[0] = get_unaligned_be32(ptr + 1);
+               dev_dbg(sdmmc_dev(host), "cmd->resp[0] = 0x%08x\n",
+                               cmd->resp[0]);
+       }
+
+out:
+       cmd->error = err;
+}
+
+static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq)
+{
+       struct rtsx_pcr *pcr = host->pcr;
+       struct mmc_host *mmc = host->mmc;
+       struct mmc_card *card = mmc->card;
+       struct mmc_data *data = mrq->data;
+       int uhs = mmc_sd_card_uhs(card);
+       int read = (data->flags & MMC_DATA_READ) ? 1 : 0;
+       u8 cfg2, trans_mode;
+       int err;
+       size_t data_len = data->blksz * data->blocks;
+
+       if (read) {
+               cfg2 = SD_CALCULATE_CRC7 | SD_CHECK_CRC16 |
+                       SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_0;
+               trans_mode = SD_TM_AUTO_READ_3;
+       } else {
+               cfg2 = SD_NO_CALCULATE_CRC7 | SD_CHECK_CRC16 |
+                       SD_NO_WAIT_BUSY_END | SD_NO_CHECK_CRC7 | SD_RSP_LEN_0;
+               trans_mode = SD_TM_AUTO_WRITE_3;
+       }
+
+       if (!uhs)
+               cfg2 |= SD_NO_CHECK_WAIT_CRC_TO;
+
+       rtsx_pci_init_cmd(pcr);
+
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, 0x00);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_H, 0xFF, 0x02);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_L,
+                       0xFF, (u8)data->blocks);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H,
+                       0xFF, (u8)(data->blocks >> 8));
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
+                       CARD_DATA_SOURCE, 0x01, RING_BUFFER);
+
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, IRQSTAT0,
+                       DMA_DONE_INT, DMA_DONE_INT);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC3,
+                       0xFF, (u8)(data_len >> 24));
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC2,
+                       0xFF, (u8)(data_len >> 16));
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC1,
+                       0xFF, (u8)(data_len >> 8));
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC0, 0xFF, (u8)data_len);
+       if (read) {
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL,
+                               0x03 | DMA_PACK_SIZE_MASK,
+                               DMA_DIR_FROM_CARD | DMA_EN | DMA_512);
+       } else {
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL,
+                               0x03 | DMA_PACK_SIZE_MASK,
+                               DMA_DIR_TO_CARD | DMA_EN | DMA_512);
+       }
+
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE,
+                       0x01, RING_BUFFER);
+
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, 0xFF,
+                       trans_mode | SD_TRANSFER_START);
+       rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER,
+                       SD_TRANSFER_END, SD_TRANSFER_END);
+
+       rtsx_pci_send_cmd_no_wait(pcr);
+
+       err = rtsx_pci_transfer_data(pcr, data->sg, data->sg_len, read, 10000);
+       if (err < 0) {
+               sd_clear_error(host);
+               return err;
+       }
+
+       return 0;
+}
+
+static inline void sd_enable_initial_mode(struct realtek_pci_sdmmc *host)
+{
+       rtsx_pci_write_register(host->pcr, SD_CFG1,
+                       SD_CLK_DIVIDE_MASK, SD_CLK_DIVIDE_128);
+}
+
+static inline void sd_disable_initial_mode(struct realtek_pci_sdmmc *host)
+{
+       rtsx_pci_write_register(host->pcr, SD_CFG1,
+                       SD_CLK_DIVIDE_MASK, SD_CLK_DIVIDE_0);
+}
+
+static void sd_normal_rw(struct realtek_pci_sdmmc *host,
+               struct mmc_request *mrq)
+{
+       struct mmc_command *cmd = mrq->cmd;
+       struct mmc_data *data = mrq->data;
+       u8 _cmd[5], *buf;
+
+       _cmd[0] = 0x40 | (u8)cmd->opcode;
+       put_unaligned_be32(cmd->arg, (u32 *)(&_cmd[1]));
+
+       buf = kzalloc(data->blksz, GFP_NOIO);
+       if (!buf) {
+               cmd->error = -ENOMEM;
+               return;
+       }
+
+       if (data->flags & MMC_DATA_READ) {
+               if (host->initial_mode)
+                       sd_disable_initial_mode(host);
+
+               cmd->error = sd_read_data(host, _cmd, (u16)data->blksz, buf,
+                               data->blksz, 200);
+
+               if (host->initial_mode)
+                       sd_enable_initial_mode(host);
+
+               sg_copy_from_buffer(data->sg, data->sg_len, buf, data->blksz);
+       } else {
+               sg_copy_to_buffer(data->sg, data->sg_len, buf, data->blksz);
+
+               cmd->error = sd_write_data(host, _cmd, (u16)data->blksz, buf,
+                               data->blksz, 200);
+       }
+
+       kfree(buf);
+}
+
+static int sd_change_phase(struct realtek_pci_sdmmc *host, u8 sample_point)
+{
+       struct rtsx_pcr *pcr = host->pcr;
+       int err;
+
+       dev_dbg(sdmmc_dev(host), "%s: sample_point = %d\n",
+                       __func__, sample_point);
+
+       rtsx_pci_init_cmd(pcr);
+
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CHANGE_CLK, CHANGE_CLK);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPRX_CTL, 0x1F, sample_point);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL, PHASE_NOT_RESET, 0);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL,
+                       PHASE_NOT_RESET, PHASE_NOT_RESET);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CHANGE_CLK, 0);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG1, SD_ASYNC_FIFO_NOT_RST, 0);
+
+       err = rtsx_pci_send_cmd(pcr, 100);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+static u8 sd_search_final_phase(struct realtek_pci_sdmmc *host, u32 phase_map)
+{
+       struct timing_phase_path path[MAX_PHASE + 1];
+       int i, j, cont_path_cnt;
+       int new_block, max_len, final_path_idx;
+       u8 final_phase = 0xFF;
+
+       /* Parse phase_map, take it as a bit-ring */
+       cont_path_cnt = 0;
+       new_block = 1;
+       j = 0;
+       for (i = 0; i < MAX_PHASE + 1; i++) {
+               if (phase_map & (1 << i)) {
+                       if (new_block) {
+                               new_block = 0;
+                               j = cont_path_cnt++;
+                               path[j].start = i;
+                               path[j].end = i;
+                       } else {
+                               path[j].end = i;
+                       }
+               } else {
+                       new_block = 1;
+                       if (cont_path_cnt) {
+                               /* Calculate path length and middle point */
+                               int idx = cont_path_cnt - 1;
+                               path[idx].len =
+                                       path[idx].end - path[idx].start + 1;
+                               path[idx].mid =
+                                       path[idx].start + path[idx].len / 2;
+                       }
+               }
+       }
+
+       if (cont_path_cnt == 0) {
+               dev_dbg(sdmmc_dev(host), "No continuous phase path\n");
+               goto finish;
+       } else {
+               /* Calculate last continuous path length and middle point */
+               int idx = cont_path_cnt - 1;
+               path[idx].len = path[idx].end - path[idx].start + 1;
+               path[idx].mid = path[idx].start + path[idx].len / 2;
+       }
+
+       /* Connect the first and last continuous paths if they are adjacent */
+       if (!path[0].start && (path[cont_path_cnt - 1].end == MAX_PHASE)) {
+               /* Using negative index */
+               path[0].start = path[cont_path_cnt - 1].start - MAX_PHASE - 1;
+               path[0].len += path[cont_path_cnt - 1].len;
+               path[0].mid = path[0].start + path[0].len / 2;
+               /* Convert negative middle point index to positive one */
+               if (path[0].mid < 0)
+                       path[0].mid += MAX_PHASE + 1;
+               cont_path_cnt--;
+       }
+
+       /* Choose the longest continuous phase path */
+       max_len = 0;
+       final_phase = 0;
+       final_path_idx = 0;
+       for (i = 0; i < cont_path_cnt; i++) {
+               if (path[i].len > max_len) {
+                       max_len = path[i].len;
+                       final_phase = (u8)path[i].mid;
+                       final_path_idx = i;
+               }
+
+               dev_dbg(sdmmc_dev(host), "path[%d].start = %d\n",
+                               i, path[i].start);
+               dev_dbg(sdmmc_dev(host), "path[%d].end = %d\n",
+                               i, path[i].end);
+               dev_dbg(sdmmc_dev(host), "path[%d].len = %d\n",
+                               i, path[i].len);
+               dev_dbg(sdmmc_dev(host), "path[%d].mid = %d\n",
+                               i, path[i].mid);
+       }
+
+finish:
+       dev_dbg(sdmmc_dev(host), "Final chosen phase: %d\n", final_phase);
+       return final_phase;
+}
+
+static void sd_wait_data_idle(struct realtek_pci_sdmmc *host)
+{
+       int err, i;
+       u8 val = 0;
+
+       for (i = 0; i < 100; i++) {
+               err = rtsx_pci_read_register(host->pcr, SD_DATA_STATE, &val);
+               if (val & SD_DATA_IDLE)
+                       return;
+
+               udelay(100);
+       }
+}
+
+static int sd_tuning_rx_cmd(struct realtek_pci_sdmmc *host,
+               u8 opcode, u8 sample_point)
+{
+       int err;
+       u8 cmd[5] = {0};
+
+       err = sd_change_phase(host, sample_point);
+       if (err < 0)
+               return err;
+
+       cmd[0] = 0x40 | opcode;
+       err = sd_read_data(host, cmd, 0x40, NULL, 0, 100);
+       if (err < 0) {
+               /* Wait till SD DATA IDLE */
+               sd_wait_data_idle(host);
+               sd_clear_error(host);
+               return err;
+       }
+
+       return 0;
+}
+
+static int sd_tuning_phase(struct realtek_pci_sdmmc *host,
+               u8 opcode, u32 *phase_map)
+{
+       int err, i;
+       u32 raw_phase_map = 0;
+
+       for (i = MAX_PHASE; i >= 0; i--) {
+               err = sd_tuning_rx_cmd(host, opcode, (u8)i);
+               if (err == 0)
+                       raw_phase_map |= 1 << i;
+       }
+
+       if (phase_map)
+               *phase_map = raw_phase_map;
+
+       return 0;
+}
+
+static int sd_tuning_rx(struct realtek_pci_sdmmc *host, u8 opcode)
+{
+       int err, i;
+       u32 raw_phase_map[RX_TUNING_CNT] = {0}, phase_map;
+       u8 final_phase;
+
+       for (i = 0; i < RX_TUNING_CNT; i++) {
+               err = sd_tuning_phase(host, opcode, &(raw_phase_map[i]));
+               if (err < 0)
+                       return err;
+
+               if (raw_phase_map[i] == 0)
+                       break;
+       }
+
+       phase_map = 0xFFFFFFFF;
+       for (i = 0; i < RX_TUNING_CNT; i++) {
+               dev_dbg(sdmmc_dev(host), "RX raw_phase_map[%d] = 0x%08x\n",
+                               i, raw_phase_map[i]);
+               phase_map &= raw_phase_map[i];
+       }
+       dev_dbg(sdmmc_dev(host), "RX phase_map = 0x%08x\n", phase_map);
+
+       if (phase_map) {
+               final_phase = sd_search_final_phase(host, phase_map);
+               if (final_phase == 0xFF)
+                       return -EINVAL;
+
+               err = sd_change_phase(host, final_phase);
+               if (err < 0)
+                       return err;
+       } else {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+       struct realtek_pci_sdmmc *host = mmc_priv(mmc);
+       struct rtsx_pcr *pcr = host->pcr;
+       struct mmc_command *cmd = mrq->cmd;
+       struct mmc_data *data = mrq->data;
+       unsigned int data_size = 0;
+
+       if (host->eject) {
+               cmd->error = -ENOMEDIUM;
+               goto finish;
+       }
+
+       mutex_lock(&pcr->pcr_mutex);
+
+       rtsx_pci_start_run(pcr);
+
+       rtsx_pci_switch_clock(pcr, host->clock, host->ssc_depth,
+                       host->initial_mode, host->double_clk, host->vpclk);
+       rtsx_pci_write_register(pcr, CARD_SELECT, 0x07, SD_MOD_SEL);
+       rtsx_pci_write_register(pcr, CARD_SHARE_MODE,
+                       CARD_SHARE_MASK, CARD_SHARE_48_SD);
+
+       mutex_lock(&host->host_mutex);
+       host->mrq = mrq;
+       mutex_unlock(&host->host_mutex);
+
+       if (mrq->data)
+               data_size = data->blocks * data->blksz;
+
+       if (!data_size || mmc_op_multi(cmd->opcode) ||
+                       (cmd->opcode == MMC_READ_SINGLE_BLOCK) ||
+                       (cmd->opcode == MMC_WRITE_BLOCK)) {
+               sd_send_cmd_get_rsp(host, cmd);
+
+               if (!cmd->error && data_size) {
+                       sd_rw_multi(host, mrq);
+
+                       if (mmc_op_multi(cmd->opcode) && mrq->stop)
+                               sd_send_cmd_get_rsp(host, mrq->stop);
+               }
+       } else {
+               sd_normal_rw(host, mrq);
+       }
+
+       if (mrq->data) {
+               if (cmd->error || data->error)
+                       data->bytes_xfered = 0;
+               else
+                       data->bytes_xfered = data->blocks * data->blksz;
+       }
+
+       mutex_unlock(&pcr->pcr_mutex);
+
+finish:
+       if (cmd->error)
+               dev_dbg(sdmmc_dev(host), "cmd->error = %d\n", cmd->error);
+
+       mutex_lock(&host->host_mutex);
+       host->mrq = NULL;
+       mutex_unlock(&host->host_mutex);
+
+       mmc_request_done(mmc, mrq);
+}
+
+static int sd_set_bus_width(struct realtek_pci_sdmmc *host,
+               unsigned char bus_width)
+{
+       int err = 0;
+       u8 width[] = {
+               [MMC_BUS_WIDTH_1] = SD_BUS_WIDTH_1BIT,
+               [MMC_BUS_WIDTH_4] = SD_BUS_WIDTH_4BIT,
+               [MMC_BUS_WIDTH_8] = SD_BUS_WIDTH_8BIT,
+       };
+
+       if (bus_width <= MMC_BUS_WIDTH_8)
+               err = rtsx_pci_write_register(host->pcr, SD_CFG1,
+                               0x03, width[bus_width]);
+
+       return err;
+}
+
+static int sd_power_on(struct realtek_pci_sdmmc *host)
+{
+       struct rtsx_pcr *pcr = host->pcr;
+       int err;
+
+       rtsx_pci_init_cmd(pcr);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SELECT, 0x07, SD_MOD_SEL);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SHARE_MODE,
+                       CARD_SHARE_MASK, CARD_SHARE_48_SD);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_EN,
+                       SD_CLK_EN, SD_CLK_EN);
+       err = rtsx_pci_send_cmd(pcr, 100);
+       if (err < 0)
+               return err;
+
+       err = rtsx_pci_card_pull_ctl_enable(pcr, RTSX_SD_CARD);
+       if (err < 0)
+               return err;
+
+       err = rtsx_pci_card_power_on(pcr, RTSX_SD_CARD);
+       if (err < 0)
+               return err;
+
+       err = rtsx_pci_write_register(pcr, CARD_OE, SD_OUTPUT_EN, SD_OUTPUT_EN);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+static int sd_power_off(struct realtek_pci_sdmmc *host)
+{
+       struct rtsx_pcr *pcr = host->pcr;
+       int err;
+
+       rtsx_pci_init_cmd(pcr);
+
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_EN, SD_CLK_EN, 0);
+       rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_OE, SD_OUTPUT_EN, 0);
+
+       err = rtsx_pci_send_cmd(pcr, 100);
+       if (err < 0)
+               return err;
+
+       err = rtsx_pci_card_power_off(pcr, RTSX_SD_CARD);
+       if (err < 0)
+               return err;
+
+       return rtsx_pci_card_pull_ctl_disable(pcr, RTSX_SD_CARD);
+}
+
+static int sd_set_power_mode(struct realtek_pci_sdmmc *host,
+               unsigned char power_mode)
+{
+       int err;
+
+       if (power_mode == MMC_POWER_OFF)
+               err = sd_power_off(host);
+       else
+               err = sd_power_on(host);
+
+       return err;
+}
+
+static int sd_set_timing(struct realtek_pci_sdmmc *host,
+               unsigned char timing, bool *ddr_mode)
+{
+       struct rtsx_pcr *pcr = host->pcr;
+       int err = 0;
+
+       *ddr_mode = false;
+
+       rtsx_pci_init_cmd(pcr);
+
+       switch (timing) {
+       case MMC_TIMING_UHS_SDR104:
+       case MMC_TIMING_UHS_SDR50:
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG1,
+                               0x0C | SD_ASYNC_FIFO_NOT_RST,
+                               SD_30_MODE | SD_ASYNC_FIFO_NOT_RST);
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL,
+                               CLK_LOW_FREQ, CLK_LOW_FREQ);
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_SOURCE, 0xFF,
+                               CRC_VAR_CLK0 | SD30_FIX_CLK | SAMPLE_VAR_CLK1);
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CLK_LOW_FREQ, 0);
+               break;
+
+       case MMC_TIMING_UHS_DDR50:
+               *ddr_mode = true;
+
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG1,
+                               0x0C | SD_ASYNC_FIFO_NOT_RST,
+                               SD_DDR_MODE | SD_ASYNC_FIFO_NOT_RST);
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL,
+                               CLK_LOW_FREQ, CLK_LOW_FREQ);
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_SOURCE, 0xFF,
+                               CRC_VAR_CLK0 | SD30_FIX_CLK | SAMPLE_VAR_CLK1);
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CLK_LOW_FREQ, 0);
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_PUSH_POINT_CTL,
+                               DDR_VAR_TX_CMD_DAT, DDR_VAR_TX_CMD_DAT);
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_SAMPLE_POINT_CTL,
+                               DDR_VAR_RX_DAT | DDR_VAR_RX_CMD,
+                               DDR_VAR_RX_DAT | DDR_VAR_RX_CMD);
+               break;
+
+       case MMC_TIMING_MMC_HS:
+       case MMC_TIMING_SD_HS:
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG1,
+                               0x0C, SD_20_MODE);
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL,
+                               CLK_LOW_FREQ, CLK_LOW_FREQ);
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_SOURCE, 0xFF,
+                               CRC_FIX_CLK | SD30_VAR_CLK0 | SAMPLE_VAR_CLK1);
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CLK_LOW_FREQ, 0);
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_PUSH_POINT_CTL,
+                               SD20_TX_SEL_MASK, SD20_TX_14_AHEAD);
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_SAMPLE_POINT_CTL,
+                               SD20_RX_SEL_MASK, SD20_RX_14_DELAY);
+               break;
+
+       default:
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
+                               SD_CFG1, 0x0C, SD_20_MODE);
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL,
+                               CLK_LOW_FREQ, CLK_LOW_FREQ);
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_SOURCE, 0xFF,
+                               CRC_FIX_CLK | SD30_VAR_CLK0 | SAMPLE_VAR_CLK1);
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CLK_LOW_FREQ, 0);
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
+                               SD_PUSH_POINT_CTL, 0xFF, 0);
+               rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_SAMPLE_POINT_CTL,
+                               SD20_RX_SEL_MASK, SD20_RX_POS_EDGE);
+               break;
+       }
+
+       err = rtsx_pci_send_cmd(pcr, 100);
+
+       return err;
+}
+
+static void sdmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+       struct realtek_pci_sdmmc *host = mmc_priv(mmc);
+       struct rtsx_pcr *pcr = host->pcr;
+
+       if (host->eject)
+               return;
+
+       mutex_lock(&pcr->pcr_mutex);
+
+       rtsx_pci_start_run(pcr);
+
+       sd_set_bus_width(host, ios->bus_width);
+       sd_set_power_mode(host, ios->power_mode);
+       sd_set_timing(host, ios->timing, &host->ddr_mode);
+
+       host->vpclk = false;
+       host->double_clk = true;
+
+       switch (ios->timing) {
+       case MMC_TIMING_UHS_SDR104:
+       case MMC_TIMING_UHS_SDR50:
+               host->ssc_depth = RTSX_SSC_DEPTH_2M;
+               host->vpclk = true;
+               host->double_clk = false;
+               break;
+       case MMC_TIMING_UHS_DDR50:
+       case MMC_TIMING_UHS_SDR25:
+               host->ssc_depth = RTSX_SSC_DEPTH_1M;
+               break;
+       default:
+               host->ssc_depth = RTSX_SSC_DEPTH_500K;
+               break;
+       }
+
+       host->initial_mode = (ios->clock <= 1000000) ? true : false;
+
+       host->clock = ios->clock;
+       rtsx_pci_switch_clock(pcr, ios->clock, host->ssc_depth,
+                       host->initial_mode, host->double_clk, host->vpclk);
+
+       mutex_unlock(&pcr->pcr_mutex);
+}
+
+static int sdmmc_get_ro(struct mmc_host *mmc)
+{
+       struct realtek_pci_sdmmc *host = mmc_priv(mmc);
+       struct rtsx_pcr *pcr = host->pcr;
+       int ro = 0;
+       u32 val;
+
+       if (host->eject)
+               return -ENOMEDIUM;
+
+       mutex_lock(&pcr->pcr_mutex);
+
+       rtsx_pci_start_run(pcr);
+
+       /* Check SD mechanical write-protect switch */
+       val = rtsx_pci_readl(pcr, RTSX_BIPR);
+       dev_dbg(sdmmc_dev(host), "%s: RTSX_BIPR = 0x%08x\n", __func__, val);
+       if (val & SD_WRITE_PROTECT)
+               ro = 1;
+
+       mutex_unlock(&pcr->pcr_mutex);
+
+       return ro;
+}
+
+static int sdmmc_get_cd(struct mmc_host *mmc)
+{
+       struct realtek_pci_sdmmc *host = mmc_priv(mmc);
+       struct rtsx_pcr *pcr = host->pcr;
+       int cd = 0;
+       u32 val;
+
+       if (host->eject)
+               return -ENOMEDIUM;
+
+       mutex_lock(&pcr->pcr_mutex);
+
+       rtsx_pci_start_run(pcr);
+
+       /* Check SD card detect */
+       val = rtsx_pci_card_exist(pcr);
+       dev_dbg(sdmmc_dev(host), "%s: RTSX_BIPR = 0x%08x\n", __func__, val);
+       if (val & SD_EXIST)
+               cd = 1;
+
+       mutex_unlock(&pcr->pcr_mutex);
+
+       return cd;
+}
+
+static int sd_wait_voltage_stable_1(struct realtek_pci_sdmmc *host)
+{
+       struct rtsx_pcr *pcr = host->pcr;
+       int err;
+       u8 stat;
+
+       /* Reference to Signal Voltage Switch Sequence in SD spec.
+        * Wait for a period of time so that the card can drive SD_CMD and
+        * SD_DAT[3:0] to low after sending back CMD11 response.
+        */
+       mdelay(1);
+
+       /* SD_CMD, SD_DAT[3:0] should be driven to low by card;
+        * If either one of SD_CMD,SD_DAT[3:0] is not low,
+        * abort the voltage switch sequence;
+        */
+       err = rtsx_pci_read_register(pcr, SD_BUS_STAT, &stat);
+       if (err < 0)
+               return err;
+
+       if (stat & (SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS |
+                               SD_DAT1_STATUS | SD_DAT0_STATUS))
+               return -EINVAL;
+
+       /* Stop toggle SD clock */
+       err = rtsx_pci_write_register(pcr, SD_BUS_STAT,
+                       0xFF, SD_CLK_FORCE_STOP);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+static int sd_wait_voltage_stable_2(struct realtek_pci_sdmmc *host)
+{
+       struct rtsx_pcr *pcr = host->pcr;
+       int err;
+       u8 stat, mask, val;
+
+       /* Wait 1.8V output of voltage regulator in card stable */
+       msleep(50);
+
+       /* Toggle SD clock again */
+       err = rtsx_pci_write_register(pcr, SD_BUS_STAT, 0xFF, SD_CLK_TOGGLE_EN);
+       if (err < 0)
+               return err;
+
+       /* Wait for a period of time so that the card can drive
+        * SD_DAT[3:0] to high at 1.8V
+        */
+       msleep(20);
+
+       /* SD_CMD, SD_DAT[3:0] should be pulled high by host */
+       err = rtsx_pci_read_register(pcr, SD_BUS_STAT, &stat);
+       if (err < 0)
+               return err;
+
+       mask = SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS |
+               SD_DAT1_STATUS | SD_DAT0_STATUS;
+       val = SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS |
+               SD_DAT1_STATUS | SD_DAT0_STATUS;
+       if ((stat & mask) != val) {
+               dev_dbg(sdmmc_dev(host),
+                       "%s: SD_BUS_STAT = 0x%x\n", __func__, stat);
+               rtsx_pci_write_register(pcr, SD_BUS_STAT,
+                               SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0);
+               rtsx_pci_write_register(pcr, CARD_CLK_EN, 0xFF, 0);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int sd_change_bank_voltage(struct realtek_pci_sdmmc *host, u8 voltage)
+{
+       struct rtsx_pcr *pcr = host->pcr;
+       int err;
+
+       if (voltage == SD_IO_3V3) {
+               err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24);
+               if (err < 0)
+                       return err;
+       } else if (voltage == SD_IO_1V8) {
+               err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C40 | 0x24);
+               if (err < 0)
+                       return err;
+       } else {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+       struct realtek_pci_sdmmc *host = mmc_priv(mmc);
+       struct rtsx_pcr *pcr = host->pcr;
+       int err = 0;
+       u8 voltage;
+
+       dev_dbg(sdmmc_dev(host), "%s: signal_voltage = %d\n",
+                       __func__, ios->signal_voltage);
+
+       if (host->eject)
+               return -ENOMEDIUM;
+
+       mutex_lock(&pcr->pcr_mutex);
+
+       rtsx_pci_start_run(pcr);
+
+       if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330)
+               voltage = SD_IO_3V3;
+       else
+               voltage = SD_IO_1V8;
+
+       if (voltage == SD_IO_1V8) {
+               err = rtsx_pci_write_register(pcr,
+                               SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_B);
+               if (err < 0)
+                       goto out;
+
+               err = sd_wait_voltage_stable_1(host);
+               if (err < 0)
+                       goto out;
+       }
+
+       err = sd_change_bank_voltage(host, voltage);
+       if (err < 0)
+               goto out;
+
+       if (voltage == SD_IO_1V8) {
+               err = sd_wait_voltage_stable_2(host);
+               if (err < 0)
+                       goto out;
+       }
+
+       /* Stop toggle SD clock in idle */
+       err = rtsx_pci_write_register(pcr, SD_BUS_STAT,
+                       SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0);
+
+out:
+       mutex_unlock(&pcr->pcr_mutex);
+
+       return err;
+}
+
+static int sdmmc_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+       struct realtek_pci_sdmmc *host = mmc_priv(mmc);
+       struct rtsx_pcr *pcr = host->pcr;
+       int err = 0;
+
+       if (host->eject)
+               return -ENOMEDIUM;
+
+       mutex_lock(&pcr->pcr_mutex);
+
+       rtsx_pci_start_run(pcr);
+
+       if (!host->ddr_mode)
+               err = sd_tuning_rx(host, MMC_SEND_TUNING_BLOCK);
+
+       mutex_unlock(&pcr->pcr_mutex);
+
+       return err;
+}
+
+static const struct mmc_host_ops realtek_pci_sdmmc_ops = {
+       .request = sdmmc_request,
+       .set_ios = sdmmc_set_ios,
+       .get_ro = sdmmc_get_ro,
+       .get_cd = sdmmc_get_cd,
+       .start_signal_voltage_switch = sdmmc_switch_voltage,
+       .execute_tuning = sdmmc_execute_tuning,
+};
+
+#ifdef CONFIG_PM
+static int rtsx_pci_sdmmc_suspend(struct platform_device *pdev,
+               pm_message_t state)
+{
+       struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev);
+       struct mmc_host *mmc = host->mmc;
+       int err;
+
+       dev_dbg(sdmmc_dev(host), "--> %s\n", __func__);
+
+       err = mmc_suspend_host(mmc);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+static int rtsx_pci_sdmmc_resume(struct platform_device *pdev)
+{
+       struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev);
+       struct mmc_host *mmc = host->mmc;
+
+       dev_dbg(sdmmc_dev(host), "--> %s\n", __func__);
+
+       return mmc_resume_host(mmc);
+}
+#else /* CONFIG_PM */
+#define rtsx_pci_sdmmc_suspend NULL
+#define rtsx_pci_sdmmc_resume NULL
+#endif /* CONFIG_PM */
+
+static void init_extra_caps(struct realtek_pci_sdmmc *host)
+{
+       struct mmc_host *mmc = host->mmc;
+       struct rtsx_pcr *pcr = host->pcr;
+
+       dev_dbg(sdmmc_dev(host), "pcr->extra_caps = 0x%x\n", pcr->extra_caps);
+
+       if (pcr->extra_caps & EXTRA_CAPS_SD_SDR50)
+               mmc->caps |= MMC_CAP_UHS_SDR50;
+       if (pcr->extra_caps & EXTRA_CAPS_SD_SDR104)
+               mmc->caps |= MMC_CAP_UHS_SDR104;
+       if (pcr->extra_caps & EXTRA_CAPS_SD_DDR50)
+               mmc->caps |= MMC_CAP_UHS_DDR50;
+       if (pcr->extra_caps & EXTRA_CAPS_MMC_HSDDR)
+               mmc->caps |= MMC_CAP_1_8V_DDR;
+       if (pcr->extra_caps & EXTRA_CAPS_MMC_8BIT)
+               mmc->caps |= MMC_CAP_8_BIT_DATA;
+}
+
+static void realtek_init_host(struct realtek_pci_sdmmc *host)
+{
+       struct mmc_host *mmc = host->mmc;
+
+       mmc->f_min = 250000;
+       mmc->f_max = 208000000;
+       mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
+       mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
+               MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
+               MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
+       mmc->max_current_330 = 400;
+       mmc->max_current_180 = 800;
+       mmc->ops = &realtek_pci_sdmmc_ops;
+
+       init_extra_caps(host);
+
+       mmc->max_segs = 256;
+       mmc->max_seg_size = 65536;
+       mmc->max_blk_size = 512;
+       mmc->max_blk_count = 65535;
+       mmc->max_req_size = 524288;
+}
+
+static void rtsx_pci_sdmmc_card_event(struct platform_device *pdev)
+{
+       struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev);
+
+       mmc_detect_change(host->mmc, 0);
+}
+
+static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev)
+{
+       struct mmc_host *mmc;
+       struct realtek_pci_sdmmc *host;
+       struct rtsx_pcr *pcr;
+       struct pcr_handle *handle = pdev->dev.platform_data;
+
+       if (!handle)
+               return -ENXIO;
+
+       pcr = handle->pcr;
+       if (!pcr)
+               return -ENXIO;
+
+       dev_dbg(&(pdev->dev), ": Realtek PCI-E SDMMC controller found\n");
+
+       mmc = mmc_alloc_host(sizeof(*host), &pdev->dev);
+       if (!mmc)
+               return -ENOMEM;
+
+       host = mmc_priv(mmc);
+       host->pcr = pcr;
+       host->mmc = mmc;
+       host->pdev = pdev;
+       platform_set_drvdata(pdev, host);
+       pcr->slots[RTSX_SD_CARD].p_dev = pdev;
+       pcr->slots[RTSX_SD_CARD].card_event = rtsx_pci_sdmmc_card_event;
+
+       mutex_init(&host->host_mutex);
+
+       realtek_init_host(host);
+
+       mmc_add_host(mmc);
+
+       return 0;
+}
+
+static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev)
+{
+       struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev);
+       struct rtsx_pcr *pcr;
+       struct mmc_host *mmc;
+
+       if (!host)
+               return 0;
+
+       pcr = host->pcr;
+       pcr->slots[RTSX_SD_CARD].p_dev = NULL;
+       pcr->slots[RTSX_SD_CARD].card_event = NULL;
+       mmc = host->mmc;
+       host->eject = true;
+
+       mutex_lock(&host->host_mutex);
+       if (host->mrq) {
+               dev_dbg(&(pdev->dev),
+                       "%s: Controller removed during transfer\n",
+                       mmc_hostname(mmc));
+
+               rtsx_pci_complete_unfinished_transfer(pcr);
+
+               host->mrq->cmd->error = -ENOMEDIUM;
+               if (host->mrq->stop)
+                       host->mrq->stop->error = -ENOMEDIUM;
+               mmc_request_done(mmc, host->mrq);
+       }
+       mutex_unlock(&host->host_mutex);
+
+       mmc_remove_host(mmc);
+       mmc_free_host(mmc);
+
+       platform_set_drvdata(pdev, NULL);
+
+       dev_dbg(&(pdev->dev),
+               ": Realtek PCI-E SDMMC controller has been removed\n");
+
+       return 0;
+}
+
+static struct platform_device_id rtsx_pci_sdmmc_ids[] = {
+       {
+               .name = DRV_NAME_RTSX_PCI_SDMMC,
+       }, {
+               /* sentinel */
+       }
+};
+MODULE_DEVICE_TABLE(platform, rtsx_pci_sdmmc_ids);
+
+static struct platform_driver rtsx_pci_sdmmc_driver = {
+       .probe          = rtsx_pci_sdmmc_drv_probe,
+       .remove         = rtsx_pci_sdmmc_drv_remove,
+       .id_table       = rtsx_pci_sdmmc_ids,
+       .suspend        = rtsx_pci_sdmmc_suspend,
+       .resume         = rtsx_pci_sdmmc_resume,
+       .driver         = {
+               .owner  = THIS_MODULE,
+               .name   = DRV_NAME_RTSX_PCI_SDMMC,
+       },
+};
+module_platform_driver(rtsx_pci_sdmmc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Wei WANG <wei_wang@realsil.com.cn>");
+MODULE_DESCRIPTION("Realtek PCI-E SD/MMC Card Host Driver");
index 4b6e4e7aca8f43835d74631871a27edb0b0ec559..0e60438ebe30ebc5a301feaf8da02e589cbfcc96 100644 (file)
@@ -57,8 +57,8 @@ config PARPORT_SERIAL
          will be called parport_serial.
 
 config PARPORT_PC_FIFO
-       bool "Use FIFO/DMA if available (EXPERIMENTAL)"
-       depends on PARPORT_PC && EXPERIMENTAL
+       bool "Use FIFO/DMA if available"
+       depends on PARPORT_PC
        help
          Many parallel port chipsets provide hardware that can speed up
          printing. Say Y here if you want to take advantage of that.
@@ -70,8 +70,8 @@ config PARPORT_PC_FIFO
          specify which IRQ/DMA to use.
 
 config PARPORT_PC_SUPERIO
-       bool "SuperIO chipset support (EXPERIMENTAL)"
-       depends on PARPORT_PC && EXPERIMENTAL
+       bool "SuperIO chipset support"
+       depends on PARPORT_PC
        help
          Saying Y here enables some probes for Super-IO chipsets in order to
          find out things like base addresses, IRQ lines and DMA channels.  It
@@ -85,8 +85,8 @@ config PARPORT_PC_PCMCIA
          ports. If unsure, say N.
 
 config PARPORT_IP32
-       tristate "SGI IP32 builtin port (EXPERIMENTAL)"
-       depends on SGI_IP32 && EXPERIMENTAL
+       tristate "SGI IP32 builtin port"
+       depends on SGI_IP32
        select PARPORT_NOT_PC
        help
          Say Y here if you need support for the parallel port on
@@ -126,8 +126,8 @@ config PARPORT_GSC
        select PARPORT_NOT_PC
 
 config PARPORT_SUNBPP
-       tristate "Sparc hardware (EXPERIMENTAL)"
-       depends on SBUS && EXPERIMENTAL
+       tristate "Sparc hardware"
+       depends on SBUS
        select PARPORT_NOT_PC
        help
          This driver provides support for the bidirectional parallel port
index 6f3ea9bbc818b3fc235ace278187341417dad32a..82e2b89d44803771c95e04b7a94fdb2a64e2db18 100644 (file)
@@ -44,6 +44,22 @@ config UIO_PDRV_GENIRQ
 
          If you don't know what to do here, say N.
 
+config UIO_DMEM_GENIRQ
+       tristate "Userspace platform driver with generic irq and dynamic memory"
+       help
+         Platform driver for Userspace I/O devices, including generic
+         interrupt handling code. Shared interrupts are not supported.
+
+         Memory regions can be specified with the same platform device
+         resources as the UIO_PDRV drivers, but dynamic regions can also
+         be specified.
+         The number and size of these regions is static,
+         but the memory allocation is not performed until
+         the associated device file is opened. The
+         memory is freed once the uio device is closed.
+
+         If you don't know what to do here, say N.
+
 config UIO_AEC
        tristate "AEC video timestamp device"
        depends on PCI
index d4dd9a5552f8ceb7c4c02acaeaff8e4299c05398..b354c539507ae0e43172c10ff55030b796bcbf68 100644 (file)
@@ -2,6 +2,7 @@ obj-$(CONFIG_UIO)       += uio.o
 obj-$(CONFIG_UIO_CIF)  += uio_cif.o
 obj-$(CONFIG_UIO_PDRV) += uio_pdrv.o
 obj-$(CONFIG_UIO_PDRV_GENIRQ)  += uio_pdrv_genirq.o
+obj-$(CONFIG_UIO_DMEM_GENIRQ)  += uio_dmem_genirq.o
 obj-$(CONFIG_UIO_AEC)  += uio_aec.o
 obj-$(CONFIG_UIO_SERCOS3)      += uio_sercos3.o
 obj-$(CONFIG_UIO_PCI_GENERIC)  += uio_pci_generic.o
index 72b22d44e8b90b9645f08a612656c9042dbb629c..1548982db58ba4b802945b09ced045ff4b1e039f 100644 (file)
@@ -78,7 +78,7 @@ static void print_board_data(struct pci_dev *pdev, struct uio_info *i)
                ioread8(i->priv + 0x07));
 }
 
-static int __devinit probe(struct pci_dev *pdev, const struct pci_device_id *id)
+static int probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
        struct uio_info *info;
        int ret;
index a84a451159edb2677ea0cad2ef2e7dd24211833f..7dd6fc60539de51a4165a7e35153a87212d56b8d 100644 (file)
@@ -40,7 +40,7 @@ static irqreturn_t hilscher_handler(int irq, struct uio_info *dev_info)
        return IRQ_HANDLED;
 }
 
-static int __devinit hilscher_pci_probe(struct pci_dev *dev,
+static int hilscher_pci_probe(struct pci_dev *dev,
                                        const struct pci_device_id *id)
 {
        struct uio_info *info;
@@ -112,7 +112,7 @@ static void hilscher_pci_remove(struct pci_dev *dev)
        kfree (info);
 }
 
-static struct pci_device_id hilscher_pci_ids[] __devinitdata = {
+static struct pci_device_id hilscher_pci_ids[] = {
        {
                .vendor =       PCI_VENDOR_ID_PLX,
                .device =       PCI_DEVICE_ID_PLX_9030,
diff --git a/drivers/uio/uio_dmem_genirq.c b/drivers/uio/uio_dmem_genirq.c
new file mode 100644 (file)
index 0000000..252434c
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+ * drivers/uio/uio_dmem_genirq.c
+ *
+ * Userspace I/O platform driver with generic IRQ handling code.
+ *
+ * Copyright (C) 2012 Damian Hobson-Garcia
+ *
+ * Based on uio_pdrv_genirq.c by Magnus Damm
+ *
+ * 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/platform_device.h>
+#include <linux/uio_driver.h>
+#include <linux/spinlock.h>
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_data/uio_dmem_genirq.h>
+#include <linux/stringify.h>
+#include <linux/pm_runtime.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+
+#define DRIVER_NAME "uio_dmem_genirq"
+#define DMEM_MAP_ERROR (~0)
+
+struct uio_dmem_genirq_platdata {
+       struct uio_info *uioinfo;
+       spinlock_t lock;
+       unsigned long flags;
+       struct platform_device *pdev;
+       unsigned int dmem_region_start;
+       unsigned int num_dmem_regions;
+       void *dmem_region_vaddr[MAX_UIO_MAPS];
+       struct mutex alloc_lock;
+       unsigned int refcnt;
+};
+
+static int uio_dmem_genirq_open(struct uio_info *info, struct inode *inode)
+{
+       struct uio_dmem_genirq_platdata *priv = info->priv;
+       struct uio_mem *uiomem;
+       int ret = 0;
+       int dmem_region = priv->dmem_region_start;
+
+       uiomem = &priv->uioinfo->mem[priv->dmem_region_start];
+
+       mutex_lock(&priv->alloc_lock);
+       while (!priv->refcnt && uiomem < &priv->uioinfo->mem[MAX_UIO_MAPS]) {
+               void *addr;
+               if (!uiomem->size)
+                       break;
+
+               addr = dma_alloc_coherent(&priv->pdev->dev, uiomem->size,
+                               (dma_addr_t *)&uiomem->addr, GFP_KERNEL);
+               if (!addr) {
+                       uiomem->addr = DMEM_MAP_ERROR;
+               }
+               priv->dmem_region_vaddr[dmem_region++] = addr;
+               ++uiomem;
+       }
+       priv->refcnt++;
+
+       mutex_unlock(&priv->alloc_lock);
+       /* Wait until the Runtime PM code has woken up the device */
+       pm_runtime_get_sync(&priv->pdev->dev);
+       return ret;
+}
+
+static int uio_dmem_genirq_release(struct uio_info *info, struct inode *inode)
+{
+       struct uio_dmem_genirq_platdata *priv = info->priv;
+       struct uio_mem *uiomem;
+       int dmem_region = priv->dmem_region_start;
+
+       /* Tell the Runtime PM code that the device has become idle */
+       pm_runtime_put_sync(&priv->pdev->dev);
+
+       uiomem = &priv->uioinfo->mem[priv->dmem_region_start];
+
+       mutex_lock(&priv->alloc_lock);
+
+       priv->refcnt--;
+       while (!priv->refcnt && uiomem < &priv->uioinfo->mem[MAX_UIO_MAPS]) {
+               if (!uiomem->size)
+                       break;
+               if (priv->dmem_region_vaddr[dmem_region]) {
+                       dma_free_coherent(&priv->pdev->dev, uiomem->size,
+                                       priv->dmem_region_vaddr[dmem_region],
+                                       uiomem->addr);
+               }
+               uiomem->addr = DMEM_MAP_ERROR;
+               ++dmem_region;
+               ++uiomem;
+       }
+
+       mutex_unlock(&priv->alloc_lock);
+       return 0;
+}
+
+static irqreturn_t uio_dmem_genirq_handler(int irq, struct uio_info *dev_info)
+{
+       struct uio_dmem_genirq_platdata *priv = dev_info->priv;
+
+       /* Just disable the interrupt in the interrupt controller, and
+        * remember the state so we can allow user space to enable it later.
+        */
+
+       if (!test_and_set_bit(0, &priv->flags))
+               disable_irq_nosync(irq);
+
+       return IRQ_HANDLED;
+}
+
+static int uio_dmem_genirq_irqcontrol(struct uio_info *dev_info, s32 irq_on)
+{
+       struct uio_dmem_genirq_platdata *priv = dev_info->priv;
+       unsigned long flags;
+
+       /* Allow user space to enable and disable the interrupt
+        * in the interrupt controller, but keep track of the
+        * state to prevent per-irq depth damage.
+        *
+        * Serialize this operation to support multiple tasks.
+        */
+
+       spin_lock_irqsave(&priv->lock, flags);
+       if (irq_on) {
+               if (test_and_clear_bit(0, &priv->flags))
+                       enable_irq(dev_info->irq);
+       } else {
+               if (!test_and_set_bit(0, &priv->flags))
+                       disable_irq(dev_info->irq);
+       }
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       return 0;
+}
+
+static int uio_dmem_genirq_probe(struct platform_device *pdev)
+{
+       struct uio_dmem_genirq_pdata *pdata = pdev->dev.platform_data;
+       struct uio_info *uioinfo = &pdata->uioinfo;
+       struct uio_dmem_genirq_platdata *priv;
+       struct uio_mem *uiomem;
+       int ret = -EINVAL;
+       int i;
+
+       if (pdev->dev.of_node) {
+               int irq;
+
+               /* alloc uioinfo for one device */
+               uioinfo = kzalloc(sizeof(*uioinfo), GFP_KERNEL);
+               if (!uioinfo) {
+                       ret = -ENOMEM;
+                       dev_err(&pdev->dev, "unable to kmalloc\n");
+                       goto bad2;
+               }
+               uioinfo->name = pdev->dev.of_node->name;
+               uioinfo->version = "devicetree";
+
+               /* Multiple IRQs are not supported */
+               irq = platform_get_irq(pdev, 0);
+               if (irq == -ENXIO)
+                       uioinfo->irq = UIO_IRQ_NONE;
+               else
+                       uioinfo->irq = irq;
+       }
+
+       if (!uioinfo || !uioinfo->name || !uioinfo->version) {
+               dev_err(&pdev->dev, "missing platform_data\n");
+               goto bad0;
+       }
+
+       if (uioinfo->handler || uioinfo->irqcontrol ||
+           uioinfo->irq_flags & IRQF_SHARED) {
+               dev_err(&pdev->dev, "interrupt configuration error\n");
+               goto bad0;
+       }
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv) {
+               ret = -ENOMEM;
+               dev_err(&pdev->dev, "unable to kmalloc\n");
+               goto bad0;
+       }
+
+       dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
+
+       priv->uioinfo = uioinfo;
+       spin_lock_init(&priv->lock);
+       priv->flags = 0; /* interrupt is enabled to begin with */
+       priv->pdev = pdev;
+       mutex_init(&priv->alloc_lock);
+
+       if (!uioinfo->irq) {
+               ret = platform_get_irq(pdev, 0);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "failed to get IRQ\n");
+                       goto bad0;
+               }
+               uioinfo->irq = ret;
+       }
+       uiomem = &uioinfo->mem[0];
+
+       for (i = 0; i < pdev->num_resources; ++i) {
+               struct resource *r = &pdev->resource[i];
+
+               if (r->flags != IORESOURCE_MEM)
+                       continue;
+
+               if (uiomem >= &uioinfo->mem[MAX_UIO_MAPS]) {
+                       dev_warn(&pdev->dev, "device has more than "
+                                       __stringify(MAX_UIO_MAPS)
+                                       " I/O memory resources.\n");
+                       break;
+               }
+
+               uiomem->memtype = UIO_MEM_PHYS;
+               uiomem->addr = r->start;
+               uiomem->size = resource_size(r);
+               ++uiomem;
+       }
+
+       priv->dmem_region_start = i;
+       priv->num_dmem_regions = pdata->num_dynamic_regions;
+
+       for (i = 0; i < pdata->num_dynamic_regions; ++i) {
+               if (uiomem >= &uioinfo->mem[MAX_UIO_MAPS]) {
+                       dev_warn(&pdev->dev, "device has more than "
+                                       __stringify(MAX_UIO_MAPS)
+                                       " dynamic and fixed memory regions.\n");
+                       break;
+               }
+               uiomem->memtype = UIO_MEM_PHYS;
+               uiomem->addr = DMEM_MAP_ERROR;
+               uiomem->size = pdata->dynamic_region_sizes[i];
+               ++uiomem;
+       }
+
+       while (uiomem < &uioinfo->mem[MAX_UIO_MAPS]) {
+               uiomem->size = 0;
+               ++uiomem;
+       }
+
+       /* This driver requires no hardware specific kernel code to handle
+        * interrupts. Instead, the interrupt handler simply disables the
+        * interrupt in the interrupt controller. User space is responsible
+        * for performing hardware specific acknowledge and re-enabling of
+        * the interrupt in the interrupt controller.
+        *
+        * Interrupt sharing is not supported.
+        */
+
+       uioinfo->handler = uio_dmem_genirq_handler;
+       uioinfo->irqcontrol = uio_dmem_genirq_irqcontrol;
+       uioinfo->open = uio_dmem_genirq_open;
+       uioinfo->release = uio_dmem_genirq_release;
+       uioinfo->priv = priv;
+
+       /* Enable Runtime PM for this device:
+        * The device starts in suspended state to allow the hardware to be
+        * turned off by default. The Runtime PM bus code should power on the
+        * hardware and enable clocks at open().
+        */
+       pm_runtime_enable(&pdev->dev);
+
+       ret = uio_register_device(&pdev->dev, priv->uioinfo);
+       if (ret) {
+               dev_err(&pdev->dev, "unable to register uio device\n");
+               goto bad1;
+       }
+
+       platform_set_drvdata(pdev, priv);
+       return 0;
+ bad1:
+       kfree(priv);
+       pm_runtime_disable(&pdev->dev);
+ bad0:
+       /* kfree uioinfo for OF */
+       if (pdev->dev.of_node)
+               kfree(uioinfo);
+ bad2:
+       return ret;
+}
+
+static int uio_dmem_genirq_remove(struct platform_device *pdev)
+{
+       struct uio_dmem_genirq_platdata *priv = platform_get_drvdata(pdev);
+
+       uio_unregister_device(priv->uioinfo);
+       pm_runtime_disable(&pdev->dev);
+
+       priv->uioinfo->handler = NULL;
+       priv->uioinfo->irqcontrol = NULL;
+
+       /* kfree uioinfo for OF */
+       if (pdev->dev.of_node)
+               kfree(priv->uioinfo);
+
+       kfree(priv);
+       return 0;
+}
+
+static int uio_dmem_genirq_runtime_nop(struct device *dev)
+{
+       /* Runtime PM callback shared between ->runtime_suspend()
+        * and ->runtime_resume(). Simply returns success.
+        *
+        * In this driver pm_runtime_get_sync() and pm_runtime_put_sync()
+        * are used at open() and release() time. This allows the
+        * Runtime PM code to turn off power to the device while the
+        * device is unused, ie before open() and after release().
+        *
+        * This Runtime PM callback does not need to save or restore
+        * any registers since user space is responsbile for hardware
+        * register reinitialization after open().
+        */
+       return 0;
+}
+
+static const struct dev_pm_ops uio_dmem_genirq_dev_pm_ops = {
+       .runtime_suspend = uio_dmem_genirq_runtime_nop,
+       .runtime_resume = uio_dmem_genirq_runtime_nop,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id uio_of_genirq_match[] = {
+       { /* empty for now */ },
+};
+MODULE_DEVICE_TABLE(of, uio_of_genirq_match);
+#else
+# define uio_of_genirq_match NULL
+#endif
+
+static struct platform_driver uio_dmem_genirq = {
+       .probe = uio_dmem_genirq_probe,
+       .remove = uio_dmem_genirq_remove,
+       .driver = {
+               .name = DRIVER_NAME,
+               .owner = THIS_MODULE,
+               .pm = &uio_dmem_genirq_dev_pm_ops,
+               .of_match_table = uio_of_genirq_match,
+       },
+};
+
+module_platform_driver(uio_dmem_genirq);
+
+MODULE_AUTHOR("Damian Hobson-Garcia");
+MODULE_DESCRIPTION("Userspace I/O platform driver with dynamic memory.");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
index a879fd5741f849f31fc417a0457cabb38049d1d3..6a4ba5e83e376b7dd2994cb36b584eeab835dd22 100644 (file)
@@ -48,7 +48,7 @@ static irqreturn_t netx_handler(int irq, struct uio_info *dev_info)
        return IRQ_HANDLED;
 }
 
-static int __devinit netx_pci_probe(struct pci_dev *dev,
+static int netx_pci_probe(struct pci_dev *dev,
                                        const struct pci_device_id *id)
 {
        struct uio_info *info;
index 0bd08ef2b39407cda69cfd1b32fea1808fd31176..14aa10c1f6defe37e73d7ff4b23054698a96f9c6 100644 (file)
@@ -53,7 +53,7 @@ static irqreturn_t irqhandler(int irq, struct uio_info *info)
        return IRQ_HANDLED;
 }
 
-static int __devinit probe(struct pci_dev *pdev,
+static int probe(struct pci_dev *pdev,
                           const struct pci_device_id *id)
 {
        struct uio_pci_generic_dev *gdev;
index 72d3646c73654958a19f99ae4ad00e5be0a46212..39be9e061700ded49d32e7e8ca9c5b2ad2d1590d 100644 (file)
@@ -60,6 +60,7 @@ static int uio_pdrv_probe(struct platform_device *pdev)
                uiomem->memtype = UIO_MEM_PHYS;
                uiomem->addr = r->start;
                uiomem->size = resource_size(r);
+               uiomem->name = r->name;
                ++uiomem;
        }
 
index 42202cd831586940bdf87b2cc04bc8e89c40808b..c122bca669b6e58c77d30d07bbe9173fe546e8cb 100644 (file)
@@ -102,7 +102,7 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
        int ret = -EINVAL;
        int i;
 
-       if (!uioinfo) {
+       if (pdev->dev.of_node) {
                int irq;
 
                /* alloc uioinfo for one device */
@@ -172,6 +172,7 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
                uiomem->memtype = UIO_MEM_PHYS;
                uiomem->addr = r->start;
                uiomem->size = resource_size(r);
+               uiomem->name = r->name;
                ++uiomem;
        }
 
index 33a7a273b4537bd31d2898a572da62f4462b4fd7..cce0f78341cc2d33bddfea55d3313f8dd0b39b5e 100644 (file)
@@ -112,7 +112,7 @@ static void pruss_cleanup(struct platform_device *dev,
        kfree(gdev);
 }
 
-static int __devinit pruss_probe(struct platform_device *dev)
+static int pruss_probe(struct platform_device *dev)
 {
        struct uio_info *p;
        struct uio_pruss_dev *gdev;
@@ -209,7 +209,7 @@ out_free:
        return ret;
 }
 
-static int __devexit pruss_remove(struct platform_device *dev)
+static int pruss_remove(struct platform_device *dev)
 {
        struct uio_pruss_dev *gdev = platform_get_drvdata(dev);
 
@@ -220,7 +220,7 @@ static int __devexit pruss_remove(struct platform_device *dev)
 
 static struct platform_driver pruss_driver = {
        .probe = pruss_probe,
-       .remove = __devexit_p(pruss_remove),
+       .remove = pruss_remove,
        .driver = {
                   .name = DRV_NAME,
                   .owner = THIS_MODULE,
index a187fa14c5c084d54dcd687e908f8dc43817a55c..81a10a5631207144d8f34adc5a56f746983dd054 100644 (file)
@@ -116,7 +116,7 @@ static int sercos3_setup_iomem(struct pci_dev *dev, struct uio_info *info,
        return 0;
 }
 
-static int __devinit sercos3_pci_probe(struct pci_dev *dev,
+static int sercos3_pci_probe(struct pci_dev *dev,
                                       const struct pci_device_id *id)
 {
        struct uio_info *info;
@@ -197,7 +197,7 @@ static void sercos3_pci_remove(struct pci_dev *dev)
        kfree(info);
 }
 
-static struct pci_device_id sercos3_pci_ids[] __devinitdata = {
+static struct pci_device_id sercos3_pci_ids[] = {
        {
                .vendor =       PCI_VENDOR_ID_PLX,
                .device =       PCI_DEVICE_ID_PLX_9030,
index 7e984034a11b03a97ae3e9fbfbfd4313297f98b8..c433a746e3f58317c8ea1e5187f70675e9dd0315 100644 (file)
@@ -26,7 +26,7 @@ config W1_MASTER_DS2490
 
 config W1_MASTER_DS2482
        tristate "Maxim DS2482 I2C to 1-Wire bridge"
-       depends on I2C && EXPERIMENTAL
+       depends on I2C
        help
          If you say yes here you get support for the Maxim DS2482
          I2C to 1-Wire bridge.
index e5f74416d4b766f6da15dc45ded4042440d7d64e..6429b9e9fb82f94227b2a0dad004dfa342113026 100644 (file)
@@ -505,19 +505,8 @@ static int ds2482_remove(struct i2c_client *client)
        return 0;
 }
 
-static int __init sensors_ds2482_init(void)
-{
-       return i2c_add_driver(&ds2482_driver);
-}
-
-static void __exit sensors_ds2482_exit(void)
-{
-       i2c_del_driver(&ds2482_driver);
-}
+module_i2c_driver(ds2482_driver);
 
 MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>");
 MODULE_DESCRIPTION("DS2482 driver");
 MODULE_LICENSE("GPL");
-
-module_init(sensors_ds2482_init);
-module_exit(sensors_ds2482_exit);
index f667c26b219571e4409b167985605d79c56d9e8b..d8667b0212d7c28121bcc2339a381c5d1ceb8cff 100644 (file)
@@ -48,14 +48,14 @@ static struct pci_device_id matrox_w1_tbl[] = {
 };
 MODULE_DEVICE_TABLE(pci, matrox_w1_tbl);
 
-static int __devinit matrox_w1_probe(struct pci_dev *, const struct pci_device_id *);
-static void __devexit matrox_w1_remove(struct pci_dev *);
+static int matrox_w1_probe(struct pci_dev *, const struct pci_device_id *);
+static void matrox_w1_remove(struct pci_dev *);
 
 static struct pci_driver matrox_w1_pci_driver = {
        .name = "matrox_w1",
        .id_table = matrox_w1_tbl,
        .probe = matrox_w1_probe,
-       .remove = __devexit_p(matrox_w1_remove),
+       .remove = matrox_w1_remove,
 };
 
 /*
@@ -152,7 +152,7 @@ static void matrox_w1_hw_init(struct matrox_device *dev)
        matrox_w1_write_reg(dev, MATROX_GET_CONTROL, 0x00);
 }
 
-static int __devinit matrox_w1_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+static int matrox_w1_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
        struct matrox_device *dev;
        int err;
@@ -220,7 +220,7 @@ err_out_free_device:
        return err;
 }
 
-static void __devexit matrox_w1_remove(struct pci_dev *pdev)
+static void matrox_w1_remove(struct pci_dev *pdev)
 {
        struct matrox_device *dev = pci_get_drvdata(pdev);
 
index 1cc61a700fa84bd7830bd37f4b3d069438f4ba87..d338b56ea2f09b5a5005f7128cdff1a07906d779 100644 (file)
@@ -103,7 +103,7 @@ static u8 mxc_w1_ds2_touch_bit(void *data, u8 bit)
        return ((__raw_readb(ctrl_addr)) >> 3) & 0x1;
 }
 
-static int __devinit mxc_w1_probe(struct platform_device *pdev)
+static int mxc_w1_probe(struct platform_device *pdev)
 {
        struct mxc_w1_device *mdev;
        struct resource *res;
@@ -117,9 +117,9 @@ static int __devinit mxc_w1_probe(struct platform_device *pdev)
        if (!mdev)
                return -ENOMEM;
 
-       mdev->clk = clk_get(&pdev->dev, "owire");
-       if (!mdev->clk) {
-               err = -ENODEV;
+       mdev->clk = clk_get(&pdev->dev, NULL);
+       if (IS_ERR(mdev->clk)) {
+               err = PTR_ERR(mdev->clk);
                goto failed_clk;
        }
 
@@ -134,7 +134,7 @@ static int __devinit mxc_w1_probe(struct platform_device *pdev)
 
        mdev->regs = ioremap(res->start, resource_size(res));
        if (!mdev->regs) {
-               printk(KERN_ERR "Cannot map frame buffer registers\n");
+               dev_err(&pdev->dev, "Cannot map mxc_w1 registers\n");
                goto failed_ioremap;
        }
 
@@ -167,7 +167,7 @@ failed_clk:
 /*
  * disassociate the w1 device from the driver
  */
-static int __devexit mxc_w1_remove(struct platform_device *pdev)
+static int mxc_w1_remove(struct platform_device *pdev)
 {
        struct mxc_w1_device *mdev = platform_get_drvdata(pdev);
        struct resource *res;
@@ -191,21 +191,9 @@ static struct platform_driver mxc_w1_driver = {
                   .name = "mxc_w1",
        },
        .probe = mxc_w1_probe,
-       .remove = mxc_w1_remove,
+       .remove = __devexit_p(mxc_w1_remove),
 };
-
-static int __init mxc_w1_init(void)
-{
-       return platform_driver_register(&mxc_w1_driver);
-}
-
-static void mxc_w1_exit(void)
-{
-       platform_driver_unregister(&mxc_w1_driver);
-}
-
-module_init(mxc_w1_init);
-module_exit(mxc_w1_exit);
+module_platform_driver(mxc_w1_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Freescale Semiconductors Inc");
index ca8e60bb2f9cb386f454a33224d6cc7906a20fc7..184dbce4abd1bbaa2595bbae48037d28bf6b301a 100644 (file)
@@ -69,12 +69,12 @@ struct hdq_data {
        int                     init_trans;
 };
 
-static int __devinit omap_hdq_probe(struct platform_device *pdev);
-static int __devexit omap_hdq_remove(struct platform_device *pdev);
+static int omap_hdq_probe(struct platform_device *pdev);
+static int omap_hdq_remove(struct platform_device *pdev);
 
 static struct platform_driver omap_hdq_driver = {
        .probe =        omap_hdq_probe,
-       .remove =       __devexit_p(omap_hdq_remove),
+       .remove =       omap_hdq_remove,
        .driver =       {
                .name = "omap_hdq",
        },
@@ -537,7 +537,7 @@ static void omap_w1_write_byte(void *_hdq, u8 byte)
        }
 }
 
-static int __devinit omap_hdq_probe(struct platform_device *pdev)
+static int omap_hdq_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct hdq_data *hdq_data;
@@ -613,7 +613,7 @@ err_w1:
        return ret;
 }
 
-static int __devexit omap_hdq_remove(struct platform_device *pdev)
+static int omap_hdq_remove(struct platform_device *pdev)
 {
        struct hdq_data *hdq_data = platform_get_drvdata(pdev);
 
index 6012c4ea3206ac58e5d8c0efc561859b10c4b4b1..85b363a5bd0f281f1a4f955dfab555da5cb71cf9 100644 (file)
@@ -16,6 +16,9 @@
 #include <linux/gpio.h>
 #include <linux/of_platform.h>
 #include <linux/of_gpio.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/err.h>
+#include <linux/of.h>
 
 #include "../w1.h"
 #include "../w1_int.h"
@@ -44,7 +47,6 @@ static u8 w1_gpio_read_bit(void *data)
        return gpio_get_value(pdata->pin) ? 1 : 0;
 }
 
-#ifdef CONFIG_OF
 static struct of_device_id w1_gpio_dt_ids[] = {
        { .compatible = "w1-gpio" },
        {}
@@ -55,11 +57,6 @@ static int w1_gpio_probe_dt(struct platform_device *pdev)
 {
        struct w1_gpio_platform_data *pdata = pdev->dev.platform_data;
        struct device_node *np = pdev->dev.of_node;
-       const struct of_device_id *of_id =
-                       of_match_device(w1_gpio_dt_ids, &pdev->dev);
-
-       if (!of_id)
-               return 0;
 
        pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
        if (!pdata)
@@ -74,41 +71,53 @@ static int w1_gpio_probe_dt(struct platform_device *pdev)
 
        return 0;
 }
-#else
-static int w1_gpio_probe_dt(struct platform_device *pdev)
-{
-       return 0;
-}
-#endif
 
 static int __init w1_gpio_probe(struct platform_device *pdev)
 {
        struct w1_bus_master *master;
        struct w1_gpio_platform_data *pdata;
+       struct pinctrl *pinctrl;
        int err;
 
-       err = w1_gpio_probe_dt(pdev);
-       if (err < 0)
-               return err;
+       pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+       if (IS_ERR(pinctrl))
+               dev_warn(&pdev->dev, "unable to select pin group\n");
+
+       if (of_have_populated_dt()) {
+               err = w1_gpio_probe_dt(pdev);
+               if (err < 0) {
+                       dev_err(&pdev->dev, "Failed to parse DT\n");
+                       return err;
+               }
+       }
 
        pdata = pdev->dev.platform_data;
 
-       if (!pdata)
+       if (!pdata) {
+               dev_err(&pdev->dev, "No configuration data\n");
                return -ENXIO;
+       }
 
        master = kzalloc(sizeof(struct w1_bus_master), GFP_KERNEL);
-       if (!master)
+       if (!master) {
+               dev_err(&pdev->dev, "Out of memory\n");
                return -ENOMEM;
+       }
 
        err = gpio_request(pdata->pin, "w1");
-       if (err)
+       if (err) {
+               dev_err(&pdev->dev, "gpio_request (pin) failed\n");
                goto free_master;
+       }
 
        if (gpio_is_valid(pdata->ext_pullup_enable_pin)) {
                err = gpio_request_one(pdata->ext_pullup_enable_pin,
                                       GPIOF_INIT_LOW, "w1 pullup");
-               if (err < 0)
+               if (err < 0) {
+                       dev_err(&pdev->dev, "gpio_request_one "
+                                       "(ext_pullup_enable_pin) failed\n");
                        goto free_gpio;
+               }
        }
 
        master->data = pdata;
@@ -123,8 +132,10 @@ static int __init w1_gpio_probe(struct platform_device *pdev)
        }
 
        err = w1_add_master_device(master);
-       if (err)
+       if (err) {
+               dev_err(&pdev->dev, "w1_add_master device failed\n");
                goto free_gpio_ext_pu;
+       }
 
        if (pdata->enable_external_pullup)
                pdata->enable_external_pullup(1);
@@ -198,23 +209,13 @@ static struct platform_driver w1_gpio_driver = {
                .owner  = THIS_MODULE,
                .of_match_table = of_match_ptr(w1_gpio_dt_ids),
        },
+       .probe = w1_gpio_probe,
        .remove = __exit_p(w1_gpio_remove),
        .suspend = w1_gpio_suspend,
        .resume = w1_gpio_resume,
 };
 
-static int __init w1_gpio_init(void)
-{
-       return platform_driver_probe(&w1_gpio_driver, w1_gpio_probe);
-}
-
-static void __exit w1_gpio_exit(void)
-{
-       platform_driver_unregister(&w1_gpio_driver);
-}
-
-module_init(w1_gpio_init);
-module_exit(w1_gpio_exit);
+module_platform_driver(w1_gpio_driver);
 
 MODULE_DESCRIPTION("GPIO w1 bus master driver");
 MODULE_AUTHOR("Ville Syrjala <syrjala@sci.fi>");
index 1a574370d2cd22ec4686604520c9f139052afba9..7994d933f040bd85ef9f26dfee1587c38e37cba5 100644 (file)
@@ -551,7 +551,6 @@ void w1_destroy_master_attributes(struct w1_master *master)
        sysfs_remove_group(&master->dev.kobj, &w1_master_defattr_group);
 }
 
-#ifdef CONFIG_HOTPLUG
 static int w1_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
        struct w1_master *md = NULL;
@@ -587,12 +586,6 @@ static int w1_uevent(struct device *dev, struct kobj_uevent_env *env)
 end:
        return err;
 }
-#else
-static int w1_uevent(struct device *dev, struct kobj_uevent_env *env)
-{
-       return 0;
-}
-#endif
 
 static int __w1_attach_slave_device(struct w1_slave *sl)
 {
index 7d041cb6da2662fada88101835b94d1aed63b9bc..2552d3e0a70f2759597c54b8218e17248412337c 100644 (file)
@@ -222,7 +222,7 @@ static void selfballoon_process(struct work_struct *work)
        if (xen_selfballooning_enabled) {
                cur_pages = totalram_pages;
                tgt_pages = cur_pages; /* default is no change */
-               goal_pages = percpu_counter_read_positive(&vm_committed_as) +
+               goal_pages = vm_memory_committed() +
                                totalreserve_pages +
                                MB2PAGES(selfballoon_reserved_mb);
 #ifdef CONFIG_FRONTSWAP
diff --git a/include/linux/mfd/rtsx_common.h b/include/linux/mfd/rtsx_common.h
new file mode 100644 (file)
index 0000000..a8d393e
--- /dev/null
@@ -0,0 +1,48 @@
+/* Driver for Realtek driver-based card reader
+ *
+ * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG <wei_wang@realsil.com.cn>
+ *   No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
+ */
+
+#ifndef __RTSX_COMMON_H
+#define __RTSX_COMMON_H
+
+#define DRV_NAME_RTSX_PCI              "rtsx_pci"
+#define DRV_NAME_RTSX_PCI_SDMMC                "rtsx_pci_sdmmc"
+#define DRV_NAME_RTSX_PCI_MS           "rtsx_pci_ms"
+
+#define RTSX_REG_PAIR(addr, val)       (((u32)(addr) << 16) | (u8)(val))
+
+#define RTSX_SSC_DEPTH_4M              0x01
+#define RTSX_SSC_DEPTH_2M              0x02
+#define RTSX_SSC_DEPTH_1M              0x03
+#define RTSX_SSC_DEPTH_500K            0x04
+#define RTSX_SSC_DEPTH_250K            0x05
+
+#define RTSX_SD_CARD                   0
+#define RTSX_MS_CARD                   1
+
+struct platform_device;
+
+struct rtsx_slot {
+       struct platform_device  *p_dev;
+       void                    (*card_event)(struct platform_device *p_dev);
+};
+
+#endif
diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h
new file mode 100644 (file)
index 0000000..060b721
--- /dev/null
@@ -0,0 +1,794 @@
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Wei WANG <wei_wang@realsil.com.cn>
+ *   No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
+ */
+
+#ifndef __RTSX_PCI_H
+#define __RTSX_PCI_H
+
+#include <linux/sched.h>
+#include <linux/pci.h>
+
+#include "rtsx_common.h"
+
+#define MAX_RW_REG_CNT                 1024
+
+/* PCI Operation Register Address */
+#define RTSX_HCBAR                     0x00
+#define RTSX_HCBCTLR                   0x04
+#define RTSX_HDBAR                     0x08
+#define RTSX_HDBCTLR                   0x0C
+#define RTSX_HAIMR                     0x10
+#define RTSX_BIPR                      0x14
+#define RTSX_BIER                      0x18
+
+/* Host command buffer control register */
+#define STOP_CMD                       (0x01 << 28)
+
+/* Host data buffer control register */
+#define SDMA_MODE                      0x00
+#define ADMA_MODE                      (0x02 << 26)
+#define STOP_DMA                       (0x01 << 28)
+#define TRIG_DMA                       (0x01 << 31)
+
+/* Host access internal memory register */
+#define HAIMR_TRANS_START              (0x01 << 31)
+#define HAIMR_READ                     0x00
+#define HAIMR_WRITE                    (0x01 << 30)
+#define HAIMR_READ_START               (HAIMR_TRANS_START | HAIMR_READ)
+#define HAIMR_WRITE_START              (HAIMR_TRANS_START | HAIMR_WRITE)
+#define HAIMR_TRANS_END                        (HAIMR_TRANS_START)
+
+/* Bus interrupt pending register */
+#define CMD_DONE_INT                   (1 << 31)
+#define DATA_DONE_INT                  (1 << 30)
+#define TRANS_OK_INT                   (1 << 29)
+#define TRANS_FAIL_INT                 (1 << 28)
+#define XD_INT                         (1 << 27)
+#define MS_INT                         (1 << 26)
+#define SD_INT                         (1 << 25)
+#define GPIO0_INT                      (1 << 24)
+#define OC_INT                         (1 << 23)
+#define SD_WRITE_PROTECT               (1 << 19)
+#define XD_EXIST                       (1 << 18)
+#define MS_EXIST                       (1 << 17)
+#define SD_EXIST                       (1 << 16)
+#define DELINK_INT                     GPIO0_INT
+#define MS_OC_INT                      (1 << 23)
+#define SD_OC_INT                      (1 << 22)
+
+#define CARD_INT               (XD_INT | MS_INT | SD_INT)
+#define NEED_COMPLETE_INT      (DATA_DONE_INT | TRANS_OK_INT | TRANS_FAIL_INT)
+#define RTSX_INT               (CMD_DONE_INT | NEED_COMPLETE_INT | \
+                                       CARD_INT | GPIO0_INT | OC_INT)
+
+#define CARD_EXIST             (XD_EXIST | MS_EXIST | SD_EXIST)
+
+/* Bus interrupt enable register */
+#define CMD_DONE_INT_EN                (1 << 31)
+#define DATA_DONE_INT_EN       (1 << 30)
+#define TRANS_OK_INT_EN                (1 << 29)
+#define TRANS_FAIL_INT_EN      (1 << 28)
+#define XD_INT_EN              (1 << 27)
+#define MS_INT_EN              (1 << 26)
+#define SD_INT_EN              (1 << 25)
+#define GPIO0_INT_EN           (1 << 24)
+#define OC_INT_EN              (1 << 23)
+#define DELINK_INT_EN          GPIO0_INT_EN
+#define MS_OC_INT_EN           (1 << 23)
+#define SD_OC_INT_EN           (1 << 22)
+
+#define READ_REG_CMD           0
+#define WRITE_REG_CMD          1
+#define CHECK_REG_CMD          2
+
+/*
+ * macros for easy use
+ */
+#define rtsx_pci_writel(pcr, reg, value) \
+       iowrite32(value, (pcr)->remap_addr + reg)
+#define rtsx_pci_readl(pcr, reg) \
+       ioread32((pcr)->remap_addr + reg)
+#define rtsx_pci_writew(pcr, reg, value) \
+       iowrite16(value, (pcr)->remap_addr + reg)
+#define rtsx_pci_readw(pcr, reg) \
+       ioread16((pcr)->remap_addr + reg)
+#define rtsx_pci_writeb(pcr, reg, value) \
+       iowrite8(value, (pcr)->remap_addr + reg)
+#define rtsx_pci_readb(pcr, reg) \
+       ioread8((pcr)->remap_addr + reg)
+
+#define rtsx_pci_read_config_byte(pcr, where, val) \
+       pci_read_config_byte((pcr)->pci, where, val)
+
+#define rtsx_pci_write_config_byte(pcr, where, val) \
+       pci_write_config_byte((pcr)->pci, where, val)
+
+#define rtsx_pci_read_config_dword(pcr, where, val) \
+       pci_read_config_dword((pcr)->pci, where, val)
+
+#define rtsx_pci_write_config_dword(pcr, where, val) \
+       pci_write_config_dword((pcr)->pci, where, val)
+
+#define STATE_TRANS_NONE       0
+#define STATE_TRANS_CMD                1
+#define STATE_TRANS_BUF                2
+#define STATE_TRANS_SG         3
+
+#define TRANS_NOT_READY                0
+#define TRANS_RESULT_OK                1
+#define TRANS_RESULT_FAIL      2
+#define TRANS_NO_DEVICE                3
+
+#define RTSX_RESV_BUF_LEN      4096
+#define HOST_CMDS_BUF_LEN      1024
+#define HOST_SG_TBL_BUF_LEN    (RTSX_RESV_BUF_LEN - HOST_CMDS_BUF_LEN)
+#define HOST_SG_TBL_ITEMS      (HOST_SG_TBL_BUF_LEN / 8)
+#define MAX_SG_ITEM_LEN                0x80000
+
+#define HOST_TO_DEVICE         0
+#define DEVICE_TO_HOST         1
+
+#define MAX_PHASE              31
+#define RX_TUNING_CNT          3
+
+/* SG descriptor */
+#define SG_INT                 0x04
+#define SG_END                 0x02
+#define SG_VALID               0x01
+
+#define SG_NO_OP               0x00
+#define SG_TRANS_DATA          (0x02 << 4)
+#define SG_LINK_DESC           (0x03 << 4)
+
+/* SD bank voltage */
+#define SD_IO_3V3              0
+#define SD_IO_1V8              1
+
+
+/* Card Clock Enable Register */
+#define SD_CLK_EN                      0x04
+#define MS_CLK_EN                      0x08
+
+/* Card Select Register */
+#define SD_MOD_SEL                     2
+#define MS_MOD_SEL                     3
+
+/* Card Output Enable Register */
+#define SD_OUTPUT_EN                   0x04
+#define MS_OUTPUT_EN                   0x08
+
+/* CARD_SHARE_MODE */
+#define CARD_SHARE_MASK                        0x0F
+#define CARD_SHARE_MULTI_LUN           0x00
+#define        CARD_SHARE_NORMAL               0x00
+#define        CARD_SHARE_48_SD                0x04
+#define        CARD_SHARE_48_MS                0x08
+/* CARD_SHARE_MODE for barossa */
+#define CARD_SHARE_BAROSSA_SD          0x01
+#define CARD_SHARE_BAROSSA_MS          0x02
+
+/* SD30_DRIVE_SEL */
+#define DRIVER_TYPE_A                  0x05
+#define DRIVER_TYPE_B                  0x03
+#define DRIVER_TYPE_C                  0x02
+#define DRIVER_TYPE_D                  0x01
+
+/* FPDCTL */
+#define SSC_POWER_DOWN                 0x01
+#define SD_OC_POWER_DOWN               0x02
+#define ALL_POWER_DOWN                 0x07
+#define OC_POWER_DOWN                  0x06
+
+/* CLK_CTL */
+#define CHANGE_CLK                     0x01
+
+/* LDO_CTL */
+#define BPP_LDO_POWB                   0x03
+#define BPP_LDO_ON                     0x00
+#define BPP_LDO_SUSPEND                        0x02
+#define BPP_LDO_OFF                    0x03
+
+/* CD_PAD_CTL */
+#define CD_DISABLE_MASK                        0x07
+#define MS_CD_DISABLE                  0x04
+#define SD_CD_DISABLE                  0x02
+#define XD_CD_DISABLE                  0x01
+#define CD_DISABLE                     0x07
+#define CD_ENABLE                      0x00
+#define MS_CD_EN_ONLY                  0x03
+#define SD_CD_EN_ONLY                  0x05
+#define XD_CD_EN_ONLY                  0x06
+#define FORCE_CD_LOW_MASK              0x38
+#define FORCE_CD_XD_LOW                        0x08
+#define FORCE_CD_SD_LOW                        0x10
+#define FORCE_CD_MS_LOW                        0x20
+#define CD_AUTO_DISABLE                        0x40
+
+/* SD_STAT1 */
+#define        SD_CRC7_ERR                     0x80
+#define        SD_CRC16_ERR                    0x40
+#define        SD_CRC_WRITE_ERR                0x20
+#define        SD_CRC_WRITE_ERR_MASK           0x1C
+#define        GET_CRC_TIME_OUT                0x02
+#define        SD_TUNING_COMPARE_ERR           0x01
+
+/* SD_STAT2 */
+#define        SD_RSP_80CLK_TIMEOUT            0x01
+
+/* SD_BUS_STAT */
+#define        SD_CLK_TOGGLE_EN                0x80
+#define        SD_CLK_FORCE_STOP               0x40
+#define        SD_DAT3_STATUS                  0x10
+#define        SD_DAT2_STATUS                  0x08
+#define        SD_DAT1_STATUS                  0x04
+#define        SD_DAT0_STATUS                  0x02
+#define        SD_CMD_STATUS                   0x01
+
+/* SD_PAD_CTL */
+#define        SD_IO_USING_1V8                 0x80
+#define        SD_IO_USING_3V3                 0x7F
+#define        TYPE_A_DRIVING                  0x00
+#define        TYPE_B_DRIVING                  0x01
+#define        TYPE_C_DRIVING                  0x02
+#define        TYPE_D_DRIVING                  0x03
+
+/* SD_SAMPLE_POINT_CTL */
+#define        DDR_FIX_RX_DAT                  0x00
+#define        DDR_VAR_RX_DAT                  0x80
+#define        DDR_FIX_RX_DAT_EDGE             0x00
+#define        DDR_FIX_RX_DAT_14_DELAY         0x40
+#define        DDR_FIX_RX_CMD                  0x00
+#define        DDR_VAR_RX_CMD                  0x20
+#define        DDR_FIX_RX_CMD_POS_EDGE         0x00
+#define        DDR_FIX_RX_CMD_14_DELAY         0x10
+#define        SD20_RX_POS_EDGE                0x00
+#define        SD20_RX_14_DELAY                0x08
+#define SD20_RX_SEL_MASK               0x08
+
+/* SD_PUSH_POINT_CTL */
+#define        DDR_FIX_TX_CMD_DAT              0x00
+#define        DDR_VAR_TX_CMD_DAT              0x80
+#define        DDR_FIX_TX_DAT_14_TSU           0x00
+#define        DDR_FIX_TX_DAT_12_TSU           0x40
+#define        DDR_FIX_TX_CMD_NEG_EDGE         0x00
+#define        DDR_FIX_TX_CMD_14_AHEAD         0x20
+#define        SD20_TX_NEG_EDGE                0x00
+#define        SD20_TX_14_AHEAD                0x10
+#define SD20_TX_SEL_MASK               0x10
+#define        DDR_VAR_SDCLK_POL_SWAP          0x01
+
+/* SD_TRANSFER */
+#define        SD_TRANSFER_START               0x80
+#define        SD_TRANSFER_END                 0x40
+#define SD_STAT_IDLE                   0x20
+#define        SD_TRANSFER_ERR                 0x10
+/* SD Transfer Mode definition */
+#define        SD_TM_NORMAL_WRITE              0x00
+#define        SD_TM_AUTO_WRITE_3              0x01
+#define        SD_TM_AUTO_WRITE_4              0x02
+#define        SD_TM_AUTO_READ_3               0x05
+#define        SD_TM_AUTO_READ_4               0x06
+#define        SD_TM_CMD_RSP                   0x08
+#define        SD_TM_AUTO_WRITE_1              0x09
+#define        SD_TM_AUTO_WRITE_2              0x0A
+#define        SD_TM_NORMAL_READ               0x0C
+#define        SD_TM_AUTO_READ_1               0x0D
+#define        SD_TM_AUTO_READ_2               0x0E
+#define        SD_TM_AUTO_TUNING               0x0F
+
+/* SD_VPTX_CTL / SD_VPRX_CTL */
+#define PHASE_CHANGE                   0x80
+#define PHASE_NOT_RESET                        0x40
+
+/* SD_DCMPS_TX_CTL / SD_DCMPS_RX_CTL */
+#define DCMPS_CHANGE                   0x80
+#define DCMPS_CHANGE_DONE              0x40
+#define DCMPS_ERROR                    0x20
+#define DCMPS_CURRENT_PHASE            0x1F
+
+/* SD Configure 1 Register */
+#define SD_CLK_DIVIDE_0                        0x00
+#define        SD_CLK_DIVIDE_256               0xC0
+#define        SD_CLK_DIVIDE_128               0x80
+#define        SD_BUS_WIDTH_1BIT               0x00
+#define        SD_BUS_WIDTH_4BIT               0x01
+#define        SD_BUS_WIDTH_8BIT               0x02
+#define        SD_ASYNC_FIFO_NOT_RST           0x10
+#define        SD_20_MODE                      0x00
+#define        SD_DDR_MODE                     0x04
+#define        SD_30_MODE                      0x08
+
+#define SD_CLK_DIVIDE_MASK             0xC0
+
+/* SD_CMD_STATE */
+#define SD_CMD_IDLE                    0x80
+
+/* SD_DATA_STATE */
+#define SD_DATA_IDLE                   0x80
+
+/* DCM_DRP_CTL */
+#define DCM_RESET                      0x08
+#define DCM_LOCKED                     0x04
+#define DCM_208M                       0x00
+#define DCM_TX                         0x01
+#define DCM_RX                         0x02
+
+/* DCM_DRP_TRIG */
+#define DRP_START                      0x80
+#define DRP_DONE                       0x40
+
+/* DCM_DRP_CFG */
+#define DRP_WRITE                      0x80
+#define DRP_READ                       0x00
+#define DCM_WRITE_ADDRESS_50           0x50
+#define DCM_WRITE_ADDRESS_51           0x51
+#define DCM_READ_ADDRESS_00            0x00
+#define DCM_READ_ADDRESS_51            0x51
+
+/* IRQSTAT0 */
+#define DMA_DONE_INT                   0x80
+#define SUSPEND_INT                    0x40
+#define LINK_RDY_INT                   0x20
+#define LINK_DOWN_INT                  0x10
+
+/* DMACTL */
+#define DMA_RST                                0x80
+#define DMA_BUSY                       0x04
+#define DMA_DIR_TO_CARD                        0x00
+#define DMA_DIR_FROM_CARD              0x02
+#define DMA_EN                         0x01
+#define DMA_128                                (0 << 4)
+#define DMA_256                                (1 << 4)
+#define DMA_512                                (2 << 4)
+#define DMA_1024                       (3 << 4)
+#define DMA_PACK_SIZE_MASK             0x30
+
+/* SSC_CTL1 */
+#define SSC_RSTB                       0x80
+#define SSC_8X_EN                      0x40
+#define SSC_FIX_FRAC                   0x20
+#define SSC_SEL_1M                     0x00
+#define SSC_SEL_2M                     0x08
+#define SSC_SEL_4M                     0x10
+#define SSC_SEL_8M                     0x18
+
+/* SSC_CTL2 */
+#define SSC_DEPTH_MASK                 0x07
+#define SSC_DEPTH_DISALBE              0x00
+#define SSC_DEPTH_4M                   0x01
+#define SSC_DEPTH_2M                   0x02
+#define SSC_DEPTH_1M                   0x03
+#define SSC_DEPTH_500K                 0x04
+#define SSC_DEPTH_250K                 0x05
+
+/* System Clock Control Register */
+#define CLK_LOW_FREQ                   0x01
+
+/* System Clock Divider Register */
+#define CLK_DIV_1                      0x01
+#define CLK_DIV_2                      0x02
+#define CLK_DIV_4                      0x03
+#define CLK_DIV_8                      0x04
+
+/* MS_CFG */
+#define        SAMPLE_TIME_RISING              0x00
+#define        SAMPLE_TIME_FALLING             0x80
+#define        PUSH_TIME_DEFAULT               0x00
+#define        PUSH_TIME_ODD                   0x40
+#define        NO_EXTEND_TOGGLE                0x00
+#define        EXTEND_TOGGLE_CHK               0x20
+#define        MS_BUS_WIDTH_1                  0x00
+#define        MS_BUS_WIDTH_4                  0x10
+#define        MS_BUS_WIDTH_8                  0x18
+#define        MS_2K_SECTOR_MODE               0x04
+#define        MS_512_SECTOR_MODE              0x00
+#define        MS_TOGGLE_TIMEOUT_EN            0x00
+#define        MS_TOGGLE_TIMEOUT_DISEN         0x01
+#define MS_NO_CHECK_INT                        0x02
+
+/* MS_TRANS_CFG */
+#define        WAIT_INT                        0x80
+#define        NO_WAIT_INT                     0x00
+#define        NO_AUTO_READ_INT_REG            0x00
+#define        AUTO_READ_INT_REG               0x40
+#define        MS_CRC16_ERR                    0x20
+#define        MS_RDY_TIMEOUT                  0x10
+#define        MS_INT_CMDNK                    0x08
+#define        MS_INT_BREQ                     0x04
+#define        MS_INT_ERR                      0x02
+#define        MS_INT_CED                      0x01
+
+/* MS_TRANSFER */
+#define        MS_TRANSFER_START               0x80
+#define        MS_TRANSFER_END                 0x40
+#define        MS_TRANSFER_ERR                 0x20
+#define        MS_BS_STATE                     0x10
+#define        MS_TM_READ_BYTES                0x00
+#define        MS_TM_NORMAL_READ               0x01
+#define        MS_TM_WRITE_BYTES               0x04
+#define        MS_TM_NORMAL_WRITE              0x05
+#define        MS_TM_AUTO_READ                 0x08
+#define        MS_TM_AUTO_WRITE                0x0C
+
+/* SD Configure 2 Register */
+#define        SD_CALCULATE_CRC7               0x00
+#define        SD_NO_CALCULATE_CRC7            0x80
+#define        SD_CHECK_CRC16                  0x00
+#define        SD_NO_CHECK_CRC16               0x40
+#define SD_NO_CHECK_WAIT_CRC_TO                0x20
+#define        SD_WAIT_BUSY_END                0x08
+#define        SD_NO_WAIT_BUSY_END             0x00
+#define        SD_CHECK_CRC7                   0x00
+#define        SD_NO_CHECK_CRC7                0x04
+#define        SD_RSP_LEN_0                    0x00
+#define        SD_RSP_LEN_6                    0x01
+#define        SD_RSP_LEN_17                   0x02
+/* SD/MMC Response Type Definition */
+#define        SD_RSP_TYPE_R0                  0x04
+#define        SD_RSP_TYPE_R1                  0x01
+#define        SD_RSP_TYPE_R1b                 0x09
+#define        SD_RSP_TYPE_R2                  0x02
+#define        SD_RSP_TYPE_R3                  0x05
+#define        SD_RSP_TYPE_R4                  0x05
+#define        SD_RSP_TYPE_R5                  0x01
+#define        SD_RSP_TYPE_R6                  0x01
+#define        SD_RSP_TYPE_R7                  0x01
+
+/* SD_CONFIURE3 */
+#define        SD_RSP_80CLK_TIMEOUT_EN         0x01
+
+/* Card Transfer Reset Register */
+#define SPI_STOP                       0x01
+#define XD_STOP                                0x02
+#define SD_STOP                                0x04
+#define MS_STOP                                0x08
+#define SPI_CLR_ERR                    0x10
+#define XD_CLR_ERR                     0x20
+#define SD_CLR_ERR                     0x40
+#define MS_CLR_ERR                     0x80
+
+/* Card Data Source Register */
+#define PINGPONG_BUFFER                        0x01
+#define RING_BUFFER                    0x00
+
+/* Card Power Control Register */
+#define PMOS_STRG_MASK                 0x10
+#define PMOS_STRG_800mA                        0x10
+#define PMOS_STRG_400mA                        0x00
+#define SD_POWER_OFF                   0x03
+#define SD_PARTIAL_POWER_ON            0x01
+#define SD_POWER_ON                    0x00
+#define SD_POWER_MASK                  0x03
+#define MS_POWER_OFF                   0x0C
+#define MS_PARTIAL_POWER_ON            0x04
+#define MS_POWER_ON                    0x00
+#define MS_POWER_MASK                  0x0C
+#define BPP_POWER_OFF                  0x0F
+#define BPP_POWER_5_PERCENT_ON         0x0E
+#define BPP_POWER_10_PERCENT_ON                0x0C
+#define BPP_POWER_15_PERCENT_ON                0x08
+#define BPP_POWER_ON                   0x00
+#define BPP_POWER_MASK                 0x0F
+
+/* PWR_GATE_CTRL */
+#define PWR_GATE_EN                    0x01
+#define LDO3318_PWR_MASK               0x06
+#define LDO_ON                         0x00
+#define LDO_SUSPEND                    0x04
+#define LDO_OFF                                0x06
+
+/* CARD_CLK_SOURCE */
+#define CRC_FIX_CLK                    (0x00 << 0)
+#define CRC_VAR_CLK0                   (0x01 << 0)
+#define CRC_VAR_CLK1                   (0x02 << 0)
+#define SD30_FIX_CLK                   (0x00 << 2)
+#define SD30_VAR_CLK0                  (0x01 << 2)
+#define SD30_VAR_CLK1                  (0x02 << 2)
+#define SAMPLE_FIX_CLK                 (0x00 << 4)
+#define SAMPLE_VAR_CLK0                        (0x01 << 4)
+#define SAMPLE_VAR_CLK1                        (0x02 << 4)
+
+#define MS_CFG                         0xFD40
+#define MS_TPC                         0xFD41
+#define MS_TRANS_CFG                   0xFD42
+#define MS_TRANSFER                    0xFD43
+#define MS_INT_REG                     0xFD44
+#define MS_BYTE_CNT                    0xFD45
+#define MS_SECTOR_CNT_L                        0xFD46
+#define MS_SECTOR_CNT_H                        0xFD47
+#define MS_DBUS_H                      0xFD48
+
+#define SD_CFG1                                0xFDA0
+#define SD_CFG2                                0xFDA1
+#define SD_CFG3                                0xFDA2
+#define SD_STAT1                       0xFDA3
+#define SD_STAT2                       0xFDA4
+#define SD_BUS_STAT                    0xFDA5
+#define SD_PAD_CTL                     0xFDA6
+#define SD_SAMPLE_POINT_CTL            0xFDA7
+#define SD_PUSH_POINT_CTL              0xFDA8
+#define SD_CMD0                                0xFDA9
+#define SD_CMD1                                0xFDAA
+#define SD_CMD2                                0xFDAB
+#define SD_CMD3                                0xFDAC
+#define SD_CMD4                                0xFDAD
+#define SD_CMD5                                0xFDAE
+#define SD_BYTE_CNT_L                  0xFDAF
+#define SD_BYTE_CNT_H                  0xFDB0
+#define SD_BLOCK_CNT_L                 0xFDB1
+#define SD_BLOCK_CNT_H                 0xFDB2
+#define SD_TRANSFER                    0xFDB3
+#define SD_CMD_STATE                   0xFDB5
+#define SD_DATA_STATE                  0xFDB6
+
+#define SRCTL                          0xFC13
+
+#define        DCM_DRP_CTL                     0xFC23
+#define        DCM_DRP_TRIG                    0xFC24
+#define        DCM_DRP_CFG                     0xFC25
+#define        DCM_DRP_WR_DATA_L               0xFC26
+#define        DCM_DRP_WR_DATA_H               0xFC27
+#define        DCM_DRP_RD_DATA_L               0xFC28
+#define        DCM_DRP_RD_DATA_H               0xFC29
+#define SD_VPCLK0_CTL                  0xFC2A
+#define SD_VPCLK1_CTL                  0xFC2B
+#define SD_DCMPS0_CTL                  0xFC2C
+#define SD_DCMPS1_CTL                  0xFC2D
+#define SD_VPTX_CTL                    SD_VPCLK0_CTL
+#define SD_VPRX_CTL                    SD_VPCLK1_CTL
+#define SD_DCMPS_TX_CTL                        SD_DCMPS0_CTL
+#define SD_DCMPS_RX_CTL                        SD_DCMPS1_CTL
+#define CARD_CLK_SOURCE                        0xFC2E
+
+#define CARD_PWR_CTL                   0xFD50
+#define CARD_CLK_SWITCH                        0xFD51
+#define CARD_SHARE_MODE                        0xFD52
+#define CARD_DRIVE_SEL                 0xFD53
+#define CARD_STOP                      0xFD54
+#define CARD_OE                                0xFD55
+#define CARD_AUTO_BLINK                        0xFD56
+#define CARD_GPIO_DIR                  0xFD57
+#define CARD_GPIO                      0xFD58
+#define CARD_DATA_SOURCE               0xFD5B
+#define CARD_SELECT                    0xFD5C
+#define SD30_DRIVE_SEL                 0xFD5E
+#define CARD_CLK_EN                    0xFD69
+#define SDIO_CTRL                      0xFD6B
+#define CD_PAD_CTL                     0xFD73
+
+#define FPDCTL                         0xFC00
+#define PDINFO                         0xFC01
+
+#define CLK_CTL                                0xFC02
+#define CLK_DIV                                0xFC03
+#define CLK_SEL                                0xFC04
+
+#define SSC_DIV_N_0                    0xFC0F
+#define SSC_DIV_N_1                    0xFC10
+#define SSC_CTL1                       0xFC11
+#define SSC_CTL2                       0xFC12
+
+#define RCCTL                          0xFC14
+
+#define FPGA_PULL_CTL                  0xFC1D
+#define OLT_LED_CTL                    0xFC1E
+#define GPIO_CTL                       0xFC1F
+
+#define LDO_CTL                                0xFC1E
+#define SYS_VER                                0xFC32
+
+#define CARD_PULL_CTL1                 0xFD60
+#define CARD_PULL_CTL2                 0xFD61
+#define CARD_PULL_CTL3                 0xFD62
+#define CARD_PULL_CTL4                 0xFD63
+#define CARD_PULL_CTL5                 0xFD64
+#define CARD_PULL_CTL6                 0xFD65
+
+/* PCI Express Related Registers */
+#define IRQEN0                         0xFE20
+#define IRQSTAT0                       0xFE21
+#define IRQEN1                         0xFE22
+#define IRQSTAT1                       0xFE23
+#define TLPRIEN                                0xFE24
+#define TLPRISTAT                      0xFE25
+#define TLPTIEN                                0xFE26
+#define TLPTISTAT                      0xFE27
+#define DMATC0                         0xFE28
+#define DMATC1                         0xFE29
+#define DMATC2                         0xFE2A
+#define DMATC3                         0xFE2B
+#define DMACTL                         0xFE2C
+#define BCTL                           0xFE2D
+#define RBBC0                          0xFE2E
+#define RBBC1                          0xFE2F
+#define RBDAT                          0xFE30
+#define RBCTL                          0xFE34
+#define CFGADDR0                       0xFE35
+#define CFGADDR1                       0xFE36
+#define CFGDATA0                       0xFE37
+#define CFGDATA1                       0xFE38
+#define CFGDATA2                       0xFE39
+#define CFGDATA3                       0xFE3A
+#define CFGRWCTL                       0xFE3B
+#define PHYRWCTL                       0xFE3C
+#define PHYDATA0                       0xFE3D
+#define PHYDATA1                       0xFE3E
+#define PHYADDR                                0xFE3F
+#define MSGRXDATA0                     0xFE40
+#define MSGRXDATA1                     0xFE41
+#define MSGRXDATA2                     0xFE42
+#define MSGRXDATA3                     0xFE43
+#define MSGTXDATA0                     0xFE44
+#define MSGTXDATA1                     0xFE45
+#define MSGTXDATA2                     0xFE46
+#define MSGTXDATA3                     0xFE47
+#define MSGTXCTL                       0xFE48
+#define PETXCFG                                0xFE49
+
+#define CDRESUMECTL                    0xFE52
+#define WAKE_SEL_CTL                   0xFE54
+#define PME_FORCE_CTL                  0xFE56
+#define ASPM_FORCE_CTL                 0xFE57
+#define PM_CLK_FORCE_CTL               0xFE58
+#define PERST_GLITCH_WIDTH             0xFE5C
+#define CHANGE_LINK_STATE              0xFE5B
+#define RESET_LOAD_REG                 0xFE5E
+#define EFUSE_CONTENT                  0xFE5F
+#define HOST_SLEEP_STATE               0xFE60
+#define SDIO_CFG                       0xFE70
+
+#define NFTS_TX_CTRL                   0xFE72
+
+#define PWR_GATE_CTRL                  0xFE75
+#define PWD_SUSPEND_EN                 0xFE76
+#define LDO_PWR_SEL                    0xFE78
+
+#define DUMMY_REG_RESET_0              0xFE90
+
+/* Memory mapping */
+#define SRAM_BASE                      0xE600
+#define RBUF_BASE                      0xF400
+#define PPBUF_BASE1                    0xF800
+#define PPBUF_BASE2                    0xFA00
+#define IMAGE_FLAG_ADDR0               0xCE80
+#define IMAGE_FLAG_ADDR1               0xCE81
+
+#define rtsx_pci_init_cmd(pcr)         ((pcr)->ci = 0)
+
+struct rtsx_pcr;
+
+struct pcr_handle {
+       struct rtsx_pcr                 *pcr;
+};
+
+struct pcr_ops {
+       int             (*extra_init_hw)(struct rtsx_pcr *pcr);
+       int             (*optimize_phy)(struct rtsx_pcr *pcr);
+       int             (*turn_on_led)(struct rtsx_pcr *pcr);
+       int             (*turn_off_led)(struct rtsx_pcr *pcr);
+       int             (*enable_auto_blink)(struct rtsx_pcr *pcr);
+       int             (*disable_auto_blink)(struct rtsx_pcr *pcr);
+       int             (*card_power_on)(struct rtsx_pcr *pcr, int card);
+       int             (*card_power_off)(struct rtsx_pcr *pcr, int card);
+       unsigned int    (*cd_deglitch)(struct rtsx_pcr *pcr);
+};
+
+enum PDEV_STAT  {PDEV_STAT_IDLE, PDEV_STAT_RUN};
+
+struct rtsx_pcr {
+       struct pci_dev                  *pci;
+       unsigned int                    id;
+
+       /* pci resources */
+       unsigned long                   addr;
+       void __iomem                    *remap_addr;
+       int                             irq;
+
+       /* host reserved buffer */
+       void                            *rtsx_resv_buf;
+       dma_addr_t                      rtsx_resv_buf_addr;
+
+       void                            *host_cmds_ptr;
+       dma_addr_t                      host_cmds_addr;
+       int                             ci;
+
+       void                            *host_sg_tbl_ptr;
+       dma_addr_t                      host_sg_tbl_addr;
+       int                             sgi;
+
+       u32                             bier;
+       char                            trans_result;
+
+       unsigned int                    card_inserted;
+       unsigned int                    card_removed;
+
+       struct delayed_work             carddet_work;
+       struct delayed_work             idle_work;
+
+       spinlock_t                      lock;
+       struct mutex                    pcr_mutex;
+       struct completion               *done;
+       struct completion               *finish_me;
+
+       unsigned int                    cur_clock;
+       bool                            ms_pmos;
+       bool                            remove_pci;
+       bool                            msi_en;
+
+#define EXTRA_CAPS_SD_SDR50            (1 << 0)
+#define EXTRA_CAPS_SD_SDR104           (1 << 1)
+#define EXTRA_CAPS_SD_DDR50            (1 << 2)
+#define EXTRA_CAPS_MMC_HSDDR           (1 << 3)
+#define EXTRA_CAPS_MMC_HS200           (1 << 4)
+#define EXTRA_CAPS_MMC_8BIT            (1 << 5)
+       u32                             extra_caps;
+
+#define IC_VER_A                       0
+#define IC_VER_B                       1
+#define IC_VER_C                       2
+#define IC_VER_D                       3
+       u8                              ic_version;
+
+       const u32                       *sd_pull_ctl_enable_tbl;
+       const u32                       *sd_pull_ctl_disable_tbl;
+       const u32                       *ms_pull_ctl_enable_tbl;
+       const u32                       *ms_pull_ctl_disable_tbl;
+
+       const struct pcr_ops            *ops;
+       enum PDEV_STAT                  state;
+
+       int                             num_slots;
+       struct rtsx_slot                *slots;
+};
+
+#define CHK_PCI_PID(pcr, pid)          ((pcr)->pci->device == (pid))
+#define PCI_VID(pcr)                   ((pcr)->pci->vendor)
+#define PCI_PID(pcr)                   ((pcr)->pci->device)
+
+void rtsx_pci_start_run(struct rtsx_pcr *pcr);
+int rtsx_pci_write_register(struct rtsx_pcr *pcr, u16 addr, u8 mask, u8 data);
+int rtsx_pci_read_register(struct rtsx_pcr *pcr, u16 addr, u8 *data);
+int rtsx_pci_write_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 val);
+int rtsx_pci_read_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 *val);
+void rtsx_pci_stop_cmd(struct rtsx_pcr *pcr);
+void rtsx_pci_add_cmd(struct rtsx_pcr *pcr,
+               u8 cmd_type, u16 reg_addr, u8 mask, u8 data);
+void rtsx_pci_send_cmd_no_wait(struct rtsx_pcr *pcr);
+int rtsx_pci_send_cmd(struct rtsx_pcr *pcr, int timeout);
+int rtsx_pci_transfer_data(struct rtsx_pcr *pcr, struct scatterlist *sglist,
+               int num_sg, bool read, int timeout);
+int rtsx_pci_read_ppbuf(struct rtsx_pcr *pcr, u8 *buf, int buf_len);
+int rtsx_pci_write_ppbuf(struct rtsx_pcr *pcr, u8 *buf, int buf_len);
+int rtsx_pci_card_pull_ctl_enable(struct rtsx_pcr *pcr, int card);
+int rtsx_pci_card_pull_ctl_disable(struct rtsx_pcr *pcr, int card);
+int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock,
+               u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk);
+int rtsx_pci_card_power_on(struct rtsx_pcr *pcr, int card);
+int rtsx_pci_card_power_off(struct rtsx_pcr *pcr, int card);
+unsigned int rtsx_pci_card_exist(struct rtsx_pcr *pcr);
+void rtsx_pci_complete_unfinished_transfer(struct rtsx_pcr *pcr);
+
+static inline u8 *rtsx_pci_get_cmd_data(struct rtsx_pcr *pcr)
+{
+       return (u8 *)(pcr->host_cmds_ptr);
+}
+
+#endif
index d09dde1e57fb43e2f5be170d31e8fceb8c41a389..9aa863da287fedf383f3c507c287aa220cbd6a86 100644 (file)
@@ -11,6 +11,8 @@ extern int sysctl_overcommit_memory;
 extern int sysctl_overcommit_ratio;
 extern struct percpu_counter vm_committed_as;
 
+unsigned long vm_memory_committed(void);
+
 static inline void vm_acct_memory(long pages)
 {
        percpu_counter_add(&vm_committed_as, pages);
diff --git a/include/linux/platform_data/uio_dmem_genirq.h b/include/linux/platform_data/uio_dmem_genirq.h
new file mode 100644 (file)
index 0000000..973c1bb
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * include/linux/platform_data/uio_dmem_genirq.h
+ *
+ * Copyright (C) 2012 Damian Hobson-Garcia
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _UIO_DMEM_GENIRQ_H
+#define _UIO_DMEM_GENIRQ_H
+
+#include <linux/uio_driver.h>
+
+struct uio_dmem_genirq_pdata {
+       struct uio_info uioinfo;
+       unsigned int *dynamic_region_sizes;
+       unsigned int num_dynamic_regions;
+};
+#endif /* _UIO_DMEM_GENIRQ_H */
index 9a796c41e7d9ecf8d474089df2d1b2235451ce3c..7d416055f08cf3605e2ae6a8160dc6b438a02c71 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -88,6 +88,20 @@ int sysctl_max_map_count __read_mostly = DEFAULT_MAX_MAP_COUNT;
  */
 struct percpu_counter vm_committed_as ____cacheline_aligned_in_smp;
 
+/*
+ * The global memory commitment made in the system can be a metric
+ * that can be used to drive ballooning decisions when Linux is hosted
+ * as a guest. On Hyper-V, the host implements a policy engine for dynamically
+ * balancing memory across competing virtual machines that are hosted.
+ * Several metrics drive this policy engine including the guest reported
+ * memory commitment.
+ */
+unsigned long vm_memory_committed(void)
+{
+       return percpu_counter_read_positive(&vm_committed_as);
+}
+EXPORT_SYMBOL_GPL(vm_memory_committed);
+
 /*
  * Check that a process has enough memory to allocate a new virtual
  * mapping. 0 means there is enough memory for the allocation to
index 45131b41bcdbaf4fdfd6c411a6d0aef526ed0883..79c3cac87afa1d1f96fc9397f50bb370980e0a84 100644 (file)
@@ -66,6 +66,21 @@ int heap_stack_gap = 0;
 
 atomic_long_t mmap_pages_allocated;
 
+/*
+ * The global memory commitment made in the system can be a metric
+ * that can be used to drive ballooning decisions when Linux is hosted
+ * as a guest. On Hyper-V, the host implements a policy engine for dynamically
+ * balancing memory across competing virtual machines that are hosted.
+ * Several metrics drive this policy engine including the guest reported
+ * memory commitment.
+ */
+unsigned long vm_memory_committed(void)
+{
+       return percpu_counter_read_positive(&vm_committed_as);
+}
+
+EXPORT_SYMBOL_GPL(vm_memory_committed);
+
 EXPORT_SYMBOL(mem_map);
 EXPORT_SYMBOL(num_physpages);
 
index 5959affd882076d73338b0dff7d0cffb496636c7..d25a46925e61d05fd8fea7c2618a24cb1e6ea4d5 100644 (file)
@@ -43,6 +43,7 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <dirent.h>
+#include <net/if.h>
 
 /*
  * KVP protocol: The user mode component first registers with the
@@ -88,6 +89,7 @@ static char *os_major = "";
 static char *os_minor = "";
 static char *processor_arch;
 static char *os_build;
+static char *os_version;
 static char *lic_version = "Unknown version";
 static struct utsname uts_buf;
 
@@ -297,7 +299,7 @@ static int kvp_file_init(void)
        return 0;
 }
 
-static int kvp_key_delete(int pool, __u8 *key, int key_size)
+static int kvp_key_delete(int pool, const char *key, int key_size)
 {
        int i;
        int j, k;
@@ -340,7 +342,7 @@ static int kvp_key_delete(int pool, __u8 *key, int key_size)
        return 1;
 }
 
-static int kvp_key_add_or_modify(int pool, __u8 *key, int key_size, __u8 *value,
+static int kvp_key_add_or_modify(int pool, const char *key, int key_size, const char *value,
                        int value_size)
 {
        int i;
@@ -394,7 +396,7 @@ static int kvp_key_add_or_modify(int pool, __u8 *key, int key_size, __u8 *value,
        return 0;
 }
 
-static int kvp_get_value(int pool, __u8 *key, int key_size, __u8 *value,
+static int kvp_get_value(int pool, const char *key, int key_size, char *value,
                        int value_size)
 {
        int i;
@@ -426,8 +428,8 @@ static int kvp_get_value(int pool, __u8 *key, int key_size, __u8 *value,
        return 1;
 }
 
-static int kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size,
-                               __u8 *value, int value_size)
+static int kvp_pool_enumerate(int pool, int index, char *key, int key_size,
+                               char *value, int value_size)
 {
        struct kvp_record *record;
 
@@ -453,7 +455,9 @@ void kvp_get_os_info(void)
        char    *p, buf[512];
 
        uname(&uts_buf);
-       os_build = uts_buf.release;
+       os_version = uts_buf.release;
+       os_build = strdup(uts_buf.release);
+
        os_name = uts_buf.sysname;
        processor_arch = uts_buf.machine;
 
@@ -462,7 +466,7 @@ void kvp_get_os_info(void)
         * string to be of the form: x.y.z
         * Strip additional information we may have.
         */
-       p = strchr(os_build, '-');
+       p = strchr(os_version, '-');
        if (p)
                *p = '\0';
 
@@ -879,7 +883,7 @@ static int kvp_process_ip_address(void *addrp,
                addr_length = INET6_ADDRSTRLEN;
        }
 
-       if ((length - *offset) < addr_length + 1)
+       if ((length - *offset) < addr_length + 2)
                return HV_E_FAIL;
        if (str == NULL) {
                strcpy(buffer, "inet_ntop failed\n");
@@ -887,11 +891,13 @@ static int kvp_process_ip_address(void *addrp,
        }
        if (*offset == 0)
                strcpy(buffer, tmp);
-       else
+       else {
+               strcat(buffer, ";");
                strcat(buffer, tmp);
-       strcat(buffer, ";");
+       }
 
        *offset += strlen(str) + 1;
+
        return 0;
 }
 
@@ -953,7 +959,9 @@ kvp_get_ip_info(int family, char *if_name, int op,
                 * supported address families; if not we gather info on
                 * the specified address family.
                 */
-               if ((family != 0) && (curp->ifa_addr->sa_family != family)) {
+               if ((((family != 0) &&
+                        (curp->ifa_addr->sa_family != family))) ||
+                        (curp->ifa_flags & IFF_LOOPBACK)) {
                        curp = curp->ifa_next;
                        continue;
                }
@@ -1478,13 +1486,19 @@ int main(void)
                len = recvfrom(fd, kvp_recv_buffer, sizeof(kvp_recv_buffer), 0,
                                addr_p, &addr_l);
 
-               if (len < 0 || addr.nl_pid) {
+               if (len < 0) {
                        syslog(LOG_ERR, "recvfrom failed; pid:%u error:%d %s",
                                        addr.nl_pid, errno, strerror(errno));
                        close(fd);
                        return -1;
                }
 
+               if (addr.nl_pid) {
+                       syslog(LOG_WARNING, "Received packet from untrusted pid:%u",
+                                       addr.nl_pid);
+                       continue;
+               }
+
                incoming_msg = (struct nlmsghdr *)kvp_recv_buffer;
                incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg);
                hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data;
@@ -1649,7 +1663,7 @@ int main(void)
                        strcpy(key_name, "OSMinorVersion");
                        break;
                case OSVersion:
-                       strcpy(key_value, os_build);
+                       strcpy(key_value, os_version);
                        strcpy(key_name, "OSVersion");
                        break;
                case ProcessorArchitecture: