]> git.openfabrics.org - ~shefty/rdma-dev.git/commitdiff
Merge branch 'drm-fixes' of git://people.freedesktop.org/~airlied/linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 14 Jan 2012 04:48:42 +0000 (20:48 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 14 Jan 2012 04:48:42 +0000 (20:48 -0800)
* 'drm-fixes' of git://people.freedesktop.org/~airlied/linux:
  dma-buf: Documentation update for Kconfig select
  nouveau: Support Optimus models for vga_switcheroo
  nouveau: properly check for _DSM function support
  dma-buf: drop option text so users don't select it.
  radeon: Call pci_clear_master() instead of open-coding it.
  gma500: Discard modes that don't fit in stolen memory
  drm: bump DRM_CONNECTOR_MAX_ENCODER from 2 to 3
  drm/radeon/kms: Fix module parameter description format
  drm/radeon/kms/ni: fix packet2 handling for VM IB parser
  ttm/dma: Remove the WARN() which is not useful.

214 files changed:
Documentation/devicetree/bindings/mfd/mc13xxx.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mfd/twl-familly.txt [new file with mode: 0644]
Documentation/filesystems/ceph.txt
Documentation/filesystems/squashfs.txt
Documentation/mmc/mmc-dev-attrs.txt
Documentation/mmc/mmc-dev-parts.txt
arch/arm/mach-sa1100/assabet.c
arch/arm/mach-sa1100/cerf.c
arch/arm/mach-sa1100/collie.c
arch/arm/mach-sa1100/generic.c
arch/arm/mach-sa1100/include/mach/mcp.h
arch/arm/mach-sa1100/lart.c
arch/arm/mach-sa1100/shannon.c
arch/arm/mach-sa1100/simpad.c
arch/arm/mach-ux500/board-mop500.c
arch/arm/mach-ux500/board-u5500.c
arch/arm/mach-ux500/include/mach/irqs-board-mop500.h
arch/arm/plat-samsung/include/plat/sdhci.h
arch/arm/plat-samsung/platformdata.c
arch/ia64/include/asm/unistd.h
arch/ia64/kernel/entry.S
arch/x86/kernel/cpu/mcheck/mce.c
drivers/Makefile
drivers/block/rbd.c
drivers/gpio/gpio-stmpe.c
drivers/input/misc/ab8500-ponkey.c
drivers/leds/Kconfig
drivers/leds/Makefile
drivers/leds/leds-max8997.c [new file with mode: 0644]
drivers/mfd/88pm860x-i2c.c
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/aat2870-core.c
drivers/mfd/ab5500-core.c
drivers/mfd/ab5500-debugfs.c
drivers/mfd/ab8500-core.c
drivers/mfd/ab8500-debugfs.c
drivers/mfd/ab8500-gpadc.c
drivers/mfd/ab8500-i2c.c
drivers/mfd/ab8500-sysctrl.c
drivers/mfd/cs5535-mfd.c
drivers/mfd/dm355evm_msp.c
drivers/mfd/intel_msic.c
drivers/mfd/jz4740-adc.c
drivers/mfd/lpc_sch.c
drivers/mfd/max8925-i2c.c
drivers/mfd/max8997.c
drivers/mfd/max8998.c
drivers/mfd/mc13xxx-core.c
drivers/mfd/mcp-core.c
drivers/mfd/mcp-sa11x0.c
drivers/mfd/omap-usb-host.c
drivers/mfd/pcf50633-adc.c
drivers/mfd/s5m-core.c [new file with mode: 0644]
drivers/mfd/s5m-irq.c [new file with mode: 0644]
drivers/mfd/sm501.c
drivers/mfd/stmpe-i2c.c [new file with mode: 0644]
drivers/mfd/stmpe-spi.c [new file with mode: 0644]
drivers/mfd/stmpe.c
drivers/mfd/stmpe.h
drivers/mfd/t7l66xb.c
drivers/mfd/tc6387xb.c
drivers/mfd/ti-ssp.c
drivers/mfd/timberdale.c
drivers/mfd/tps65910-irq.c
drivers/mfd/tps65910.c
drivers/mfd/tps65912-spi.c
drivers/mfd/twl-core.c
drivers/mfd/twl4030-audio.c
drivers/mfd/twl4030-irq.c
drivers/mfd/twl4030-madc.c
drivers/mfd/twl4030-power.c
drivers/mfd/twl6040-core.c
drivers/mfd/ucb1x00-core.c
drivers/mfd/ucb1x00-ts.c
drivers/mfd/vx855.c
drivers/mfd/wm831x-core.c
drivers/mfd/wm831x-i2c.c
drivers/mfd/wm831x-irq.c
drivers/mfd/wm831x-spi.c
drivers/mfd/wm8350-core.c
drivers/mfd/wm8350-i2c.c
drivers/mfd/wm8400-core.c
drivers/misc/Kconfig
drivers/misc/Makefile
drivers/misc/ab8500-pwm.c
drivers/misc/max8997-muic.c [new file with mode: 0644]
drivers/mmc/Makefile
drivers/mmc/card/block.c
drivers/mmc/card/mmc_test.c
drivers/mmc/card/queue.c
drivers/mmc/core/Makefile
drivers/mmc/core/bus.c
drivers/mmc/core/cd-gpio.c [new file with mode: 0644]
drivers/mmc/core/core.c
drivers/mmc/core/core.h
drivers/mmc/core/debugfs.c
drivers/mmc/core/host.c
drivers/mmc/core/mmc.c
drivers/mmc/core/sd.c
drivers/mmc/core/sdio.c
drivers/mmc/core/sdio_io.c
drivers/mmc/core/sdio_ops.c
drivers/mmc/host/Makefile
drivers/mmc/host/at91_mci.c
drivers/mmc/host/bfin_sdh.c
drivers/mmc/host/cb710-mmc.c
drivers/mmc/host/dw_mmc.c
drivers/mmc/host/dw_mmc.h
drivers/mmc/host/jz4740_mmc.c
drivers/mmc/host/mmc_spi.c
drivers/mmc/host/mmci.c
drivers/mmc/host/msm_sdcc.c
drivers/mmc/host/mxcmmc.c
drivers/mmc/host/mxs-mmc.c
drivers/mmc/host/omap_hsmmc.c
drivers/mmc/host/pxamci.c
drivers/mmc/host/s3cmci.c
drivers/mmc/host/sdhci-cns3xxx.c
drivers/mmc/host/sdhci-dove.c
drivers/mmc/host/sdhci-esdhc-imx.c
drivers/mmc/host/sdhci-esdhc.h
drivers/mmc/host/sdhci-of-esdhc.c
drivers/mmc/host/sdhci-of-hlwd.c
drivers/mmc/host/sdhci-pci-data.c [new file with mode: 0644]
drivers/mmc/host/sdhci-pci.c
drivers/mmc/host/sdhci-pxav2.c
drivers/mmc/host/sdhci-pxav3.c
drivers/mmc/host/sdhci-s3c.c
drivers/mmc/host/sdhci-spear.c
drivers/mmc/host/sdhci-tegra.c
drivers/mmc/host/sdhci.c
drivers/mmc/host/sdhci.h
drivers/mmc/host/sh_mmcif.c
drivers/mmc/host/sh_mobile_sdhi.c
drivers/mmc/host/tifm_sd.c
drivers/mmc/host/tmio_mmc.c
drivers/mmc/host/tmio_mmc.h
drivers/mmc/host/tmio_mmc_pio.c
drivers/mtd/ubi/debug.h
drivers/mtd/ubi/vtbl.c
drivers/regulator/ab8500.c
drivers/rtc/rtc-ab8500.c
drivers/rtc/rtc-max8925.c
drivers/usb/otg/ab8500-usb.c
fs/aio.c
fs/autofs4/waitq.c
fs/ceph/dir.c
fs/ceph/export.c
fs/ceph/inode.c
fs/ceph/mds_client.c
fs/ceph/super.c
fs/ceph/super.h
fs/ceph/xattr.c
fs/dcache.c
fs/gfs2/glock.c
fs/gfs2/glock.h
fs/gfs2/incore.h
fs/gfs2/inode.c
fs/gfs2/lock_dlm.c
fs/gfs2/main.c
fs/gfs2/ops_fstype.c
fs/gfs2/recovery.c
fs/gfs2/rgrp.c
fs/gfs2/sys.c
fs/gfs2/sys.h
fs/squashfs/cache.c
fs/squashfs/inode.c
fs/squashfs/squashfs_fs_sb.h
fs/squashfs/super.c
fs/ubifs/debug.c
fs/ubifs/debug.h
fs/ubifs/journal.c
fs/ubifs/replay.c
fs/ubifs/tnc.c
fs/ubifs/tnc_misc.c
include/linux/amba/mmci.h
include/linux/dcache.h
include/linux/gfs2_ondisk.h
include/linux/i2c/twl.h
include/linux/mfd/88pm860x.h
include/linux/mfd/ab5500/ab5500.h [deleted file]
include/linux/mfd/ab8500.h [deleted file]
include/linux/mfd/ab8500/gpadc.h [deleted file]
include/linux/mfd/ab8500/gpio.h [deleted file]
include/linux/mfd/ab8500/sysctrl.h [deleted file]
include/linux/mfd/abx500/ab5500.h [new file with mode: 0644]
include/linux/mfd/abx500/ab8500-gpadc.h [new file with mode: 0644]
include/linux/mfd/abx500/ab8500-gpio.h [new file with mode: 0644]
include/linux/mfd/abx500/ab8500-sysctrl.h [new file with mode: 0644]
include/linux/mfd/abx500/ab8500.h [new file with mode: 0644]
include/linux/mfd/max8925.h
include/linux/mfd/max8997.h
include/linux/mfd/mc13xxx.h
include/linux/mfd/mcp.h
include/linux/mfd/s5m87xx/s5m-core.h [new file with mode: 0644]
include/linux/mfd/s5m87xx/s5m-pmic.h [new file with mode: 0644]
include/linux/mfd/s5m87xx/s5m-rtc.h [new file with mode: 0644]
include/linux/mfd/stmpe.h
include/linux/mfd/ucb1x00.h
include/linux/mmc/card.h
include/linux/mmc/cd-gpio.h [new file with mode: 0644]
include/linux/mmc/core.h
include/linux/mmc/dw_mmc.h
include/linux/mmc/host.h
include/linux/mmc/mmc.h
include/linux/mmc/sdhci-pci-data.h [new file with mode: 0644]
include/linux/mmc/sdhci.h
include/linux/mmc/sdio.h
include/linux/mod_devicetable.h
net/ceph/crush/mapper.c
net/ceph/crypto.c
net/ceph/osd_client.c
scripts/mod/file2alias.c

diff --git a/Documentation/devicetree/bindings/mfd/mc13xxx.txt b/Documentation/devicetree/bindings/mfd/mc13xxx.txt
new file mode 100644 (file)
index 0000000..19f6af4
--- /dev/null
@@ -0,0 +1,78 @@
+* Freescale MC13783/MC13892 Power Management Integrated Circuit (PMIC)
+
+Required properties:
+- compatible : Should be "fsl,mc13783" or "fsl,mc13892"
+
+Optional properties:
+- fsl,mc13xxx-uses-adc : Indicate the ADC is being used
+- fsl,mc13xxx-uses-codec : Indicate the Audio Codec is being used
+- fsl,mc13xxx-uses-rtc : Indicate the RTC is being used
+- fsl,mc13xxx-uses-touch : Indicate the touchscreen controller is being used
+
+Sub-nodes:
+- regulators : Contain the regulator nodes.  The MC13892 regulators are
+  bound using their names as listed below with their registers and bits
+  for enabling.
+
+    vcoincell : regulator VCOINCELL (register 13, bit 23)
+    sw1       : regulator SW1      (register 24, bit 0)
+    sw2       : regulator SW2      (register 25, bit 0)
+    sw3       : regulator SW3      (register 26, bit 0)
+    sw4       : regulator SW4      (register 27, bit 0)
+    swbst     : regulator SWBST            (register 29, bit 20)
+    vgen1     : regulator VGEN1            (register 32, bit 0)
+    viohi     : regulator VIOHI            (register 32, bit 3)
+    vdig      : regulator VDIG     (register 32, bit 9)
+    vgen2     : regulator VGEN2            (register 32, bit 12)
+    vpll      : regulator VPLL     (register 32, bit 15)
+    vusb2     : regulator VUSB2            (register 32, bit 18)
+    vgen3     : regulator VGEN3            (register 33, bit 0)
+    vcam      : regulator VCAM     (register 33, bit 6)
+    vvideo    : regulator VVIDEO    (register 33, bit 12)
+    vaudio    : regulator VAUDIO    (register 33, bit 15)
+    vsd       : regulator VSD      (register 33, bit 18)
+    gpo1      : regulator GPO1     (register 34, bit 6)
+    gpo2      : regulator GPO2     (register 34, bit 8)
+    gpo3      : regulator GPO3     (register 34, bit 10)
+    gpo4      : regulator GPO4     (register 34, bit 12)
+    pwgt1spi  : regulator PWGT1SPI  (register 34, bit 15)
+    pwgt2spi  : regulator PWGT2SPI  (register 34, bit 16)
+    vusb      : regulator VUSB     (register 50, bit 3)
+
+  The bindings details of individual regulator device can be found in:
+  Documentation/devicetree/bindings/regulator/regulator.txt
+
+Examples:
+
+ecspi@70010000 { /* ECSPI1 */
+       fsl,spi-num-chipselects = <2>;
+       cs-gpios = <&gpio3 24 0>, /* GPIO4_24 */
+                  <&gpio3 25 0>; /* GPIO4_25 */
+       status = "okay";
+
+       pmic: mc13892@0 {
+               #address-cells = <1>;
+               #size-cells = <0>;
+               compatible = "fsl,mc13892";
+               spi-max-frequency = <6000000>;
+               reg = <0>;
+               interrupt-parent = <&gpio0>;
+               interrupts = <8>;
+
+               regulators {
+                       sw1_reg: mc13892__sw1 {
+                               regulator-min-microvolt = <600000>;
+                               regulator-max-microvolt = <1375000>;
+                               regulator-boot-on;
+                               regulator-always-on;
+                       };
+
+                       sw2_reg: mc13892__sw2 {
+                               regulator-min-microvolt = <900000>;
+                               regulator-max-microvolt = <1850000>;
+                               regulator-boot-on;
+                               regulator-always-on;
+                       };
+               };
+       };
+};
diff --git a/Documentation/devicetree/bindings/mfd/twl-familly.txt b/Documentation/devicetree/bindings/mfd/twl-familly.txt
new file mode 100644 (file)
index 0000000..a66fcf9
--- /dev/null
@@ -0,0 +1,47 @@
+Texas Instruments TWL family
+
+The TWLs are Integrated Power Management Chips.
+Some version might contain much more analog function like
+USB transceiver or Audio amplifier.
+These chips are connected to an i2c bus.
+
+
+Required properties:
+- compatible : Must be "ti,twl4030";
+  For Integrated power-management/audio CODEC device used in OMAP3
+  based boards
+- compatible : Must be "ti,twl6030";
+  For Integrated power-management used in OMAP4 based boards
+- interrupts : This i2c device has an IRQ line connected to the main SoC
+- interrupt-controller : Since the twl support several interrupts internally,
+  it is considered as an interrupt controller cascaded to the SoC one.
+- #interrupt-cells = <1>;
+- interrupt-parent : The parent interrupt controller.
+
+Optional node:
+- Child nodes contain in the twl. The twl family is made of several variants
+  that support a different number of features.
+  The children nodes will thus depend of the capability of the variant.
+
+
+Example:
+/*
+ * Integrated Power Management Chip
+ * http://www.ti.com/lit/ds/symlink/twl6030.pdf
+ */
+twl@48 {
+    compatible = "ti,twl6030";
+    reg = <0x48>;
+    interrupts = <39>; /* IRQ_SYS_1N cascaded to gic */
+    interrupt-controller;
+    #interrupt-cells = <1>;
+    interrupt-parent = <&gic>;
+    #address-cells = <1>;
+    #size-cells = <0>;
+
+    twl_rtc {
+        compatible = "ti,twl_rtc";
+        interrupts = <11>;
+        reg = <0>;
+    };
+};
index 763d8ebbbebdeb59129742daa5f5b7a105ac8fbb..d6030aa3337605e7550397a303820952c8666bc8 100644 (file)
@@ -119,12 +119,20 @@ Mount Options
        must rely on TCP's error correction to detect data corruption
        in the data payload.
 
-  noasyncreaddir
-       Disable client's use its local cache to satisfy readdir
-       requests.  (This does not change correctness; the client uses
-       cached metadata only when a lease or capability ensures it is
-       valid.)
+  dcache
+        Use the dcache contents to perform negative lookups and
+        readdir when the client has the entire directory contents in
+        its cache.  (This does not change correctness; the client uses
+        cached metadata only when a lease or capability ensures it is
+        valid.)
+
+  nodcache
+        Do not use the dcache as above.  This avoids a significant amount of
+        complex code, sacrificing performance without affecting correctness,
+        and is useful for tracking down bugs.
 
+  noasyncreaddir
+       Do not use the dcache as above for readdir.
 
 More Information
 ================
index 7db3ebda5a4c9afdbd1c4a7baffd039ad220933e..403c090aca39954d378809b55b0585f74085422c 100644 (file)
@@ -93,8 +93,8 @@ byte alignment:
 
 Compressed data blocks are written to the filesystem as files are read from
 the source directory, and checked for duplicates.  Once all file data has been
-written the completed inode, directory, fragment, export and uid/gid lookup
-tables are written.
+written the completed inode, directory, fragment, export, uid/gid lookup and
+xattr tables are written.
 
 3.1 Compression options
 -----------------------
@@ -151,7 +151,7 @@ in each metadata block.  Directories are sorted in alphabetical order,
 and at lookup the index is scanned linearly looking for the first filename
 alphabetically larger than the filename being looked up.  At this point the
 location of the metadata block the filename is in has been found.
-The general idea of the index is ensure only one metadata block needs to be
+The general idea of the index is to ensure only one metadata block needs to be
 decompressed to do a lookup irrespective of the length of the directory.
 This scheme has the advantage that it doesn't require extra memory overhead
 and doesn't require much extra storage on disk.
index 8898a95b41e5ee1b277cd64697870726b11bc2fa..22ae8441489fcef3fe8f21ff4322dc3a8b85e5e6 100644 (file)
@@ -64,3 +64,13 @@ Note on Erase Size and Preferred Erase Size:
        size specified by the card.
 
        "preferred_erase_size" is in bytes.
+
+SD/MMC/SDIO Clock Gating Attribute
+==================================
+
+Read and write access is provided to following attribute.
+This attribute appears only if CONFIG_MMC_CLKGATE is enabled.
+
+       clkgate_delay   Tune the clock gating delay with desired value in milliseconds.
+
+echo <desired delay> > /sys/class/mmc_host/mmcX/clkgate_delay
index 2db28b8e662fc870a0f63066c39ff24b1666eafa..f08d078d43cff58e7ab939dae1f1108f7c3a517b 100644 (file)
@@ -25,3 +25,16 @@ echo 0 > /sys/block/mmcblkXbootY/force_ro
 To re-enable read-only access:
 
 echo 1 > /sys/block/mmcblkXbootY/force_ro
+
+The boot partitions can also be locked read only until the next power on,
+with:
+
+echo 1 > /sys/block/mmcblkXbootY/ro_lock_until_next_power_on
+
+This is a feature of the card and not of the kernel. If the card does
+not support boot partition locking, the file will not exist. If the
+feature has been disabled on the card, the file will be read-only.
+
+The boot partitions can also be locked permanently, but this feature is
+not accessible through sysfs in order to avoid accidental or malicious
+bricking.
index 6b93e200bcaceecec1bdc18c27929480b860066b..5bc6b3837b2033cbaccf57b69b91e07ffd0fe76f 100644 (file)
@@ -202,6 +202,7 @@ static struct irda_platform_data assabet_irda_data = {
 static struct mcp_plat_data assabet_mcp_data = {
        .mccr0          = MCCR0_ADM,
        .sclk_rate      = 11981000,
+       .codec          = "ucb1x00",
 };
 
 static void __init assabet_init(void)
@@ -252,6 +253,17 @@ static void __init assabet_init(void)
        sa11x0_register_mtd(&assabet_flash_data, assabet_flash_resources,
                            ARRAY_SIZE(assabet_flash_resources));
        sa11x0_register_irda(&assabet_irda_data);
+
+       /*
+        * Setup the PPC unit correctly.
+        */
+       PPDR &= ~PPC_RXD4;
+       PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM;
+       PSDR |= PPC_RXD4;
+       PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
+       PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
+
+       ASSABET_BCR_set(ASSABET_BCR_CODEC_RST);
        sa11x0_register_mcp(&assabet_mcp_data);
 }
 
index 11bb6d0b9be377b6c926f3e759a03a21d2e9ffff..d12d0f48b1dc4bec59f9a9939edc6c93e5f3ddcb 100644 (file)
@@ -124,12 +124,23 @@ static void __init cerf_map_io(void)
 static struct mcp_plat_data cerf_mcp_data = {
        .mccr0          = MCCR0_ADM,
        .sclk_rate      = 11981000,
+       .codec          = "ucb1x00",
 };
 
 static void __init cerf_init(void)
 {
        platform_add_devices(cerf_devices, ARRAY_SIZE(cerf_devices));
        sa11x0_register_mtd(&cerf_flash_data, &cerf_flash_resource, 1);
+
+       /*
+        * Setup the PPC unit correctly.
+        */
+       PPDR &= ~PPC_RXD4;
+       PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM;
+       PSDR |= PPC_RXD4;
+       PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
+       PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
+
        sa11x0_register_mcp(&cerf_mcp_data);
 }
 
index b9060e236def9e90204fa2be99734ea3a4fc6417..c483912d08af4535ce964992a19d66f511c80c21 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/timer.h>
 #include <linux/gpio.h>
 #include <linux/pda_power.h>
+#include <linux/mfd/ucb1x00.h>
 
 #include <mach/hardware.h>
 #include <asm/mach-types.h>
@@ -85,10 +86,15 @@ static struct scoop_pcmcia_config collie_pcmcia_config = {
        .num_devs       = 1,
 };
 
+static struct ucb1x00_plat_data collie_ucb1x00_data = {
+       .gpio_base      = COLLIE_TC35143_GPIO_BASE,
+};
+
 static struct mcp_plat_data collie_mcp_data = {
        .mccr0          = MCCR0_ADM | MCCR0_ExtClk,
        .sclk_rate      = 9216000,
-       .gpio_base      = COLLIE_TC35143_GPIO_BASE,
+       .codec          = "ucb1x00",
+       .codec_pdata    = &collie_ucb1x00_data,
 };
 
 /*
@@ -351,6 +357,16 @@ static void __init collie_init(void)
 
        sa11x0_register_mtd(&collie_flash_data, collie_flash_resources,
                            ARRAY_SIZE(collie_flash_resources));
+
+       /*
+        * Setup the PPC unit correctly.
+        */
+       PPDR &= ~PPC_RXD4;
+       PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM;
+       PSDR |= PPC_RXD4;
+       PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
+       PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
+
        sa11x0_register_mcp(&collie_mcp_data);
 
        sharpsl_save_param();
index 480d2ea46b000a9f862c7bef28280a31730b47d9..e3a28ca2a7b754fff9b095eb5c58e113525b16df 100644 (file)
@@ -217,10 +217,15 @@ static struct platform_device sa11x0uart3_device = {
 static struct resource sa11x0mcp_resources[] = {
        [0] = {
                .start  = __PREG(Ser4MCCR0),
-               .end    = __PREG(Ser4MCCR0) + 0xffff,
+               .end    = __PREG(Ser4MCCR0) + 0x1C - 1,
                .flags  = IORESOURCE_MEM,
        },
        [1] = {
+               .start  = __PREG(Ser4MCCR1),
+               .end    = __PREG(Ser4MCCR1) + 0x4 - 1,
+               .flags  = IORESOURCE_MEM,
+       },
+       [2] = {
                .start  = IRQ_Ser4MCP,
                .end    = IRQ_Ser4MCP,
                .flags  = IORESOURCE_IRQ,
index ed1a331508a754bf2e802f537689a97ee2b524c5..586cec898b35ae8578025727e3ec4e2b679964e0 100644 (file)
@@ -17,6 +17,8 @@ struct mcp_plat_data {
        u32 mccr1;
        unsigned int sclk_rate;
        int gpio_base;
+       const char *codec;
+       void *codec_pdata;
 };
 
 #endif
index af4e2761f3dbf4a6254bfab53f98080334e5f387..d117ceab6215c4f8d814504aa313bacf17460b45 100644 (file)
 static struct mcp_plat_data lart_mcp_data = {
        .mccr0          = MCCR0_ADM,
        .sclk_rate      = 11981000,
+       .codec          = "ucb1x00",
 };
 
 static void __init lart_init(void)
 {
+       /*
+        * Setup the PPC unit correctly.
+        */
+       PPDR &= ~PPC_RXD4;
+       PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM;
+       PSDR |= PPC_RXD4;
+       PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
+       PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
+
        sa11x0_register_mcp(&lart_mcp_data);
 }
 
index 318b2b766a0b3ee7b8921c2904b65c0fdd551c8c..748d34435b3f070000f2afcfc420253d85ccb060 100644 (file)
@@ -55,11 +55,22 @@ static struct resource shannon_flash_resource = {
 static struct mcp_plat_data shannon_mcp_data = {
        .mccr0          = MCCR0_ADM,
        .sclk_rate      = 11981000,
+       .codec          = "ucb1x00",
 };
 
 static void __init shannon_init(void)
 {
        sa11x0_register_mtd(&shannon_flash_data, &shannon_flash_resource, 1);
+
+       /*
+        * Setup the PPC unit correctly.
+        */
+       PPDR &= ~PPC_RXD4;
+       PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM;
+       PSDR |= PPC_RXD4;
+       PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
+       PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
+
        sa11x0_register_mcp(&shannon_mcp_data);
 }
 
index e17c04d6e32428af6aa7c8eb1fbc35ca6d7a02d8..458ececefa58a122332de40798d3df7aae3d1ea2 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/mtd/partitions.h>
 #include <linux/io.h>
 #include <linux/gpio.h>
+#include <linux/mfd/ucb1x00.h>
 
 #include <asm/irq.h>
 #include <mach/hardware.h>
@@ -187,10 +188,15 @@ static struct resource simpad_flash_resources [] = {
        }
 };
 
+static struct ucb1x00_plat_data simpad_ucb1x00_data = {
+       .gpio_base      = SIMPAD_UCB1X00_GPIO_BASE,
+};
+
 static struct mcp_plat_data simpad_mcp_data = {
        .mccr0          = MCCR0_ADM,
        .sclk_rate      = 11981000,
-       .gpio_base      = SIMPAD_UCB1X00_GPIO_BASE,
+       .codec          = "ucb1300",
+       .codec_pdata    = &simpad_ucb1x00_data,
 };
 
 
@@ -378,6 +384,16 @@ static int __init simpad_init(void)
 
        sa11x0_register_mtd(&simpad_flash_data, simpad_flash_resources,
                              ARRAY_SIZE(simpad_flash_resources));
+
+       /*
+        * Setup the PPC unit correctly.
+        */
+       PPDR &= ~PPC_RXD4;
+       PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM;
+       PSDR |= PPC_RXD4;
+       PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
+       PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
+
        sa11x0_register_mcp(&simpad_mcp_data);
 
        ret = platform_add_devices(devices, ARRAY_SIZE(devices));
index 9361a5290177d11a26d9190e328147e497ec686a..5c00712907d18ef715a74fa3caa27b38fba095f6 100644 (file)
 #include <linux/amba/pl022.h>
 #include <linux/amba/serial.h>
 #include <linux/spi/spi.h>
-#include <linux/mfd/ab8500.h>
+#include <linux/mfd/abx500/ab8500.h>
 #include <linux/regulator/ab8500.h>
 #include <linux/mfd/tc3589x.h>
 #include <linux/mfd/tps6105x.h>
-#include <linux/mfd/ab8500/gpio.h>
+#include <linux/mfd/abx500/ab8500-gpio.h>
 #include <linux/leds-lp5521.h>
 #include <linux/input.h>
 #include <linux/smsc911x.h>
index fe1569b67c9182cea704dab1fc4ddd1145388dbe..9de9e9c4dbbbcf2322e0fbaf3140537e31b89d4a 100644 (file)
@@ -10,7 +10,7 @@
 #include <linux/amba/bus.h>
 #include <linux/irq.h>
 #include <linux/i2c.h>
-#include <linux/mfd/ab5500/ab5500.h>
+#include <linux/mfd/abx500/ab5500.h>
 
 #include <asm/hardware/gic.h>
 #include <asm/mach/arch.h>
index 47969909836c9bb1441b7143dc5c4e156915267f..d2d4131435a680b61fcb6f84f6b092affe72f5cd 100644 (file)
@@ -9,7 +9,7 @@
 #define __MACH_IRQS_BOARD_MOP500_H
 
 /* Number of AB8500 irqs is taken from header file */
-#include <linux/mfd/ab8500.h>
+#include <linux/mfd/abx500/ab8500.h>
 
 #define MOP500_AB8500_IRQ_BASE         IRQ_BOARD_START
 #define MOP500_AB8500_IRQ_END          (MOP500_AB8500_IRQ_BASE \
index 656dc00d30edde87d8d44ed6dada500672355394..f82f888b91a95bb0a238975b4c575408728eb1e9 100644 (file)
@@ -63,6 +63,7 @@ enum clk_types {
 struct s3c_sdhci_platdata {
        unsigned int    max_width;
        unsigned int    host_caps;
+       unsigned int    pm_caps;
        enum cd_types   cd_type;
        enum clk_types  clk_type;
 
index ceb9fa3a80c0768a10411740547a42dd0fbcceb1..0f707184eae0b0d07cb7cecc2e3d5464182ee4ce 100644 (file)
@@ -53,6 +53,8 @@ void s3c_sdhci_set_platdata(struct s3c_sdhci_platdata *pd,
                set->cfg_gpio = pd->cfg_gpio;
        if (pd->host_caps)
                set->host_caps |= pd->host_caps;
+       if (pd->pm_caps)
+               set->pm_caps |= pd->pm_caps;
        if (pd->clk_type)
                set->clk_type = pd->clk_type;
 }
index 7617248f0d11e9d906d82d88d582ed3c2626c558..7a3bd252494468dcc5f4da779a62fc4500d65805 100644 (file)
 #define __NR_sendmmsg                  1331
 #define __NR_process_vm_readv          1332
 #define __NR_process_vm_writev         1333
+#define __NR_accept4                   1334
 
 #ifdef __KERNEL__
 
 
-#define NR_syscalls                    310 /* length of syscall table */
+#define NR_syscalls                    311 /* length of syscall table */
 
 /*
  * The following defines stop scripts/checksyscalls.sh from complaining about
index 5b31d46aff67b10e31c35bb6ea273549247613b5..1ccbe12a4d84fe6585455a45b367647b1fa2c240 100644 (file)
@@ -1779,6 +1779,7 @@ sys_call_table:
        data8 sys_sendmmsg
        data8 sys_process_vm_readv
        data8 sys_process_vm_writev
+       data8 sys_accept4
 
        .org sys_call_table + 8*NR_syscalls     // guard against failures to increase NR_syscalls
 #endif /* __IA64_ASM_PARAVIRTUALIZED_NATIVE */
index f22a9f7f6390a62ce3d028df8dba0723b232b28a..29ba3297e48006bea5987678bb4806d3c534fc2c 100644 (file)
@@ -2011,7 +2011,7 @@ static __cpuinit int mce_device_create(unsigned int cpu)
        if (!mce_available(&boot_cpu_data))
                return -EIO;
 
-       memset(&dev->kobj, 0, sizeof(struct kobject));
+       memset(dev, 0, sizeof(struct device));
        dev->id  = cpu;
        dev->bus = &mce_subsys;
 
index 1b3142127bf5f7308f3e7fc9fb83ff88a4730f2e..c07be024b962aa9ad8cce1ef635f37acd0a18fae 100644 (file)
@@ -97,7 +97,7 @@ obj-$(CONFIG_EISA)            += eisa/
 obj-y                          += lguest/
 obj-$(CONFIG_CPU_FREQ)         += cpufreq/
 obj-$(CONFIG_CPU_IDLE)         += cpuidle/
-obj-$(CONFIG_MMC)              += mmc/
+obj-y                          += mmc/
 obj-$(CONFIG_MEMSTICK)         += memstick/
 obj-y                          += leds/
 obj-$(CONFIG_INFINIBAND)       += infiniband/
index 148ab944378d57bdaec596e96bed6dc131777a28..3fd31dec8c9c1980fcdc8f7aa95b28d23addcb5b 100644 (file)
@@ -2184,6 +2184,8 @@ static ssize_t rbd_add(struct bus_type *bus,
        INIT_LIST_HEAD(&rbd_dev->node);
        INIT_LIST_HEAD(&rbd_dev->snaps);
 
+       init_rwsem(&rbd_dev->header.snap_rwsem);
+
        /* generate unique id: find highest unique id, add one */
        mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
 
index 4c980b573328eaeb4432504284ffe249fdb66a45..87a68a896abf7a0a010aa6395db5b0a53ba9ba41 100644 (file)
@@ -65,7 +65,14 @@ static void stmpe_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
        u8 reg = stmpe->regs[which] - (offset / 8);
        u8 mask = 1 << (offset % 8);
 
-       stmpe_reg_write(stmpe, reg, mask);
+       /*
+        * Some variants have single register for gpio set/clear functionality.
+        * For them we need to write 0 to clear and 1 to set.
+        */
+       if (stmpe->regs[STMPE_IDX_GPSR_LSB] == stmpe->regs[STMPE_IDX_GPCR_LSB])
+               stmpe_set_bits(stmpe, reg, mask, val ? mask : 0);
+       else
+               stmpe_reg_write(stmpe, reg, mask);
 }
 
 static int stmpe_gpio_direction_output(struct gpio_chip *chip,
@@ -132,6 +139,10 @@ static int stmpe_gpio_irq_set_type(struct irq_data *d, unsigned int type)
        if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH)
                return -EINVAL;
 
+       /* STMPE801 doesn't have RE and FE registers */
+       if (stmpe_gpio->stmpe->partnum == STMPE801)
+               return 0;
+
        if (type == IRQ_TYPE_EDGE_RISING)
                stmpe_gpio->regs[REG_RE][regoffset] |= mask;
        else
@@ -165,6 +176,11 @@ static void stmpe_gpio_irq_sync_unlock(struct irq_data *d)
        int i, j;
 
        for (i = 0; i < CACHE_NR_REGS; i++) {
+               /* STMPE801 doesn't have RE and FE registers */
+               if ((stmpe->partnum == STMPE801) &&
+                               (i != REG_IE))
+                       continue;
+
                for (j = 0; j < num_banks; j++) {
                        u8 old = stmpe_gpio->oldregs[i][j];
                        u8 new = stmpe_gpio->regs[i][j];
@@ -241,8 +257,11 @@ static irqreturn_t stmpe_gpio_irq(int irq, void *dev)
                }
 
                stmpe_reg_write(stmpe, statmsbreg + i, status[i]);
-               stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_GPEDR_MSB] + i,
-                               status[i]);
+
+               /* Edge detect register is not present on 801 */
+               if (stmpe->partnum != STMPE801)
+                       stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_GPEDR_MSB]
+                                       + i, status[i]);
        }
 
        return IRQ_HANDLED;
index 79d9016336355a4885681ca68a2a0249b3b5894e..350fd0c385d2449115ea332fdf8f27601c46485c 100644 (file)
@@ -12,7 +12,7 @@
 #include <linux/platform_device.h>
 #include <linux/input.h>
 #include <linux/interrupt.h>
-#include <linux/mfd/ab8500.h>
+#include <linux/mfd/abx500/ab8500.h>
 #include <linux/slab.h>
 
 /**
index 897a77dfa9d7dd923ec7567c26c729e1901e1757..c957c344233f4ed283df710222492f1ee38d9bf0 100644 (file)
@@ -396,6 +396,13 @@ config LEDS_TCA6507
          LED driver chips accessed via the I2C bus.
          Driver support brightness control and hardware-assisted blinking.
 
+config LEDS_MAX8997
+       tristate "LED support for MAX8997 PMIC"
+       depends on LEDS_CLASS && MFD_MAX8997
+       help
+         This option enables support for on-chip LED drivers on
+         MAXIM MAX8997 PMIC.
+
 config LEDS_TRIGGERS
        bool "LED Trigger support"
        depends on LEDS_CLASS
index 5c9dc4b000d5d2ab9996de7909623d030e2352fa..b8a9723477f0819b2fb07e2c19a61847888945e6 100644 (file)
@@ -44,6 +44,7 @@ obj-$(CONFIG_LEDS_NS2)                        += leds-ns2.o
 obj-$(CONFIG_LEDS_NETXBIG)             += leds-netxbig.o
 obj-$(CONFIG_LEDS_ASIC3)               += leds-asic3.o
 obj-$(CONFIG_LEDS_RENESAS_TPU)         += leds-renesas-tpu.o
+obj-$(CONFIG_LEDS_MAX8997)             += leds-max8997.o
 
 # LED SPI Drivers
 obj-$(CONFIG_LEDS_DAC124S085)          += leds-dac124s085.o
diff --git a/drivers/leds/leds-max8997.c b/drivers/leds/leds-max8997.c
new file mode 100644 (file)
index 0000000..f4c0e37
--- /dev/null
@@ -0,0 +1,372 @@
+/*
+ * leds-max8997.c - LED class driver for MAX8997 LEDs.
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * Donggeun Kim <dg77.kim@samsung.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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/leds.h>
+#include <linux/mfd/max8997.h>
+#include <linux/mfd/max8997-private.h>
+#include <linux/platform_device.h>
+
+#define MAX8997_LED_FLASH_SHIFT                        3
+#define MAX8997_LED_FLASH_CUR_MASK             0xf8
+#define MAX8997_LED_MOVIE_SHIFT                        4
+#define MAX8997_LED_MOVIE_CUR_MASK             0xf0
+
+#define MAX8997_LED_FLASH_MAX_BRIGHTNESS       0x1f
+#define MAX8997_LED_MOVIE_MAX_BRIGHTNESS       0xf
+#define MAX8997_LED_NONE_MAX_BRIGHTNESS                0
+
+#define MAX8997_LED0_FLASH_MASK                        0x1
+#define MAX8997_LED0_FLASH_PIN_MASK            0x5
+#define MAX8997_LED0_MOVIE_MASK                        0x8
+#define MAX8997_LED0_MOVIE_PIN_MASK            0x28
+
+#define MAX8997_LED1_FLASH_MASK                        0x2
+#define MAX8997_LED1_FLASH_PIN_MASK            0x6
+#define MAX8997_LED1_MOVIE_MASK                        0x10
+#define MAX8997_LED1_MOVIE_PIN_MASK            0x30
+
+#define MAX8997_LED_BOOST_ENABLE_MASK          (1 << 6)
+
+struct max8997_led {
+       struct max8997_dev *iodev;
+       struct led_classdev cdev;
+       bool enabled;
+       int id;
+       enum max8997_led_mode led_mode;
+       struct mutex mutex;
+};
+
+static void max8997_led_clear_mode(struct max8997_led *led,
+                       enum max8997_led_mode mode)
+{
+       struct i2c_client *client = led->iodev->i2c;
+       u8 val = 0, mask = 0;
+       int ret;
+
+       switch (mode) {
+       case MAX8997_FLASH_MODE:
+               mask = led->id ?
+                     MAX8997_LED1_FLASH_MASK : MAX8997_LED0_FLASH_MASK;
+               break;
+       case MAX8997_MOVIE_MODE:
+               mask = led->id ?
+                     MAX8997_LED1_MOVIE_MASK : MAX8997_LED0_MOVIE_MASK;
+               break;
+       case MAX8997_FLASH_PIN_CONTROL_MODE:
+               mask = led->id ?
+                     MAX8997_LED1_FLASH_PIN_MASK : MAX8997_LED0_FLASH_PIN_MASK;
+               break;
+       case MAX8997_MOVIE_PIN_CONTROL_MODE:
+               mask = led->id ?
+                     MAX8997_LED1_MOVIE_PIN_MASK : MAX8997_LED0_MOVIE_PIN_MASK;
+               break;
+       default:
+               break;
+       }
+
+       if (mask) {
+               ret = max8997_update_reg(client,
+                               MAX8997_REG_LEN_CNTL, val, mask);
+               if (ret)
+                       dev_err(led->iodev->dev,
+                               "failed to update register(%d)\n", ret);
+       }
+}
+
+static void max8997_led_set_mode(struct max8997_led *led,
+                       enum max8997_led_mode mode)
+{
+       int ret;
+       struct i2c_client *client = led->iodev->i2c;
+       u8 mask = 0;
+
+       /* First, clear the previous mode */
+       max8997_led_clear_mode(led, led->led_mode);
+
+       switch (mode) {
+       case MAX8997_FLASH_MODE:
+               mask = led->id ?
+                     MAX8997_LED1_FLASH_MASK : MAX8997_LED0_FLASH_MASK;
+               led->cdev.max_brightness = MAX8997_LED_FLASH_MAX_BRIGHTNESS;
+               break;
+       case MAX8997_MOVIE_MODE:
+               mask = led->id ?
+                     MAX8997_LED1_MOVIE_MASK : MAX8997_LED0_MOVIE_MASK;
+               led->cdev.max_brightness = MAX8997_LED_MOVIE_MAX_BRIGHTNESS;
+               break;
+       case MAX8997_FLASH_PIN_CONTROL_MODE:
+               mask = led->id ?
+                     MAX8997_LED1_FLASH_PIN_MASK : MAX8997_LED0_FLASH_PIN_MASK;
+               led->cdev.max_brightness = MAX8997_LED_FLASH_MAX_BRIGHTNESS;
+               break;
+       case MAX8997_MOVIE_PIN_CONTROL_MODE:
+               mask = led->id ?
+                     MAX8997_LED1_MOVIE_PIN_MASK : MAX8997_LED0_MOVIE_PIN_MASK;
+               led->cdev.max_brightness = MAX8997_LED_MOVIE_MAX_BRIGHTNESS;
+               break;
+       default:
+               led->cdev.max_brightness = MAX8997_LED_NONE_MAX_BRIGHTNESS;
+               break;
+       }
+
+       if (mask) {
+               ret = max8997_update_reg(client,
+                               MAX8997_REG_LEN_CNTL, mask, mask);
+               if (ret)
+                       dev_err(led->iodev->dev,
+                               "failed to update register(%d)\n", ret);
+       }
+
+       led->led_mode = mode;
+}
+
+static void max8997_led_enable(struct max8997_led *led, bool enable)
+{
+       int ret;
+       struct i2c_client *client = led->iodev->i2c;
+       u8 val = 0, mask = MAX8997_LED_BOOST_ENABLE_MASK;
+
+       if (led->enabled == enable)
+               return;
+
+       val = enable ? MAX8997_LED_BOOST_ENABLE_MASK : 0;
+
+       ret = max8997_update_reg(client, MAX8997_REG_BOOST_CNTL, val, mask);
+       if (ret)
+               dev_err(led->iodev->dev,
+                       "failed to update register(%d)\n", ret);
+
+       led->enabled = enable;
+}
+
+static void max8997_led_set_current(struct max8997_led *led,
+                               enum led_brightness value)
+{
+       int ret;
+       struct i2c_client *client = led->iodev->i2c;
+       u8 val = 0, mask = 0, reg = 0;
+
+       switch (led->led_mode) {
+       case MAX8997_FLASH_MODE:
+       case MAX8997_FLASH_PIN_CONTROL_MODE:
+               val = value << MAX8997_LED_FLASH_SHIFT;
+               mask = MAX8997_LED_FLASH_CUR_MASK;
+               reg = led->id ? MAX8997_REG_FLASH2_CUR : MAX8997_REG_FLASH1_CUR;
+               break;
+       case MAX8997_MOVIE_MODE:
+       case MAX8997_MOVIE_PIN_CONTROL_MODE:
+               val = value << MAX8997_LED_MOVIE_SHIFT;
+               mask = MAX8997_LED_MOVIE_CUR_MASK;
+               reg = MAX8997_REG_MOVIE_CUR;
+               break;
+       default:
+               break;
+       }
+
+       if (mask) {
+               ret = max8997_update_reg(client, reg, val, mask);
+               if (ret)
+                       dev_err(led->iodev->dev,
+                               "failed to update register(%d)\n", ret);
+       }
+}
+
+static void max8997_led_brightness_set(struct led_classdev *led_cdev,
+                               enum led_brightness value)
+{
+       struct max8997_led *led =
+                       container_of(led_cdev, struct max8997_led, cdev);
+
+       if (value) {
+               max8997_led_set_current(led, value);
+               max8997_led_enable(led, true);
+       } else {
+               max8997_led_set_current(led, value);
+               max8997_led_enable(led, false);
+       }
+}
+
+static ssize_t max8997_led_show_mode(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct max8997_led *led =
+                       container_of(led_cdev, struct max8997_led, cdev);
+       ssize_t ret = 0;
+
+       mutex_lock(&led->mutex);
+
+       switch (led->led_mode) {
+       case MAX8997_FLASH_MODE:
+               ret += sprintf(buf, "FLASH\n");
+               break;
+       case MAX8997_MOVIE_MODE:
+               ret += sprintf(buf, "MOVIE\n");
+               break;
+       case MAX8997_FLASH_PIN_CONTROL_MODE:
+               ret += sprintf(buf, "FLASH_PIN_CONTROL\n");
+               break;
+       case MAX8997_MOVIE_PIN_CONTROL_MODE:
+               ret += sprintf(buf, "MOVIE_PIN_CONTROL\n");
+               break;
+       default:
+               ret += sprintf(buf, "NONE\n");
+               break;
+       }
+
+       mutex_unlock(&led->mutex);
+
+       return ret;
+}
+
+static ssize_t max8997_led_store_mode(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct max8997_led *led =
+                       container_of(led_cdev, struct max8997_led, cdev);
+       enum max8997_led_mode mode;
+
+       mutex_lock(&led->mutex);
+
+       if (!strncmp(buf, "FLASH_PIN_CONTROL", 17))
+               mode = MAX8997_FLASH_PIN_CONTROL_MODE;
+       else if (!strncmp(buf, "MOVIE_PIN_CONTROL", 17))
+               mode = MAX8997_MOVIE_PIN_CONTROL_MODE;
+       else if (!strncmp(buf, "FLASH", 5))
+               mode = MAX8997_FLASH_MODE;
+       else if (!strncmp(buf, "MOVIE", 5))
+               mode = MAX8997_MOVIE_MODE;
+       else
+               mode = MAX8997_NONE;
+
+       max8997_led_set_mode(led, mode);
+
+       mutex_unlock(&led->mutex);
+
+       return size;
+}
+
+static DEVICE_ATTR(mode, 0644, max8997_led_show_mode, max8997_led_store_mode);
+
+static int __devinit max8997_led_probe(struct platform_device *pdev)
+{
+       struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+       struct max8997_platform_data *pdata = dev_get_platdata(iodev->dev);
+       struct max8997_led *led;
+       char name[20];
+       int ret = 0;
+
+       if (pdata == NULL) {
+               dev_err(&pdev->dev, "no platform data\n");
+               return -ENODEV;
+       }
+
+       led = kzalloc(sizeof(*led), GFP_KERNEL);
+       if (led == NULL) {
+               ret = -ENOMEM;
+               goto err_mem;
+       }
+
+       led->id = pdev->id;
+       snprintf(name, sizeof(name), "max8997-led%d", pdev->id);
+
+       led->cdev.name = name;
+       led->cdev.brightness_set = max8997_led_brightness_set;
+       led->cdev.flags |= LED_CORE_SUSPENDRESUME;
+       led->cdev.brightness = 0;
+       led->iodev = iodev;
+
+       /* initialize mode and brightness according to platform_data */
+       if (pdata->led_pdata) {
+               u8 mode = 0, brightness = 0;
+
+               mode = pdata->led_pdata->mode[led->id];
+               brightness = pdata->led_pdata->brightness[led->id];
+
+               max8997_led_set_mode(led, pdata->led_pdata->mode[led->id]);
+
+               if (brightness > led->cdev.max_brightness)
+                       brightness = led->cdev.max_brightness;
+               max8997_led_set_current(led, brightness);
+               led->cdev.brightness = brightness;
+       } else {
+               max8997_led_set_mode(led, MAX8997_NONE);
+               max8997_led_set_current(led, 0);
+       }
+
+       mutex_init(&led->mutex);
+
+       platform_set_drvdata(pdev, led);
+
+       ret = led_classdev_register(&pdev->dev, &led->cdev);
+       if (ret < 0)
+               goto err_led;
+
+       ret = device_create_file(led->cdev.dev, &dev_attr_mode);
+       if (ret != 0) {
+               dev_err(&pdev->dev,
+                       "failed to create file: %d\n", ret);
+               goto err_file;
+       }
+
+       return 0;
+
+err_file:
+       led_classdev_unregister(&led->cdev);
+err_led:
+       kfree(led);
+err_mem:
+       return ret;
+}
+
+static int __devexit max8997_led_remove(struct platform_device *pdev)
+{
+       struct max8997_led *led = platform_get_drvdata(pdev);
+
+       device_remove_file(led->cdev.dev, &dev_attr_mode);
+       led_classdev_unregister(&led->cdev);
+       kfree(led);
+
+       return 0;
+}
+
+static struct platform_driver max8997_led_driver = {
+       .driver = {
+               .name  = "max8997-led",
+               .owner = THIS_MODULE,
+       },
+       .probe  = max8997_led_probe,
+       .remove = __devexit_p(max8997_led_remove),
+};
+
+static int __init max8997_led_init(void)
+{
+       return platform_driver_register(&max8997_led_driver);
+}
+module_init(max8997_led_init);
+
+static void __exit max8997_led_exit(void)
+{
+       platform_driver_unregister(&max8997_led_driver);
+}
+module_exit(max8997_led_exit);
+
+MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
+MODULE_DESCRIPTION("MAX8997 LED driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:max8997-led");
index e017dc88622ada48f145457585e5fc376a6d155e..f93dd9571c3c81e8b44f26dc51d54b0901039cd6 100644 (file)
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/regmap.h>
 #include <linux/mfd/88pm860x.h>
 #include <linux/slab.h>
 
-static inline int pm860x_read_device(struct i2c_client *i2c,
-                                    int reg, int bytes, void *dest)
-{
-       unsigned char data;
-       int ret;
-
-       data = (unsigned char)reg;
-       ret = i2c_master_send(i2c, &data, 1);
-       if (ret < 0)
-               return ret;
-
-       ret = i2c_master_recv(i2c, dest, bytes);
-       if (ret < 0)
-               return ret;
-       return 0;
-}
-
-static inline int pm860x_write_device(struct i2c_client *i2c,
-                                     int reg, int bytes, void *src)
-{
-       unsigned char buf[bytes + 1];
-       int ret;
-
-       buf[0] = (unsigned char)reg;
-       memcpy(&buf[1], src, bytes);
-
-       ret = i2c_master_send(i2c, buf, bytes + 1);
-       if (ret < 0)
-               return ret;
-       return 0;
-}
-
 int pm860x_reg_read(struct i2c_client *i2c, int reg)
 {
        struct pm860x_chip *chip = i2c_get_clientdata(i2c);
-       unsigned char data;
+       struct regmap *map = (i2c == chip->client) ? chip->regmap
+                               : chip->regmap_companion;
+       unsigned int data;
        int ret;
 
-       mutex_lock(&chip->io_lock);
-       ret = pm860x_read_device(i2c, reg, 1, &data);
-       mutex_unlock(&chip->io_lock);
-
+       ret = regmap_read(map, reg, &data);
        if (ret < 0)
                return ret;
        else
@@ -68,12 +37,11 @@ int pm860x_reg_write(struct i2c_client *i2c, int reg,
                     unsigned char data)
 {
        struct pm860x_chip *chip = i2c_get_clientdata(i2c);
+       struct regmap *map = (i2c == chip->client) ? chip->regmap
+                               : chip->regmap_companion;
        int ret;
 
-       mutex_lock(&chip->io_lock);
-       ret = pm860x_write_device(i2c, reg, 1, &data);
-       mutex_unlock(&chip->io_lock);
-
+       ret = regmap_write(map, reg, data);
        return ret;
 }
 EXPORT_SYMBOL(pm860x_reg_write);
@@ -82,12 +50,11 @@ int pm860x_bulk_read(struct i2c_client *i2c, int reg,
                     int count, unsigned char *buf)
 {
        struct pm860x_chip *chip = i2c_get_clientdata(i2c);
+       struct regmap *map = (i2c == chip->client) ? chip->regmap
+                               : chip->regmap_companion;
        int ret;
 
-       mutex_lock(&chip->io_lock);
-       ret = pm860x_read_device(i2c, reg, count, buf);
-       mutex_unlock(&chip->io_lock);
-
+       ret = regmap_raw_read(map, reg, buf, count);
        return ret;
 }
 EXPORT_SYMBOL(pm860x_bulk_read);
@@ -96,12 +63,11 @@ int pm860x_bulk_write(struct i2c_client *i2c, int reg,
                      int count, unsigned char *buf)
 {
        struct pm860x_chip *chip = i2c_get_clientdata(i2c);
+       struct regmap *map = (i2c == chip->client) ? chip->regmap
+                               : chip->regmap_companion;
        int ret;
 
-       mutex_lock(&chip->io_lock);
-       ret = pm860x_write_device(i2c, reg, count, buf);
-       mutex_unlock(&chip->io_lock);
-
+       ret = regmap_raw_write(map, reg, buf, count);
        return ret;
 }
 EXPORT_SYMBOL(pm860x_bulk_write);
@@ -110,39 +76,78 @@ int pm860x_set_bits(struct i2c_client *i2c, int reg,
                    unsigned char mask, unsigned char data)
 {
        struct pm860x_chip *chip = i2c_get_clientdata(i2c);
-       unsigned char value;
+       struct regmap *map = (i2c == chip->client) ? chip->regmap
+                               : chip->regmap_companion;
        int ret;
 
-       mutex_lock(&chip->io_lock);
-       ret = pm860x_read_device(i2c, reg, 1, &value);
-       if (ret < 0)
-               goto out;
-       value &= ~mask;
-       value |= data;
-       ret = pm860x_write_device(i2c, reg, 1, &value);
-out:
-       mutex_unlock(&chip->io_lock);
+       ret = regmap_update_bits(map, reg, mask, data);
        return ret;
 }
 EXPORT_SYMBOL(pm860x_set_bits);
 
+static int read_device(struct i2c_client *i2c, int reg,
+                      int bytes, void *dest)
+{
+       unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX + 3];
+       unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX + 2];
+       struct i2c_adapter *adap = i2c->adapter;
+       struct i2c_msg msg[2] = {{i2c->addr, 0, 1, msgbuf0},
+                                {i2c->addr, I2C_M_RD, 0, msgbuf1},
+                               };
+       int num = 1, ret = 0;
+
+       if (dest == NULL)
+               return -EINVAL;
+       msgbuf0[0] = (unsigned char)reg;        /* command */
+       msg[1].len = bytes;
+
+       /* if data needs to read back, num should be 2 */
+       if (bytes > 0)
+               num = 2;
+       ret = adap->algo->master_xfer(adap, msg, num);
+       memcpy(dest, msgbuf1, bytes);
+       if (ret < 0)
+               return ret;
+       return 0;
+}
+
+static int write_device(struct i2c_client *i2c, int reg,
+                       int bytes, void *src)
+{
+       unsigned char buf[bytes + 1];
+       struct i2c_adapter *adap = i2c->adapter;
+       struct i2c_msg msg;
+       int ret;
+
+       buf[0] = (unsigned char)reg;
+       memcpy(&buf[1], src, bytes);
+       msg.addr = i2c->addr;
+       msg.flags = 0;
+       msg.len = bytes + 1;
+       msg.buf = buf;
+
+       ret = adap->algo->master_xfer(adap, &msg, 1);
+       if (ret < 0)
+               return ret;
+       return 0;
+}
+
 int pm860x_page_reg_read(struct i2c_client *i2c, int reg)
 {
-       struct pm860x_chip *chip = i2c_get_clientdata(i2c);
        unsigned char zero = 0;
        unsigned char data;
        int ret;
 
-       mutex_lock(&chip->io_lock);
-       pm860x_write_device(i2c, 0xFA, 0, &zero);
-       pm860x_write_device(i2c, 0xFB, 0, &zero);
-       pm860x_write_device(i2c, 0xFF, 0, &zero);
-       ret = pm860x_read_device(i2c, reg, 1, &data);
+       i2c_lock_adapter(i2c->adapter);
+       read_device(i2c, 0xFA, 0, &zero);
+       read_device(i2c, 0xFB, 0, &zero);
+       read_device(i2c, 0xFF, 0, &zero);
+       ret = read_device(i2c, reg, 1, &data);
        if (ret >= 0)
                ret = (int)data;
-       pm860x_write_device(i2c, 0xFE, 0, &zero);
-       pm860x_write_device(i2c, 0xFC, 0, &zero);
-       mutex_unlock(&chip->io_lock);
+       read_device(i2c, 0xFE, 0, &zero);
+       read_device(i2c, 0xFC, 0, &zero);
+       i2c_unlock_adapter(i2c->adapter);
        return ret;
 }
 EXPORT_SYMBOL(pm860x_page_reg_read);
@@ -150,18 +155,17 @@ EXPORT_SYMBOL(pm860x_page_reg_read);
 int pm860x_page_reg_write(struct i2c_client *i2c, int reg,
                          unsigned char data)
 {
-       struct pm860x_chip *chip = i2c_get_clientdata(i2c);
        unsigned char zero;
        int ret;
 
-       mutex_lock(&chip->io_lock);
-       pm860x_write_device(i2c, 0xFA, 0, &zero);
-       pm860x_write_device(i2c, 0xFB, 0, &zero);
-       pm860x_write_device(i2c, 0xFF, 0, &zero);
-       ret = pm860x_write_device(i2c, reg, 1, &data);
-       pm860x_write_device(i2c, 0xFE, 0, &zero);
-       pm860x_write_device(i2c, 0xFC, 0, &zero);
-       mutex_unlock(&chip->io_lock);
+       i2c_lock_adapter(i2c->adapter);
+       read_device(i2c, 0xFA, 0, &zero);
+       read_device(i2c, 0xFB, 0, &zero);
+       read_device(i2c, 0xFF, 0, &zero);
+       ret = write_device(i2c, reg, 1, &data);
+       read_device(i2c, 0xFE, 0, &zero);
+       read_device(i2c, 0xFC, 0, &zero);
+       i2c_unlock_adapter(i2c->adapter);
        return ret;
 }
 EXPORT_SYMBOL(pm860x_page_reg_write);
@@ -169,18 +173,17 @@ EXPORT_SYMBOL(pm860x_page_reg_write);
 int pm860x_page_bulk_read(struct i2c_client *i2c, int reg,
                          int count, unsigned char *buf)
 {
-       struct pm860x_chip *chip = i2c_get_clientdata(i2c);
        unsigned char zero = 0;
        int ret;
 
-       mutex_lock(&chip->io_lock);
-       pm860x_write_device(i2c, 0xFA, 0, &zero);
-       pm860x_write_device(i2c, 0xFB, 0, &zero);
-       pm860x_write_device(i2c, 0xFF, 0, &zero);
-       ret = pm860x_read_device(i2c, reg, count, buf);
-       pm860x_write_device(i2c, 0xFE, 0, &zero);
-       pm860x_write_device(i2c, 0xFC, 0, &zero);
-       mutex_unlock(&chip->io_lock);
+       i2c_lock_adapter(i2c->adapter);
+       read_device(i2c, 0xfa, 0, &zero);
+       read_device(i2c, 0xfb, 0, &zero);
+       read_device(i2c, 0xff, 0, &zero);
+       ret = read_device(i2c, reg, count, buf);
+       read_device(i2c, 0xFE, 0, &zero);
+       read_device(i2c, 0xFC, 0, &zero);
+       i2c_unlock_adapter(i2c->adapter);
        return ret;
 }
 EXPORT_SYMBOL(pm860x_page_bulk_read);
@@ -188,18 +191,18 @@ EXPORT_SYMBOL(pm860x_page_bulk_read);
 int pm860x_page_bulk_write(struct i2c_client *i2c, int reg,
                           int count, unsigned char *buf)
 {
-       struct pm860x_chip *chip = i2c_get_clientdata(i2c);
        unsigned char zero = 0;
        int ret;
 
-       mutex_lock(&chip->io_lock);
-       pm860x_write_device(i2c, 0xFA, 0, &zero);
-       pm860x_write_device(i2c, 0xFB, 0, &zero);
-       pm860x_write_device(i2c, 0xFF, 0, &zero);
-       ret = pm860x_write_device(i2c, reg, count, buf);
-       pm860x_write_device(i2c, 0xFE, 0, &zero);
-       pm860x_write_device(i2c, 0xFC, 0, &zero);
-       mutex_unlock(&chip->io_lock);
+       i2c_lock_adapter(i2c->adapter);
+       read_device(i2c, 0xFA, 0, &zero);
+       read_device(i2c, 0xFB, 0, &zero);
+       read_device(i2c, 0xFF, 0, &zero);
+       ret = write_device(i2c, reg, count, buf);
+       read_device(i2c, 0xFE, 0, &zero);
+       read_device(i2c, 0xFC, 0, &zero);
+       i2c_unlock_adapter(i2c->adapter);
+       i2c_unlock_adapter(i2c->adapter);
        return ret;
 }
 EXPORT_SYMBOL(pm860x_page_bulk_write);
@@ -207,25 +210,24 @@ EXPORT_SYMBOL(pm860x_page_bulk_write);
 int pm860x_page_set_bits(struct i2c_client *i2c, int reg,
                         unsigned char mask, unsigned char data)
 {
-       struct pm860x_chip *chip = i2c_get_clientdata(i2c);
        unsigned char zero;
        unsigned char value;
        int ret;
 
-       mutex_lock(&chip->io_lock);
-       pm860x_write_device(i2c, 0xFA, 0, &zero);
-       pm860x_write_device(i2c, 0xFB, 0, &zero);
-       pm860x_write_device(i2c, 0xFF, 0, &zero);
-       ret = pm860x_read_device(i2c, reg, 1, &value);
+       i2c_lock_adapter(i2c->adapter);
+       read_device(i2c, 0xFA, 0, &zero);
+       read_device(i2c, 0xFB, 0, &zero);
+       read_device(i2c, 0xFF, 0, &zero);
+       ret = read_device(i2c, reg, 1, &value);
        if (ret < 0)
                goto out;
        value &= ~mask;
        value |= data;
-       ret = pm860x_write_device(i2c, reg, 1, &value);
+       ret = write_device(i2c, reg, 1, &value);
 out:
-       pm860x_write_device(i2c, 0xFE, 0, &zero);
-       pm860x_write_device(i2c, 0xFC, 0, &zero);
-       mutex_unlock(&chip->io_lock);
+       read_device(i2c, 0xFE, 0, &zero);
+       read_device(i2c, 0xFC, 0, &zero);
+       i2c_unlock_adapter(i2c->adapter);
        return ret;
 }
 EXPORT_SYMBOL(pm860x_page_set_bits);
@@ -257,11 +259,17 @@ static int verify_addr(struct i2c_client *i2c)
        return 0;
 }
 
+static struct regmap_config pm860x_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+};
+
 static int __devinit pm860x_probe(struct i2c_client *client,
                                  const struct i2c_device_id *id)
 {
        struct pm860x_platform_data *pdata = client->dev.platform_data;
        struct pm860x_chip *chip;
+       int ret;
 
        if (!pdata) {
                pr_info("No platform data in %s!\n", __func__);
@@ -273,10 +281,17 @@ static int __devinit pm860x_probe(struct i2c_client *client,
                return -ENOMEM;
 
        chip->id = verify_addr(client);
+       chip->regmap = regmap_init_i2c(client, &pm860x_regmap_config);
+       if (IS_ERR(chip->regmap)) {
+               ret = PTR_ERR(chip->regmap);
+               dev_err(&client->dev, "Failed to allocate register map: %d\n",
+                               ret);
+               kfree(chip);
+               return ret;
+       }
        chip->client = client;
        i2c_set_clientdata(client, chip);
        chip->dev = &client->dev;
-       mutex_init(&chip->io_lock);
        dev_set_drvdata(chip->dev, chip);
 
        /*
@@ -290,6 +305,14 @@ static int __devinit pm860x_probe(struct i2c_client *client,
                chip->companion_addr = pdata->companion_addr;
                chip->companion = i2c_new_dummy(chip->client->adapter,
                                                chip->companion_addr);
+               chip->regmap_companion = regmap_init_i2c(chip->companion,
+                                                       &pm860x_regmap_config);
+               if (IS_ERR(chip->regmap_companion)) {
+                       ret = PTR_ERR(chip->regmap_companion);
+                       dev_err(&chip->companion->dev,
+                               "Failed to allocate register map: %d\n", ret);
+                       return ret;
+               }
                i2c_set_clientdata(chip->companion, chip);
        }
 
@@ -302,7 +325,11 @@ static int __devexit pm860x_remove(struct i2c_client *client)
        struct pm860x_chip *chip = i2c_get_clientdata(client);
 
        pm860x_device_exit(chip);
-       i2c_unregister_device(chip->companion);
+       if (chip->companion) {
+               regmap_exit(chip->regmap_companion);
+               i2c_unregister_device(chip->companion);
+       }
+       regmap_exit(chip->regmap);
        kfree(chip);
        return 0;
 }
index 053208d31fb9f2ff3bad030e184fd67e1966ea3d..cd13e9f2f5e668d14927b698b851b6d0f99dd7bb 100644 (file)
@@ -12,6 +12,7 @@ config MFD_CORE
 config MFD_88PM860X
        bool "Support Marvell 88PM8606/88PM8607"
        depends on I2C=y && GENERIC_HARDIRQS
+       select REGMAP_I2C
        select MFD_CORE
        help
          This supports for Marvell 88PM8606/88PM8607 Power Management IC.
@@ -199,7 +200,7 @@ config MENELAUS
 
 config TWL4030_CORE
        bool "Texas Instruments TWL4030/TWL5030/TWL6030/TPS659x0 Support"
-       depends on I2C=y && GENERIC_HARDIRQS
+       depends on I2C=y && GENERIC_HARDIRQS && IRQ_DOMAIN
        help
          Say yes here if you have TWL4030 / TWL6030 family chip on your board.
          This core driver provides register access and IRQ handling
@@ -257,7 +258,7 @@ config TWL6040_CORE
 
 config MFD_STMPE
        bool "Support STMicroelectronics STMPE"
-       depends on I2C=y && GENERIC_HARDIRQS
+       depends on (I2C=y || SPI_MASTER=y) && GENERIC_HARDIRQS
        select MFD_CORE
        help
          Support for the STMPE family of I/O Expanders from
@@ -278,6 +279,23 @@ config MFD_STMPE
                Keypad: stmpe-keypad
                Touchscreen: stmpe-ts
 
+menu "STMPE Interface Drivers"
+depends on MFD_STMPE
+
+config STMPE_I2C
+       bool "STMPE I2C Inteface"
+       depends on I2C=y
+       default y
+       help
+         This is used to enable I2C interface of STMPE
+
+config STMPE_SPI
+       bool "STMPE SPI Inteface"
+       depends on SPI_MASTER
+       help
+         This is used to enable SPI interface of STMPE
+endmenu
+
 config MFD_TC3589X
        bool "Support Toshiba TC35892 and variants"
        depends on I2C=y && GENERIC_HARDIRQS
@@ -311,7 +329,7 @@ config MFD_TC6387XB
 
 config MFD_TC6393XB
        bool "Support Toshiba TC6393XB"
-       depends on GPIOLIB && ARM
+       depends on GPIOLIB && ARM && HAVE_CLK
        select MFD_CORE
        select MFD_TMIO
        help
@@ -399,6 +417,17 @@ config MFD_MAX8998
          additional drivers must be enabled in order to use the functionality
          of the device.
 
+config MFD_S5M_CORE
+       bool "SAMSUNG S5M Series Support"
+       depends on I2C=y && GENERIC_HARDIRQS
+       select MFD_CORE
+       select REGMAP_I2C
+       help
+        Support for the Samsung Electronics S5M MFD series.
+        This driver provies common support for accessing the device,
+        additional drivers must be enabled in order to use the functionality
+        of the device
+
 config MFD_WM8400
        tristate "Support Wolfson Microelectronics WM8400"
        select MFD_CORE
index 47591fc7d0d2ccca33fc01348ca1e99304d715ec..b953bab934f7f872a7f53307a865a7efa22312ac 100644 (file)
@@ -16,6 +16,8 @@ obj-$(CONFIG_MFD_DM355EVM_MSP)        += dm355evm_msp.o
 obj-$(CONFIG_MFD_TI_SSP)       += ti-ssp.o
 
 obj-$(CONFIG_MFD_STMPE)                += stmpe.o
+obj-$(CONFIG_STMPE_I2C)                += stmpe-i2c.o
+obj-$(CONFIG_STMPE_SPI)                += stmpe-spi.o
 obj-$(CONFIG_MFD_TC3589X)      += tc3589x.o
 obj-$(CONFIG_MFD_T7L66XB)      += t7l66xb.o tmio_core.o
 obj-$(CONFIG_MFD_TC6387XB)     += tc6387xb.o tmio_core.o
@@ -109,3 +111,4 @@ obj-$(CONFIG_MFD_PM8XXX_IRQ)        += pm8xxx-irq.o
 obj-$(CONFIG_TPS65911_COMPARATOR)      += tps65911-comparator.o
 obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
 obj-$(CONFIG_MFD_INTEL_MSIC)   += intel_msic.o
+obj-$(CONFIG_MFD_S5M_CORE)     += s5m-core.o s5m-irq.o
index 02c42015ba5108344c46cd29986d30e64e5cd304..3aa36eb5c79bcf769616013cd3176ed04af500bc 100644 (file)
@@ -407,13 +407,13 @@ static int aat2870_i2c_probe(struct i2c_client *client,
                aat2870->init(aat2870);
 
        if (aat2870->en_pin >= 0) {
-               ret = gpio_request(aat2870->en_pin, "aat2870-en");
+               ret = gpio_request_one(aat2870->en_pin, GPIOF_OUT_INIT_HIGH,
+                                      "aat2870-en");
                if (ret < 0) {
                        dev_err(&client->dev,
                                "Failed to request GPIO %d\n", aat2870->en_pin);
                        goto out_kfree;
                }
-               gpio_direction_output(aat2870->en_pin, 1);
        }
 
        aat2870_enable(aat2870);
@@ -468,9 +468,10 @@ static int aat2870_i2c_remove(struct i2c_client *client)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int aat2870_i2c_suspend(struct i2c_client *client, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int aat2870_i2c_suspend(struct device *dev)
 {
+       struct i2c_client *client = to_i2c_client(dev);
        struct aat2870_data *aat2870 = i2c_get_clientdata(client);
 
        aat2870_disable(aat2870);
@@ -478,8 +479,9 @@ static int aat2870_i2c_suspend(struct i2c_client *client, pm_message_t state)
        return 0;
 }
 
-static int aat2870_i2c_resume(struct i2c_client *client)
+static int aat2870_i2c_resume(struct device *dev)
 {
+       struct i2c_client *client = to_i2c_client(dev);
        struct aat2870_data *aat2870 = i2c_get_clientdata(client);
        struct aat2870_register *reg = NULL;
        int i;
@@ -495,12 +497,12 @@ static int aat2870_i2c_resume(struct i2c_client *client)
 
        return 0;
 }
-#else
-#define aat2870_i2c_suspend    NULL
-#define aat2870_i2c_resume     NULL
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(aat2870_pm_ops, aat2870_i2c_suspend,
+                        aat2870_i2c_resume);
 
-static struct i2c_device_id aat2870_i2c_id_table[] = {
+static const struct i2c_device_id aat2870_i2c_id_table[] = {
        { "aat2870", 0 },
        { }
 };
@@ -510,11 +512,10 @@ static struct i2c_driver aat2870_i2c_driver = {
        .driver = {
                .name   = "aat2870",
                .owner  = THIS_MODULE,
+               .pm     = &aat2870_pm_ops,
        },
        .probe          = aat2870_i2c_probe,
        .remove         = aat2870_i2c_remove,
-       .suspend        = aat2870_i2c_suspend,
-       .resume         = aat2870_i2c_resume,
        .id_table       = aat2870_i2c_id_table,
 };
 
index ec10629a0b0b151b5235432dc08bb238f4783b22..bd56a764dea1f36cb522225e09ce4992e0733a56 100644 (file)
@@ -22,8 +22,8 @@
 #include <linux/irq.h>
 #include <linux/interrupt.h>
 #include <linux/random.h>
-#include <linux/mfd/ab5500/ab5500.h>
 #include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab5500.h>
 #include <linux/list.h>
 #include <linux/bitops.h>
 #include <linux/spinlock.h>
index b7b2d3483fd4e1e1f8bb5b6a2feb6e7c349c57f3..72006940937ae54a1500ac66fd3461d2e6ca9b32 100644 (file)
@@ -7,8 +7,8 @@
 #include <linux/module.h>
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
-#include <linux/mfd/ab5500/ab5500.h>
 #include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab5500.h>
 #include <linux/uaccess.h>
 
 #include "ab5500-core.h"
index d3d572b2317b888be174bf52d3d17c65b391ef07..53e2a80f42facb931f56c4adcde9221f5b5c69e1 100644 (file)
@@ -17,7 +17,7 @@
 #include <linux/platform_device.h>
 #include <linux/mfd/core.h>
 #include <linux/mfd/abx500.h>
-#include <linux/mfd/ab8500.h>
+#include <linux/mfd/abx500/ab8500.h>
 #include <linux/regulator/ab8500.h>
 
 /*
index dedb7f65cea600a205f83fc2e81e385649cf74ee..9a0211aa88971c0207b3caa18d30eab6cb57821a 100644 (file)
@@ -13,7 +13,7 @@
 #include <linux/platform_device.h>
 
 #include <linux/mfd/abx500.h>
-#include <linux/mfd/ab8500.h>
+#include <linux/mfd/abx500/ab8500.h>
 
 static u32 debug_bank;
 static u32 debug_address;
index e985d1701a83df56a463cb47ff2a1169bc894ba6..c39fc716e1dcf520592bc866cba0be4e661d0e09 100644 (file)
@@ -18,9 +18,9 @@
 #include <linux/err.h>
 #include <linux/slab.h>
 #include <linux/list.h>
-#include <linux/mfd/ab8500.h>
 #include <linux/mfd/abx500.h>
-#include <linux/mfd/ab8500/gpadc.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/abx500/ab8500-gpadc.h>
 
 /*
  * GPADC register offsets
index 9be541c6b004c8adc9cf6cde3416c6bff31c9e31..087fecd71ce032b2ad459467c02aef47197f13ea 100644 (file)
@@ -10,7 +10,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
-#include <linux/mfd/ab8500.h>
+#include <linux/mfd/abx500/ab8500.h>
 #include <linux/mfd/db8500-prcmu.h>
 
 static int ab8500_i2c_write(struct ab8500 *ab8500, u16 addr, u8 data)
index f20feefac19020969dc55a24bb8a8d162934adb5..c28d4eb1eff019517d166476c590652f2ac1b8b1 100644 (file)
@@ -7,9 +7,9 @@
 #include <linux/err.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
-#include <linux/mfd/ab8500.h>
 #include <linux/mfd/abx500.h>
-#include <linux/mfd/ab8500/sysctrl.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/abx500/ab8500-sysctrl.h>
 
 static struct device *sysctrl_dev;
 
index 155fa04078821d6fc8d5a5475225bba3a436ba06..315fef5d466ac3f2d380246ffa75a056d131cb52 100644 (file)
@@ -172,14 +172,14 @@ static void __devexit cs5535_mfd_remove(struct pci_dev *pdev)
        pci_disable_device(pdev);
 }
 
-static struct pci_device_id cs5535_mfd_pci_tbl[] = {
+static DEFINE_PCI_DEVICE_TABLE(cs5535_mfd_pci_tbl) = {
        { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) },
        { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) },
        { 0, }
 };
 MODULE_DEVICE_TABLE(pci, cs5535_mfd_pci_tbl);
 
-static struct pci_driver cs5535_mfd_drv = {
+static struct pci_driver cs5535_mfd_driver = {
        .name = DRV_NAME,
        .id_table = cs5535_mfd_pci_tbl,
        .probe = cs5535_mfd_probe,
@@ -188,12 +188,12 @@ static struct pci_driver cs5535_mfd_drv = {
 
 static int __init cs5535_mfd_init(void)
 {
-       return pci_register_driver(&cs5535_mfd_drv);
+       return pci_register_driver(&cs5535_mfd_driver);
 }
 
 static void __exit cs5535_mfd_exit(void)
 {
-       pci_unregister_driver(&cs5535_mfd_drv);
+       pci_unregister_driver(&cs5535_mfd_driver);
 }
 
 module_init(cs5535_mfd_init);
index 8ad88da647b9abfd201c659eb92ec12072b03a60..7710227d284e82f3ea98bd6dcf307abd177f71e9 100644 (file)
@@ -308,8 +308,7 @@ static int add_children(struct i2c_client *client)
        for (i = 0; i < ARRAY_SIZE(config_inputs); i++) {
                int gpio = dm355evm_msp_gpio.base + config_inputs[i].offset;
 
-               gpio_request(gpio, config_inputs[i].label);
-               gpio_direction_input(gpio);
+               gpio_request_one(gpio, GPIOF_IN, config_inputs[i].label);
 
                /* make it easy for userspace to see these */
                gpio_export(gpio, false);
index 97c27762174f1527af8c2b72c5438c4eebc12144..b76657eb0c51044503e3c71b7345c98596d33cbd 100644 (file)
@@ -485,17 +485,7 @@ static struct platform_driver intel_msic_driver = {
        },
 };
 
-static int __init intel_msic_init(void)
-{
-       return platform_driver_register(&intel_msic_driver);
-}
-module_init(intel_msic_init);
-
-static void __exit intel_msic_exit(void)
-{
-       platform_driver_unregister(&intel_msic_driver);
-}
-module_exit(intel_msic_exit);
+module_platform_driver(intel_msic_driver);
 
 MODULE_DESCRIPTION("Driver for Intel MSIC");
 MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
index ef39528088f2298a47e7f62f69317b2a6af428ee..87662a17dec62d527a3af1b8b7fb8c782c85dd92 100644 (file)
@@ -181,7 +181,7 @@ static struct resource jz4740_battery_resources[] = {
        },
 };
 
-const struct mfd_cell jz4740_adc_cells[] = {
+static struct mfd_cell jz4740_adc_cells[] = {
        {
                .id = 0,
                .name = "jz4740-hwmon",
@@ -338,17 +338,7 @@ static struct platform_driver jz4740_adc_driver = {
        },
 };
 
-static int __init jz4740_adc_init(void)
-{
-       return platform_driver_register(&jz4740_adc_driver);
-}
-module_init(jz4740_adc_init);
-
-static void __exit jz4740_adc_exit(void)
-{
-       platform_driver_unregister(&jz4740_adc_driver);
-}
-module_exit(jz4740_adc_exit);
+module_platform_driver(jz4740_adc_driver);
 
 MODULE_DESCRIPTION("JZ4740 SoC ADC driver");
 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
index ea1169b04779597cb4bf8964b26d7f861ba75afa..abc421364a454f0f596f7e5be734e346838318cb 100644 (file)
@@ -74,7 +74,7 @@ static struct mfd_cell tunnelcreek_cells[] = {
        },
 };
 
-static struct pci_device_id lpc_sch_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(lpc_sch_ids) = {
        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) },
        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ITC_LPC) },
        { 0, }
index 0219115e00c73f84c925045afceb96d65ea83ea3..d9e4b36edee984e3f5008b02f16d8d9becdb703f 100644 (file)
@@ -161,6 +161,8 @@ static int __devinit max8925_probe(struct i2c_client *client,
        chip->adc = i2c_new_dummy(chip->i2c->adapter, ADC_I2C_ADDR);
        i2c_set_clientdata(chip->adc, chip);
 
+       device_init_wakeup(&client->dev, 1);
+
        max8925_device_init(chip, pdata);
 
        return 0;
@@ -177,10 +179,35 @@ static int __devexit max8925_remove(struct i2c_client *client)
        return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int max8925_suspend(struct device *dev)
+{
+       struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+       struct max8925_chip *chip = i2c_get_clientdata(client);
+
+       if (device_may_wakeup(dev) && chip->wakeup_flag)
+               enable_irq_wake(chip->core_irq);
+       return 0;
+}
+
+static int max8925_resume(struct device *dev)
+{
+       struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+       struct max8925_chip *chip = i2c_get_clientdata(client);
+
+       if (device_may_wakeup(dev) && chip->wakeup_flag)
+               disable_irq_wake(chip->core_irq);
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(max8925_pm_ops, max8925_suspend, max8925_resume);
+
 static struct i2c_driver max8925_driver = {
        .driver = {
                .name   = "max8925",
                .owner  = THIS_MODULE,
+               .pm     = &max8925_pm_ops,
        },
        .probe          = max8925_probe,
        .remove         = __devexit_p(max8925_remove),
index 5be53ae9b61cf2a6d6c318c881e43605b3dee526..cb83a7ab53e73b9825c4eb527211ab49ccd33a7e 100644 (file)
@@ -43,7 +43,8 @@ static struct mfd_cell max8997_devs[] = {
        { .name = "max8997-battery", },
        { .name = "max8997-haptic", },
        { .name = "max8997-muic", },
-       { .name = "max8997-flash", },
+       { .name = "max8997-led", .id = 1 },
+       { .name = "max8997-led", .id = 2 },
 };
 
 int max8997_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest)
index de4096aee24877fccbaf36ae981b06483d41da50..6ef56d28c05686bf298f589cebf91c43fcc707b8 100644 (file)
@@ -176,6 +176,8 @@ static int max8998_i2c_probe(struct i2c_client *i2c,
        if (ret < 0)
                goto err;
 
+       device_init_wakeup(max8998->dev, max8998->wakeup);
+
        return ret;
 
 err:
@@ -210,7 +212,7 @@ static int max8998_suspend(struct device *dev)
        struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
        struct max8998_dev *max8998 = i2c_get_clientdata(i2c);
 
-       if (max8998->wakeup)
+       if (device_may_wakeup(dev))
                irq_set_irq_wake(max8998->irq, 1);
        return 0;
 }
@@ -220,7 +222,7 @@ static int max8998_resume(struct device *dev)
        struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
        struct max8998_dev *max8998 = i2c_get_clientdata(i2c);
 
-       if (max8998->wakeup)
+       if (device_may_wakeup(dev))
                irq_set_irq_wake(max8998->irq, 0);
        /*
         * In LP3974, if IRQ registers are not "read & clear"
index e9619acc02375342276d84e5d9aee83e7a2597a8..7122386b4e3cf1830dbb3cab2f552efefeb43834 100644 (file)
 #include <linux/spi/spi.h>
 #include <linux/mfd/core.h>
 #include <linux/mfd/mc13xxx.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
 
 struct mc13xxx {
        struct spi_device *spidev;
        struct mutex lock;
        int irq;
+       int flags;
 
        irq_handler_t irqhandler[MC13XXX_NUM_IRQ];
        void *irqdata[MC13XXX_NUM_IRQ];
@@ -550,10 +554,7 @@ static const char *mc13xxx_get_chipname(struct mc13xxx *mc13xxx)
 
 int mc13xxx_get_flags(struct mc13xxx *mc13xxx)
 {
-       struct mc13xxx_platform_data *pdata =
-               dev_get_platdata(&mc13xxx->spidev->dev);
-
-       return pdata->flags;
+       return mc13xxx->flags;
 }
 EXPORT_SYMBOL(mc13xxx_get_flags);
 
@@ -615,13 +616,13 @@ int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, unsigned int mode,
                break;
 
        case MC13XXX_ADC_MODE_SINGLE_CHAN:
-               adc0 |= old_adc0 & MC13XXX_ADC0_TSMOD_MASK;
+               adc0 |= old_adc0 & MC13XXX_ADC0_CONFIG_MASK;
                adc1 |= (channel & 0x7) << MC13XXX_ADC1_CHAN0_SHIFT;
                adc1 |= MC13XXX_ADC1_RAND;
                break;
 
        case MC13XXX_ADC_MODE_MULT_CHAN:
-               adc0 |= old_adc0 & MC13XXX_ADC0_TSMOD_MASK;
+               adc0 |= old_adc0 & MC13XXX_ADC0_CONFIG_MASK;
                adc1 |= 4 << MC13XXX_ADC1_CHAN1_SHIFT;
                break;
 
@@ -696,17 +697,67 @@ static int mc13xxx_add_subdevice(struct mc13xxx *mc13xxx, const char *format)
        return mc13xxx_add_subdevice_pdata(mc13xxx, format, NULL, 0);
 }
 
+#ifdef CONFIG_OF
+static int mc13xxx_probe_flags_dt(struct mc13xxx *mc13xxx)
+{
+       struct device_node *np = mc13xxx->spidev->dev.of_node;
+
+       if (!np)
+               return -ENODEV;
+
+       if (of_get_property(np, "fsl,mc13xxx-uses-adc", NULL))
+               mc13xxx->flags |= MC13XXX_USE_ADC;
+
+       if (of_get_property(np, "fsl,mc13xxx-uses-codec", NULL))
+               mc13xxx->flags |= MC13XXX_USE_CODEC;
+
+       if (of_get_property(np, "fsl,mc13xxx-uses-rtc", NULL))
+               mc13xxx->flags |= MC13XXX_USE_RTC;
+
+       if (of_get_property(np, "fsl,mc13xxx-uses-touch", NULL))
+               mc13xxx->flags |= MC13XXX_USE_TOUCHSCREEN;
+
+       return 0;
+}
+#else
+static inline int mc13xxx_probe_flags_dt(struct mc13xxx *mc13xxx)
+{
+       return -ENODEV;
+}
+#endif
+
+static const struct spi_device_id mc13xxx_device_id[] = {
+       {
+               .name = "mc13783",
+               .driver_data = MC13XXX_ID_MC13783,
+       }, {
+               .name = "mc13892",
+               .driver_data = MC13XXX_ID_MC13892,
+       }, {
+               /* sentinel */
+       }
+};
+MODULE_DEVICE_TABLE(spi, mc13xxx_device_id);
+
+static const struct of_device_id mc13xxx_dt_ids[] = {
+       { .compatible = "fsl,mc13783", .data = (void *) MC13XXX_ID_MC13783, },
+       { .compatible = "fsl,mc13892", .data = (void *) MC13XXX_ID_MC13892, },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mc13xxx_dt_ids);
+
 static int mc13xxx_probe(struct spi_device *spi)
 {
+       const struct of_device_id *of_id;
+       struct spi_driver *sdrv = to_spi_driver(spi->dev.driver);
        struct mc13xxx *mc13xxx;
        struct mc13xxx_platform_data *pdata = dev_get_platdata(&spi->dev);
        enum mc13xxx_id id;
        int ret;
 
-       if (!pdata) {
-               dev_err(&spi->dev, "invalid platform data\n");
-               return -EINVAL;
-       }
+       of_id = of_match_device(mc13xxx_dt_ids, &spi->dev);
+       if (of_id)
+               sdrv->id_table = &mc13xxx_device_id[(enum mc13xxx_id) of_id->data];
 
        mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL);
        if (!mc13xxx)
@@ -749,28 +800,33 @@ err_revision:
 
        mc13xxx_unlock(mc13xxx);
 
-       if (pdata->flags & MC13XXX_USE_ADC)
+       if (mc13xxx_probe_flags_dt(mc13xxx) < 0 && pdata)
+               mc13xxx->flags = pdata->flags;
+
+       if (mc13xxx->flags & MC13XXX_USE_ADC)
                mc13xxx_add_subdevice(mc13xxx, "%s-adc");
 
-       if (pdata->flags & MC13XXX_USE_CODEC)
+       if (mc13xxx->flags & MC13XXX_USE_CODEC)
                mc13xxx_add_subdevice(mc13xxx, "%s-codec");
 
-       mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator",
-               &pdata->regulators, sizeof(pdata->regulators));
-
-       if (pdata->flags & MC13XXX_USE_RTC)
+       if (mc13xxx->flags & MC13XXX_USE_RTC)
                mc13xxx_add_subdevice(mc13xxx, "%s-rtc");
 
-       if (pdata->flags & MC13XXX_USE_TOUCHSCREEN)
+       if (mc13xxx->flags & MC13XXX_USE_TOUCHSCREEN)
                mc13xxx_add_subdevice(mc13xxx, "%s-ts");
 
-       if (pdata->leds)
+       if (pdata) {
+               mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator",
+                       &pdata->regulators, sizeof(pdata->regulators));
                mc13xxx_add_subdevice_pdata(mc13xxx, "%s-led",
                                pdata->leds, sizeof(*pdata->leds));
-
-       if (pdata->buttons)
                mc13xxx_add_subdevice_pdata(mc13xxx, "%s-pwrbutton",
                                pdata->buttons, sizeof(*pdata->buttons));
+       } else {
+               mc13xxx_add_subdevice(mc13xxx, "%s-regulator");
+               mc13xxx_add_subdevice(mc13xxx, "%s-led");
+               mc13xxx_add_subdevice(mc13xxx, "%s-pwrbutton");
+       }
 
        return 0;
 }
@@ -788,25 +844,12 @@ static int __devexit mc13xxx_remove(struct spi_device *spi)
        return 0;
 }
 
-static const struct spi_device_id mc13xxx_device_id[] = {
-       {
-               .name = "mc13783",
-               .driver_data = MC13XXX_ID_MC13783,
-       }, {
-               .name = "mc13892",
-               .driver_data = MC13XXX_ID_MC13892,
-       }, {
-               /* sentinel */
-       }
-};
-MODULE_DEVICE_TABLE(spi, mc13xxx_device_id);
-
 static struct spi_driver mc13xxx_driver = {
        .id_table = mc13xxx_device_id,
        .driver = {
                .name = "mc13xxx",
-               .bus = &spi_bus_type,
                .owner = THIS_MODULE,
+               .of_match_table = mc13xxx_dt_ids,
        },
        .probe = mc13xxx_probe,
        .remove = __devexit_p(mc13xxx_remove),
index 84815f9ef636eb0e91c26c1869094bb5c83977d0..63be60bc3455396e764fa57661f497d5670b04a8 100644 (file)
 #define to_mcp(d)              container_of(d, struct mcp, attached_device)
 #define to_mcp_driver(d)       container_of(d, struct mcp_driver, drv)
 
+static const struct mcp_device_id *mcp_match_id(const struct mcp_device_id *id,
+                                               const char *codec)
+{
+       while (id->name[0]) {
+               if (strcmp(codec, id->name) == 0)
+                       return id;
+               id++;
+       }
+       return NULL;
+}
+
+const struct mcp_device_id *mcp_get_device_id(const struct mcp *mcp)
+{
+       const struct mcp_driver *driver =
+               to_mcp_driver(mcp->attached_device.driver);
+
+       return mcp_match_id(driver->id_table, mcp->codec);
+}
+EXPORT_SYMBOL(mcp_get_device_id);
+
 static int mcp_bus_match(struct device *dev, struct device_driver *drv)
 {
-       return 1;
+       const struct mcp *mcp = to_mcp(dev);
+       const struct mcp_driver *driver = to_mcp_driver(drv);
+
+       if (driver->id_table)
+               return !!mcp_match_id(driver->id_table, mcp->codec);
+
+       return 0;
 }
 
 static int mcp_bus_probe(struct device *dev)
@@ -74,9 +100,18 @@ static int mcp_bus_resume(struct device *dev)
        return ret;
 }
 
+static int mcp_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+       struct mcp *mcp = to_mcp(dev);
+
+       add_uevent_var(env, "MODALIAS=%s%s", MCP_MODULE_PREFIX, mcp->codec);
+       return 0;
+}
+
 static struct bus_type mcp_bus_type = {
        .name           = "mcp",
        .match          = mcp_bus_match,
+       .uevent         = mcp_bus_uevent,
        .probe          = mcp_bus_probe,
        .remove         = mcp_bus_remove,
        .suspend        = mcp_bus_suspend,
@@ -212,9 +247,14 @@ struct mcp *mcp_host_alloc(struct device *parent, size_t size)
 }
 EXPORT_SYMBOL(mcp_host_alloc);
 
-int mcp_host_register(struct mcp *mcp)
+int mcp_host_register(struct mcp *mcp, void *pdata)
 {
+       if (!mcp->codec)
+               return -EINVAL;
+
+       mcp->attached_device.platform_data = pdata;
        dev_set_name(&mcp->attached_device, "mcp0");
+       request_module("%s%s", MCP_MODULE_PREFIX, mcp->codec);
        return device_register(&mcp->attached_device);
 }
 EXPORT_SYMBOL(mcp_host_register);
index 2dab02d9ac8b6f87168e3e6e1069023f9372fe93..9adc2eb6949252031b3964320d3e6e1dfa4d4ed0 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/spinlock.h>
 #include <linux/platform_device.h>
 #include <linux/mfd/mcp.h>
+#include <linux/io.h>
 
 #include <mach/dma.h>
 #include <mach/hardware.h>
 #include <asm/system.h>
 #include <mach/mcp.h>
 
-#include <mach/assabet.h>
-
+/* Register offsets */
+#define MCCR0  0x00
+#define MCDR0  0x08
+#define MCDR1  0x0C
+#define MCDR2  0x10
+#define MCSR   0x18
+#define MCCR1  0x00
 
 struct mcp_sa11x0 {
-       u32     mccr0;
-       u32     mccr1;
+       u32             mccr0;
+       u32             mccr1;
+       unsigned char   *mccr0_base;
+       unsigned char   *mccr1_base;
 };
 
 #define priv(mcp)      ((struct mcp_sa11x0 *)mcp_priv(mcp))
@@ -39,25 +47,25 @@ struct mcp_sa11x0 {
 static void
 mcp_sa11x0_set_telecom_divisor(struct mcp *mcp, unsigned int divisor)
 {
-       unsigned int mccr0;
+       struct mcp_sa11x0 *priv = priv(mcp);
 
        divisor /= 32;
 
-       mccr0 = Ser4MCCR0 & ~0x00007f00;
-       mccr0 |= divisor << 8;
-       Ser4MCCR0 = mccr0;
+       priv->mccr0 &= ~0x00007f00;
+       priv->mccr0 |= divisor << 8;
+       __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0);
 }
 
 static void
 mcp_sa11x0_set_audio_divisor(struct mcp *mcp, unsigned int divisor)
 {
-       unsigned int mccr0;
+       struct mcp_sa11x0 *priv = priv(mcp);
 
        divisor /= 32;
 
-       mccr0 = Ser4MCCR0 & ~0x0000007f;
-       mccr0 |= divisor;
-       Ser4MCCR0 = mccr0;
+       priv->mccr0 &= ~0x0000007f;
+       priv->mccr0 |= divisor;
+       __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0);
 }
 
 /*
@@ -71,12 +79,16 @@ mcp_sa11x0_write(struct mcp *mcp, unsigned int reg, unsigned int val)
 {
        int ret = -ETIME;
        int i;
+       u32 mcpreg;
+       struct mcp_sa11x0 *priv = priv(mcp);
 
-       Ser4MCDR2 = reg << 17 | MCDR2_Wr | (val & 0xffff);
+       mcpreg = reg << 17 | MCDR2_Wr | (val & 0xffff);
+       __raw_writel(mcpreg, priv->mccr0_base + MCDR2);
 
        for (i = 0; i < 2; i++) {
                udelay(mcp->rw_timeout);
-               if (Ser4MCSR & MCSR_CWC) {
+               mcpreg = __raw_readl(priv->mccr0_base + MCSR);
+               if (mcpreg & MCSR_CWC) {
                        ret = 0;
                        break;
                }
@@ -97,13 +109,18 @@ mcp_sa11x0_read(struct mcp *mcp, unsigned int reg)
 {
        int ret = -ETIME;
        int i;
+       u32 mcpreg;
+       struct mcp_sa11x0 *priv = priv(mcp);
 
-       Ser4MCDR2 = reg << 17 | MCDR2_Rd;
+       mcpreg = reg << 17 | MCDR2_Rd;
+       __raw_writel(mcpreg, priv->mccr0_base + MCDR2);
 
        for (i = 0; i < 2; i++) {
                udelay(mcp->rw_timeout);
-               if (Ser4MCSR & MCSR_CRC) {
-                       ret = Ser4MCDR2 & 0xffff;
+               mcpreg = __raw_readl(priv->mccr0_base + MCSR);
+               if (mcpreg & MCSR_CRC) {
+                       ret = __raw_readl(priv->mccr0_base + MCDR2)
+                               & 0xffff;
                        break;
                }
        }
@@ -116,13 +133,19 @@ mcp_sa11x0_read(struct mcp *mcp, unsigned int reg)
 
 static void mcp_sa11x0_enable(struct mcp *mcp)
 {
-       Ser4MCSR = -1;
-       Ser4MCCR0 |= MCCR0_MCE;
+       struct mcp_sa11x0 *priv = priv(mcp);
+
+       __raw_writel(-1, priv->mccr0_base + MCSR);
+       priv->mccr0 |= MCCR0_MCE;
+       __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0);
 }
 
 static void mcp_sa11x0_disable(struct mcp *mcp)
 {
-       Ser4MCCR0 &= ~MCCR0_MCE;
+       struct mcp_sa11x0 *priv = priv(mcp);
+
+       priv->mccr0 &= ~MCCR0_MCE;
+       __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0);
 }
 
 /*
@@ -142,50 +165,69 @@ static int mcp_sa11x0_probe(struct platform_device *pdev)
        struct mcp_plat_data *data = pdev->dev.platform_data;
        struct mcp *mcp;
        int ret;
+       struct mcp_sa11x0 *priv;
+       struct resource *res_mem0, *res_mem1;
+       u32 size0, size1;
 
        if (!data)
                return -ENODEV;
 
-       if (!request_mem_region(0x80060000, 0x60, "sa11x0-mcp"))
+       if (!data->codec)
+               return -ENODEV;
+
+       res_mem0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res_mem0)
+               return -ENODEV;
+       size0 = res_mem0->end - res_mem0->start + 1;
+
+       res_mem1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       if (!res_mem1)
+               return -ENODEV;
+       size1 = res_mem1->end - res_mem1->start + 1;
+
+       if (!request_mem_region(res_mem0->start, size0, "sa11x0-mcp"))
                return -EBUSY;
 
+       if (!request_mem_region(res_mem1->start, size1, "sa11x0-mcp")) {
+               ret = -EBUSY;
+               goto release;
+       }
+
        mcp = mcp_host_alloc(&pdev->dev, sizeof(struct mcp_sa11x0));
        if (!mcp) {
                ret = -ENOMEM;
-               goto release;
+               goto release2;
        }
 
+       priv = priv(mcp);
+
        mcp->owner              = THIS_MODULE;
        mcp->ops                = &mcp_sa11x0;
        mcp->sclk_rate          = data->sclk_rate;
-       mcp->dma_audio_rd       = DMA_Ser4MCP0Rd;
-       mcp->dma_audio_wr       = DMA_Ser4MCP0Wr;
-       mcp->dma_telco_rd       = DMA_Ser4MCP1Rd;
-       mcp->dma_telco_wr       = DMA_Ser4MCP1Wr;
-       mcp->gpio_base          = data->gpio_base;
+       mcp->dma_audio_rd       = DDAR_DevAdd(res_mem0->start + MCDR0)
+                               + DDAR_DevRd + DDAR_Brst4 + DDAR_8BitDev;
+       mcp->dma_audio_wr       = DDAR_DevAdd(res_mem0->start + MCDR0)
+                               + DDAR_DevWr + DDAR_Brst4 + DDAR_8BitDev;
+       mcp->dma_telco_rd       = DDAR_DevAdd(res_mem0->start + MCDR1)
+                               + DDAR_DevRd + DDAR_Brst4 + DDAR_8BitDev;
+       mcp->dma_telco_wr       = DDAR_DevAdd(res_mem0->start + MCDR1)
+                               + DDAR_DevWr + DDAR_Brst4 + DDAR_8BitDev;
+       mcp->codec              = data->codec;
 
        platform_set_drvdata(pdev, mcp);
 
-       if (machine_is_assabet()) {
-               ASSABET_BCR_set(ASSABET_BCR_CODEC_RST);
-       }
-
-       /*
-        * Setup the PPC unit correctly.
-        */
-       PPDR &= ~PPC_RXD4;
-       PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM;
-       PSDR |= PPC_RXD4;
-       PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
-       PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM);
-
        /*
         * Initialise device.  Note that we initially
         * set the sampling rate to minimum.
         */
-       Ser4MCSR = -1;
-       Ser4MCCR1 = data->mccr1;
-       Ser4MCCR0 = data->mccr0 | 0x7f7f;
+       priv->mccr0_base = ioremap(res_mem0->start, size0);
+       priv->mccr1_base = ioremap(res_mem1->start, size1);
+
+       __raw_writel(-1, priv->mccr0_base + MCSR);
+       priv->mccr1 = data->mccr1;
+       priv->mccr0 = data->mccr0 | 0x7f7f;
+       __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0);
+       __raw_writel(priv->mccr1, priv->mccr1_base + MCCR1);
 
        /*
         * Calculate the read/write timeout (us) from the bit clock
@@ -195,36 +237,53 @@ static int mcp_sa11x0_probe(struct platform_device *pdev)
        mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) /
                          mcp->sclk_rate;
 
-       ret = mcp_host_register(mcp);
+       ret = mcp_host_register(mcp, data->codec_pdata);
        if (ret == 0)
                goto out;
 
+ release2:
+       release_mem_region(res_mem1->start, size1);
  release:
-       release_mem_region(0x80060000, 0x60);
+       release_mem_region(res_mem0->start, size0);
        platform_set_drvdata(pdev, NULL);
 
  out:
        return ret;
 }
 
-static int mcp_sa11x0_remove(struct platform_device *dev)
+static int mcp_sa11x0_remove(struct platform_device *pdev)
 {
-       struct mcp *mcp = platform_get_drvdata(dev);
+       struct mcp *mcp = platform_get_drvdata(pdev);
+       struct mcp_sa11x0 *priv = priv(mcp);
+       struct resource *res_mem;
+       u32 size;
 
-       platform_set_drvdata(dev, NULL);
+       platform_set_drvdata(pdev, NULL);
        mcp_host_unregister(mcp);
-       release_mem_region(0x80060000, 0x60);
 
+       res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res_mem) {
+               size = res_mem->end - res_mem->start + 1;
+               release_mem_region(res_mem->start, size);
+       }
+       res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       if (res_mem) {
+               size = res_mem->end - res_mem->start + 1;
+               release_mem_region(res_mem->start, size);
+       }
+       iounmap(priv->mccr0_base);
+       iounmap(priv->mccr1_base);
        return 0;
 }
 
 static int mcp_sa11x0_suspend(struct platform_device *dev, pm_message_t state)
 {
        struct mcp *mcp = platform_get_drvdata(dev);
+       struct mcp_sa11x0 *priv = priv(mcp);
+       u32 mccr0;
 
-       priv(mcp)->mccr0 = Ser4MCCR0;
-       priv(mcp)->mccr1 = Ser4MCCR1;
-       Ser4MCCR0 &= ~MCCR0_MCE;
+       mccr0 = priv->mccr0 & ~MCCR0_MCE;
+       __raw_writel(mccr0, priv->mccr0_base + MCCR0);
 
        return 0;
 }
@@ -232,9 +291,10 @@ static int mcp_sa11x0_suspend(struct platform_device *dev, pm_message_t state)
 static int mcp_sa11x0_resume(struct platform_device *dev)
 {
        struct mcp *mcp = platform_get_drvdata(dev);
+       struct mcp_sa11x0 *priv = priv(mcp);
 
-       Ser4MCCR1 = priv(mcp)->mccr1;
-       Ser4MCCR0 = priv(mcp)->mccr0;
+       __raw_writel(priv->mccr0, priv->mccr0_base + MCCR0);
+       __raw_writel(priv->mccr1, priv->mccr1_base + MCCR1);
 
        return 0;
 }
@@ -251,24 +311,14 @@ static struct platform_driver mcp_sa11x0_driver = {
        .resume         = mcp_sa11x0_resume,
        .driver         = {
                .name   = "sa11x0-mcp",
+               .owner  = THIS_MODULE,
        },
 };
 
 /*
  * This needs re-working
  */
-static int __init mcp_sa11x0_init(void)
-{
-       return platform_driver_register(&mcp_sa11x0_driver);
-}
-
-static void __exit mcp_sa11x0_exit(void)
-{
-       platform_driver_unregister(&mcp_sa11x0_driver);
-}
-
-module_init(mcp_sa11x0_init);
-module_exit(mcp_sa11x0_exit);
+module_platform_driver(mcp_sa11x0_driver);
 
 MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
 MODULE_DESCRIPTION("SA11x0 multimedia communications port driver");
index 3f565ef3e149438c86a0054f354627231a2258b2..68ac2c55d5ae0bb1381c0681b641846079345f54 100644 (file)
@@ -503,19 +503,13 @@ static void omap_usbhs_init(struct device *dev)
        spin_lock_irqsave(&omap->lock, flags);
 
        if (pdata->ehci_data->phy_reset) {
-               if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) {
-                       gpio_request(pdata->ehci_data->reset_gpio_port[0],
-                                               "USB1 PHY reset");
-                       gpio_direction_output
-                               (pdata->ehci_data->reset_gpio_port[0], 0);
-               }
+               if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0]))
+                       gpio_request_one(pdata->ehci_data->reset_gpio_port[0],
+                                        GPIOF_OUT_INIT_LOW, "USB1 PHY reset");
 
-               if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1])) {
-                       gpio_request(pdata->ehci_data->reset_gpio_port[1],
-                                               "USB2 PHY reset");
-                       gpio_direction_output
-                               (pdata->ehci_data->reset_gpio_port[1], 0);
-               }
+               if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1]))
+                       gpio_request_one(pdata->ehci_data->reset_gpio_port[1],
+                                        GPIOF_OUT_INIT_LOW, "USB2 PHY reset");
 
                /* Hold the PHY in RESET for enough time till DIR is high */
                udelay(10);
index aed0d2a9b032e0b3e5babe7949037df19470e1c7..3927c17e4175d9cd4bc28b9c756286a7f1b4efd4 100644 (file)
@@ -249,17 +249,7 @@ static struct platform_driver pcf50633_adc_driver = {
        .remove = __devexit_p(pcf50633_adc_remove),
 };
 
-static int __init pcf50633_adc_init(void)
-{
-       return platform_driver_register(&pcf50633_adc_driver);
-}
-module_init(pcf50633_adc_init);
-
-static void __exit pcf50633_adc_exit(void)
-{
-       platform_driver_unregister(&pcf50633_adc_driver);
-}
-module_exit(pcf50633_adc_exit);
+module_platform_driver(pcf50633_adc_driver);
 
 MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
 MODULE_DESCRIPTION("PCF50633 adc driver");
diff --git a/drivers/mfd/s5m-core.c b/drivers/mfd/s5m-core.c
new file mode 100644 (file)
index 0000000..e075c11
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * s5m87xx.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd
+ *              http://www.samsung.com
+ *
+ *  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 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+#include <linux/mutex.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/s5m87xx/s5m-core.h>
+#include <linux/mfd/s5m87xx/s5m-pmic.h>
+#include <linux/mfd/s5m87xx/s5m-rtc.h>
+#include <linux/regmap.h>
+
+static struct mfd_cell s5m87xx_devs[] = {
+       {
+               .name = "s5m8767-pmic",
+       }, {
+               .name = "s5m-rtc",
+       },
+};
+
+int s5m_reg_read(struct s5m87xx_dev *s5m87xx, u8 reg, void *dest)
+{
+       return regmap_read(s5m87xx->regmap, reg, dest);
+}
+EXPORT_SYMBOL_GPL(s5m_reg_read);
+
+int s5m_bulk_read(struct s5m87xx_dev *s5m87xx, u8 reg, int count, u8 *buf)
+{
+       return regmap_bulk_read(s5m87xx->regmap, reg, buf, count);;
+}
+EXPORT_SYMBOL_GPL(s5m_bulk_read);
+
+int s5m_reg_write(struct s5m87xx_dev *s5m87xx, u8 reg, u8 value)
+{
+       return regmap_write(s5m87xx->regmap, reg, value);
+}
+EXPORT_SYMBOL_GPL(s5m_reg_write);
+
+int s5m_bulk_write(struct s5m87xx_dev *s5m87xx, u8 reg, int count, u8 *buf)
+{
+       return regmap_raw_write(s5m87xx->regmap, reg, buf, count * sizeof(u16));
+}
+EXPORT_SYMBOL_GPL(s5m_bulk_write);
+
+int s5m_reg_update(struct s5m87xx_dev *s5m87xx, u8 reg, u8 val, u8 mask)
+{
+       return regmap_update_bits(s5m87xx->regmap, reg, mask, val);
+}
+EXPORT_SYMBOL_GPL(s5m_reg_update);
+
+static struct regmap_config s5m_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+};
+
+static int s5m87xx_i2c_probe(struct i2c_client *i2c,
+                           const struct i2c_device_id *id)
+{
+       struct s5m_platform_data *pdata = i2c->dev.platform_data;
+       struct s5m87xx_dev *s5m87xx;
+       int ret = 0;
+       int error;
+
+       s5m87xx = kzalloc(sizeof(struct s5m87xx_dev), GFP_KERNEL);
+       if (s5m87xx == NULL)
+               return -ENOMEM;
+
+       i2c_set_clientdata(i2c, s5m87xx);
+       s5m87xx->dev = &i2c->dev;
+       s5m87xx->i2c = i2c;
+       s5m87xx->irq = i2c->irq;
+       s5m87xx->type = id->driver_data;
+
+       if (pdata) {
+               s5m87xx->device_type = pdata->device_type;
+               s5m87xx->ono = pdata->ono;
+               s5m87xx->irq_base = pdata->irq_base;
+               s5m87xx->wakeup = pdata->wakeup;
+       }
+
+       s5m87xx->regmap = regmap_init_i2c(i2c, &s5m_regmap_config);
+       if (IS_ERR(s5m87xx->regmap)) {
+               error = PTR_ERR(s5m87xx->regmap);
+               dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+                       error);
+               goto err;
+       }
+
+       s5m87xx->rtc = i2c_new_dummy(i2c->adapter, RTC_I2C_ADDR);
+       i2c_set_clientdata(s5m87xx->rtc, s5m87xx);
+
+       if (pdata->cfg_pmic_irq)
+               pdata->cfg_pmic_irq();
+
+       s5m_irq_init(s5m87xx);
+
+       pm_runtime_set_active(s5m87xx->dev);
+
+       ret = mfd_add_devices(s5m87xx->dev, -1,
+                               s5m87xx_devs, ARRAY_SIZE(s5m87xx_devs),
+                               NULL, 0);
+
+       if (ret < 0)
+               goto err;
+
+       return ret;
+
+err:
+       mfd_remove_devices(s5m87xx->dev);
+       s5m_irq_exit(s5m87xx);
+       i2c_unregister_device(s5m87xx->rtc);
+       regmap_exit(s5m87xx->regmap);
+       kfree(s5m87xx);
+       return ret;
+}
+
+static int s5m87xx_i2c_remove(struct i2c_client *i2c)
+{
+       struct s5m87xx_dev *s5m87xx = i2c_get_clientdata(i2c);
+
+       mfd_remove_devices(s5m87xx->dev);
+       s5m_irq_exit(s5m87xx);
+       i2c_unregister_device(s5m87xx->rtc);
+       regmap_exit(s5m87xx->regmap);
+       kfree(s5m87xx);
+       return 0;
+}
+
+static const struct i2c_device_id s5m87xx_i2c_id[] = {
+       { "s5m87xx", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, s5m87xx_i2c_id);
+
+static struct i2c_driver s5m87xx_i2c_driver = {
+       .driver = {
+                  .name = "s5m87xx",
+                  .owner = THIS_MODULE,
+       },
+       .probe = s5m87xx_i2c_probe,
+       .remove = s5m87xx_i2c_remove,
+       .id_table = s5m87xx_i2c_id,
+};
+
+static int __init s5m87xx_i2c_init(void)
+{
+       return i2c_add_driver(&s5m87xx_i2c_driver);
+}
+
+subsys_initcall(s5m87xx_i2c_init);
+
+static void __exit s5m87xx_i2c_exit(void)
+{
+       i2c_del_driver(&s5m87xx_i2c_driver);
+}
+module_exit(s5m87xx_i2c_exit);
+
+MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>");
+MODULE_DESCRIPTION("Core support for the S5M MFD");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/s5m-irq.c b/drivers/mfd/s5m-irq.c
new file mode 100644 (file)
index 0000000..de76dfb
--- /dev/null
@@ -0,0 +1,487 @@
+/*
+ * s5m-irq.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd
+ *              http://www.samsung.com
+ *
+ *  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 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mfd/s5m87xx/s5m-core.h>
+
+struct s5m_irq_data {
+       int reg;
+       int mask;
+};
+
+static struct s5m_irq_data s5m8767_irqs[] = {
+       [S5M8767_IRQ_PWRR] = {
+               .reg = 1,
+               .mask = S5M8767_IRQ_PWRR_MASK,
+       },
+       [S5M8767_IRQ_PWRF] = {
+               .reg = 1,
+               .mask = S5M8767_IRQ_PWRF_MASK,
+       },
+       [S5M8767_IRQ_PWR1S] = {
+               .reg = 1,
+               .mask = S5M8767_IRQ_PWR1S_MASK,
+       },
+       [S5M8767_IRQ_JIGR] = {
+               .reg = 1,
+               .mask = S5M8767_IRQ_JIGR_MASK,
+       },
+       [S5M8767_IRQ_JIGF] = {
+               .reg = 1,
+               .mask = S5M8767_IRQ_JIGF_MASK,
+       },
+       [S5M8767_IRQ_LOWBAT2] = {
+               .reg = 1,
+               .mask = S5M8767_IRQ_LOWBAT2_MASK,
+       },
+       [S5M8767_IRQ_LOWBAT1] = {
+               .reg = 1,
+               .mask = S5M8767_IRQ_LOWBAT1_MASK,
+       },
+       [S5M8767_IRQ_MRB] = {
+               .reg = 2,
+               .mask = S5M8767_IRQ_MRB_MASK,
+       },
+       [S5M8767_IRQ_DVSOK2] = {
+               .reg = 2,
+               .mask = S5M8767_IRQ_DVSOK2_MASK,
+       },
+       [S5M8767_IRQ_DVSOK3] = {
+               .reg = 2,
+               .mask = S5M8767_IRQ_DVSOK3_MASK,
+       },
+       [S5M8767_IRQ_DVSOK4] = {
+               .reg = 2,
+               .mask = S5M8767_IRQ_DVSOK4_MASK,
+       },
+       [S5M8767_IRQ_RTC60S] = {
+               .reg = 3,
+               .mask = S5M8767_IRQ_RTC60S_MASK,
+       },
+       [S5M8767_IRQ_RTCA1] = {
+               .reg = 3,
+               .mask = S5M8767_IRQ_RTCA1_MASK,
+       },
+       [S5M8767_IRQ_RTCA2] = {
+               .reg = 3,
+               .mask = S5M8767_IRQ_RTCA2_MASK,
+       },
+       [S5M8767_IRQ_SMPL] = {
+               .reg = 3,
+               .mask = S5M8767_IRQ_SMPL_MASK,
+       },
+       [S5M8767_IRQ_RTC1S] = {
+               .reg = 3,
+               .mask = S5M8767_IRQ_RTC1S_MASK,
+       },
+       [S5M8767_IRQ_WTSR] = {
+               .reg = 3,
+               .mask = S5M8767_IRQ_WTSR_MASK,
+       },
+};
+
+static struct s5m_irq_data s5m8763_irqs[] = {
+       [S5M8763_IRQ_DCINF] = {
+               .reg = 1,
+               .mask = S5M8763_IRQ_DCINF_MASK,
+       },
+       [S5M8763_IRQ_DCINR] = {
+               .reg = 1,
+               .mask = S5M8763_IRQ_DCINR_MASK,
+       },
+       [S5M8763_IRQ_JIGF] = {
+               .reg = 1,
+               .mask = S5M8763_IRQ_JIGF_MASK,
+       },
+       [S5M8763_IRQ_JIGR] = {
+               .reg = 1,
+               .mask = S5M8763_IRQ_JIGR_MASK,
+       },
+       [S5M8763_IRQ_PWRONF] = {
+               .reg = 1,
+               .mask = S5M8763_IRQ_PWRONF_MASK,
+       },
+       [S5M8763_IRQ_PWRONR] = {
+               .reg = 1,
+               .mask = S5M8763_IRQ_PWRONR_MASK,
+       },
+       [S5M8763_IRQ_WTSREVNT] = {
+               .reg = 2,
+               .mask = S5M8763_IRQ_WTSREVNT_MASK,
+       },
+       [S5M8763_IRQ_SMPLEVNT] = {
+               .reg = 2,
+               .mask = S5M8763_IRQ_SMPLEVNT_MASK,
+       },
+       [S5M8763_IRQ_ALARM1] = {
+               .reg = 2,
+               .mask = S5M8763_IRQ_ALARM1_MASK,
+       },
+       [S5M8763_IRQ_ALARM0] = {
+               .reg = 2,
+               .mask = S5M8763_IRQ_ALARM0_MASK,
+       },
+       [S5M8763_IRQ_ONKEY1S] = {
+               .reg = 3,
+               .mask = S5M8763_IRQ_ONKEY1S_MASK,
+       },
+       [S5M8763_IRQ_TOPOFFR] = {
+               .reg = 3,
+               .mask = S5M8763_IRQ_TOPOFFR_MASK,
+       },
+       [S5M8763_IRQ_DCINOVPR] = {
+               .reg = 3,
+               .mask = S5M8763_IRQ_DCINOVPR_MASK,
+       },
+       [S5M8763_IRQ_CHGRSTF] = {
+               .reg = 3,
+               .mask = S5M8763_IRQ_CHGRSTF_MASK,
+       },
+       [S5M8763_IRQ_DONER] = {
+               .reg = 3,
+               .mask = S5M8763_IRQ_DONER_MASK,
+       },
+       [S5M8763_IRQ_CHGFAULT] = {
+               .reg = 3,
+               .mask = S5M8763_IRQ_CHGFAULT_MASK,
+       },
+       [S5M8763_IRQ_LOBAT1] = {
+               .reg = 4,
+               .mask = S5M8763_IRQ_LOBAT1_MASK,
+       },
+       [S5M8763_IRQ_LOBAT2] = {
+               .reg = 4,
+               .mask = S5M8763_IRQ_LOBAT2_MASK,
+       },
+};
+
+static inline struct s5m_irq_data *
+irq_to_s5m8767_irq(struct s5m87xx_dev *s5m87xx, int irq)
+{
+       return &s5m8767_irqs[irq - s5m87xx->irq_base];
+}
+
+static void s5m8767_irq_lock(struct irq_data *data)
+{
+       struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
+
+       mutex_lock(&s5m87xx->irqlock);
+}
+
+static void s5m8767_irq_sync_unlock(struct irq_data *data)
+{
+       struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(s5m87xx->irq_masks_cur); i++) {
+               if (s5m87xx->irq_masks_cur[i] != s5m87xx->irq_masks_cache[i]) {
+                       s5m87xx->irq_masks_cache[i] = s5m87xx->irq_masks_cur[i];
+                       s5m_reg_write(s5m87xx, S5M8767_REG_INT1M + i,
+                                       s5m87xx->irq_masks_cur[i]);
+               }
+       }
+
+       mutex_unlock(&s5m87xx->irqlock);
+}
+
+static void s5m8767_irq_unmask(struct irq_data *data)
+{
+       struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
+       struct s5m_irq_data *irq_data = irq_to_s5m8767_irq(s5m87xx,
+                                                              data->irq);
+
+       s5m87xx->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
+}
+
+static void s5m8767_irq_mask(struct irq_data *data)
+{
+       struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
+       struct s5m_irq_data *irq_data = irq_to_s5m8767_irq(s5m87xx,
+                                                              data->irq);
+
+       s5m87xx->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
+}
+
+static struct irq_chip s5m8767_irq_chip = {
+       .name = "s5m8767",
+       .irq_bus_lock = s5m8767_irq_lock,
+       .irq_bus_sync_unlock = s5m8767_irq_sync_unlock,
+       .irq_mask = s5m8767_irq_mask,
+       .irq_unmask = s5m8767_irq_unmask,
+};
+
+static inline struct s5m_irq_data *
+irq_to_s5m8763_irq(struct s5m87xx_dev *s5m87xx, int irq)
+{
+       return &s5m8763_irqs[irq - s5m87xx->irq_base];
+}
+
+static void s5m8763_irq_lock(struct irq_data *data)
+{
+       struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
+
+       mutex_lock(&s5m87xx->irqlock);
+}
+
+static void s5m8763_irq_sync_unlock(struct irq_data *data)
+{
+       struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(s5m87xx->irq_masks_cur); i++) {
+               if (s5m87xx->irq_masks_cur[i] != s5m87xx->irq_masks_cache[i]) {
+                       s5m87xx->irq_masks_cache[i] = s5m87xx->irq_masks_cur[i];
+                       s5m_reg_write(s5m87xx, S5M8763_REG_IRQM1 + i,
+                                       s5m87xx->irq_masks_cur[i]);
+               }
+       }
+
+       mutex_unlock(&s5m87xx->irqlock);
+}
+
+static void s5m8763_irq_unmask(struct irq_data *data)
+{
+       struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
+       struct s5m_irq_data *irq_data = irq_to_s5m8763_irq(s5m87xx,
+                                                              data->irq);
+
+       s5m87xx->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
+}
+
+static void s5m8763_irq_mask(struct irq_data *data)
+{
+       struct s5m87xx_dev *s5m87xx = irq_data_get_irq_chip_data(data);
+       struct s5m_irq_data *irq_data = irq_to_s5m8763_irq(s5m87xx,
+                                                              data->irq);
+
+       s5m87xx->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
+}
+
+static struct irq_chip s5m8763_irq_chip = {
+       .name = "s5m8763",
+       .irq_bus_lock = s5m8763_irq_lock,
+       .irq_bus_sync_unlock = s5m8763_irq_sync_unlock,
+       .irq_mask = s5m8763_irq_mask,
+       .irq_unmask = s5m8763_irq_unmask,
+};
+
+
+static irqreturn_t s5m8767_irq_thread(int irq, void *data)
+{
+       struct s5m87xx_dev *s5m87xx = data;
+       u8 irq_reg[NUM_IRQ_REGS-1];
+       int ret;
+       int i;
+
+
+       ret = s5m_bulk_read(s5m87xx, S5M8767_REG_INT1,
+                               NUM_IRQ_REGS - 1, irq_reg);
+       if (ret < 0) {
+               dev_err(s5m87xx->dev, "Failed to read interrupt register: %d\n",
+                               ret);
+               return IRQ_NONE;
+       }
+
+       for (i = 0; i < NUM_IRQ_REGS - 1; i++)
+               irq_reg[i] &= ~s5m87xx->irq_masks_cur[i];
+
+       for (i = 0; i < S5M8767_IRQ_NR; i++) {
+               if (irq_reg[s5m8767_irqs[i].reg - 1] & s5m8767_irqs[i].mask)
+                       handle_nested_irq(s5m87xx->irq_base + i);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t s5m8763_irq_thread(int irq, void *data)
+{
+       struct s5m87xx_dev *s5m87xx = data;
+       u8 irq_reg[NUM_IRQ_REGS];
+       int ret;
+       int i;
+
+       ret = s5m_bulk_read(s5m87xx, S5M8763_REG_IRQ1,
+                               NUM_IRQ_REGS, irq_reg);
+       if (ret < 0) {
+               dev_err(s5m87xx->dev, "Failed to read interrupt register: %d\n",
+                               ret);
+               return IRQ_NONE;
+       }
+
+       for (i = 0; i < NUM_IRQ_REGS; i++)
+               irq_reg[i] &= ~s5m87xx->irq_masks_cur[i];
+
+       for (i = 0; i < S5M8763_IRQ_NR; i++) {
+               if (irq_reg[s5m8763_irqs[i].reg - 1] & s5m8763_irqs[i].mask)
+                       handle_nested_irq(s5m87xx->irq_base + i);
+       }
+
+       return IRQ_HANDLED;
+}
+
+int s5m_irq_resume(struct s5m87xx_dev *s5m87xx)
+{
+       if (s5m87xx->irq && s5m87xx->irq_base){
+               switch (s5m87xx->device_type) {
+               case S5M8763X:
+                       s5m8763_irq_thread(s5m87xx->irq_base, s5m87xx);
+                       break;
+               case S5M8767X:
+                       s5m8767_irq_thread(s5m87xx->irq_base, s5m87xx);
+                       break;
+               default:
+                       break;
+
+               }
+       }
+       return 0;
+}
+
+int s5m_irq_init(struct s5m87xx_dev *s5m87xx)
+{
+       int i;
+       int cur_irq;
+       int ret = 0;
+       int type = s5m87xx->device_type;
+
+       if (!s5m87xx->irq) {
+               dev_warn(s5m87xx->dev,
+                        "No interrupt specified, no interrupts\n");
+               s5m87xx->irq_base = 0;
+               return 0;
+       }
+
+       if (!s5m87xx->irq_base) {
+               dev_err(s5m87xx->dev,
+                       "No interrupt base specified, no interrupts\n");
+               return 0;
+       }
+
+       mutex_init(&s5m87xx->irqlock);
+
+       switch (type) {
+       case S5M8763X:
+               for (i = 0; i < NUM_IRQ_REGS; i++) {
+                       s5m87xx->irq_masks_cur[i] = 0xff;
+                       s5m87xx->irq_masks_cache[i] = 0xff;
+                       s5m_reg_write(s5m87xx, S5M8763_REG_IRQM1 + i,
+                                               0xff);
+               }
+
+               s5m_reg_write(s5m87xx, S5M8763_REG_STATUSM1, 0xff);
+               s5m_reg_write(s5m87xx, S5M8763_REG_STATUSM2, 0xff);
+
+               for (i = 0; i < S5M8763_IRQ_NR; i++) {
+                       cur_irq = i + s5m87xx->irq_base;
+                       irq_set_chip_data(cur_irq, s5m87xx);
+                       irq_set_chip_and_handler(cur_irq, &s5m8763_irq_chip,
+                                                handle_edge_irq);
+                       irq_set_nested_thread(cur_irq, 1);
+#ifdef CONFIG_ARM
+                       set_irq_flags(cur_irq, IRQF_VALID);
+#else
+                       irq_set_noprobe(cur_irq);
+#endif
+               }
+
+               ret = request_threaded_irq(s5m87xx->irq, NULL,
+                                       s5m8763_irq_thread,
+                                       IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                       "s5m87xx-irq", s5m87xx);
+               if (ret) {
+                       dev_err(s5m87xx->dev, "Failed to request IRQ %d: %d\n",
+                               s5m87xx->irq, ret);
+                       return ret;
+               }
+               break;
+       case S5M8767X:
+               for (i = 0; i < NUM_IRQ_REGS - 1; i++) {
+                       s5m87xx->irq_masks_cur[i] = 0xff;
+                       s5m87xx->irq_masks_cache[i] = 0xff;
+                       s5m_reg_write(s5m87xx, S5M8767_REG_INT1M + i,
+                                               0xff);
+               }
+               for (i = 0; i < S5M8767_IRQ_NR; i++) {
+                       cur_irq = i + s5m87xx->irq_base;
+                       irq_set_chip_data(cur_irq, s5m87xx);
+                       if (ret) {
+                               dev_err(s5m87xx->dev,
+                                       "Failed to irq_set_chip_data %d: %d\n",
+                                       s5m87xx->irq, ret);
+                               return ret;
+                       }
+
+                       irq_set_chip_and_handler(cur_irq, &s5m8767_irq_chip,
+                                                handle_edge_irq);
+                       irq_set_nested_thread(cur_irq, 1);
+#ifdef CONFIG_ARM
+                       set_irq_flags(cur_irq, IRQF_VALID);
+#else
+                       irq_set_noprobe(cur_irq);
+#endif
+               }
+
+               ret = request_threaded_irq(s5m87xx->irq, NULL,
+                                          s5m8767_irq_thread,
+                                          IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                          "s5m87xx-irq", s5m87xx);
+               if (ret) {
+                       dev_err(s5m87xx->dev, "Failed to request IRQ %d: %d\n",
+                               s5m87xx->irq, ret);
+                       return ret;
+               }
+               break;
+       default:
+               break;
+       }
+
+       if (!s5m87xx->ono)
+               return 0;
+
+       switch (type) {
+       case S5M8763X:
+               ret = request_threaded_irq(s5m87xx->ono, NULL,
+                                               s5m8763_irq_thread,
+                                               IRQF_TRIGGER_FALLING |
+                                               IRQF_TRIGGER_RISING |
+                                               IRQF_ONESHOT, "s5m87xx-ono",
+                                               s5m87xx);
+               break;
+       case S5M8767X:
+               ret = request_threaded_irq(s5m87xx->ono, NULL,
+                                       s5m8767_irq_thread,
+                                       IRQF_TRIGGER_FALLING |
+                                       IRQF_TRIGGER_RISING |
+                                       IRQF_ONESHOT, "s5m87xx-ono", s5m87xx);
+               break;
+       default:
+               break;
+       }
+
+       if (ret)
+               dev_err(s5m87xx->dev, "Failed to request IRQ %d: %d\n",
+                       s5m87xx->ono, ret);
+
+       return 0;
+}
+
+void s5m_irq_exit(struct s5m87xx_dev *s5m87xx)
+{
+       if (s5m87xx->ono)
+               free_irq(s5m87xx->ono, s5m87xx);
+
+       if (s5m87xx->irq)
+               free_irq(s5m87xx->irq, s5m87xx);
+}
index df3702c1756df9aae08dcfbbf1bde345d7520172..f4d86117f44a21bcc140e93b8fd116798d6d5054 100644 (file)
@@ -1720,7 +1720,7 @@ static int sm501_plat_remove(struct platform_device *dev)
        return 0;
 }
 
-static struct pci_device_id sm501_pci_tbl[] = {
+static DEFINE_PCI_DEVICE_TABLE(sm501_pci_tbl) = {
        { 0x126f, 0x0501, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
        { 0, },
 };
diff --git a/drivers/mfd/stmpe-i2c.c b/drivers/mfd/stmpe-i2c.c
new file mode 100644 (file)
index 0000000..373f423
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * ST Microelectronics MFD: stmpe's i2c client specific driver
+ *
+ * Copyright (C) ST-Ericsson SA 2010
+ * Copyright (C) ST Microelectronics SA 2011
+ *
+ * License Terms: GNU General Public License, version 2
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ * Author: Viresh Kumar <viresh.kumar@st.com> for ST Microelectronics
+ */
+
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include "stmpe.h"
+
+static int i2c_reg_read(struct stmpe *stmpe, u8 reg)
+{
+       struct i2c_client *i2c = stmpe->client;
+
+       return i2c_smbus_read_byte_data(i2c, reg);
+}
+
+static int i2c_reg_write(struct stmpe *stmpe, u8 reg, u8 val)
+{
+       struct i2c_client *i2c = stmpe->client;
+
+       return i2c_smbus_write_byte_data(i2c, reg, val);
+}
+
+static int i2c_block_read(struct stmpe *stmpe, u8 reg, u8 length, u8 *values)
+{
+       struct i2c_client *i2c = stmpe->client;
+
+       return i2c_smbus_read_i2c_block_data(i2c, reg, length, values);
+}
+
+static int i2c_block_write(struct stmpe *stmpe, u8 reg, u8 length,
+               const u8 *values)
+{
+       struct i2c_client *i2c = stmpe->client;
+
+       return i2c_smbus_write_i2c_block_data(i2c, reg, length, values);
+}
+
+static struct stmpe_client_info i2c_ci = {
+       .read_byte = i2c_reg_read,
+       .write_byte = i2c_reg_write,
+       .read_block = i2c_block_read,
+       .write_block = i2c_block_write,
+};
+
+static int __devinit
+stmpe_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
+{
+       i2c_ci.data = (void *)id;
+       i2c_ci.irq = i2c->irq;
+       i2c_ci.client = i2c;
+       i2c_ci.dev = &i2c->dev;
+
+       return stmpe_probe(&i2c_ci, id->driver_data);
+}
+
+static int __devexit stmpe_i2c_remove(struct i2c_client *i2c)
+{
+       struct stmpe *stmpe = dev_get_drvdata(&i2c->dev);
+
+       return stmpe_remove(stmpe);
+}
+
+static const struct i2c_device_id stmpe_i2c_id[] = {
+       { "stmpe610", STMPE610 },
+       { "stmpe801", STMPE801 },
+       { "stmpe811", STMPE811 },
+       { "stmpe1601", STMPE1601 },
+       { "stmpe2401", STMPE2401 },
+       { "stmpe2403", STMPE2403 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, stmpe_id);
+
+static struct i2c_driver stmpe_i2c_driver = {
+       .driver.name    = "stmpe-i2c",
+       .driver.owner   = THIS_MODULE,
+#ifdef CONFIG_PM
+       .driver.pm      = &stmpe_dev_pm_ops,
+#endif
+       .probe          = stmpe_i2c_probe,
+       .remove         = __devexit_p(stmpe_i2c_remove),
+       .id_table       = stmpe_i2c_id,
+};
+
+static int __init stmpe_init(void)
+{
+       return i2c_add_driver(&stmpe_i2c_driver);
+}
+subsys_initcall(stmpe_init);
+
+static void __exit stmpe_exit(void)
+{
+       i2c_del_driver(&stmpe_i2c_driver);
+}
+module_exit(stmpe_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("STMPE MFD I2C Interface Driver");
+MODULE_AUTHOR("Rabin Vincent <rabin.vincent@stericsson.com>");
diff --git a/drivers/mfd/stmpe-spi.c b/drivers/mfd/stmpe-spi.c
new file mode 100644 (file)
index 0000000..b58c43c
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * ST Microelectronics MFD: stmpe's spi client specific driver
+ *
+ * Copyright (C) ST Microelectronics SA 2011
+ *
+ * License Terms: GNU General Public License, version 2
+ * Author: Viresh Kumar <viresh.kumar@st.com> for ST Microelectronics
+ */
+
+#include <linux/spi/spi.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include "stmpe.h"
+
+#define READ_CMD       (1 << 7)
+
+static int spi_reg_read(struct stmpe *stmpe, u8 reg)
+{
+       struct spi_device *spi = stmpe->client;
+       int status = spi_w8r16(spi, reg | READ_CMD);
+
+       return (status < 0) ? status : status >> 8;
+}
+
+static int spi_reg_write(struct stmpe *stmpe, u8 reg, u8 val)
+{
+       struct spi_device *spi = stmpe->client;
+       u16 cmd = (val << 8) | reg;
+
+       return spi_write(spi, (const u8 *)&cmd, 2);
+}
+
+static int spi_block_read(struct stmpe *stmpe, u8 reg, u8 length, u8 *values)
+{
+       int ret, i;
+
+       for (i = 0; i < length; i++) {
+               ret = spi_reg_read(stmpe, reg + i);
+               if (ret < 0)
+                       return ret;
+               *(values + i) = ret;
+       }
+
+       return 0;
+}
+
+static int spi_block_write(struct stmpe *stmpe, u8 reg, u8 length,
+               const u8 *values)
+{
+       int ret = 0, i;
+
+       for (i = length; i > 0; i--, reg++) {
+               ret = spi_reg_write(stmpe, reg, *(values + i - 1));
+               if (ret < 0)
+                       return ret;
+       }
+
+       return ret;
+}
+
+static void spi_init(struct stmpe *stmpe)
+{
+       struct spi_device *spi = stmpe->client;
+
+       spi->bits_per_word = 8;
+
+       /* This register is only present for stmpe811 */
+       if (stmpe->variant->id_val == 0x0811)
+               spi_reg_write(stmpe, STMPE811_REG_SPI_CFG, spi->mode);
+
+       if (spi_setup(spi) < 0)
+               dev_dbg(&spi->dev, "spi_setup failed\n");
+}
+
+static struct stmpe_client_info spi_ci = {
+       .read_byte = spi_reg_read,
+       .write_byte = spi_reg_write,
+       .read_block = spi_block_read,
+       .write_block = spi_block_write,
+       .init = spi_init,
+};
+
+static int __devinit
+stmpe_spi_probe(struct spi_device *spi)
+{
+       const struct spi_device_id *id = spi_get_device_id(spi);
+
+       /* don't exceed max specified rate - 1MHz - Limitation of STMPE */
+       if (spi->max_speed_hz > 1000000) {
+               dev_dbg(&spi->dev, "f(sample) %d KHz?\n",
+                               (spi->max_speed_hz/1000));
+               return -EINVAL;
+       }
+
+       spi_ci.irq = spi->irq;
+       spi_ci.client = spi;
+       spi_ci.dev = &spi->dev;
+
+       return stmpe_probe(&spi_ci, id->driver_data);
+}
+
+static int __devexit stmpe_spi_remove(struct spi_device *spi)
+{
+       struct stmpe *stmpe = dev_get_drvdata(&spi->dev);
+
+       return stmpe_remove(stmpe);
+}
+
+static const struct spi_device_id stmpe_spi_id[] = {
+       { "stmpe610", STMPE610 },
+       { "stmpe801", STMPE801 },
+       { "stmpe811", STMPE811 },
+       { "stmpe1601", STMPE1601 },
+       { "stmpe2401", STMPE2401 },
+       { "stmpe2403", STMPE2403 },
+       { }
+};
+MODULE_DEVICE_TABLE(spi, stmpe_id);
+
+static struct spi_driver stmpe_spi_driver = {
+       .driver = {
+               .name   = "stmpe-spi",
+               .bus    = &spi_bus_type,
+               .owner  = THIS_MODULE,
+#ifdef CONFIG_PM
+               .pm     = &stmpe_dev_pm_ops,
+#endif
+       },
+       .probe          = stmpe_spi_probe,
+       .remove         = __devexit_p(stmpe_spi_remove),
+       .id_table       = stmpe_spi_id,
+};
+
+static int __init stmpe_init(void)
+{
+       return spi_register_driver(&stmpe_spi_driver);
+}
+subsys_initcall(stmpe_init);
+
+static void __exit stmpe_exit(void)
+{
+       spi_unregister_driver(&stmpe_spi_driver);
+}
+module_exit(stmpe_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("STMPE MFD SPI Interface Driver");
+MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>");
index 2963689cf45c2c93dcd56e56fea26b0b4debdf92..e07947e56b2a0e2bd68d5aa7da434ae6aa5ed878 100644 (file)
@@ -1,18 +1,20 @@
 /*
+ * ST Microelectronics MFD: stmpe's driver
+ *
  * Copyright (C) ST-Ericsson SA 2010
  *
  * License Terms: GNU General Public License, version 2
  * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
  */
 
+#include <linux/gpio.h>
+#include <linux/export.h>
 #include <linux/kernel.h>
-#include <linux/module.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
+#include <linux/pm.h>
 #include <linux/slab.h>
-#include <linux/i2c.h>
 #include <linux/mfd/core.h>
-#include <linux/mfd/stmpe.h>
 #include "stmpe.h"
 
 static int __stmpe_enable(struct stmpe *stmpe, unsigned int blocks)
@@ -29,10 +31,9 @@ static int __stmpe_reg_read(struct stmpe *stmpe, u8 reg)
 {
        int ret;
 
-       ret = i2c_smbus_read_byte_data(stmpe->i2c, reg);
+       ret = stmpe->ci->read_byte(stmpe, reg);
        if (ret < 0)
-               dev_err(stmpe->dev, "failed to read reg %#x: %d\n",
-                       reg, ret);
+               dev_err(stmpe->dev, "failed to read reg %#x: %d\n", reg, ret);
 
        dev_vdbg(stmpe->dev, "rd: reg %#x => data %#x\n", reg, ret);
 
@@ -45,10 +46,9 @@ static int __stmpe_reg_write(struct stmpe *stmpe, u8 reg, u8 val)
 
        dev_vdbg(stmpe->dev, "wr: reg %#x <= %#x\n", reg, val);
 
-       ret = i2c_smbus_write_byte_data(stmpe->i2c, reg, val);
+       ret = stmpe->ci->write_byte(stmpe, reg, val);
        if (ret < 0)
-               dev_err(stmpe->dev, "failed to write reg %#x: %d\n",
-                       reg, ret);
+               dev_err(stmpe->dev, "failed to write reg %#x: %d\n", reg, ret);
 
        return ret;
 }
@@ -72,10 +72,9 @@ static int __stmpe_block_read(struct stmpe *stmpe, u8 reg, u8 length,
 {
        int ret;
 
-       ret = i2c_smbus_read_i2c_block_data(stmpe->i2c, reg, length, values);
+       ret = stmpe->ci->read_block(stmpe, reg, length, values);
        if (ret < 0)
-               dev_err(stmpe->dev, "failed to read regs %#x: %d\n",
-                       reg, ret);
+               dev_err(stmpe->dev, "failed to read regs %#x: %d\n", reg, ret);
 
        dev_vdbg(stmpe->dev, "rd: reg %#x (%d) => ret %#x\n", reg, length, ret);
        stmpe_dump_bytes("stmpe rd: ", values, length);
@@ -91,11 +90,9 @@ static int __stmpe_block_write(struct stmpe *stmpe, u8 reg, u8 length,
        dev_vdbg(stmpe->dev, "wr: regs %#x (%d)\n", reg, length);
        stmpe_dump_bytes("stmpe wr: ", values, length);
 
-       ret = i2c_smbus_write_i2c_block_data(stmpe->i2c, reg, length,
-                                            values);
+       ret = stmpe->ci->write_block(stmpe, reg, length, values);
        if (ret < 0)
-               dev_err(stmpe->dev, "failed to write regs %#x: %d\n",
-                       reg, ret);
+               dev_err(stmpe->dev, "failed to write regs %#x: %d\n", reg, ret);
 
        return ret;
 }
@@ -245,12 +242,14 @@ int stmpe_set_altfunc(struct stmpe *stmpe, u32 pins, enum stmpe_block block)
        u8 regaddr = stmpe->regs[STMPE_IDX_GPAFR_U_MSB];
        int af_bits = variant->af_bits;
        int numregs = DIV_ROUND_UP(stmpe->num_gpios * af_bits, 8);
-       int afperreg = 8 / af_bits;
        int mask = (1 << af_bits) - 1;
        u8 regs[numregs];
-       int af;
-       int ret;
+       int af, afperreg, ret;
+
+       if (!variant->get_altfunc)
+               return 0;
 
+       afperreg = 8 / af_bits;
        mutex_lock(&stmpe->lock);
 
        ret = __stmpe_enable(stmpe, STMPE_BLOCK_GPIO);
@@ -325,7 +324,51 @@ static struct mfd_cell stmpe_keypad_cell = {
 };
 
 /*
- * Touchscreen (STMPE811)
+ * STMPE801
+ */
+static const u8 stmpe801_regs[] = {
+       [STMPE_IDX_CHIP_ID]     = STMPE801_REG_CHIP_ID,
+       [STMPE_IDX_ICR_LSB]     = STMPE801_REG_SYS_CTRL,
+       [STMPE_IDX_GPMR_LSB]    = STMPE801_REG_GPIO_MP_STA,
+       [STMPE_IDX_GPSR_LSB]    = STMPE801_REG_GPIO_SET_PIN,
+       [STMPE_IDX_GPCR_LSB]    = STMPE801_REG_GPIO_SET_PIN,
+       [STMPE_IDX_GPDR_LSB]    = STMPE801_REG_GPIO_DIR,
+       [STMPE_IDX_IEGPIOR_LSB] = STMPE801_REG_GPIO_INT_EN,
+       [STMPE_IDX_ISGPIOR_MSB] = STMPE801_REG_GPIO_INT_STA,
+
+};
+
+static struct stmpe_variant_block stmpe801_blocks[] = {
+       {
+               .cell   = &stmpe_gpio_cell,
+               .irq    = 0,
+               .block  = STMPE_BLOCK_GPIO,
+       },
+};
+
+static int stmpe801_enable(struct stmpe *stmpe, unsigned int blocks,
+                          bool enable)
+{
+       if (blocks & STMPE_BLOCK_GPIO)
+               return 0;
+       else
+               return -EINVAL;
+}
+
+static struct stmpe_variant_info stmpe801 = {
+       .name           = "stmpe801",
+       .id_val         = STMPE801_ID,
+       .id_mask        = 0xffff,
+       .num_gpios      = 8,
+       .regs           = stmpe801_regs,
+       .blocks         = stmpe801_blocks,
+       .num_blocks     = ARRAY_SIZE(stmpe801_blocks),
+       .num_irqs       = STMPE801_NR_INTERNAL_IRQS,
+       .enable         = stmpe801_enable,
+};
+
+/*
+ * Touchscreen (STMPE811 or STMPE610)
  */
 
 static struct resource stmpe_ts_resources[] = {
@@ -350,7 +393,7 @@ static struct mfd_cell stmpe_ts_cell = {
 };
 
 /*
- * STMPE811
+ * STMPE811 or STMPE610
  */
 
 static const u8 stmpe811_regs[] = {
@@ -421,6 +464,21 @@ static struct stmpe_variant_info stmpe811 = {
        .get_altfunc    = stmpe811_get_altfunc,
 };
 
+/* Similar to 811, except number of gpios */
+static struct stmpe_variant_info stmpe610 = {
+       .name           = "stmpe610",
+       .id_val         = 0x0811,
+       .id_mask        = 0xffff,
+       .num_gpios      = 6,
+       .af_bits        = 1,
+       .regs           = stmpe811_regs,
+       .blocks         = stmpe811_blocks,
+       .num_blocks     = ARRAY_SIZE(stmpe811_blocks),
+       .num_irqs       = STMPE811_NR_INTERNAL_IRQS,
+       .enable         = stmpe811_enable,
+       .get_altfunc    = stmpe811_get_altfunc,
+};
+
 /*
  * STMPE1601
  */
@@ -655,6 +713,8 @@ static struct stmpe_variant_info stmpe2403 = {
 };
 
 static struct stmpe_variant_info *stmpe_variant_info[] = {
+       [STMPE610]      = &stmpe610,
+       [STMPE801]      = &stmpe801,
        [STMPE811]      = &stmpe811,
        [STMPE1601]     = &stmpe1601,
        [STMPE2401]     = &stmpe2401,
@@ -671,6 +731,11 @@ static irqreturn_t stmpe_irq(int irq, void *data)
        int ret;
        int i;
 
+       if (variant->id_val == STMPE801_ID) {
+               handle_nested_irq(stmpe->irq_base);
+               return IRQ_HANDLED;
+       }
+
        ret = stmpe_block_read(stmpe, israddr, num, isr);
        if (ret < 0)
                return IRQ_NONE;
@@ -757,14 +822,17 @@ static struct irq_chip stmpe_irq_chip = {
 
 static int __devinit stmpe_irq_init(struct stmpe *stmpe)
 {
+       struct irq_chip *chip = NULL;
        int num_irqs = stmpe->variant->num_irqs;
        int base = stmpe->irq_base;
        int irq;
 
+       if (stmpe->variant->id_val != STMPE801_ID)
+               chip = &stmpe_irq_chip;
+
        for (irq = base; irq < base + num_irqs; irq++) {
                irq_set_chip_data(irq, stmpe);
-               irq_set_chip_and_handler(irq, &stmpe_irq_chip,
-                                        handle_edge_irq);
+               irq_set_chip_and_handler(irq, chip, handle_edge_irq);
                irq_set_nested_thread(irq, 1);
 #ifdef CONFIG_ARM
                set_irq_flags(irq, IRQF_VALID);
@@ -796,7 +864,7 @@ static int __devinit stmpe_chip_init(struct stmpe *stmpe)
        unsigned int irq_trigger = stmpe->pdata->irq_trigger;
        int autosleep_timeout = stmpe->pdata->autosleep_timeout;
        struct stmpe_variant_info *variant = stmpe->variant;
-       u8 icr = STMPE_ICR_LSB_GIM;
+       u8 icr;
        unsigned int id;
        u8 data[2];
        int ret;
@@ -819,16 +887,32 @@ static int __devinit stmpe_chip_init(struct stmpe *stmpe)
        if (ret)
                return ret;
 
-       if (irq_trigger == IRQF_TRIGGER_FALLING ||
-           irq_trigger == IRQF_TRIGGER_RISING)
-               icr |= STMPE_ICR_LSB_EDGE;
+       if (id == STMPE801_ID)
+               icr = STMPE801_REG_SYS_CTRL_INT_EN;
+       else
+               icr = STMPE_ICR_LSB_GIM;
+
+       /* STMPE801 doesn't support Edge interrupts */
+       if (id != STMPE801_ID) {
+               if (irq_trigger == IRQF_TRIGGER_FALLING ||
+                               irq_trigger == IRQF_TRIGGER_RISING)
+                       icr |= STMPE_ICR_LSB_EDGE;
+       }
 
        if (irq_trigger == IRQF_TRIGGER_RISING ||
-           irq_trigger == IRQF_TRIGGER_HIGH)
-               icr |= STMPE_ICR_LSB_HIGH;
+                       irq_trigger == IRQF_TRIGGER_HIGH) {
+               if (id == STMPE801_ID)
+                       icr |= STMPE801_REG_SYS_CTRL_INT_HI;
+               else
+                       icr |= STMPE_ICR_LSB_HIGH;
+       }
 
-       if (stmpe->pdata->irq_invert_polarity)
-               icr ^= STMPE_ICR_LSB_HIGH;
+       if (stmpe->pdata->irq_invert_polarity) {
+               if (id == STMPE801_ID)
+                       icr ^= STMPE801_REG_SYS_CTRL_INT_HI;
+               else
+                       icr ^= STMPE_ICR_LSB_HIGH;
+       }
 
        if (stmpe->pdata->autosleep) {
                ret = stmpe_autosleep(stmpe, autosleep_timeout);
@@ -873,32 +957,10 @@ static int __devinit stmpe_devices_init(struct stmpe *stmpe)
        return ret;
 }
 
-#ifdef CONFIG_PM
-static int stmpe_suspend(struct device *dev)
-{
-       struct i2c_client *i2c = to_i2c_client(dev);
-
-       if (device_may_wakeup(&i2c->dev))
-               enable_irq_wake(i2c->irq);
-
-       return 0;
-}
-
-static int stmpe_resume(struct device *dev)
-{
-       struct i2c_client *i2c = to_i2c_client(dev);
-
-       if (device_may_wakeup(&i2c->dev))
-               disable_irq_wake(i2c->irq);
-
-       return 0;
-}
-#endif
-
-static int __devinit stmpe_probe(struct i2c_client *i2c,
-                                const struct i2c_device_id *id)
+/* Called from client specific probe routines */
+int __devinit stmpe_probe(struct stmpe_client_info *ci, int partnum)
 {
-       struct stmpe_platform_data *pdata = i2c->dev.platform_data;
+       struct stmpe_platform_data *pdata = dev_get_platdata(ci->dev);
        struct stmpe *stmpe;
        int ret;
 
@@ -912,30 +974,43 @@ static int __devinit stmpe_probe(struct i2c_client *i2c,
        mutex_init(&stmpe->irq_lock);
        mutex_init(&stmpe->lock);
 
-       stmpe->dev = &i2c->dev;
-       stmpe->i2c = i2c;
-
+       stmpe->dev = ci->dev;
+       stmpe->client = ci->client;
        stmpe->pdata = pdata;
        stmpe->irq_base = pdata->irq_base;
-
-       stmpe->partnum = id->driver_data;
-       stmpe->variant = stmpe_variant_info[stmpe->partnum];
+       stmpe->ci = ci;
+       stmpe->partnum = partnum;
+       stmpe->variant = stmpe_variant_info[partnum];
        stmpe->regs = stmpe->variant->regs;
        stmpe->num_gpios = stmpe->variant->num_gpios;
+       dev_set_drvdata(stmpe->dev, stmpe);
 
-       i2c_set_clientdata(i2c, stmpe);
+       if (ci->init)
+               ci->init(stmpe);
+
+       if (pdata->irq_over_gpio) {
+               ret = gpio_request_one(pdata->irq_gpio, GPIOF_DIR_IN, "stmpe");
+               if (ret) {
+                       dev_err(stmpe->dev, "failed to request IRQ GPIO: %d\n",
+                                       ret);
+                       goto out_free;
+               }
+
+               stmpe->irq = gpio_to_irq(pdata->irq_gpio);
+       } else {
+               stmpe->irq = ci->irq;
+       }
 
        ret = stmpe_chip_init(stmpe);
        if (ret)
-               goto out_free;
+               goto free_gpio;
 
        ret = stmpe_irq_init(stmpe);
        if (ret)
-               goto out_free;
+               goto free_gpio;
 
-       ret = request_threaded_irq(stmpe->i2c->irq, NULL, stmpe_irq,
-                                  pdata->irq_trigger | IRQF_ONESHOT,
-                                  "stmpe", stmpe);
+       ret = request_threaded_irq(stmpe->irq, NULL, stmpe_irq,
+                       pdata->irq_trigger | IRQF_ONESHOT, "stmpe", stmpe);
        if (ret) {
                dev_err(stmpe->dev, "failed to request IRQ: %d\n", ret);
                goto out_removeirq;
@@ -951,67 +1026,55 @@ static int __devinit stmpe_probe(struct i2c_client *i2c,
 
 out_removedevs:
        mfd_remove_devices(stmpe->dev);
-       free_irq(stmpe->i2c->irq, stmpe);
+       free_irq(stmpe->irq, stmpe);
 out_removeirq:
        stmpe_irq_remove(stmpe);
+free_gpio:
+       if (pdata->irq_over_gpio)
+               gpio_free(pdata->irq_gpio);
 out_free:
        kfree(stmpe);
        return ret;
 }
 
-static int __devexit stmpe_remove(struct i2c_client *client)
+int stmpe_remove(struct stmpe *stmpe)
 {
-       struct stmpe *stmpe = i2c_get_clientdata(client);
-
        mfd_remove_devices(stmpe->dev);
 
-       free_irq(stmpe->i2c->irq, stmpe);
+       free_irq(stmpe->irq, stmpe);
        stmpe_irq_remove(stmpe);
 
+       if (stmpe->pdata->irq_over_gpio)
+               gpio_free(stmpe->pdata->irq_gpio);
+
        kfree(stmpe);
 
        return 0;
 }
 
-static const struct i2c_device_id stmpe_id[] = {
-       { "stmpe811", STMPE811 },
-       { "stmpe1601", STMPE1601 },
-       { "stmpe2401", STMPE2401 },
-       { "stmpe2403", STMPE2403 },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, stmpe_id);
-
 #ifdef CONFIG_PM
-static const struct dev_pm_ops stmpe_dev_pm_ops = {
-       .suspend        = stmpe_suspend,
-       .resume         = stmpe_resume,
-};
-#endif
+static int stmpe_suspend(struct device *dev)
+{
+       struct stmpe *stmpe = dev_get_drvdata(dev);
 
-static struct i2c_driver stmpe_driver = {
-       .driver.name    = "stmpe",
-       .driver.owner   = THIS_MODULE,
-#ifdef CONFIG_PM
-       .driver.pm      = &stmpe_dev_pm_ops,
-#endif
-       .probe          = stmpe_probe,
-       .remove         = __devexit_p(stmpe_remove),
-       .id_table       = stmpe_id,
-};
+       if (device_may_wakeup(dev))
+               enable_irq_wake(stmpe->irq);
 
-static int __init stmpe_init(void)
-{
-       return i2c_add_driver(&stmpe_driver);
+       return 0;
 }
-subsys_initcall(stmpe_init);
 
-static void __exit stmpe_exit(void)
+static int stmpe_resume(struct device *dev)
 {
-       i2c_del_driver(&stmpe_driver);
+       struct stmpe *stmpe = dev_get_drvdata(dev);
+
+       if (device_may_wakeup(dev))
+               disable_irq_wake(stmpe->irq);
+
+       return 0;
 }
-module_exit(stmpe_exit);
 
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("STMPE MFD core driver");
-MODULE_AUTHOR("Rabin Vincent <rabin.vincent@stericsson.com>");
+const struct dev_pm_ops stmpe_dev_pm_ops = {
+       .suspend        = stmpe_suspend,
+       .resume         = stmpe_resume,
+};
+#endif
index e4ee38956583069fbf912ac78e59279dbd6615e2..7b8e13f5b764dc5fde59a8dcb8389820abe017cd 100644 (file)
@@ -8,6 +8,14 @@
 #ifndef __STMPE_H
 #define __STMPE_H
 
+#include <linux/device.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/stmpe.h>
+#include <linux/printk.h>
+#include <linux/types.h>
+
+extern const struct dev_pm_ops stmpe_dev_pm_ops;
+
 #ifdef STMPE_DUMP_BYTES
 static inline void stmpe_dump_bytes(const char *str, const void *buf,
                                    size_t len)
@@ -67,10 +75,54 @@ struct stmpe_variant_info {
        int (*enable_autosleep)(struct stmpe *stmpe, int autosleep_timeout);
 };
 
+/**
+ * struct stmpe_client_info - i2c or spi specific routines/info
+ * @data: client specific data
+ * @read_byte: read single byte
+ * @write_byte: write single byte
+ * @read_block: read block or multiple bytes
+ * @write_block: write block or multiple bytes
+ * @init: client init routine, called during probe
+ */
+struct stmpe_client_info {
+       void *data;
+       int irq;
+       void *client;
+       struct device *dev;
+       int (*read_byte)(struct stmpe *stmpe, u8 reg);
+       int (*write_byte)(struct stmpe *stmpe, u8 reg, u8 val);
+       int (*read_block)(struct stmpe *stmpe, u8 reg, u8 len, u8 *values);
+       int (*write_block)(struct stmpe *stmpe, u8 reg, u8 len,
+                       const u8 *values);
+       void (*init)(struct stmpe *stmpe);
+};
+
+int stmpe_probe(struct stmpe_client_info *ci, int partnum);
+int stmpe_remove(struct stmpe *stmpe);
+
 #define STMPE_ICR_LSB_HIGH     (1 << 2)
 #define STMPE_ICR_LSB_EDGE     (1 << 1)
 #define STMPE_ICR_LSB_GIM      (1 << 0)
 
+/*
+ * STMPE801
+ */
+#define STMPE801_ID                    0x0108
+#define STMPE801_NR_INTERNAL_IRQS      1
+
+#define STMPE801_REG_CHIP_ID           0x00
+#define STMPE801_REG_VERSION_ID                0x02
+#define STMPE801_REG_SYS_CTRL          0x04
+#define STMPE801_REG_GPIO_INT_EN       0x08
+#define STMPE801_REG_GPIO_INT_STA      0x09
+#define STMPE801_REG_GPIO_MP_STA       0x10
+#define STMPE801_REG_GPIO_SET_PIN      0x11
+#define STMPE801_REG_GPIO_DIR          0x12
+
+#define STMPE801_REG_SYS_CTRL_RESET    (1 << 7)
+#define STMPE801_REG_SYS_CTRL_INT_EN   (1 << 2)
+#define STMPE801_REG_SYS_CTRL_INT_HI   (1 << 0)
+
 /*
  * STMPE811
  */
@@ -87,6 +139,7 @@ struct stmpe_variant_info {
 
 #define STMPE811_REG_CHIP_ID           0x00
 #define STMPE811_REG_SYS_CTRL2         0x04
+#define STMPE811_REG_SPI_CFG           0x08
 #define STMPE811_REG_INT_CTRL          0x09
 #define STMPE811_REG_INT_EN            0x0A
 #define STMPE811_REG_INT_STA           0x0B
index 91ad21ef7721cddd335e5470717f3f878191bf0d..2d9e8799e733c6644c18aa9335e9796ef0c0709f 100644 (file)
@@ -442,21 +442,7 @@ static struct platform_driver t7l66xb_platform_driver = {
 
 /*--------------------------------------------------------------------------*/
 
-static int __init t7l66xb_init(void)
-{
-       int retval = 0;
-
-       retval = platform_driver_register(&t7l66xb_platform_driver);
-       return retval;
-}
-
-static void __exit t7l66xb_exit(void)
-{
-       platform_driver_unregister(&t7l66xb_platform_driver);
-}
-
-module_init(t7l66xb_init);
-module_exit(t7l66xb_exit);
+module_platform_driver(t7l66xb_platform_driver);
 
 MODULE_DESCRIPTION("Toshiba T7L66XB core driver");
 MODULE_LICENSE("GPL v2");
index 71bc835324d8743d55ff95383b03027652645f7b..d20a284ad4baca528c36eb351200ff16fdd5282e 100644 (file)
@@ -234,19 +234,7 @@ static struct platform_driver tc6387xb_platform_driver = {
        .resume         = tc6387xb_resume,
 };
 
-
-static int __init tc6387xb_init(void)
-{
-       return platform_driver_register(&tc6387xb_platform_driver);
-}
-
-static void __exit tc6387xb_exit(void)
-{
-       platform_driver_unregister(&tc6387xb_platform_driver);
-}
-
-module_init(tc6387xb_init);
-module_exit(tc6387xb_exit);
+module_platform_driver(tc6387xb_platform_driver);
 
 MODULE_DESCRIPTION("Toshiba TC6387XB core driver");
 MODULE_LICENSE("GPL v2");
index af9ab0e5ca64ac9f9f9f3fe1faed06fb6b13a0fb..4fb0e6c8e8fe0fbfee94299b7c0de45920621a25 100644 (file)
@@ -458,17 +458,7 @@ static struct platform_driver ti_ssp_driver = {
        }
 };
 
-static int __init ti_ssp_init(void)
-{
-       return platform_driver_register(&ti_ssp_driver);
-}
-module_init(ti_ssp_init);
-
-static void __exit ti_ssp_exit(void)
-{
-       platform_driver_unregister(&ti_ssp_driver);
-}
-module_exit(ti_ssp_exit);
+module_platform_driver(ti_ssp_driver);
 
 MODULE_DESCRIPTION("Sequencer Serial Port (SSP) Driver");
 MODULE_AUTHOR("Cyril Chemparathy");
index 02d65692ceb415a1ae95a863dd660adda46807c9..0ba26fb12cf532f948fefa1e4df6599ce7db347b 100644 (file)
@@ -857,7 +857,7 @@ static void __devexit timb_remove(struct pci_dev *dev)
        kfree(priv);
 }
 
-static struct pci_device_id timberdale_pci_tbl[] = {
+static DEFINE_PCI_DEVICE_TABLE(timberdale_pci_tbl) = {
        { PCI_DEVICE(PCI_VENDOR_ID_TIMB, PCI_DEVICE_ID_TIMB) },
        { 0 }
 };
index a56be931551c9ac7ee34e546a18891e8a2b6549d..95c0d7978bec4a67cfaa74d8c93b430dc715974b 100644 (file)
@@ -215,6 +215,7 @@ int tps65910_irq_init(struct tps65910 *tps65910, int irq,
 
 int tps65910_irq_exit(struct tps65910 *tps65910)
 {
-       free_irq(tps65910->chip_irq, tps65910);
+       if (tps65910->chip_irq)
+               free_irq(tps65910->chip_irq, tps65910);
        return 0;
 }
index c1da84bc1573f563c4b698f0bc786068e7f911bc..01cf5012a08fb26c3b561fde99341d6ab84749e2 100644 (file)
@@ -172,15 +172,12 @@ static int tps65910_i2c_probe(struct i2c_client *i2c,
 
        tps65910_gpio_init(tps65910, pmic_plat_data->gpio_base);
 
-       ret = tps65910_irq_init(tps65910, init_data->irq, init_data);
-       if (ret < 0)
-               goto err;
+       tps65910_irq_init(tps65910, init_data->irq, init_data);
 
        kfree(init_data);
        return ret;
 
 err:
-       mfd_remove_devices(tps65910->dev);
        kfree(tps65910);
        kfree(init_data);
        return ret;
@@ -190,8 +187,8 @@ static int tps65910_i2c_remove(struct i2c_client *i2c)
 {
        struct tps65910 *tps65910 = i2c_get_clientdata(i2c);
 
-       mfd_remove_devices(tps65910->dev);
        tps65910_irq_exit(tps65910);
+       mfd_remove_devices(tps65910->dev);
        kfree(tps65910);
 
        return 0;
index 6d71e0d2574498abb3c5d22c4b91cb24fefe4c5b..27d3302d56b8f2b13bb8ffad371c33d67d3e529c 100644 (file)
@@ -111,7 +111,6 @@ static int __devexit tps65912_spi_remove(struct spi_device *spi)
 static struct spi_driver tps65912_spi_driver = {
        .driver = {
                .name = "tps65912",
-               .bus = &spi_bus_type,
                .owner = THIS_MODULE,
        },
        .probe  = tps65912_spi_probe,
index 61e70cfaa774fb977adcba8cc697a06edd673210..e04e04ddc15e41f933b2a58de5f3bc8d28e86c25 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/clk.h>
 #include <linux/err.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/irqdomain.h>
 
 #include <linux/regulator/machine.h>
 
 
 #define TWL_MODULE_LAST TWL4030_MODULE_LAST
 
+#define TWL4030_NR_IRQS    8
+#define TWL6030_NR_IRQS    20
+
 /* Base Address defns for twl4030_map[] */
 
 /* subchip/slave 0 - USB ID */
@@ -255,6 +263,7 @@ struct twl_client {
 
 static struct twl_client twl_modules[TWL_NUM_SLAVES];
 
+static struct irq_domain domain;
 
 /* mapping the module id to slave id and base address */
 struct twl_mapping {
@@ -1183,14 +1192,48 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
        int                             status;
        unsigned                        i;
        struct twl4030_platform_data    *pdata = client->dev.platform_data;
+       struct device_node              *node = client->dev.of_node;
        u8 temp;
        int ret = 0;
+       int nr_irqs = TWL4030_NR_IRQS;
+
+       if ((id->driver_data) & TWL6030_CLASS)
+               nr_irqs = TWL6030_NR_IRQS;
+
+       if (node && !pdata) {
+               /*
+                * XXX: Temporary pdata until the information is correctly
+                * retrieved by every TWL modules from DT.
+                */
+               pdata = devm_kzalloc(&client->dev,
+                                    sizeof(struct twl4030_platform_data),
+                                    GFP_KERNEL);
+               if (!pdata)
+                       return -ENOMEM;
+       }
 
        if (!pdata) {
                dev_dbg(&client->dev, "no platform data?\n");
                return -EINVAL;
        }
 
+       status = irq_alloc_descs(-1, pdata->irq_base, nr_irqs, 0);
+       if (IS_ERR_VALUE(status)) {
+               dev_err(&client->dev, "Fail to allocate IRQ descs\n");
+               return status;
+       }
+
+       pdata->irq_base = status;
+       pdata->irq_end = pdata->irq_base + nr_irqs;
+
+       domain.irq_base = pdata->irq_base;
+       domain.nr_irq = nr_irqs;
+#ifdef CONFIG_OF_IRQ
+       domain.of_node = of_node_get(node);
+       domain.ops = &irq_domain_simple_ops;
+#endif
+       irq_domain_add(&domain);
+
        if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) {
                dev_dbg(&client->dev, "can't talk I2C?\n");
                return -EIO;
@@ -1270,7 +1313,13 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
                twl_i2c_write_u8(TWL4030_MODULE_INTBR, temp, REG_GPPUPDCTR1);
        }
 
-       status = add_children(pdata, id->driver_data);
+#ifdef CONFIG_OF_DEVICE
+       if (node)
+               status = of_platform_populate(node, NULL, NULL, &client->dev);
+       else
+#endif
+               status = add_children(pdata, id->driver_data);
+
 fail:
        if (status < 0)
                twl_remove(client);
index ae51ab5d0e5d935f9be35c0465b8cd20f07fdb47..838ce4eb444e24ce44bd3b120fe21a9f977d6e75 100644 (file)
@@ -261,17 +261,7 @@ static struct platform_driver twl4030_audio_driver = {
        },
 };
 
-static int __devinit twl4030_audio_init(void)
-{
-       return platform_driver_register(&twl4030_audio_driver);
-}
-module_init(twl4030_audio_init);
-
-static void __devexit twl4030_audio_exit(void)
-{
-       platform_driver_unregister(&twl4030_audio_driver);
-}
-module_exit(twl4030_audio_exit);
+module_platform_driver(twl4030_audio_driver);
 
 MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
 MODULE_LICENSE("GPL");
index 29f11e0765feef54093b839b6e288ae56bc0bf3f..b69bb517b102a4595b76342afeb58e0c9b3270c2 100644 (file)
@@ -492,7 +492,7 @@ static void twl4030_sih_bus_sync_unlock(struct irq_data *data)
                        u8      bytes[4];
                } imr;
 
-               /* byte[0] gets overwriten as we write ... */
+               /* byte[0] gets overwritten as we write ... */
                imr.word = cpu_to_le32(agent->imr << 8);
                agent->imr_change_pending = false;
 
@@ -667,6 +667,7 @@ int twl4030_sih_setup(int module)
                irq_set_chip_data(irq, agent);
                irq_set_chip_and_handler(irq, &twl4030_sih_irq_chip,
                                         handle_edge_irq);
+               irq_set_nested_thread(irq, 1);
                activate_irq(irq);
        }
 
index 834f824d3c11075b18098a5c0b64a1abf066cd69..456ecb5ac4fe52e1a5515f1793eda270c5407056 100644 (file)
@@ -807,19 +807,7 @@ static struct platform_driver twl4030_madc_driver = {
                   },
 };
 
-static int __init twl4030_madc_init(void)
-{
-       return platform_driver_register(&twl4030_madc_driver);
-}
-
-module_init(twl4030_madc_init);
-
-static void __exit twl4030_madc_exit(void)
-{
-       platform_driver_unregister(&twl4030_madc_driver);
-}
-
-module_exit(twl4030_madc_exit);
+module_platform_driver(twl4030_madc_driver);
 
 MODULE_DESCRIPTION("TWL4030 ADC driver");
 MODULE_LICENSE("GPL");
index a764676f09220146fb0ebc84addfe5e79ad9943e..d905f5171153e7f5ee3d8a808c493255883dd704 100644 (file)
@@ -34,7 +34,8 @@
 static u8 twl4030_start_script_address = 0x2b;
 
 #define PWR_P1_SW_EVENTS       0x10
-#define PWR_DEVOFF     (1<<0)
+#define PWR_DEVOFF             (1 << 0)
+#define SEQ_OFFSYNC            (1 << 0)
 
 #define PHY_TO_OFF_PM_MASTER(p)                (p - 0x36)
 #define PHY_TO_OFF_PM_RECEIVER(p)      (p - 0x5b)
@@ -511,12 +512,27 @@ int twl4030_remove_script(u8 flags)
        return err;
 }
 
+/*
+ * In master mode, start the power off sequence.
+ * After a successful execution, TWL shuts down the power to the SoC
+ * and all peripherals connected to it.
+ */
+void twl4030_power_off(void)
+{
+       int err;
+
+       err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, PWR_DEVOFF,
+                              TWL4030_PM_MASTER_P1_SW_EVENTS);
+       if (err)
+               pr_err("TWL4030 Unable to power off\n");
+}
+
 void __init twl4030_power_init(struct twl4030_power_data *twl4030_scripts)
 {
        int err = 0;
        int i;
        struct twl4030_resconfig *resconfig;
-       u8 address = twl4030_start_script_address;
+       u8 val, address = twl4030_start_script_address;
 
        err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
                        TWL4030_PM_MASTER_KEY_CFG1,
@@ -548,6 +564,28 @@ void __init twl4030_power_init(struct twl4030_power_data *twl4030_scripts)
                }
        }
 
+       /* Board has to be wired properly to use this feature */
+       if (twl4030_scripts->use_poweroff && !pm_power_off) {
+               /* Default for SEQ_OFFSYNC is set, lets ensure this */
+               err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &val,
+                                     TWL4030_PM_MASTER_CFG_P123_TRANSITION);
+               if (err) {
+                       pr_warning("TWL4030 Unable to read registers\n");
+
+               } else if (!(val & SEQ_OFFSYNC)) {
+                       val |= SEQ_OFFSYNC;
+                       err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, val,
+                                       TWL4030_PM_MASTER_CFG_P123_TRANSITION);
+                       if (err) {
+                               pr_err("TWL4030 Unable to setup SEQ_OFFSYNC\n");
+                               goto relock;
+                       }
+               }
+
+               pm_power_off = twl4030_power_off;
+       }
+
+relock:
        err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0,
                        TWL4030_PM_MASTER_PROTECT_KEY);
        if (err)
index 268f80fd04394e2e7abca10d065d0be20625d37e..dda86293dc9fc0a3f8baaf678591d764824cfe14 100644 (file)
@@ -509,13 +509,10 @@ static int __devinit twl6040_probe(struct platform_device *pdev)
                twl6040->audpwron = -EINVAL;
 
        if (gpio_is_valid(twl6040->audpwron)) {
-               ret = gpio_request(twl6040->audpwron, "audpwron");
+               ret = gpio_request_one(twl6040->audpwron, GPIOF_OUT_INIT_LOW,
+                                      "audpwron");
                if (ret)
                        goto gpio1_err;
-
-               ret = gpio_direction_output(twl6040->audpwron, 0);
-               if (ret)
-                       goto gpio2_err;
        }
 
        /* codec interrupt */
@@ -619,18 +616,7 @@ static struct platform_driver twl6040_driver = {
        },
 };
 
-static int __devinit twl6040_init(void)
-{
-       return platform_driver_register(&twl6040_driver);
-}
-module_init(twl6040_init);
-
-static void __devexit twl6040_exit(void)
-{
-       platform_driver_unregister(&twl6040_driver);
-}
-
-module_exit(twl6040_exit);
+module_platform_driver(twl6040_driver);
 
 MODULE_DESCRIPTION("TWL6040 MFD");
 MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>");
index b281217334eb3c68d35c7695b8104dd327e6bcf6..91c4f25e0e558fe20a04ae10e1554d98f0f3fff9 100644 (file)
@@ -36,6 +36,15 @@ static DEFINE_MUTEX(ucb1x00_mutex);
 static LIST_HEAD(ucb1x00_drivers);
 static LIST_HEAD(ucb1x00_devices);
 
+static struct mcp_device_id ucb1x00_id[] = {
+       { "ucb1x00", 0 },  /* auto-detection */
+       { "ucb1200", UCB_ID_1200 },
+       { "ucb1300", UCB_ID_1300 },
+       { "tc35143", UCB_ID_TC35143 },
+       { }
+};
+MODULE_DEVICE_TABLE(mcp, ucb1x00_id);
+
 /**
  *     ucb1x00_io_set_dir - set IO direction
  *     @ucb: UCB1x00 structure describing chip
@@ -527,17 +536,33 @@ static struct class ucb1x00_class = {
 
 static int ucb1x00_probe(struct mcp *mcp)
 {
+       const struct mcp_device_id *mid;
        struct ucb1x00 *ucb;
        struct ucb1x00_driver *drv;
+       struct ucb1x00_plat_data *pdata;
        unsigned int id;
        int ret = -ENODEV;
        int temp;
 
        mcp_enable(mcp);
        id = mcp_reg_read(mcp, UCB_ID);
+       mid = mcp_get_device_id(mcp);
 
-       if (id != UCB_ID_1200 && id != UCB_ID_1300 && id != UCB_ID_TC35143) {
-               printk(KERN_WARNING "UCB1x00 ID not found: %04x\n", id);
+       if (mid && mid->driver_data) {
+               if (id != mid->driver_data) {
+                       printk(KERN_WARNING "%s wrong ID %04x found: %04x\n",
+                               mid->name, (unsigned int) mid->driver_data, id);
+                       goto err_disable;
+               }
+       } else {
+               mid = &ucb1x00_id[1];
+               while (mid->driver_data) {
+                       if (id == mid->driver_data)
+                               break;
+                       mid++;
+               }
+               printk(KERN_WARNING "%s ID not found: %04x\n",
+                       ucb1x00_id[0].name, id);
                goto err_disable;
        }
 
@@ -546,28 +571,28 @@ static int ucb1x00_probe(struct mcp *mcp)
        if (!ucb)
                goto err_disable;
 
-
+       pdata = mcp->attached_device.platform_data;
        ucb->dev.class = &ucb1x00_class;
        ucb->dev.parent = &mcp->attached_device;
-       dev_set_name(&ucb->dev, "ucb1x00");
+       dev_set_name(&ucb->dev, mid->name);
 
        spin_lock_init(&ucb->lock);
        spin_lock_init(&ucb->io_lock);
        sema_init(&ucb->adc_sem, 1);
 
-       ucb->id  = id;
+       ucb->id  = mid;
        ucb->mcp = mcp;
        ucb->irq = ucb1x00_detect_irq(ucb);
        if (ucb->irq == NO_IRQ) {
-               printk(KERN_ERR "UCB1x00: IRQ probe failed\n");
+               printk(KERN_ERR "%s: IRQ probe failed\n", mid->name);
                ret = -ENODEV;
                goto err_free;
        }
 
        ucb->gpio.base = -1;
-       if (mcp->gpio_base != 0) {
+       if (pdata && (pdata->gpio_base >= 0)) {
                ucb->gpio.label = dev_name(&ucb->dev);
-               ucb->gpio.base = mcp->gpio_base;
+               ucb->gpio.base = pdata->gpio_base;
                ucb->gpio.ngpio = 10;
                ucb->gpio.set = ucb1x00_gpio_set;
                ucb->gpio.get = ucb1x00_gpio_get;
@@ -580,10 +605,10 @@ static int ucb1x00_probe(struct mcp *mcp)
                dev_info(&ucb->dev, "gpio_base not set so no gpiolib support");
 
        ret = request_irq(ucb->irq, ucb1x00_irq, IRQF_TRIGGER_RISING,
-                         "UCB1x00", ucb);
+                         mid->name, ucb);
        if (ret) {
-               printk(KERN_ERR "ucb1x00: unable to grab irq%d: %d\n",
-                       ucb->irq, ret);
+               printk(KERN_ERR "%s: unable to grab irq%d: %d\n",
+                       mid->name, ucb->irq, ret);
                goto err_gpio;
        }
 
@@ -705,6 +730,7 @@ static struct mcp_driver ucb1x00_driver = {
        .remove         = ucb1x00_remove,
        .suspend        = ucb1x00_suspend,
        .resume         = ucb1x00_resume,
+       .id_table       = ucb1x00_id,
 };
 
 static int __init ucb1x00_init(void)
index 38ffbd50a0d27bc24cbc9826c5dae1e95a54b560..40ec3c118868d7f02e104d2ed10e2f766840a0f4 100644 (file)
@@ -382,7 +382,7 @@ static int ucb1x00_ts_add(struct ucb1x00_dev *dev)
        ts->adcsync = adcsync ? UCB_SYNC : UCB_NOSYNC;
 
        idev->name       = "Touchscreen panel";
-       idev->id.product = ts->ucb->id;
+       idev->id.product = ts->ucb->id->driver_data;
        idev->open       = ucb1x00_ts_open;
        idev->close      = ucb1x00_ts_close;
 
index d698703dbd462c73b7267d9cd1a37a55bdd99d60..b73cc15e00818d4e15f2f44f9fe0a9f12e6196a6 100644 (file)
@@ -118,7 +118,7 @@ static void __devexit vx855_remove(struct pci_dev *pdev)
        pci_disable_device(pdev);
 }
 
-static struct pci_device_id vx855_pci_tbl[] = {
+static DEFINE_PCI_DEVICE_TABLE(vx855_pci_tbl) = {
        { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX855) },
        { 0, }
 };
index 0a2b8d41a7020229223c7dcf5876d2d9cc532b43..f5e54fae8ada924bb029a1afe9a0239f3eb959b3 100644 (file)
@@ -559,6 +559,8 @@ static int wm831x_write(struct wm831x *wm831x, unsigned short reg,
                dev_vdbg(wm831x->dev, "Write %04x to R%d(0x%x)\n",
                         buf[i], reg + i, reg + i);
                ret = regmap_write(wm831x->regmap, reg + i, buf[i]);
+               if (ret != 0)
+                       return ret;
        }
 
        return 0;
@@ -1875,7 +1877,6 @@ err_irq:
 err_regmap:
        mfd_remove_devices(wm831x->dev);
        regmap_exit(wm831x->regmap);
-       kfree(wm831x);
        return ret;
 }
 
@@ -1887,7 +1888,6 @@ void wm831x_device_exit(struct wm831x *wm831x)
                free_irq(wm831x->irq_base + WM831X_IRQ_AUXADC_DATA, wm831x);
        wm831x_irq_exit(wm831x);
        regmap_exit(wm831x->regmap);
-       kfree(wm831x);
 }
 
 int wm831x_device_suspend(struct wm831x *wm831x)
index ac8da1d439daab72b9118ae6cf8675f3bac6dc9a..cb15609b0a4871ff7999498440703901fb511076 100644 (file)
@@ -30,7 +30,7 @@ static int wm831x_i2c_probe(struct i2c_client *i2c,
        struct wm831x *wm831x;
        int ret;
 
-       wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL);
+       wm831x = devm_kzalloc(&i2c->dev, sizeof(struct wm831x), GFP_KERNEL);
        if (wm831x == NULL)
                return -ENOMEM;
 
@@ -42,7 +42,6 @@ static int wm831x_i2c_probe(struct i2c_client *i2c,
                ret = PTR_ERR(wm831x->regmap);
                dev_err(wm831x->dev, "Failed to allocate register map: %d\n",
                        ret);
-               kfree(wm831x);
                return ret;
        }
 
index f4747a4a9a93fb8d3c906d5060442117860ddaf7..bec4d053916012a696442fff39b847be647b9db0 100644 (file)
@@ -325,11 +325,6 @@ static inline int irq_data_to_status_reg(struct wm831x_irq_data *irq_data)
        return WM831X_INTERRUPT_STATUS_1 - 1 + irq_data->reg;
 }
 
-static inline int irq_data_to_mask_reg(struct wm831x_irq_data *irq_data)
-{
-       return WM831X_INTERRUPT_STATUS_1_MASK - 1 + irq_data->reg;
-}
-
 static inline struct wm831x_irq_data *irq_to_wm831x_irq(struct wm831x *wm831x,
                                                        int irq)
 {
@@ -477,8 +472,7 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data)
                handle_nested_irq(wm831x->irq_base + WM831X_IRQ_TCHPD);
        if (primary & WM831X_TCHDATA_INT)
                handle_nested_irq(wm831x->irq_base + WM831X_IRQ_TCHDATA);
-       if (primary & (WM831X_TCHDATA_EINT | WM831X_TCHPD_EINT))
-               goto out;
+       primary &= ~(WM831X_TCHDATA_EINT | WM831X_TCHPD_EINT);
 
        for (i = 0; i < ARRAY_SIZE(wm831x_irqs); i++) {
                int offset = wm831x_irqs[i].reg - 1;
index 8d6a9a969dbc246e87d46eb8bf38e0b62b796149..62ef3254105ffdebef5dcdcfa476a038ca14f997 100644 (file)
@@ -30,7 +30,7 @@ static int __devinit wm831x_spi_probe(struct spi_device *spi)
 
        type = (enum wm831x_parent)id->driver_data;
 
-       wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL);
+       wm831x = devm_kzalloc(&spi->dev, sizeof(struct wm831x), GFP_KERNEL);
        if (wm831x == NULL)
                return -ENOMEM;
 
@@ -45,7 +45,6 @@ static int __devinit wm831x_spi_probe(struct spi_device *spi)
                ret = PTR_ERR(wm831x->regmap);
                dev_err(wm831x->dev, "Failed to allocate register map: %d\n",
                        ret);
-               kfree(wm831x);
                return ret;
        }
 
@@ -95,7 +94,6 @@ MODULE_DEVICE_TABLE(spi, wm831x_spi_id);
 static struct spi_driver wm831x_spi_driver = {
        .driver = {
                .name   = "wm831x",
-               .bus    = &spi_bus_type,
                .owner  = THIS_MODULE,
                .pm     = &wm831x_spi_pm,
        },
index e81cc31e42020a8ceff6620d73b3f251ca6aeffd..dd1caaac55e4d4138ccc69b19997793418a76e7d 100644 (file)
@@ -573,6 +573,8 @@ int wm8350_device_init(struct wm8350 *wm8350, int irq,
        u16 id1, id2, mask_rev;
        u16 cust_id, mode, chip_rev;
 
+       dev_set_drvdata(wm8350->dev, wm8350);
+
        /* get WM8350 revision and config mode */
        ret = wm8350->read_dev(wm8350, WM8350_RESET_ID, sizeof(id1), &id1);
        if (ret != 0) {
index 5fe5de166adb39bdafb2e7714ce046f58b6ccb3b..d955faaf27c4a8f824301ae5e3e34998f4333bf0 100644 (file)
@@ -63,7 +63,7 @@ static int wm8350_i2c_probe(struct i2c_client *i2c,
        struct wm8350 *wm8350;
        int ret = 0;
 
-       wm8350 = kzalloc(sizeof(struct wm8350), GFP_KERNEL);
+       wm8350 = devm_kzalloc(&i2c->dev, sizeof(struct wm8350), GFP_KERNEL);
        if (wm8350 == NULL)
                return -ENOMEM;
 
@@ -80,7 +80,6 @@ static int wm8350_i2c_probe(struct i2c_client *i2c,
        return ret;
 
 err:
-       kfree(wm8350);
        return ret;
 }
 
@@ -89,7 +88,6 @@ static int wm8350_i2c_remove(struct i2c_client *i2c)
        struct wm8350 *wm8350 = i2c_get_clientdata(i2c);
 
        wm8350_device_exit(wm8350);
-       kfree(wm8350);
 
        return 0;
 }
index 62b4626f4561029102ece804a7de96708186b2cb..2204893444a6ca6a4f2a245023169e1ff9e69379 100644 (file)
@@ -344,7 +344,7 @@ static int wm8400_i2c_probe(struct i2c_client *i2c,
        struct wm8400 *wm8400;
        int ret;
 
-       wm8400 = kzalloc(sizeof(struct wm8400), GFP_KERNEL);
+       wm8400 = devm_kzalloc(&i2c->dev, sizeof(struct wm8400), GFP_KERNEL);
        if (wm8400 == NULL) {
                ret = -ENOMEM;
                goto err;
@@ -353,7 +353,7 @@ static int wm8400_i2c_probe(struct i2c_client *i2c,
        wm8400->regmap = regmap_init_i2c(i2c, &wm8400_regmap_config);
        if (IS_ERR(wm8400->regmap)) {
                ret = PTR_ERR(wm8400->regmap);
-               goto struct_err;
+               goto err;
        }
 
        wm8400->dev = &i2c->dev;
@@ -367,8 +367,6 @@ static int wm8400_i2c_probe(struct i2c_client *i2c,
 
 map_err:
        regmap_exit(wm8400->regmap);
-struct_err:
-       kfree(wm8400);
 err:
        return ret;
 }
@@ -379,7 +377,6 @@ static int wm8400_i2c_remove(struct i2c_client *i2c)
 
        wm8400_release(wm8400);
        regmap_exit(wm8400->regmap);
-       kfree(wm8400);
 
        return 0;
 }
index 5664696f2d3a8512c6ef96aca461dde22e5d486f..6a1a092db1461f3413defc6d11ca74ce68b9d282 100644 (file)
@@ -500,6 +500,14 @@ config USB_SWITCH_FSA9480
          stereo and mono audio, video, microphone and UART data to use
          a common connector port.
 
+config MAX8997_MUIC
+       tristate "MAX8997 MUIC Support"
+       depends on MFD_MAX8997
+       help
+         If you say yes here you get support for the MUIC device of
+         Maxim MAX8997 PMIC.
+         The MAX8997 MUIC is a USB port accessory detector and switch.
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
index b26495a0255494d7c1ee03f987d4a12ac39b8196..3e1d80106f04e36fad4b82806b8939afcfae50cc 100644 (file)
@@ -48,3 +48,4 @@ obj-y                         += lis3lv02d/
 obj-y                          += carma/
 obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
 obj-$(CONFIG_ALTERA_STAPL)     +=altera-stapl/
+obj-$(CONFIG_MAX8997_MUIC)     += max8997-muic.o
index 2208a9d526222a9ebb30a0f2538553029e6f4e19..d7a9aa14e5d5aafd8c0efc907b127147a202d197 100644 (file)
@@ -8,8 +8,8 @@
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/pwm.h>
-#include <linux/mfd/ab8500.h>
 #include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500.h>
 #include <linux/module.h>
 
 /*
diff --git a/drivers/misc/max8997-muic.c b/drivers/misc/max8997-muic.c
new file mode 100644 (file)
index 0000000..d74ef41
--- /dev/null
@@ -0,0 +1,505 @@
+/*
+ * max8997-muic.c - MAX8997 muic driver for the Maxim 8997
+ *
+ *  Copyright (C) 2011 Samsung Electrnoics
+ *  Donggeun Kim <dg77.kim@samsung.com>
+ *
+ * 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 of the License, 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/kobject.h>
+#include <linux/mfd/max8997.h>
+#include <linux/mfd/max8997-private.h>
+
+/* MAX8997-MUIC STATUS1 register */
+#define STATUS1_ADC_SHIFT              0
+#define STATUS1_ADCLOW_SHIFT           5
+#define STATUS1_ADCERR_SHIFT           6
+#define STATUS1_ADC_MASK               (0x1f << STATUS1_ADC_SHIFT)
+#define STATUS1_ADCLOW_MASK            (0x1 << STATUS1_ADCLOW_SHIFT)
+#define STATUS1_ADCERR_MASK            (0x1 << STATUS1_ADCERR_SHIFT)
+
+/* MAX8997-MUIC STATUS2 register */
+#define STATUS2_CHGTYP_SHIFT           0
+#define STATUS2_CHGDETRUN_SHIFT                3
+#define STATUS2_DCDTMR_SHIFT           4
+#define STATUS2_DBCHG_SHIFT            5
+#define STATUS2_VBVOLT_SHIFT           6
+#define STATUS2_CHGTYP_MASK            (0x7 << STATUS2_CHGTYP_SHIFT)
+#define STATUS2_CHGDETRUN_MASK         (0x1 << STATUS2_CHGDETRUN_SHIFT)
+#define STATUS2_DCDTMR_MASK            (0x1 << STATUS2_DCDTMR_SHIFT)
+#define STATUS2_DBCHG_MASK             (0x1 << STATUS2_DBCHG_SHIFT)
+#define STATUS2_VBVOLT_MASK            (0x1 << STATUS2_VBVOLT_SHIFT)
+
+/* MAX8997-MUIC STATUS3 register */
+#define STATUS3_OVP_SHIFT              2
+#define STATUS3_OVP_MASK               (0x1 << STATUS3_OVP_SHIFT)
+
+/* MAX8997-MUIC CONTROL1 register */
+#define COMN1SW_SHIFT                  0
+#define COMP2SW_SHIFT                  3
+#define COMN1SW_MASK                   (0x7 << COMN1SW_SHIFT)
+#define COMP2SW_MASK                   (0x7 << COMP2SW_SHIFT)
+#define SW_MASK                                (COMP2SW_MASK | COMN1SW_MASK)
+
+#define MAX8997_SW_USB         ((1 << COMP2SW_SHIFT) | (1 << COMN1SW_SHIFT))
+#define MAX8997_SW_AUDIO       ((2 << COMP2SW_SHIFT) | (2 << COMN1SW_SHIFT))
+#define MAX8997_SW_UART                ((3 << COMP2SW_SHIFT) | (3 << COMN1SW_SHIFT))
+#define MAX8997_SW_OPEN                ((0 << COMP2SW_SHIFT) | (0 << COMN1SW_SHIFT))
+
+#define        MAX8997_ADC_GROUND              0x00
+#define        MAX8997_ADC_MHL                 0x01
+#define        MAX8997_ADC_JIG_USB_1           0x18
+#define        MAX8997_ADC_JIG_USB_2           0x19
+#define        MAX8997_ADC_DESKDOCK            0x1a
+#define        MAX8997_ADC_JIG_UART            0x1c
+#define        MAX8997_ADC_CARDOCK             0x1d
+#define        MAX8997_ADC_OPEN                0x1f
+
+struct max8997_muic_irq {
+       unsigned int irq;
+       const char *name;
+};
+
+static struct max8997_muic_irq muic_irqs[] = {
+       { MAX8997_MUICIRQ_ADCError, "muic-ADC_error" },
+       { MAX8997_MUICIRQ_ADCLow, "muic-ADC_low" },
+       { MAX8997_MUICIRQ_ADC, "muic-ADC" },
+       { MAX8997_MUICIRQ_VBVolt, "muic-VB_voltage" },
+       { MAX8997_MUICIRQ_DBChg, "muic-DB_charger" },
+       { MAX8997_MUICIRQ_DCDTmr, "muic-DCD_timer" },
+       { MAX8997_MUICIRQ_ChgDetRun, "muic-CDR_status" },
+       { MAX8997_MUICIRQ_ChgTyp, "muic-charger_type" },
+       { MAX8997_MUICIRQ_OVP, "muic-over_voltage" },
+};
+
+struct max8997_muic_info {
+       struct device *dev;
+       struct max8997_dev *iodev;
+       struct i2c_client *muic;
+       struct max8997_muic_platform_data *muic_pdata;
+
+       int irq;
+       struct work_struct irq_work;
+
+       enum max8997_muic_charger_type pre_charger_type;
+       int pre_adc;
+
+       struct mutex mutex;
+};
+
+static int max8997_muic_handle_usb(struct max8997_muic_info *info,
+                       enum max8997_muic_usb_type usb_type, bool attached)
+{
+       struct max8997_muic_platform_data *mdata = info->muic_pdata;
+       int ret = 0;
+
+       if (usb_type == MAX8997_USB_HOST) {
+               /* switch to USB */
+               ret = max8997_update_reg(info->muic, MAX8997_MUIC_REG_CONTROL1,
+                               attached ? MAX8997_SW_USB : MAX8997_SW_OPEN,
+                               SW_MASK);
+               if (ret) {
+                       dev_err(info->dev, "failed to update muic register\n");
+                       goto out;
+               }
+       }
+
+       if (mdata->usb_callback)
+               mdata->usb_callback(usb_type, attached);
+out:
+       return ret;
+}
+
+static void max8997_muic_handle_mhl(struct max8997_muic_info *info,
+                       bool attached)
+{
+       struct max8997_muic_platform_data *mdata = info->muic_pdata;
+
+       if (mdata->mhl_callback)
+               mdata->mhl_callback(attached);
+}
+
+static int max8997_muic_handle_dock(struct max8997_muic_info *info,
+                       int adc, bool attached)
+{
+       struct max8997_muic_platform_data *mdata = info->muic_pdata;
+       int ret = 0;
+
+       /* switch to AUDIO */
+       ret = max8997_update_reg(info->muic, MAX8997_MUIC_REG_CONTROL1,
+                               attached ? MAX8997_SW_AUDIO : MAX8997_SW_OPEN,
+                               SW_MASK);
+       if (ret) {
+               dev_err(info->dev, "failed to update muic register\n");
+               goto out;
+       }
+
+       switch (adc) {
+       case MAX8997_ADC_DESKDOCK:
+               if (mdata->deskdock_callback)
+                       mdata->deskdock_callback(attached);
+               break;
+       case MAX8997_ADC_CARDOCK:
+               if (mdata->cardock_callback)
+                       mdata->cardock_callback(attached);
+               break;
+       default:
+               break;
+       }
+out:
+       return ret;
+}
+
+static int max8997_muic_handle_jig_uart(struct max8997_muic_info *info,
+                       bool attached)
+{
+       struct max8997_muic_platform_data *mdata = info->muic_pdata;
+       int ret = 0;
+
+       /* switch to UART */
+       ret = max8997_update_reg(info->muic, MAX8997_MUIC_REG_CONTROL1,
+                               attached ? MAX8997_SW_UART : MAX8997_SW_OPEN,
+                               SW_MASK);
+       if (ret) {
+               dev_err(info->dev, "failed to update muic register\n");
+               goto out;
+       }
+
+       if (mdata->uart_callback)
+               mdata->uart_callback(attached);
+out:
+       return ret;
+}
+
+static int max8997_muic_handle_adc_detach(struct max8997_muic_info *info)
+{
+       int ret = 0;
+
+       switch (info->pre_adc) {
+       case MAX8997_ADC_GROUND:
+               ret = max8997_muic_handle_usb(info, MAX8997_USB_HOST, false);
+               break;
+       case MAX8997_ADC_MHL:
+               max8997_muic_handle_mhl(info, false);
+               break;
+       case MAX8997_ADC_JIG_USB_1:
+       case MAX8997_ADC_JIG_USB_2:
+               ret = max8997_muic_handle_usb(info, MAX8997_USB_DEVICE, false);
+               break;
+       case MAX8997_ADC_DESKDOCK:
+       case MAX8997_ADC_CARDOCK:
+               ret = max8997_muic_handle_dock(info, info->pre_adc, false);
+               break;
+       case MAX8997_ADC_JIG_UART:
+               ret = max8997_muic_handle_jig_uart(info, false);
+               break;
+       default:
+               break;
+       }
+
+       return ret;
+}
+
+static int max8997_muic_handle_adc(struct max8997_muic_info *info, int adc)
+{
+       int ret = 0;
+
+       switch (adc) {
+       case MAX8997_ADC_GROUND:
+               ret = max8997_muic_handle_usb(info, MAX8997_USB_HOST, true);
+               break;
+       case MAX8997_ADC_MHL:
+               max8997_muic_handle_mhl(info, true);
+               break;
+       case MAX8997_ADC_JIG_USB_1:
+       case MAX8997_ADC_JIG_USB_2:
+               ret = max8997_muic_handle_usb(info, MAX8997_USB_DEVICE, true);
+               break;
+       case MAX8997_ADC_DESKDOCK:
+       case MAX8997_ADC_CARDOCK:
+               ret = max8997_muic_handle_dock(info, adc, true);
+               break;
+       case MAX8997_ADC_JIG_UART:
+               ret = max8997_muic_handle_jig_uart(info, true);
+               break;
+       case MAX8997_ADC_OPEN:
+               ret = max8997_muic_handle_adc_detach(info);
+               break;
+       default:
+               break;
+       }
+
+       info->pre_adc = adc;
+
+       return ret;
+}
+
+static int max8997_muic_handle_charger_type(struct max8997_muic_info *info,
+                               enum max8997_muic_charger_type charger_type)
+{
+       struct max8997_muic_platform_data *mdata = info->muic_pdata;
+       u8 adc;
+       int ret;
+
+       ret = max8997_read_reg(info->muic, MAX8997_MUIC_REG_STATUS1, &adc);
+       if (ret) {
+               dev_err(info->dev, "failed to read muic register\n");
+               goto out;
+       }
+
+       switch (charger_type) {
+       case MAX8997_CHARGER_TYPE_NONE:
+               if (mdata->charger_callback)
+                       mdata->charger_callback(false, charger_type);
+               if (info->pre_charger_type == MAX8997_CHARGER_TYPE_USB) {
+                       max8997_muic_handle_usb(info,
+                                       MAX8997_USB_DEVICE, false);
+               }
+               break;
+       case MAX8997_CHARGER_TYPE_USB:
+               if ((adc & STATUS1_ADC_MASK) == MAX8997_ADC_OPEN) {
+                       max8997_muic_handle_usb(info,
+                                       MAX8997_USB_DEVICE, true);
+               }
+               if (mdata->charger_callback)
+                       mdata->charger_callback(true, charger_type);
+               break;
+       case MAX8997_CHARGER_TYPE_DOWNSTREAM_PORT:
+       case MAX8997_CHARGER_TYPE_DEDICATED_CHG:
+       case MAX8997_CHARGER_TYPE_500MA:
+       case MAX8997_CHARGER_TYPE_1A:
+               if (mdata->charger_callback)
+                       mdata->charger_callback(true, charger_type);
+               break;
+       default:
+               break;
+       }
+
+       info->pre_charger_type = charger_type;
+out:
+       return ret;
+}
+
+static void max8997_muic_irq_work(struct work_struct *work)
+{
+       struct max8997_muic_info *info = container_of(work,
+                       struct max8997_muic_info, irq_work);
+       struct max8997_platform_data *pdata =
+                               dev_get_platdata(info->iodev->dev);
+       u8 status[3];
+       u8 adc, chg_type;
+
+       int irq_type = info->irq - pdata->irq_base;
+       int ret;
+
+       mutex_lock(&info->mutex);
+
+       ret = max8997_bulk_read(info->muic, MAX8997_MUIC_REG_STATUS1,
+                               3, status);
+       if (ret) {
+               dev_err(info->dev, "failed to read muic register\n");
+               mutex_unlock(&info->mutex);
+               return;
+       }
+
+       dev_dbg(info->dev, "%s: STATUS1:0x%x, 2:0x%x\n", __func__,
+                       status[0], status[1]);
+
+       switch (irq_type) {
+       case MAX8997_MUICIRQ_ADC:
+               adc = status[0] & STATUS1_ADC_MASK;
+               adc >>= STATUS1_ADC_SHIFT;
+
+               max8997_muic_handle_adc(info, adc);
+               break;
+       case MAX8997_MUICIRQ_ChgTyp:
+               chg_type = status[1] & STATUS2_CHGTYP_MASK;
+               chg_type >>= STATUS2_CHGTYP_SHIFT;
+
+               max8997_muic_handle_charger_type(info, chg_type);
+               break;
+       default:
+               dev_info(info->dev, "misc interrupt: %s occurred\n",
+                        muic_irqs[irq_type].name);
+               break;
+       }
+
+       mutex_unlock(&info->mutex);
+
+       return;
+}
+
+static irqreturn_t max8997_muic_irq_handler(int irq, void *data)
+{
+       struct max8997_muic_info *info = data;
+
+       dev_dbg(info->dev, "irq:%d\n", irq);
+       info->irq = irq;
+
+       schedule_work(&info->irq_work);
+
+       return IRQ_HANDLED;
+}
+
+static void max8997_muic_detect_dev(struct max8997_muic_info *info)
+{
+       int ret;
+       u8 status[2], adc, chg_type;
+
+       ret = max8997_bulk_read(info->muic, MAX8997_MUIC_REG_STATUS1,
+                               2, status);
+       if (ret) {
+               dev_err(info->dev, "failed to read muic register\n");
+               return;
+       }
+
+       dev_info(info->dev, "STATUS1:0x%x, STATUS2:0x%x\n",
+                       status[0], status[1]);
+
+       adc = status[0] & STATUS1_ADC_MASK;
+       adc >>= STATUS1_ADC_SHIFT;
+
+       chg_type = status[1] & STATUS2_CHGTYP_MASK;
+       chg_type >>= STATUS2_CHGTYP_SHIFT;
+
+       max8997_muic_handle_adc(info, adc);
+       max8997_muic_handle_charger_type(info, chg_type);
+}
+
+static void max8997_initialize_device(struct max8997_muic_info *info)
+{
+       struct max8997_muic_platform_data *mdata = info->muic_pdata;
+       int i;
+
+       for (i = 0; i < mdata->num_init_data; i++) {
+               max8997_write_reg(info->muic, mdata->init_data[i].addr,
+                               mdata->init_data[i].data);
+       }
+}
+
+static int __devinit max8997_muic_probe(struct platform_device *pdev)
+{
+       struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+       struct max8997_platform_data *pdata = dev_get_platdata(iodev->dev);
+       struct max8997_muic_info *info;
+       int ret, i;
+
+       info = kzalloc(sizeof(struct max8997_muic_info), GFP_KERNEL);
+       if (!info) {
+               dev_err(&pdev->dev, "failed to allocate memory\n");
+               ret = -ENOMEM;
+               goto err_kfree;
+       }
+
+       if (!pdata->muic_pdata) {
+               dev_err(&pdev->dev, "failed to get platform_data\n");
+               ret = -EINVAL;
+               goto err_pdata;
+       }
+       info->muic_pdata = pdata->muic_pdata;
+
+       info->dev = &pdev->dev;
+       info->iodev = iodev;
+       info->muic = iodev->muic;
+
+       platform_set_drvdata(pdev, info);
+       mutex_init(&info->mutex);
+
+       INIT_WORK(&info->irq_work, max8997_muic_irq_work);
+
+       for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) {
+               struct max8997_muic_irq *muic_irq = &muic_irqs[i];
+
+               ret = request_threaded_irq(pdata->irq_base + muic_irq->irq,
+                               NULL, max8997_muic_irq_handler,
+                               0, muic_irq->name,
+                               info);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "failed: irq request (IRQ: %d,"
+                               " error :%d)\n",
+                               muic_irq->irq, ret);
+
+                       for (i = i - 1; i >= 0; i--)
+                               free_irq(muic_irq->irq, info);
+
+                       goto err_irq;
+               }
+       }
+
+       /* Initialize registers according to platform data */
+       max8997_initialize_device(info);
+
+       /* Initial device detection */
+       max8997_muic_detect_dev(info);
+
+       return ret;
+
+err_irq:
+err_pdata:
+       kfree(info);
+err_kfree:
+       return ret;
+}
+
+static int __devexit max8997_muic_remove(struct platform_device *pdev)
+{
+       struct max8997_muic_info *info = platform_get_drvdata(pdev);
+       struct max8997_platform_data *pdata =
+                               dev_get_platdata(info->iodev->dev);
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(muic_irqs); i++)
+               free_irq(pdata->irq_base + muic_irqs[i].irq, info);
+       cancel_work_sync(&info->irq_work);
+
+       kfree(info);
+
+       return 0;
+}
+
+static struct platform_driver max8997_muic_driver = {
+       .driver         = {
+               .name   = "max8997-muic",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = max8997_muic_probe,
+       .remove         = __devexit_p(max8997_muic_remove),
+};
+
+static int __init max8997_muic_init(void)
+{
+       return platform_driver_register(&max8997_muic_driver);
+}
+module_init(max8997_muic_init);
+
+static void __exit max8997_muic_exit(void)
+{
+       platform_driver_unregister(&max8997_muic_driver);
+}
+module_exit(max8997_muic_exit);
+
+MODULE_DESCRIPTION("Maxim MAX8997 MUIC driver");
+MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
+MODULE_LICENSE("GPL");
index 12eef393e2160103ac3e63dadcefd5817e99a6a9..400756ec7c492dfd527dc5154b6601ae0ef1060d 100644 (file)
@@ -6,5 +6,4 @@ subdir-ccflags-$(CONFIG_MMC_DEBUG) := -DDEBUG
 
 obj-$(CONFIG_MMC)              += core/
 obj-$(CONFIG_MMC)              += card/
-obj-$(CONFIG_MMC)              += host/
-
+obj-$(subst m,y,$(CONFIG_MMC)) += host/
index 1e0e27cbe98786e82abf3d5d9e6ede36cc8f0eb2..0cad48a284a85366e6ad72aa607fd6d9f3da59ce 100644 (file)
@@ -107,6 +107,8 @@ struct mmc_blk_data {
         */
        unsigned int    part_curr;
        struct device_attribute force_ro;
+       struct device_attribute power_ro_lock;
+       int     area_type;
 };
 
 static DEFINE_MUTEX(open_lock);
@@ -119,6 +121,7 @@ enum mmc_blk_status {
        MMC_BLK_ABORT,
        MMC_BLK_DATA_ERR,
        MMC_BLK_ECC_ERR,
+       MMC_BLK_NOMEDIUM,
 };
 
 module_param(perdev_minors, int, 0444);
@@ -165,6 +168,70 @@ static void mmc_blk_put(struct mmc_blk_data *md)
        mutex_unlock(&open_lock);
 }
 
+static ssize_t power_ro_lock_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       int ret;
+       struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+       struct mmc_card *card = md->queue.card;
+       int locked = 0;
+
+       if (card->ext_csd.boot_ro_lock & EXT_CSD_BOOT_WP_B_PERM_WP_EN)
+               locked = 2;
+       else if (card->ext_csd.boot_ro_lock & EXT_CSD_BOOT_WP_B_PWR_WP_EN)
+               locked = 1;
+
+       ret = snprintf(buf, PAGE_SIZE, "%d\n", locked);
+
+       return ret;
+}
+
+static ssize_t power_ro_lock_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       int ret;
+       struct mmc_blk_data *md, *part_md;
+       struct mmc_card *card;
+       unsigned long set;
+
+       if (kstrtoul(buf, 0, &set))
+               return -EINVAL;
+
+       if (set != 1)
+               return count;
+
+       md = mmc_blk_get(dev_to_disk(dev));
+       card = md->queue.card;
+
+       mmc_claim_host(card->host);
+
+       ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BOOT_WP,
+                               card->ext_csd.boot_ro_lock |
+                               EXT_CSD_BOOT_WP_B_PWR_WP_EN,
+                               card->ext_csd.part_time);
+       if (ret)
+               pr_err("%s: Locking boot partition ro until next power on failed: %d\n", md->disk->disk_name, ret);
+       else
+               card->ext_csd.boot_ro_lock |= EXT_CSD_BOOT_WP_B_PWR_WP_EN;
+
+       mmc_release_host(card->host);
+
+       if (!ret) {
+               pr_info("%s: Locking boot partition ro until next power on\n",
+                       md->disk->disk_name);
+               set_disk_ro(md->disk, 1);
+
+               list_for_each_entry(part_md, &md->part, part)
+                       if (part_md->area_type == MMC_BLK_DATA_AREA_BOOT) {
+                               pr_info("%s: Locking boot partition ro until next power on\n", part_md->disk->disk_name);
+                               set_disk_ro(part_md->disk, 1);
+                       }
+       }
+
+       mmc_blk_put(md);
+       return count;
+}
+
 static ssize_t force_ro_show(struct device *dev, struct device_attribute *attr,
                             char *buf)
 {
@@ -266,6 +333,9 @@ static struct mmc_blk_ioc_data *mmc_blk_ioctl_copy_from_user(
                goto idata_err;
        }
 
+       if (!idata->buf_bytes)
+               return idata;
+
        idata->buf = kzalloc(idata->buf_bytes, GFP_KERNEL);
        if (!idata->buf) {
                err = -ENOMEM;
@@ -312,25 +382,6 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
        if (IS_ERR(idata))
                return PTR_ERR(idata);
 
-       cmd.opcode = idata->ic.opcode;
-       cmd.arg = idata->ic.arg;
-       cmd.flags = idata->ic.flags;
-
-       data.sg = &sg;
-       data.sg_len = 1;
-       data.blksz = idata->ic.blksz;
-       data.blocks = idata->ic.blocks;
-
-       sg_init_one(data.sg, idata->buf, idata->buf_bytes);
-
-       if (idata->ic.write_flag)
-               data.flags = MMC_DATA_WRITE;
-       else
-               data.flags = MMC_DATA_READ;
-
-       mrq.cmd = &cmd;
-       mrq.data = &data;
-
        md = mmc_blk_get(bdev->bd_disk);
        if (!md) {
                err = -EINVAL;
@@ -343,6 +394,48 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
                goto cmd_done;
        }
 
+       cmd.opcode = idata->ic.opcode;
+       cmd.arg = idata->ic.arg;
+       cmd.flags = idata->ic.flags;
+
+       if (idata->buf_bytes) {
+               data.sg = &sg;
+               data.sg_len = 1;
+               data.blksz = idata->ic.blksz;
+               data.blocks = idata->ic.blocks;
+
+               sg_init_one(data.sg, idata->buf, idata->buf_bytes);
+
+               if (idata->ic.write_flag)
+                       data.flags = MMC_DATA_WRITE;
+               else
+                       data.flags = MMC_DATA_READ;
+
+               /* data.flags must already be set before doing this. */
+               mmc_set_data_timeout(&data, card);
+
+               /* Allow overriding the timeout_ns for empirical tuning. */
+               if (idata->ic.data_timeout_ns)
+                       data.timeout_ns = idata->ic.data_timeout_ns;
+
+               if ((cmd.flags & MMC_RSP_R1B) == MMC_RSP_R1B) {
+                       /*
+                        * Pretend this is a data transfer and rely on the
+                        * host driver to compute timeout.  When all host
+                        * drivers support cmd.cmd_timeout for R1B, this
+                        * can be changed to:
+                        *
+                        *     mrq.data = NULL;
+                        *     cmd.cmd_timeout = idata->ic.cmd_timeout_ms;
+                        */
+                       data.timeout_ns = idata->ic.cmd_timeout_ms * 1000000;
+               }
+
+               mrq.data = &data;
+       }
+
+       mrq.cmd = &cmd;
+
        mmc_claim_host(card->host);
 
        if (idata->ic.is_acmd) {
@@ -351,24 +444,6 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
                        goto cmd_rel_host;
        }
 
-       /* data.flags must already be set before doing this. */
-       mmc_set_data_timeout(&data, card);
-       /* Allow overriding the timeout_ns for empirical tuning. */
-       if (idata->ic.data_timeout_ns)
-               data.timeout_ns = idata->ic.data_timeout_ns;
-
-       if ((cmd.flags & MMC_RSP_R1B) == MMC_RSP_R1B) {
-               /*
-                * Pretend this is a data transfer and rely on the host driver
-                * to compute timeout.  When all host drivers support
-                * cmd.cmd_timeout for R1B, this can be changed to:
-                *
-                *     mrq.data = NULL;
-                *     cmd.cmd_timeout = idata->ic.cmd_timeout_ms;
-                */
-               data.timeout_ns = idata->ic.cmd_timeout_ms * 1000000;
-       }
-
        mmc_wait_for_req(card->host, &mrq);
 
        if (cmd.error) {
@@ -565,6 +640,7 @@ static int get_card_status(struct mmc_card *card, u32 *status, int retries)
        return err;
 }
 
+#define ERR_NOMEDIUM   3
 #define ERR_RETRY      2
 #define ERR_ABORT      1
 #define ERR_CONTINUE   0
@@ -632,6 +708,9 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
        u32 status, stop_status = 0;
        int err, retry;
 
+       if (mmc_card_removed(card))
+               return ERR_NOMEDIUM;
+
        /*
         * Try to get card status which indicates both the card state
         * and why there was no response.  If the first attempt fails,
@@ -648,8 +727,12 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
        }
 
        /* We couldn't get a response from the card.  Give up. */
-       if (err)
+       if (err) {
+               /* Check if the card is removed */
+               if (mmc_detect_card_removed(card->host))
+                       return ERR_NOMEDIUM;
                return ERR_ABORT;
+       }
 
        /* Flag ECC errors */
        if ((status & R1_CARD_ECC_FAILED) ||
@@ -922,6 +1005,8 @@ static int mmc_blk_err_check(struct mmc_card *card,
                        return MMC_BLK_RETRY;
                case ERR_ABORT:
                        return MMC_BLK_ABORT;
+               case ERR_NOMEDIUM:
+                       return MMC_BLK_NOMEDIUM;
                case ERR_CONTINUE:
                        break;
                }
@@ -1255,6 +1340,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
                        if (!ret)
                                goto start_new_req;
                        break;
+               case MMC_BLK_NOMEDIUM:
+                       goto cmd_abort;
                }
 
                if (ret) {
@@ -1271,6 +1358,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
 
  cmd_abort:
        spin_lock_irq(&md->lock);
+       if (mmc_card_removed(card))
+               req->cmd_flags |= REQ_QUIET;
        while (ret)
                ret = __blk_end_request(req, -EIO, blk_rq_cur_bytes(req));
        spin_unlock_irq(&md->lock);
@@ -1339,7 +1428,8 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
                                              struct device *parent,
                                              sector_t size,
                                              bool default_ro,
-                                             const char *subname)
+                                             const char *subname,
+                                             int area_type)
 {
        struct mmc_blk_data *md;
        int devidx, ret;
@@ -1364,11 +1454,12 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
        if (!subname) {
                md->name_idx = find_first_zero_bit(name_use, max_devices);
                __set_bit(md->name_idx, name_use);
-       }
-       else
+       } else
                md->name_idx = ((struct mmc_blk_data *)
                                dev_to_disk(parent)->private_data)->name_idx;
 
+       md->area_type = area_type;
+
        /*
         * Set the read-only status based on the supported commands
         * and the write protect switch.
@@ -1462,7 +1553,8 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
                size = card->csd.capacity << (card->csd.read_blkbits - 9);
        }
 
-       md = mmc_blk_alloc_req(card, &card->dev, size, false, NULL);
+       md = mmc_blk_alloc_req(card, &card->dev, size, false, NULL,
+                                       MMC_BLK_DATA_AREA_MAIN);
        return md;
 }
 
@@ -1471,13 +1563,14 @@ static int mmc_blk_alloc_part(struct mmc_card *card,
                              unsigned int part_type,
                              sector_t size,
                              bool default_ro,
-                             const char *subname)
+                             const char *subname,
+                             int area_type)
 {
        char cap_str[10];
        struct mmc_blk_data *part_md;
 
        part_md = mmc_blk_alloc_req(card, disk_to_dev(md->disk), size, default_ro,
-                                   subname);
+                                   subname, area_type);
        if (IS_ERR(part_md))
                return PTR_ERR(part_md);
        part_md->part_type = part_type;
@@ -1510,7 +1603,8 @@ static int mmc_blk_alloc_parts(struct mmc_card *card, struct mmc_blk_data *md)
                                card->part[idx].part_cfg,
                                card->part[idx].size >> 9,
                                card->part[idx].force_ro,
-                               card->part[idx].name);
+                               card->part[idx].name,
+                               card->part[idx].area_type);
                        if (ret)
                                return ret;
                }
@@ -1539,9 +1633,16 @@ mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card)
 
 static void mmc_blk_remove_req(struct mmc_blk_data *md)
 {
+       struct mmc_card *card;
+
        if (md) {
+               card = md->queue.card;
                if (md->disk->flags & GENHD_FL_UP) {
                        device_remove_file(disk_to_dev(md->disk), &md->force_ro);
+                       if ((md->area_type & MMC_BLK_DATA_AREA_BOOT) &&
+                                       card->ext_csd.boot_ro_lockable)
+                               device_remove_file(disk_to_dev(md->disk),
+                                       &md->power_ro_lock);
 
                        /* Stop new requests from getting into the queue */
                        del_gendisk(md->disk);
@@ -1570,6 +1671,7 @@ static void mmc_blk_remove_parts(struct mmc_card *card,
 static int mmc_add_disk(struct mmc_blk_data *md)
 {
        int ret;
+       struct mmc_card *card = md->queue.card;
 
        add_disk(md->disk);
        md->force_ro.show = force_ro_show;
@@ -1579,18 +1681,53 @@ static int mmc_add_disk(struct mmc_blk_data *md)
        md->force_ro.attr.mode = S_IRUGO | S_IWUSR;
        ret = device_create_file(disk_to_dev(md->disk), &md->force_ro);
        if (ret)
-               del_gendisk(md->disk);
+               goto force_ro_fail;
+
+       if ((md->area_type & MMC_BLK_DATA_AREA_BOOT) &&
+            card->ext_csd.boot_ro_lockable) {
+               mode_t mode;
+
+               if (card->ext_csd.boot_ro_lock & EXT_CSD_BOOT_WP_B_PWR_WP_DIS)
+                       mode = S_IRUGO;
+               else
+                       mode = S_IRUGO | S_IWUSR;
+
+               md->power_ro_lock.show = power_ro_lock_show;
+               md->power_ro_lock.store = power_ro_lock_store;
+               md->power_ro_lock.attr.mode = mode;
+               md->power_ro_lock.attr.name =
+                                       "ro_lock_until_next_power_on";
+               ret = device_create_file(disk_to_dev(md->disk),
+                               &md->power_ro_lock);
+               if (ret)
+                       goto power_ro_lock_fail;
+       }
+       return ret;
+
+power_ro_lock_fail:
+       device_remove_file(disk_to_dev(md->disk), &md->force_ro);
+force_ro_fail:
+       del_gendisk(md->disk);
 
        return ret;
 }
 
+#define CID_MANFID_SANDISK     0x2
+#define CID_MANFID_TOSHIBA     0x11
+#define CID_MANFID_MICRON      0x13
+
 static const struct mmc_fixup blk_fixups[] =
 {
-       MMC_FIXUP("SEM02G", 0x2, 0x100, add_quirk, MMC_QUIRK_INAND_CMD38),
-       MMC_FIXUP("SEM04G", 0x2, 0x100, add_quirk, MMC_QUIRK_INAND_CMD38),
-       MMC_FIXUP("SEM08G", 0x2, 0x100, add_quirk, MMC_QUIRK_INAND_CMD38),
-       MMC_FIXUP("SEM16G", 0x2, 0x100, add_quirk, MMC_QUIRK_INAND_CMD38),
-       MMC_FIXUP("SEM32G", 0x2, 0x100, add_quirk, MMC_QUIRK_INAND_CMD38),
+       MMC_FIXUP("SEM02G", CID_MANFID_SANDISK, 0x100, add_quirk,
+                 MMC_QUIRK_INAND_CMD38),
+       MMC_FIXUP("SEM04G", CID_MANFID_SANDISK, 0x100, add_quirk,
+                 MMC_QUIRK_INAND_CMD38),
+       MMC_FIXUP("SEM08G", CID_MANFID_SANDISK, 0x100, add_quirk,
+                 MMC_QUIRK_INAND_CMD38),
+       MMC_FIXUP("SEM16G", CID_MANFID_SANDISK, 0x100, add_quirk,
+                 MMC_QUIRK_INAND_CMD38),
+       MMC_FIXUP("SEM32G", CID_MANFID_SANDISK, 0x100, add_quirk,
+                 MMC_QUIRK_INAND_CMD38),
 
        /*
         * Some MMC cards experience performance degradation with CMD23
@@ -1600,18 +1737,18 @@ static const struct mmc_fixup blk_fixups[] =
         *
         * N.B. This doesn't affect SD cards.
         */
-       MMC_FIXUP("MMC08G", 0x11, CID_OEMID_ANY, add_quirk_mmc,
+       MMC_FIXUP("MMC08G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
                  MMC_QUIRK_BLK_NO_CMD23),
-       MMC_FIXUP("MMC16G", 0x11, CID_OEMID_ANY, add_quirk_mmc,
+       MMC_FIXUP("MMC16G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
                  MMC_QUIRK_BLK_NO_CMD23),
-       MMC_FIXUP("MMC32G", 0x11, CID_OEMID_ANY, add_quirk_mmc,
+       MMC_FIXUP("MMC32G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
                  MMC_QUIRK_BLK_NO_CMD23),
 
        /*
         * Some Micron MMC cards needs longer data read timeout than
         * indicated in CSD.
         */
-       MMC_FIXUP(CID_NAME_ANY, 0x13, 0x200, add_quirk_mmc,
+       MMC_FIXUP(CID_NAME_ANY, CID_MANFID_MICRON, 0x200, add_quirk_mmc,
                  MMC_QUIRK_LONG_READ_TIME),
 
        END_FIXUP
index e99bdc18002df76b09b54e269714401596419f7c..759714ed6bee8f791b2cbd4a5959f2ca12d7e9ec 100644 (file)
@@ -1581,6 +1581,7 @@ static int mmc_test_area_init(struct mmc_test_card *test, int erase, int fill)
 
        t->max_segs = test->card->host->max_segs;
        t->max_seg_sz = test->card->host->max_seg_size;
+       t->max_seg_sz -= t->max_seg_sz % 512;
 
        t->max_tfr = t->max_sz;
        if (t->max_tfr >> 9 > test->card->host->max_blk_count)
index dcad59cbfef11a873b0dfa49ef9dcd4e8fcf1d83..2517547b4366a9bbdc6eea69ce1f1dc35ef09f08 100644 (file)
@@ -29,6 +29,8 @@
  */
 static int mmc_prep_request(struct request_queue *q, struct request *req)
 {
+       struct mmc_queue *mq = q->queuedata;
+
        /*
         * We only like normal block requests and discards.
         */
@@ -37,6 +39,9 @@ static int mmc_prep_request(struct request_queue *q, struct request *req)
                return BLKPREP_KILL;
        }
 
+       if (mq && mmc_card_removed(mq->card))
+               return BLKPREP_KILL;
+
        req->cmd_flags |= REQ_DONTPREP;
 
        return BLKPREP_OK;
index 639501970b412c7315c4d516da44aa9bf930a203..dca4428380f15c53adf73ce79810e7e26c0543f9 100644 (file)
@@ -7,6 +7,6 @@ mmc_core-y                      := core.o bus.o host.o \
                                   mmc.o mmc_ops.o sd.o sd_ops.o \
                                   sdio.o sdio_ops.o sdio_bus.o \
                                   sdio_cis.o sdio_io.o sdio_irq.o \
-                                  quirks.o
+                                  quirks.o cd-gpio.o
 
 mmc_core-$(CONFIG_DEBUG_FS)    += debugfs.o
index 6be49249895a21bab3c06cd4bc658f347ed99124..5d011a39dfff3b68a88813e202c76a4722fa3c57 100644 (file)
@@ -303,10 +303,11 @@ int mmc_add_card(struct mmc_card *card)
                        mmc_card_ddr_mode(card) ? "DDR " : "",
                        type);
        } else {
-               printk(KERN_INFO "%s: new %s%s%s card at address %04x\n",
+               pr_info("%s: new %s%s%s%s card at address %04x\n",
                        mmc_hostname(card->host),
-                       mmc_sd_card_uhs(card) ? "ultra high speed " :
+                       mmc_card_uhs(card) ? "ultra high speed " :
                        (mmc_card_highspeed(card) ? "high speed " : ""),
+                       (mmc_card_hs200(card) ? "HS200 " : ""),
                        mmc_card_ddr_mode(card) ? "DDR " : "",
                        type, card->rca);
        }
diff --git a/drivers/mmc/core/cd-gpio.c b/drivers/mmc/core/cd-gpio.c
new file mode 100644 (file)
index 0000000..082202a
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Generic GPIO card-detect helper
+ *
+ * Copyright (C) 2011, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * 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/err.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/mmc/host.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+struct mmc_cd_gpio {
+       unsigned int gpio;
+       char label[0];
+};
+
+static irqreturn_t mmc_cd_gpio_irqt(int irq, void *dev_id)
+{
+       /* Schedule a card detection after a debounce timeout */
+       mmc_detect_change(dev_id, msecs_to_jiffies(100));
+       return IRQ_HANDLED;
+}
+
+int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio,
+                       unsigned int irq, unsigned long flags)
+{
+       size_t len = strlen(dev_name(host->parent)) + 4;
+       struct mmc_cd_gpio *cd = kmalloc(sizeof(*cd) + len, GFP_KERNEL);
+       int ret;
+
+       if (!cd)
+               return -ENOMEM;
+
+       snprintf(cd->label, len, "%s cd", dev_name(host->parent));
+
+       ret = gpio_request_one(gpio, GPIOF_DIR_IN, cd->label);
+       if (ret < 0)
+               goto egpioreq;
+
+       ret = request_threaded_irq(irq, NULL, mmc_cd_gpio_irqt,
+                                  flags, cd->label, host);
+       if (ret < 0)
+               goto eirqreq;
+
+       cd->gpio = gpio;
+       host->hotplug.irq = irq;
+       host->hotplug.handler_priv = cd;
+
+       return 0;
+
+eirqreq:
+       gpio_free(gpio);
+egpioreq:
+       kfree(cd);
+       return ret;
+}
+EXPORT_SYMBOL(mmc_cd_gpio_request);
+
+void mmc_cd_gpio_free(struct mmc_host *host)
+{
+       struct mmc_cd_gpio *cd = host->hotplug.handler_priv;
+
+       free_irq(host->hotplug.irq, host);
+       gpio_free(cd->gpio);
+       kfree(cd);
+}
+EXPORT_SYMBOL(mmc_cd_gpio_free);
index 950b97d7412a4f6f5f562401f132ed0d568a969a..bec0bf21c8793ba13d40701ca9f22ee1bb51db2d 100644 (file)
@@ -140,7 +140,7 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
                        cmd->retries = 0;
        }
 
-       if (err && cmd->retries) {
+       if (err && cmd->retries && !mmc_card_removed(host->card)) {
                /*
                 * Request starter must handle retries - see
                 * mmc_wait_for_req_done().
@@ -247,6 +247,11 @@ static void __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
 {
        init_completion(&mrq->completion);
        mrq->done = mmc_wait_done;
+       if (mmc_card_removed(host->card)) {
+               mrq->cmd->error = -ENOMEDIUM;
+               complete(&mrq->completion);
+               return;
+       }
        mmc_start_request(host, mrq);
 }
 
@@ -259,7 +264,8 @@ static void mmc_wait_for_req_done(struct mmc_host *host,
                wait_for_completion(&mrq->completion);
 
                cmd = mrq->cmd;
-               if (!cmd->error || !cmd->retries)
+               if (!cmd->error || !cmd->retries ||
+                   mmc_card_removed(host->card))
                        break;
 
                pr_debug("%s: req failed (CMD%u): %d, retrying...\n",
@@ -1456,7 +1462,7 @@ void mmc_detect_change(struct mmc_host *host, unsigned long delay)
        WARN_ON(host->removed);
        spin_unlock_irqrestore(&host->lock, flags);
 #endif
-
+       host->detect_change = 1;
        mmc_schedule_delayed_work(&host->detect, delay);
 }
 
@@ -2049,6 +2055,43 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
        return -EIO;
 }
 
+int _mmc_detect_card_removed(struct mmc_host *host)
+{
+       int ret;
+
+       if ((host->caps & MMC_CAP_NONREMOVABLE) || !host->bus_ops->alive)
+               return 0;
+
+       if (!host->card || mmc_card_removed(host->card))
+               return 1;
+
+       ret = host->bus_ops->alive(host);
+       if (ret) {
+               mmc_card_set_removed(host->card);
+               pr_debug("%s: card remove detected\n", mmc_hostname(host));
+       }
+
+       return ret;
+}
+
+int mmc_detect_card_removed(struct mmc_host *host)
+{
+       struct mmc_card *card = host->card;
+
+       WARN_ON(!host->claimed);
+       /*
+        * The card will be considered unchanged unless we have been asked to
+        * detect a change or host requires polling to provide card detection.
+        */
+       if (card && !host->detect_change && !(host->caps & MMC_CAP_NEEDS_POLL))
+               return mmc_card_removed(card);
+
+       host->detect_change = 0;
+
+       return _mmc_detect_card_removed(host);
+}
+EXPORT_SYMBOL(mmc_detect_card_removed);
+
 void mmc_rescan(struct work_struct *work)
 {
        static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
@@ -2069,6 +2112,8 @@ void mmc_rescan(struct work_struct *work)
            && !(host->caps & MMC_CAP_NONREMOVABLE))
                host->bus_ops->detect(host);
 
+       host->detect_change = 0;
+
        /*
         * Let mmc_bus_put() free the bus/bus_ops if we've found that
         * the card is no longer present.
@@ -2130,6 +2175,7 @@ void mmc_stop_host(struct mmc_host *host)
 
        mmc_bus_get(host);
        if (host->bus_ops && !host->bus_dead) {
+               /* Calling bus_ops->remove() with a claimed host can deadlock */
                if (host->bus_ops->remove)
                        host->bus_ops->remove(host);
 
@@ -2201,6 +2247,9 @@ int mmc_card_awake(struct mmc_host *host)
 {
        int err = -ENOSYS;
 
+       if (host->caps2 & MMC_CAP2_NO_SLEEP_CMD)
+               return 0;
+
        mmc_bus_get(host);
 
        if (host->bus_ops && !host->bus_dead && host->bus_ops->awake)
@@ -2216,6 +2265,9 @@ int mmc_card_sleep(struct mmc_host *host)
 {
        int err = -ENOSYS;
 
+       if (host->caps2 & MMC_CAP2_NO_SLEEP_CMD)
+               return 0;
+
        mmc_bus_get(host);
 
        if (host->bus_ops && !host->bus_dead && host->bus_ops->sleep)
@@ -2270,6 +2322,7 @@ EXPORT_SYMBOL(mmc_flush_cache);
 int mmc_cache_ctrl(struct mmc_host *host, u8 enable)
 {
        struct mmc_card *card = host->card;
+       unsigned int timeout;
        int err = 0;
 
        if (!(host->caps2 & MMC_CAP2_CACHE_CTRL) ||
@@ -2280,16 +2333,18 @@ int mmc_cache_ctrl(struct mmc_host *host, u8 enable)
                        (card->ext_csd.cache_size > 0)) {
                enable = !!enable;
 
-               if (card->ext_csd.cache_ctrl ^ enable)
+               if (card->ext_csd.cache_ctrl ^ enable) {
+                       timeout = enable ? card->ext_csd.generic_cmd6_time : 0;
                        err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
-                                       EXT_CSD_CACHE_CTRL, enable, 0);
-               if (err)
-                       pr_err("%s: cache %s error %d\n",
-                                       mmc_hostname(card->host),
-                                       enable ? "on" : "off",
-                                       err);
-               else
-                       card->ext_csd.cache_ctrl = enable;
+                                       EXT_CSD_CACHE_CTRL, enable, timeout);
+                       if (err)
+                               pr_err("%s: cache %s error %d\n",
+                                               mmc_hostname(card->host),
+                                               enable ? "on" : "off",
+                                               err);
+                       else
+                               card->ext_csd.cache_ctrl = enable;
+               }
        }
 
        return err;
@@ -2310,7 +2365,13 @@ int mmc_suspend_host(struct mmc_host *host)
                cancel_delayed_work(&host->disable);
        cancel_delayed_work(&host->detect);
        mmc_flush_scheduled_work();
-       err = mmc_cache_ctrl(host, 0);
+       if (mmc_try_claim_host(host)) {
+               err = mmc_cache_ctrl(host, 0);
+               mmc_do_release_host(host);
+       } else {
+               err = -EBUSY;
+       }
+
        if (err)
                goto out;
 
@@ -2338,7 +2399,9 @@ int mmc_suspend_host(struct mmc_host *host)
                        if (err == -ENOSYS || !host->bus_ops->resume) {
                                /*
                                 * We simply "remove" the card in this case.
-                                * It will be redetected on resume.
+                                * It will be redetected on resume.  (Calling
+                                * bus_ops->remove() with a claimed host can
+                                * deadlock.)
                                 */
                                if (host->bus_ops->remove)
                                        host->bus_ops->remove(host);
@@ -2431,11 +2494,11 @@ int mmc_pm_notify(struct notifier_block *notify_block,
                if (!host->bus_ops || host->bus_ops->suspend)
                        break;
 
-               mmc_claim_host(host);
-
+               /* Calling bus_ops->remove() with a claimed host can deadlock */
                if (host->bus_ops->remove)
                        host->bus_ops->remove(host);
 
+               mmc_claim_host(host);
                mmc_detach_bus(host);
                mmc_power_off(host);
                mmc_release_host(host);
index 14664f1fb16fb560c526dd30817cc10ae6db1adf..34009241213c4640aa6ba28aa5d66c63484ec655 100644 (file)
@@ -24,6 +24,7 @@ struct mmc_bus_ops {
        int (*resume)(struct mmc_host *);
        int (*power_save)(struct mmc_host *);
        int (*power_restore)(struct mmc_host *);
+       int (*alive)(struct mmc_host *);
 };
 
 void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
@@ -59,6 +60,8 @@ void mmc_rescan(struct work_struct *work);
 void mmc_start_host(struct mmc_host *host);
 void mmc_stop_host(struct mmc_host *host);
 
+int _mmc_detect_card_removed(struct mmc_host *host);
+
 int mmc_attach_mmc(struct mmc_host *host);
 int mmc_attach_sd(struct mmc_host *host);
 int mmc_attach_sdio(struct mmc_host *host);
index 3923880118b64d0519d7eb00d084e9709b271b59..9ab5b17d488ae45f63756a43bfe8917e88b52e86 100644 (file)
@@ -57,6 +57,8 @@ static int mmc_ios_show(struct seq_file *s, void *data)
        const char *str;
 
        seq_printf(s, "clock:\t\t%u Hz\n", ios->clock);
+       if (host->actual_clock)
+               seq_printf(s, "actual clock:\t%u Hz\n", host->actual_clock);
        seq_printf(s, "vdd:\t\t%u ", ios->vdd);
        if ((1 << ios->vdd) & MMC_VDD_165_195)
                seq_printf(s, "(1.65 - 1.95 V)\n");
@@ -133,6 +135,9 @@ static int mmc_ios_show(struct seq_file *s, void *data)
        case MMC_TIMING_UHS_DDR50:
                str = "sd uhs DDR50";
                break;
+       case MMC_TIMING_MMC_HS200:
+               str = "mmc high-speed SDR200";
+               break;
        default:
                str = "invalid";
                break;
index d31c78b72b0fd8d1e53adb2f403454790896fafe..30055f2b0d445b3e0c08ed131f939146fef68626 100644 (file)
@@ -54,6 +54,27 @@ static DEFINE_IDR(mmc_host_idr);
 static DEFINE_SPINLOCK(mmc_host_lock);
 
 #ifdef CONFIG_MMC_CLKGATE
+static ssize_t clkgate_delay_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct mmc_host *host = cls_dev_to_mmc_host(dev);
+       return snprintf(buf, PAGE_SIZE, "%lu\n", host->clkgate_delay);
+}
+
+static ssize_t clkgate_delay_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct mmc_host *host = cls_dev_to_mmc_host(dev);
+       unsigned long flags, value;
+
+       if (kstrtoul(buf, 0, &value))
+               return -EINVAL;
+
+       spin_lock_irqsave(&host->clk_lock, flags);
+       host->clkgate_delay = value;
+       spin_unlock_irqrestore(&host->clk_lock, flags);
+       return count;
+}
 
 /*
  * Enabling clock gating will make the core call out to the host
@@ -114,7 +135,7 @@ static void mmc_host_clk_gate_delayed(struct mmc_host *host)
 static void mmc_host_clk_gate_work(struct work_struct *work)
 {
        struct mmc_host *host = container_of(work, struct mmc_host,
-                                             clk_gate_work);
+                                             clk_gate_work.work);
 
        mmc_host_clk_gate_delayed(host);
 }
@@ -131,6 +152,8 @@ void mmc_host_clk_hold(struct mmc_host *host)
 {
        unsigned long flags;
 
+       /* cancel any clock gating work scheduled by mmc_host_clk_release() */
+       cancel_delayed_work_sync(&host->clk_gate_work);
        mutex_lock(&host->clk_gate_mutex);
        spin_lock_irqsave(&host->clk_lock, flags);
        if (host->clk_gated) {
@@ -180,7 +203,8 @@ void mmc_host_clk_release(struct mmc_host *host)
        host->clk_requests--;
        if (mmc_host_may_gate_card(host->card) &&
            !host->clk_requests)
-               queue_work(system_nrt_wq, &host->clk_gate_work);
+               queue_delayed_work(system_nrt_wq, &host->clk_gate_work,
+                               msecs_to_jiffies(host->clkgate_delay));
        spin_unlock_irqrestore(&host->clk_lock, flags);
 }
 
@@ -213,8 +237,13 @@ static inline void mmc_host_clk_init(struct mmc_host *host)
        host->clk_requests = 0;
        /* Hold MCI clock for 8 cycles by default */
        host->clk_delay = 8;
+       /*
+        * Default clock gating delay is 200ms.
+        * This value can be tuned by writing into sysfs entry.
+        */
+       host->clkgate_delay = 200;
        host->clk_gated = false;
-       INIT_WORK(&host->clk_gate_work, mmc_host_clk_gate_work);
+       INIT_DELAYED_WORK(&host->clk_gate_work, mmc_host_clk_gate_work);
        spin_lock_init(&host->clk_lock);
        mutex_init(&host->clk_gate_mutex);
 }
@@ -229,7 +258,7 @@ static inline void mmc_host_clk_exit(struct mmc_host *host)
         * Wait for any outstanding gate and then make sure we're
         * ungated before exiting.
         */
-       if (cancel_work_sync(&host->clk_gate_work))
+       if (cancel_delayed_work_sync(&host->clk_gate_work))
                mmc_host_clk_gate_delayed(host);
        if (host->clk_gated)
                mmc_host_clk_hold(host);
@@ -237,6 +266,17 @@ static inline void mmc_host_clk_exit(struct mmc_host *host)
        WARN_ON(host->clk_requests > 1);
 }
 
+static inline void mmc_host_clk_sysfs_init(struct mmc_host *host)
+{
+       host->clkgate_delay_attr.show = clkgate_delay_show;
+       host->clkgate_delay_attr.store = clkgate_delay_store;
+       sysfs_attr_init(&host->clkgate_delay_attr.attr);
+       host->clkgate_delay_attr.attr.name = "clkgate_delay";
+       host->clkgate_delay_attr.attr.mode = S_IRUGO | S_IWUSR;
+       if (device_create_file(&host->class_dev, &host->clkgate_delay_attr))
+               pr_err("%s: Failed to create clkgate_delay sysfs entry\n",
+                               mmc_hostname(host));
+}
 #else
 
 static inline void mmc_host_clk_init(struct mmc_host *host)
@@ -247,6 +287,10 @@ static inline void mmc_host_clk_exit(struct mmc_host *host)
 {
 }
 
+static inline void mmc_host_clk_sysfs_init(struct mmc_host *host)
+{
+}
+
 #endif
 
 /**
@@ -335,6 +379,7 @@ int mmc_add_host(struct mmc_host *host)
 #ifdef CONFIG_DEBUG_FS
        mmc_add_host_debugfs(host);
 #endif
+       mmc_host_clk_sysfs_init(host);
 
        mmc_start_host(host);
        register_pm_notifier(&host->pm_notify);
index d240427c12462dd545b233d6d6e75787e4b48abb..59b9ba52e66a1fb420ae26f7da3350b0c8d1d592 100644 (file)
@@ -286,6 +286,27 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
        }
        card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE];
        switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) {
+       case EXT_CSD_CARD_TYPE_SDR_ALL:
+       case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_8V:
+       case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_2V:
+       case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_52:
+               card->ext_csd.hs_max_dtr = 200000000;
+               card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_200;
+               break;
+       case EXT_CSD_CARD_TYPE_SDR_1_2V_ALL:
+       case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_8V:
+       case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_2V:
+       case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_52:
+               card->ext_csd.hs_max_dtr = 200000000;
+               card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_2V;
+               break;
+       case EXT_CSD_CARD_TYPE_SDR_1_8V_ALL:
+       case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_8V:
+       case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_2V:
+       case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_52:
+               card->ext_csd.hs_max_dtr = 200000000;
+               card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_8V;
+               break;
        case EXT_CSD_CARD_TYPE_DDR_52 | EXT_CSD_CARD_TYPE_52 |
             EXT_CSD_CARD_TYPE_26:
                card->ext_csd.hs_max_dtr = 52000000;
@@ -348,7 +369,8 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
                                part_size = ext_csd[EXT_CSD_BOOT_MULT] << 17;
                                mmc_part_add(card, part_size,
                                        EXT_CSD_PART_CONFIG_ACC_BOOT0 + idx,
-                                       "boot%d", idx, true);
+                                       "boot%d", idx, true,
+                                       MMC_BLK_DATA_AREA_BOOT);
                        }
                }
        }
@@ -435,7 +457,8 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
                                        hc_wp_grp_sz);
                                mmc_part_add(card, part_size << 19,
                                        EXT_CSD_PART_CONFIG_ACC_GP0 + idx,
-                                       "gp%d", idx, false);
+                                       "gp%d", idx, false,
+                                       MMC_BLK_DATA_AREA_GP);
                        }
                }
                card->ext_csd.sec_trim_mult =
@@ -446,6 +469,14 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
                        ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT];
                card->ext_csd.trim_timeout = 300 *
                        ext_csd[EXT_CSD_TRIM_MULT];
+
+               /*
+                * Note that the call to mmc_part_add above defaults to read
+                * only. If this default assumption is changed, the call must
+                * take into account the value of boot_locked below.
+                */
+               card->ext_csd.boot_ro_lock = ext_csd[EXT_CSD_BOOT_WP];
+               card->ext_csd.boot_ro_lockable = true;
        }
 
        if (card->ext_csd.rev >= 5) {
@@ -689,6 +720,79 @@ static int mmc_select_powerclass(struct mmc_card *card,
        return err;
 }
 
+/*
+ * Selects the desired buswidth and switch to the HS200 mode
+ * if bus width set without error
+ */
+static int mmc_select_hs200(struct mmc_card *card)
+{
+       int idx, err = 0;
+       struct mmc_host *host;
+       static unsigned ext_csd_bits[] = {
+               EXT_CSD_BUS_WIDTH_4,
+               EXT_CSD_BUS_WIDTH_8,
+       };
+       static unsigned bus_widths[] = {
+               MMC_BUS_WIDTH_4,
+               MMC_BUS_WIDTH_8,
+       };
+
+       BUG_ON(!card);
+
+       host = card->host;
+
+       if (card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_2V &&
+           host->caps2 & MMC_CAP2_HS200_1_2V_SDR)
+               if (mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120, 0))
+                       err = mmc_set_signal_voltage(host,
+                                                    MMC_SIGNAL_VOLTAGE_180, 0);
+
+       /* If fails try again during next card power cycle */
+       if (err)
+               goto err;
+
+       idx = (host->caps & MMC_CAP_8_BIT_DATA) ? 1 : 0;
+
+       /*
+        * Unlike SD, MMC cards dont have a configuration register to notify
+        * supported bus width. So bus test command should be run to identify
+        * the supported bus width or compare the ext csd values of current
+        * bus width and ext csd values of 1 bit mode read earlier.
+        */
+       for (; idx >= 0; idx--) {
+
+               /*
+                * Host is capable of 8bit transfer, then switch
+                * the device to work in 8bit transfer mode. If the
+                * mmc switch command returns error then switch to
+                * 4bit transfer mode. On success set the corresponding
+                * bus width on the host.
+                */
+               err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+                                EXT_CSD_BUS_WIDTH,
+                                ext_csd_bits[idx],
+                                card->ext_csd.generic_cmd6_time);
+               if (err)
+                       continue;
+
+               mmc_set_bus_width(card->host, bus_widths[idx]);
+
+               if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST))
+                       err = mmc_compare_ext_csds(card, bus_widths[idx]);
+               else
+