diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2010-07-16 09:15:27 +1000 |
---|---|---|
committer | Stephen Rothwell <sfr@canb.auug.org.au> | 2010-07-16 09:15:27 +1000 |
commit | d85a129ef5ff8cca6c84a9fb04e4dcede799cde6 (patch) | |
tree | 7a214bf20bce440e51d6c555fe7077098610a62e /arch | |
parent | b2b814b6099b9e1a2ea4f2be181ce54f1af1ab02 (diff) | |
parent | 54cdb0768e086e9a20482f2ae3c77ef582135902 (diff) |
Merge remote branch 'msm/for-next'
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-msm/Kconfig | 7 | ||||
-rw-r--r-- | arch/arm/mach-msm/Makefile | 4 | ||||
-rw-r--r-- | arch/arm/mach-msm/acpuclock-arm11.c | 1 | ||||
-rw-r--r-- | arch/arm/mach-msm/board-msm7x30.c | 28 | ||||
-rw-r--r-- | arch/arm/mach-msm/board-qsd8x50.c | 27 | ||||
-rw-r--r-- | arch/arm/mach-msm/board-trout-gpio.c | 115 | ||||
-rw-r--r-- | arch/arm/mach-msm/board-trout-mmc.c | 189 | ||||
-rw-r--r-- | arch/arm/mach-msm/board-trout.c | 11 | ||||
-rw-r--r-- | arch/arm/mach-msm/clock.c | 1 | ||||
-rw-r--r-- | arch/arm/mach-msm/devices-msm7x30.c | 21 | ||||
-rw-r--r-- | arch/arm/mach-msm/devices-qsd8x50.c | 21 | ||||
-rw-r--r-- | arch/arm/mach-msm/devices.h | 33 | ||||
-rw-r--r-- | arch/arm/mach-msm/gpio_hw-7x30.h | 100 | ||||
-rw-r--r-- | arch/arm/mach-msm/gpio_hw-7xxx.h | 84 | ||||
-rw-r--r-- | arch/arm/mach-msm/gpio_hw-8x50.h | 100 | ||||
-rw-r--r-- | arch/arm/mach-msm/gpio_hw.h | 35 | ||||
-rw-r--r-- | arch/arm/mach-msm/include/mach/gpio.h | 2 | ||||
-rw-r--r-- | arch/arm/mach-msm/msm7200a-gpio.c | 421 | ||||
-rw-r--r-- | arch/arm/mach-msm/msm7200a-gpio.h | 87 |
19 files changed, 1278 insertions, 9 deletions
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index 47264a76eeb3..737cf3ea543b 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -10,6 +10,7 @@ config ARCH_MSM7X00A select MSM_SMD select MSM_SMD_PKG3 select CPU_V6 + select MSM7200A_GPIO config ARCH_MSM7X30 bool "MSM7x30" @@ -18,6 +19,7 @@ config ARCH_MSM7X30 select MSM_VIC select CPU_V7 select MSM_REMOTE_SPINLOCK_DEKKERS + select MSM7200A_GPIO config ARCH_QSD8X50 bool "QSD8X50" @@ -26,6 +28,8 @@ config ARCH_QSD8X50 select MSM_VIC select CPU_V7 select MSM_REMOTE_SPINLOCK_LDREX + select MSM7200A_GPIO + endchoice config MSM_SOC_REV_A @@ -106,4 +110,7 @@ config MSM_SMD_PKG3 config MSM_SMD bool +config MSM7200A_GPIO + bool + endif diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 7ff8020d4d24..df7bf971258b 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -15,8 +15,8 @@ obj-$(CONFIG_ARCH_QSD8X50) += sirc.o obj-$(CONFIG_MSM_SMD) += smd.o smd_debug.o obj-$(CONFIG_MSM_SMD) += last_radio_log.o -obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o devices-msm7x00.o +obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o obj-$(CONFIG_ARCH_QSD8X50) += board-qsd8x50.o devices-qsd8x50.o - +obj-$(CONFIG_MSM7200A_GPIO) += msm7200a-gpio.o diff --git a/arch/arm/mach-msm/acpuclock-arm11.c b/arch/arm/mach-msm/acpuclock-arm11.c index af5e85b91d02..6ca42fd5ce2a 100644 --- a/arch/arm/mach-msm/acpuclock-arm11.c +++ b/arch/arm/mach-msm/acpuclock-arm11.c @@ -17,7 +17,6 @@ * */ -#include <linux/version.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/list.h> diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c index e32981928c77..2db85d5dde0e 100644 --- a/arch/arm/mach-msm/board-msm7x30.c +++ b/arch/arm/mach-msm/board-msm7x30.c @@ -55,7 +55,29 @@ static void msm7x30_init_uart2(void) } #endif -static struct platform_device *devices[] __initdata = { +/* + * Early devices are those which provide a system service which will be + * required by one or more of the function calls in msm7x30_init. + * These devices must be probed and online first in order for + * the init routine to run successfully. + */ +static struct platform_device *early_devices[] __initdata = { + &msm_gpio_devices[0], + &msm_gpio_devices[1], + &msm_gpio_devices[2], + &msm_gpio_devices[3], + &msm_gpio_devices[4], + &msm_gpio_devices[5], + &msm_gpio_devices[6], + &msm_gpio_devices[7], +}; + +/* + * Late devices are those which are dependent upon services initialized + * by msm7x30_init, or which simply have no dependents and can have + * their initialization deferred. + */ +static struct platform_device *late_devices[] __initdata = { #if defined(CONFIG_SERIAL_MSM) || defined(CONFIG_MSM_SERIAL_DEBUGGER) &msm_device_uart2, #endif @@ -69,11 +91,11 @@ static void __init msm7x30_init_irq(void) static void __init msm7x30_init(void) { - platform_add_devices(devices, ARRAY_SIZE(devices)); + platform_add_devices(early_devices, ARRAY_SIZE(early_devices)); #ifdef CONFIG_SERIAL_MSM_CONSOLE msm7x30_init_uart2(); #endif - + platform_add_devices(late_devices, ARRAY_SIZE(late_devices)); } static void __init msm7x30_map_io(void) diff --git a/arch/arm/mach-msm/board-qsd8x50.c b/arch/arm/mach-msm/board-qsd8x50.c index e3cc80792d6c..508333c999ef 100644 --- a/arch/arm/mach-msm/board-qsd8x50.c +++ b/arch/arm/mach-msm/board-qsd8x50.c @@ -40,7 +40,29 @@ static struct msm_gpio uart3_config_data[] = { { GPIO_CFG(87, 1, GPIO_OUTPUT, GPIO_PULL_DOWN, GPIO_2MA), "UART2_Tx"}, }; -static struct platform_device *devices[] __initdata = { +/* + * Early devices are those which provide a system service which will be + * required by one or more of the function calls in qsd8x50_init. + * These devices must be probed and online first in order for + * the init routine to run successfully. + */ +static struct platform_device *early_devices[] __initdata = { + &msm_gpio_devices[0], + &msm_gpio_devices[1], + &msm_gpio_devices[2], + &msm_gpio_devices[3], + &msm_gpio_devices[4], + &msm_gpio_devices[5], + &msm_gpio_devices[6], + &msm_gpio_devices[7], +}; + +/* + * Late devices are those which are dependent upon services initialized + * by qsd8x50_init, or which simply have no dependents and can have + * their initialization deferred. + */ +static struct platform_device *late_devices[] __initdata = { &msm_device_uart3, }; @@ -64,8 +86,9 @@ static void __init qsd8x50_init_irq(void) static void __init qsd8x50_init(void) { + platform_add_devices(early_devices, ARRAY_SIZE(early_devices)); msm8x50_init_uart3(); - platform_add_devices(devices, ARRAY_SIZE(devices)); + platform_add_devices(late_devices, ARRAY_SIZE(late_devices)); } MACHINE_START(QSD8X50_SURF, "QCT QSD8X50 SURF") diff --git a/arch/arm/mach-msm/board-trout-gpio.c b/arch/arm/mach-msm/board-trout-gpio.c index 523d213bf79e..c50f3afc3134 100644 --- a/arch/arm/mach-msm/board-trout-gpio.c +++ b/arch/arm/mach-msm/board-trout-gpio.c @@ -15,10 +15,20 @@ #include <linux/module.h> #include <linux/io.h> #include <linux/irq.h> +#include <linux/interrupt.h> #include <linux/gpio.h> #include "board-trout.h" +static uint8_t trout_int_mask[2] = { + [0] = 0xff, /* mask all interrupts */ + [1] = 0xff, +}; +static uint8_t trout_sleep_int_mask[] = { + [0] = 0xff, + [1] = 0xff, +}; + struct msm_gpio_chip { struct gpio_chip chip; void __iomem *reg; /* Base of register bank */ @@ -95,16 +105,121 @@ static struct msm_gpio_chip msm_gpio_banks[] = { TROUT_GPIO_BANK("VIRTUAL", 0x12, TROUT_GPIO_VIRTUAL_BASE, 0), }; +static void trout_gpio_irq_ack(unsigned int irq) +{ + int bank = TROUT_INT_TO_BANK(irq); + uint8_t mask = TROUT_INT_TO_MASK(irq); + int reg = TROUT_BANK_TO_STAT_REG(bank); + /*printk(KERN_INFO "trout_gpio_irq_ack irq %d\n", irq);*/ + writeb(mask, TROUT_CPLD_BASE + reg); +} + +static void trout_gpio_irq_mask(unsigned int irq) +{ + unsigned long flags; + uint8_t reg_val; + int bank = TROUT_INT_TO_BANK(irq); + uint8_t mask = TROUT_INT_TO_MASK(irq); + int reg = TROUT_BANK_TO_MASK_REG(bank); + + local_irq_save(flags); + reg_val = trout_int_mask[bank] |= mask; + /*printk(KERN_INFO "trout_gpio_irq_mask irq %d => %d:%02x\n", + irq, bank, reg_val);*/ + writeb(reg_val, TROUT_CPLD_BASE + reg); + local_irq_restore(flags); +} + +static void trout_gpio_irq_unmask(unsigned int irq) +{ + unsigned long flags; + uint8_t reg_val; + int bank = TROUT_INT_TO_BANK(irq); + uint8_t mask = TROUT_INT_TO_MASK(irq); + int reg = TROUT_BANK_TO_MASK_REG(bank); + + local_irq_save(flags); + reg_val = trout_int_mask[bank] &= ~mask; + /*printk(KERN_INFO "trout_gpio_irq_unmask irq %d => %d:%02x\n", + irq, bank, reg_val);*/ + writeb(reg_val, TROUT_CPLD_BASE + reg); + local_irq_restore(flags); +} + +int trout_gpio_irq_set_wake(unsigned int irq, unsigned int on) +{ + unsigned long flags; + int bank = TROUT_INT_TO_BANK(irq); + uint8_t mask = TROUT_INT_TO_MASK(irq); + + local_irq_save(flags); + if(on) + trout_sleep_int_mask[bank] &= ~mask; + else + trout_sleep_int_mask[bank] |= mask; + local_irq_restore(flags); + return 0; +} + +static void trout_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + int j, m; + unsigned v; + int bank; + int stat_reg; + int int_base = TROUT_INT_START; + uint8_t int_mask; + + for (bank = 0; bank < 2; bank++) { + stat_reg = TROUT_BANK_TO_STAT_REG(bank); + v = readb(TROUT_CPLD_BASE + stat_reg); + int_mask = trout_int_mask[bank]; + if (v & int_mask) { + writeb(v & int_mask, TROUT_CPLD_BASE + stat_reg); + printk(KERN_ERR "trout_gpio_irq_handler: got masked " + "interrupt: %d:%02x\n", bank, v & int_mask); + } + v &= ~int_mask; + while (v) { + m = v & -v; + j = fls(m) - 1; + /*printk(KERN_INFO "msm_gpio_irq_handler %d:%02x %02x b" + "it %d irq %d\n", bank, v, m, j, int_base + j);*/ + v &= ~m; + generic_handle_irq(int_base + j); + } + int_base += TROUT_INT_BANK0_COUNT; + } + desc->chip->ack(irq); +} + +static struct irq_chip trout_gpio_irq_chip = { + .name = "troutgpio", + .ack = trout_gpio_irq_ack, + .mask = trout_gpio_irq_mask, + .unmask = trout_gpio_irq_unmask, + .set_wake = trout_gpio_irq_set_wake, +}; + /* * Called from the processor-specific init to enable GPIO pin support. */ int __init trout_init_gpio(void) { int i; + for(i = TROUT_INT_START; i <= TROUT_INT_END; i++) { + set_irq_chip(i, &trout_gpio_irq_chip); + set_irq_handler(i, handle_edge_irq); + set_irq_flags(i, IRQF_VALID); + } for (i = 0; i < ARRAY_SIZE(msm_gpio_banks); i++) gpiochip_add(&msm_gpio_banks[i].chip); + set_irq_type(MSM_GPIO_TO_INT(17), IRQF_TRIGGER_HIGH); + set_irq_chained_handler(MSM_GPIO_TO_INT(17), trout_gpio_irq_handler); + set_irq_wake(MSM_GPIO_TO_INT(17), 1); + return 0; } diff --git a/arch/arm/mach-msm/board-trout-mmc.c b/arch/arm/mach-msm/board-trout-mmc.c new file mode 100644 index 000000000000..7a773f8c13dc --- /dev/null +++ b/arch/arm/mach-msm/board-trout-mmc.c @@ -0,0 +1,189 @@ +/* linux/arch/arm/mach-msm/board-trout-mmc.c +** Author: Brian Swetland <swetland@google.com> +*/ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/mmc/host.h> +#include <linux/mmc/sdio_ids.h> +#include <linux/err.h> +#include <linux/debugfs.h> + +#include <asm/gpio.h> +#include <asm/io.h> + +#include <mach/vreg.h> + +#include <mach/mmc.h> + +#include "devices.h" + +#include "board-trout.h" + +#include "proc_comm.h" + +#define DEBUG_SDSLOT_VDD 1 + +extern int msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat, + unsigned int stat_irq, unsigned long stat_irq_flags); + +/* ---- COMMON ---- */ +static void config_gpio_table(uint32_t *table, int len) +{ + int n; + unsigned id; + for(n = 0; n < len; n++) { + id = table[n]; + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + } +} + +/* ---- SDCARD ---- */ + +static uint32_t sdcard_on_gpio_table[] = { + PCOM_GPIO_CFG(62, 2, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */ + PCOM_GPIO_CFG(63, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* CMD */ + PCOM_GPIO_CFG(64, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* DAT3 */ + PCOM_GPIO_CFG(65, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* DAT2 */ + PCOM_GPIO_CFG(66, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(67, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT0 */ +}; + +static uint32_t sdcard_off_gpio_table[] = { + PCOM_GPIO_CFG(62, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CLK */ + PCOM_GPIO_CFG(63, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CMD */ + PCOM_GPIO_CFG(64, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(65, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(66, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(67, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */ +}; + +static uint opt_disable_sdcard; + +static int __init trout_disablesdcard_setup(char *str) +{ + int cal = simple_strtol(str, NULL, 0); + + opt_disable_sdcard = cal; + return 1; +} + +__setup("board_trout.disable_sdcard=", trout_disablesdcard_setup); + +static struct vreg *vreg_sdslot; /* SD slot power */ + +struct mmc_vdd_xlat { + int mask; + int level; +}; + +static struct mmc_vdd_xlat mmc_vdd_table[] = { + { MMC_VDD_165_195, 1800 }, + { MMC_VDD_20_21, 2050 }, + { MMC_VDD_21_22, 2150 }, + { MMC_VDD_22_23, 2250 }, + { MMC_VDD_23_24, 2350 }, + { MMC_VDD_24_25, 2450 }, + { MMC_VDD_25_26, 2550 }, + { MMC_VDD_26_27, 2650 }, + { MMC_VDD_27_28, 2750 }, + { MMC_VDD_28_29, 2850 }, + { MMC_VDD_29_30, 2950 }, +}; + +static unsigned int sdslot_vdd = 0xffffffff; +static unsigned int sdslot_vreg_enabled; + +static uint32_t trout_sdslot_switchvdd(struct device *dev, unsigned int vdd) +{ + int i, rc; + + BUG_ON(!vreg_sdslot); + + if (vdd == sdslot_vdd) + return 0; + + sdslot_vdd = vdd; + + if (vdd == 0) { +#if DEBUG_SDSLOT_VDD + printk("%s: Disabling SD slot power\n", __func__); +#endif + config_gpio_table(sdcard_off_gpio_table, + ARRAY_SIZE(sdcard_off_gpio_table)); + vreg_disable(vreg_sdslot); + sdslot_vreg_enabled = 0; + return 0; + } + + if (!sdslot_vreg_enabled) { + rc = vreg_enable(vreg_sdslot); + if (rc) { + printk(KERN_ERR "%s: Error enabling vreg (%d)\n", + __func__, rc); + } + config_gpio_table(sdcard_on_gpio_table, + ARRAY_SIZE(sdcard_on_gpio_table)); + sdslot_vreg_enabled = 1; + } + + for (i = 0; i < ARRAY_SIZE(mmc_vdd_table); i++) { + if (mmc_vdd_table[i].mask == (1 << vdd)) { +#if DEBUG_SDSLOT_VDD + printk("%s: Setting level to %u\n", + __func__, mmc_vdd_table[i].level); +#endif + rc = vreg_set_level(vreg_sdslot, + mmc_vdd_table[i].level); + if (rc) { + printk(KERN_ERR + "%s: Error setting vreg level (%d)\n", + __func__, rc); + } + return 0; + } + } + + printk(KERN_ERR "%s: Invalid VDD %d specified\n", __func__, vdd); + return 0; +} + +static unsigned int trout_sdslot_status(struct device *dev) +{ + unsigned int status; + + status = (unsigned int) gpio_get_value(TROUT_GPIO_SDMC_CD_N); + return (!status); +} + +#define TROUT_MMC_VDD MMC_VDD_165_195 | MMC_VDD_20_21 | MMC_VDD_21_22 \ + | MMC_VDD_22_23 | MMC_VDD_23_24 | MMC_VDD_24_25 \ + | MMC_VDD_25_26 | MMC_VDD_26_27 | MMC_VDD_27_28 \ + | MMC_VDD_28_29 | MMC_VDD_29_30 + +static struct mmc_platform_data trout_sdslot_data = { + .ocr_mask = TROUT_MMC_VDD, + .status = trout_sdslot_status, + .translate_vdd = trout_sdslot_switchvdd, +}; + +int __init trout_init_mmc(unsigned int sys_rev) +{ + sdslot_vreg_enabled = 0; + + vreg_sdslot = vreg_get(0, "gp6"); + if (IS_ERR(vreg_sdslot)) + return PTR_ERR(vreg_sdslot); + + set_irq_wake(TROUT_GPIO_TO_INT(TROUT_GPIO_SDMC_CD_N), 1); + + if (!opt_disable_sdcard) + msm_add_sdcc(2, &trout_sdslot_data, + TROUT_GPIO_TO_INT(TROUT_GPIO_SDMC_CD_N), 0); + else + printk(KERN_INFO "trout: SD-Card interface disabled\n"); + return 0; +} + diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c index e69a1502e4e8..469e0be3499d 100644 --- a/arch/arm/mach-msm/board-trout.c +++ b/arch/arm/mach-msm/board-trout.c @@ -30,6 +30,8 @@ #include "devices.h" #include "board-trout.h" +extern int trout_init_mmc(unsigned int); + static struct platform_device *devices[] __initdata = { &msm_device_uart3, &msm_device_smd, @@ -55,7 +57,16 @@ static void __init trout_fixup(struct machine_desc *desc, struct tag *tags, static void __init trout_init(void) { + int rc; + platform_add_devices(devices, ARRAY_SIZE(devices)); + +#ifdef CONFIG_MMC + rc = trout_init_mmc(system_rev); + if (rc) + printk(KERN_CRIT "%s: MMC init failure (%d)\n", __func__, rc); +#endif + } static struct map_desc trout_io_desc[] __initdata = { diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c index 9cb1276ab749..c57210f4f06a 100644 --- a/arch/arm/mach-msm/clock.c +++ b/arch/arm/mach-msm/clock.c @@ -14,7 +14,6 @@ * */ -#include <linux/version.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> diff --git a/arch/arm/mach-msm/devices-msm7x30.c b/arch/arm/mach-msm/devices-msm7x30.c index b449e8ad2904..ef3185e52c57 100644 --- a/arch/arm/mach-msm/devices-msm7x30.c +++ b/arch/arm/mach-msm/devices-msm7x30.c @@ -126,3 +126,24 @@ struct clk msm_clocks_7x30[] = { unsigned msm_num_clocks_7x30 = ARRAY_SIZE(msm_clocks_7x30); +static struct msm7200a_gpio_platform_data gpio_platform_data[] = { + MSM7200A_GPIO_PLATFORM_DATA(0, 0, 15, INT_GPIO_GROUP1), + MSM7200A_GPIO_PLATFORM_DATA(1, 16, 43, INT_GPIO_GROUP2), + MSM7200A_GPIO_PLATFORM_DATA(2, 44, 67, INT_GPIO_GROUP1), + MSM7200A_GPIO_PLATFORM_DATA(3, 68, 94, INT_GPIO_GROUP1), + MSM7200A_GPIO_PLATFORM_DATA(4, 95, 106, INT_GPIO_GROUP1), + MSM7200A_GPIO_PLATFORM_DATA(5, 107, 133, INT_GPIO_GROUP1), + MSM7200A_GPIO_PLATFORM_DATA(6, 134, 150, INT_GPIO_GROUP1), + MSM7200A_GPIO_PLATFORM_DATA(7, 151, 181, INT_GPIO_GROUP1), +}; + +struct platform_device msm_gpio_devices[] = { + MSM7200A_GPIO_DEVICE(0, gpio_platform_data), + MSM7200A_GPIO_DEVICE(1, gpio_platform_data), + MSM7200A_GPIO_DEVICE(2, gpio_platform_data), + MSM7200A_GPIO_DEVICE(3, gpio_platform_data), + MSM7200A_GPIO_DEVICE(4, gpio_platform_data), + MSM7200A_GPIO_DEVICE(5, gpio_platform_data), + MSM7200A_GPIO_DEVICE(6, gpio_platform_data), + MSM7200A_GPIO_DEVICE(7, gpio_platform_data), +}; diff --git a/arch/arm/mach-msm/devices-qsd8x50.c b/arch/arm/mach-msm/devices-qsd8x50.c index 4d4a50785e34..2342654ae58d 100644 --- a/arch/arm/mach-msm/devices-qsd8x50.c +++ b/arch/arm/mach-msm/devices-qsd8x50.c @@ -90,3 +90,24 @@ struct clk msm_clocks_8x50[] = { unsigned msm_num_clocks_8x50 = ARRAY_SIZE(msm_clocks_8x50); +static struct msm7200a_gpio_platform_data gpio_platform_data[] = { + MSM7200A_GPIO_PLATFORM_DATA(0, 0, 15, INT_GPIO_GROUP1), + MSM7200A_GPIO_PLATFORM_DATA(1, 16, 42, INT_GPIO_GROUP2), + MSM7200A_GPIO_PLATFORM_DATA(2, 43, 67, INT_GPIO_GROUP1), + MSM7200A_GPIO_PLATFORM_DATA(3, 68, 94, INT_GPIO_GROUP1), + MSM7200A_GPIO_PLATFORM_DATA(4, 95, 103, INT_GPIO_GROUP1), + MSM7200A_GPIO_PLATFORM_DATA(5, 104, 121, INT_GPIO_GROUP1), + MSM7200A_GPIO_PLATFORM_DATA(6, 122, 152, INT_GPIO_GROUP1), + MSM7200A_GPIO_PLATFORM_DATA(7, 153, 164, INT_GPIO_GROUP1), +}; + +struct platform_device msm_gpio_devices[] = { + MSM7200A_GPIO_DEVICE(0, gpio_platform_data), + MSM7200A_GPIO_DEVICE(1, gpio_platform_data), + MSM7200A_GPIO_DEVICE(2, gpio_platform_data), + MSM7200A_GPIO_DEVICE(3, gpio_platform_data), + MSM7200A_GPIO_DEVICE(4, gpio_platform_data), + MSM7200A_GPIO_DEVICE(5, gpio_platform_data), + MSM7200A_GPIO_DEVICE(6, gpio_platform_data), + MSM7200A_GPIO_DEVICE(7, gpio_platform_data), +}; diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h index 568443e76423..fd71edceb19b 100644 --- a/arch/arm/mach-msm/devices.h +++ b/arch/arm/mach-msm/devices.h @@ -17,6 +17,8 @@ #define __ARCH_ARM_MACH_MSM_DEVICES_H #include "clock.h" +#include "gpio_hw.h" +#include "msm7200a-gpio.h" extern struct platform_device msm_device_uart1; extern struct platform_device msm_device_uart2; @@ -44,4 +46,35 @@ extern unsigned msm_num_clocks_7x30; extern struct clk msm_clocks_8x50[]; extern unsigned msm_num_clocks_8x50; +#define MSM7200A_GPIO_PLATFORM_DATA(ix, begin, end, irq) \ + [ix] = { \ + .gpio_base = begin, \ + .ngpio = end - begin + 1, \ + .irq_base = MSM_GPIO_TO_INT(begin), \ + .irq_summary = irq, \ + .latch_level_irqs = false, \ + .regs = { \ + .in = GPIO_IN_ ## ix, \ + .out = GPIO_OUT_ ## ix, \ + .oe = GPIO_OE_ ## ix, \ + .int_status = GPIO_INT_STATUS_ ## ix, \ + .int_clear = GPIO_INT_CLEAR_ ## ix, \ + .int_en = GPIO_INT_EN_ ## ix, \ + .int_edge = GPIO_INT_EDGE_ ## ix, \ + .int_pos = GPIO_INT_POS_ ## ix, \ + }, \ + } + +#define MSM7200A_GPIO_DEVICE(ix, pdata) \ + { \ + .name = "msm7200a-gpio", \ + .id = ix, \ + .num_resources = 0, \ + .dev = { \ + .platform_data = &pdata[ix], \ + }, \ + } + +extern struct platform_device msm_gpio_devices[]; + #endif diff --git a/arch/arm/mach-msm/gpio_hw-7x30.h b/arch/arm/mach-msm/gpio_hw-7x30.h new file mode 100644 index 000000000000..8c28a3f72010 --- /dev/null +++ b/arch/arm/mach-msm/gpio_hw-7x30.h @@ -0,0 +1,100 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ +#ifndef __ARCH_ARM_MACH_MSM_GPIO_HW_7X30_H +#define __ARCH_ARM_MACH_MSM_GPIO_HW_7X30_H + +/* output value */ +#define GPIO_OUT_0 GPIO1_REG(0x00) /* gpio 15-0 */ +#define GPIO_OUT_1 GPIO2_REG(0x00) /* gpio 43-16 */ +#define GPIO_OUT_2 GPIO1_REG(0x04) /* gpio 67-44 */ +#define GPIO_OUT_3 GPIO1_REG(0x08) /* gpio 94-68 */ +#define GPIO_OUT_4 GPIO1_REG(0x0C) /* gpio 106-95 */ +#define GPIO_OUT_5 GPIO1_REG(0x50) /* gpio 133-107 */ +#define GPIO_OUT_6 GPIO1_REG(0xC4) /* gpio 150-134 */ +#define GPIO_OUT_7 GPIO1_REG(0x214) /* gpio 181-151 */ + +/* same pin map as above, output enable */ +#define GPIO_OE_0 GPIO1_REG(0x10) +#define GPIO_OE_1 GPIO2_REG(0x08) +#define GPIO_OE_2 GPIO1_REG(0x14) +#define GPIO_OE_3 GPIO1_REG(0x18) +#define GPIO_OE_4 GPIO1_REG(0x1C) +#define GPIO_OE_5 GPIO1_REG(0x54) +#define GPIO_OE_6 GPIO1_REG(0xC8) +#define GPIO_OE_7 GPIO1_REG(0x218) + +/* same pin map as above, input read */ +#define GPIO_IN_0 GPIO1_REG(0x34) +#define GPIO_IN_1 GPIO2_REG(0x20) +#define GPIO_IN_2 GPIO1_REG(0x38) +#define GPIO_IN_3 GPIO1_REG(0x3C) +#define GPIO_IN_4 GPIO1_REG(0x40) +#define GPIO_IN_5 GPIO1_REG(0x44) +#define GPIO_IN_6 GPIO1_REG(0xCC) +#define GPIO_IN_7 GPIO1_REG(0x21C) + +/* same pin map as above, 1=edge 0=level interrupt */ +#define GPIO_INT_EDGE_0 GPIO1_REG(0x60) +#define GPIO_INT_EDGE_1 GPIO2_REG(0x50) +#define GPIO_INT_EDGE_2 GPIO1_REG(0x64) +#define GPIO_INT_EDGE_3 GPIO1_REG(0x68) +#define GPIO_INT_EDGE_4 GPIO1_REG(0x6C) +#define GPIO_INT_EDGE_5 GPIO1_REG(0xC0) +#define GPIO_INT_EDGE_6 GPIO1_REG(0xD0) +#define GPIO_INT_EDGE_7 GPIO1_REG(0x240) + +/* same pin map as above, 1=positive 0=negative */ +#define GPIO_INT_POS_0 GPIO1_REG(0x70) +#define GPIO_INT_POS_1 GPIO2_REG(0x58) +#define GPIO_INT_POS_2 GPIO1_REG(0x74) +#define GPIO_INT_POS_3 GPIO1_REG(0x78) +#define GPIO_INT_POS_4 GPIO1_REG(0x7C) +#define GPIO_INT_POS_5 GPIO1_REG(0xBC) +#define GPIO_INT_POS_6 GPIO1_REG(0xD4) +#define GPIO_INT_POS_7 GPIO1_REG(0x228) + +/* same pin map as above, interrupt enable */ +#define GPIO_INT_EN_0 GPIO1_REG(0x80) +#define GPIO_INT_EN_1 GPIO2_REG(0x60) +#define GPIO_INT_EN_2 GPIO1_REG(0x84) +#define GPIO_INT_EN_3 GPIO1_REG(0x88) +#define GPIO_INT_EN_4 GPIO1_REG(0x8C) +#define GPIO_INT_EN_5 GPIO1_REG(0xB8) +#define GPIO_INT_EN_6 GPIO1_REG(0xD8) +#define GPIO_INT_EN_7 GPIO1_REG(0x22C) + +/* same pin map as above, write 1 to clear interrupt */ +#define GPIO_INT_CLEAR_0 GPIO1_REG(0x90) +#define GPIO_INT_CLEAR_1 GPIO2_REG(0x68) +#define GPIO_INT_CLEAR_2 GPIO1_REG(0x94) +#define GPIO_INT_CLEAR_3 GPIO1_REG(0x98) +#define GPIO_INT_CLEAR_4 GPIO1_REG(0x9C) +#define GPIO_INT_CLEAR_5 GPIO1_REG(0xB4) +#define GPIO_INT_CLEAR_6 GPIO1_REG(0xDC) +#define GPIO_INT_CLEAR_7 GPIO1_REG(0x230) + +/* same pin map as above, 1=interrupt pending */ +#define GPIO_INT_STATUS_0 GPIO1_REG(0xA0) +#define GPIO_INT_STATUS_1 GPIO2_REG(0x70) +#define GPIO_INT_STATUS_2 GPIO1_REG(0xA4) +#define GPIO_INT_STATUS_3 GPIO1_REG(0xA8) +#define GPIO_INT_STATUS_4 GPIO1_REG(0xAC) +#define GPIO_INT_STATUS_5 GPIO1_REG(0xB0) +#define GPIO_INT_STATUS_6 GPIO1_REG(0xE0) +#define GPIO_INT_STATUS_7 GPIO1_REG(0x234) + +#endif diff --git a/arch/arm/mach-msm/gpio_hw-7xxx.h b/arch/arm/mach-msm/gpio_hw-7xxx.h new file mode 100644 index 000000000000..3408478ba9df --- /dev/null +++ b/arch/arm/mach-msm/gpio_hw-7xxx.h @@ -0,0 +1,84 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ +#ifndef __ARCH_ARM_MACH_MSM_GPIO_HW_7XXX_H +#define __ARCH_ARM_MACH_MSM_GPIO_HW_7XXX_H + +/* output value */ +#define GPIO_OUT_0 GPIO1_REG(0x00) /* gpio 15-0 */ +#define GPIO_OUT_1 GPIO2_REG(0x00) /* gpio 42-16 */ +#define GPIO_OUT_2 GPIO1_REG(0x04) /* gpio 67-43 */ +#define GPIO_OUT_3 GPIO1_REG(0x08) /* gpio 94-68 */ +#define GPIO_OUT_4 GPIO1_REG(0x0C) /* gpio 106-95 */ +#define GPIO_OUT_5 GPIO1_REG(0x50) /* gpio 107-121 */ + +/* same pin map as above, output enable */ +#define GPIO_OE_0 GPIO1_REG(0x10) +#define GPIO_OE_1 GPIO2_REG(0x08) +#define GPIO_OE_2 GPIO1_REG(0x14) +#define GPIO_OE_3 GPIO1_REG(0x18) +#define GPIO_OE_4 GPIO1_REG(0x1C) +#define GPIO_OE_5 GPIO1_REG(0x54) + +/* same pin map as above, input read */ +#define GPIO_IN_0 GPIO1_REG(0x34) +#define GPIO_IN_1 GPIO2_REG(0x20) +#define GPIO_IN_2 GPIO1_REG(0x38) +#define GPIO_IN_3 GPIO1_REG(0x3C) +#define GPIO_IN_4 GPIO1_REG(0x40) +#define GPIO_IN_5 GPIO1_REG(0x44) + +/* same pin map as above, 1=edge 0=level interrupt */ +#define GPIO_INT_EDGE_0 GPIO1_REG(0x60) +#define GPIO_INT_EDGE_1 GPIO2_REG(0x50) +#define GPIO_INT_EDGE_2 GPIO1_REG(0x64) +#define GPIO_INT_EDGE_3 GPIO1_REG(0x68) +#define GPIO_INT_EDGE_4 GPIO1_REG(0x6C) +#define GPIO_INT_EDGE_5 GPIO1_REG(0xC0) + +/* same pin map as above, 1=positive 0=negative */ +#define GPIO_INT_POS_0 GPIO1_REG(0x70) +#define GPIO_INT_POS_1 GPIO2_REG(0x58) +#define GPIO_INT_POS_2 GPIO1_REG(0x74) +#define GPIO_INT_POS_3 GPIO1_REG(0x78) +#define GPIO_INT_POS_4 GPIO1_REG(0x7C) +#define GPIO_INT_POS_5 GPIO1_REG(0xBC) + +/* same pin map as above, interrupt enable */ +#define GPIO_INT_EN_0 GPIO1_REG(0x80) +#define GPIO_INT_EN_1 GPIO2_REG(0x60) +#define GPIO_INT_EN_2 GPIO1_REG(0x84) +#define GPIO_INT_EN_3 GPIO1_REG(0x88) +#define GPIO_INT_EN_4 GPIO1_REG(0x8C) +#define GPIO_INT_EN_5 GPIO1_REG(0xB8) + +/* same pin map as above, write 1 to clear interrupt */ +#define GPIO_INT_CLEAR_0 GPIO1_REG(0x90) +#define GPIO_INT_CLEAR_1 GPIO2_REG(0x68) +#define GPIO_INT_CLEAR_2 GPIO1_REG(0x94) +#define GPIO_INT_CLEAR_3 GPIO1_REG(0x98) +#define GPIO_INT_CLEAR_4 GPIO1_REG(0x9C) +#define GPIO_INT_CLEAR_5 GPIO1_REG(0xB4) + +/* same pin map as above, 1=interrupt pending */ +#define GPIO_INT_STATUS_0 GPIO1_REG(0xA0) +#define GPIO_INT_STATUS_1 GPIO2_REG(0x70) +#define GPIO_INT_STATUS_2 GPIO1_REG(0xA4) +#define GPIO_INT_STATUS_3 GPIO1_REG(0xA8) +#define GPIO_INT_STATUS_4 GPIO1_REG(0xAC) +#define GPIO_INT_STATUS_5 GPIO1_REG(0xB0) + +#endif diff --git a/arch/arm/mach-msm/gpio_hw-8x50.h b/arch/arm/mach-msm/gpio_hw-8x50.h new file mode 100644 index 000000000000..94319435ae61 --- /dev/null +++ b/arch/arm/mach-msm/gpio_hw-8x50.h @@ -0,0 +1,100 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ +#ifndef __ARCH_ARM_MACH_MSM_GPIO_HW_8X50_H +#define __ARCH_ARM_MACH_MSM_GPIO_HW_8X50_H + +/* output value */ +#define GPIO_OUT_0 GPIO1_REG(0x00) /* gpio 15-0 */ +#define GPIO_OUT_1 GPIO2_REG(0x00) /* gpio 42-16 */ +#define GPIO_OUT_2 GPIO1_REG(0x04) /* gpio 67-43 */ +#define GPIO_OUT_3 GPIO1_REG(0x08) /* gpio 94-68 */ +#define GPIO_OUT_4 GPIO1_REG(0x0C) /* gpio 103-95 */ +#define GPIO_OUT_5 GPIO1_REG(0x10) /* gpio 121-104 */ +#define GPIO_OUT_6 GPIO1_REG(0x14) /* gpio 152-122 */ +#define GPIO_OUT_7 GPIO1_REG(0x18) /* gpio 164-153 */ + +/* same pin map as above, output enable */ +#define GPIO_OE_0 GPIO1_REG(0x20) +#define GPIO_OE_1 GPIO2_REG(0x08) +#define GPIO_OE_2 GPIO1_REG(0x24) +#define GPIO_OE_3 GPIO1_REG(0x28) +#define GPIO_OE_4 GPIO1_REG(0x2C) +#define GPIO_OE_5 GPIO1_REG(0x30) +#define GPIO_OE_6 GPIO1_REG(0x34) +#define GPIO_OE_7 GPIO1_REG(0x38) + +/* same pin map as above, input read */ +#define GPIO_IN_0 GPIO1_REG(0x50) +#define GPIO_IN_1 GPIO2_REG(0x20) +#define GPIO_IN_2 GPIO1_REG(0x54) +#define GPIO_IN_3 GPIO1_REG(0x58) +#define GPIO_IN_4 GPIO1_REG(0x5C) +#define GPIO_IN_5 GPIO1_REG(0x60) +#define GPIO_IN_6 GPIO1_REG(0x64) +#define GPIO_IN_7 GPIO1_REG(0x68) + +/* same pin map as above, 1=edge 0=level interrupt */ +#define GPIO_INT_EDGE_0 GPIO1_REG(0x70) +#define GPIO_INT_EDGE_1 GPIO2_REG(0x50) +#define GPIO_INT_EDGE_2 GPIO1_REG(0x74) +#define GPIO_INT_EDGE_3 GPIO1_REG(0x78) +#define GPIO_INT_EDGE_4 GPIO1_REG(0x7C) +#define GPIO_INT_EDGE_5 GPIO1_REG(0x80) +#define GPIO_INT_EDGE_6 GPIO1_REG(0x84) +#define GPIO_INT_EDGE_7 GPIO1_REG(0x88) + +/* same pin map as above, 1=positive 0=negative */ +#define GPIO_INT_POS_0 GPIO1_REG(0x90) +#define GPIO_INT_POS_1 GPIO2_REG(0x58) +#define GPIO_INT_POS_2 GPIO1_REG(0x94) +#define GPIO_INT_POS_3 GPIO1_REG(0x98) +#define GPIO_INT_POS_4 GPIO1_REG(0x9C) +#define GPIO_INT_POS_5 GPIO1_REG(0xA0) +#define GPIO_INT_POS_6 GPIO1_REG(0xA4) +#define GPIO_INT_POS_7 GPIO1_REG(0xA8) + +/* same pin map as above, interrupt enable */ +#define GPIO_INT_EN_0 GPIO1_REG(0xB0) +#define GPIO_INT_EN_1 GPIO2_REG(0x60) +#define GPIO_INT_EN_2 GPIO1_REG(0xB4) +#define GPIO_INT_EN_3 GPIO1_REG(0xB8) +#define GPIO_INT_EN_4 GPIO1_REG(0xBC) +#define GPIO_INT_EN_5 GPIO1_REG(0xC0) +#define GPIO_INT_EN_6 GPIO1_REG(0xC4) +#define GPIO_INT_EN_7 GPIO1_REG(0xC8) + +/* same pin map as above, write 1 to clear interrupt */ +#define GPIO_INT_CLEAR_0 GPIO1_REG(0xD0) +#define GPIO_INT_CLEAR_1 GPIO2_REG(0x68) +#define GPIO_INT_CLEAR_2 GPIO1_REG(0xD4) +#define GPIO_INT_CLEAR_3 GPIO1_REG(0xD8) +#define GPIO_INT_CLEAR_4 GPIO1_REG(0xDC) +#define GPIO_INT_CLEAR_5 GPIO1_REG(0xE0) +#define GPIO_INT_CLEAR_6 GPIO1_REG(0xE4) +#define GPIO_INT_CLEAR_7 GPIO1_REG(0xE8) + +/* same pin map as above, 1=interrupt pending */ +#define GPIO_INT_STATUS_0 GPIO1_REG(0xF0) +#define GPIO_INT_STATUS_1 GPIO2_REG(0x70) +#define GPIO_INT_STATUS_2 GPIO1_REG(0xF4) +#define GPIO_INT_STATUS_3 GPIO1_REG(0xF8) +#define GPIO_INT_STATUS_4 GPIO1_REG(0xFC) +#define GPIO_INT_STATUS_5 GPIO1_REG(0x100) +#define GPIO_INT_STATUS_6 GPIO1_REG(0x104) +#define GPIO_INT_STATUS_7 GPIO1_REG(0x108) + +#endif diff --git a/arch/arm/mach-msm/gpio_hw.h b/arch/arm/mach-msm/gpio_hw.h new file mode 100644 index 000000000000..627dea550ac3 --- /dev/null +++ b/arch/arm/mach-msm/gpio_hw.h @@ -0,0 +1,35 @@ +/* Copyright (c) 2007, Google, Inc. + * Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __ARCH_ARM_MACH_MSM_GPIO_HW_H +#define __ARCH_ARM_MACH_MSM_GPIO_HW_H + +#include <mach/msm_iomap.h> + +#if defined(CONFIG_ARCH_MSM7X30) +#define GPIO1_REG(off) (MSM_GPIO1_BASE + (off)) +#define GPIO2_REG(off) (MSM_GPIO2_BASE + 0x400 + (off)) +#else +#define GPIO1_REG(off) (MSM_GPIO1_BASE + 0x800 + (off)) +#define GPIO2_REG(off) (MSM_GPIO2_BASE + 0xC00 + (off)) +#endif + +#if defined(CONFIG_ARCH_QSD8X50) +#include "gpio_hw-8x50.h" +#elif defined(CONFIG_ARCH_MSM7X30) +#include "gpio_hw-7x30.h" +#else +#include "gpio_hw-7xxx.h" +#endif + +#endif diff --git a/arch/arm/mach-msm/include/mach/gpio.h b/arch/arm/mach-msm/include/mach/gpio.h index 83e47c0d5c2e..ff80758e93b4 100644 --- a/arch/arm/mach-msm/include/mach/gpio.h +++ b/arch/arm/mach-msm/include/mach/gpio.h @@ -16,6 +16,8 @@ #ifndef __ASM_ARCH_MSM_GPIO_H #define __ASM_ARCH_MSM_GPIO_H +#define ARCH_NR_GPIOS 512 + #include <asm-generic/gpio.h> #define gpio_get_value __gpio_get_value diff --git a/arch/arm/mach-msm/msm7200a-gpio.c b/arch/arm/mach-msm/msm7200a-gpio.c new file mode 100644 index 000000000000..a490765e29e4 --- /dev/null +++ b/arch/arm/mach-msm/msm7200a-gpio.c @@ -0,0 +1,421 @@ +/* + * Driver for Qualcomm MSM7200a and related SoC GPIO. + * Supported chipset families include: + * MSM7x01(a), MSM7x25, MSM7x27, MSM7x30, QSD8x50(a) + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ +#include <linux/kernel.h> +#include <linux/gpio.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include "msm7200a-gpio.h" + +enum { + IRQ_MASK_NORMAL = 0, + IRQ_MASK_WAKE_ON, + IRQ_MASK_MAX +}; + +struct msm_gpio_dev { + struct gpio_chip gpio_chip; + spinlock_t lock; + unsigned irq_base; + unsigned irq_summary; + bool latch_level_irqs; + struct msm7200a_gpio_regs regs; + u32 irq_masks[IRQ_MASK_MAX]; + int nsuspend; +}; + +static inline struct msm_gpio_dev *to_msm_gpio_dev(struct gpio_chip *chip) +{ + return container_of(chip, struct msm_gpio_dev, gpio_chip); +} + +/* + * This function assumes that msm_gpio_dev::lock is held. + */ +static inline void set_gpio_bit(unsigned n, void __iomem *reg) +{ + writel(readl(reg) | BIT(n), reg); +} + +/* + * This function assumes that msm_gpio_dev::lock is held. + */ +static inline void clr_gpio_bit(unsigned n, void __iomem *reg) +{ + writel(readl(reg) & ~BIT(n), reg); +} + +/* + * This function assumes that msm_gpio_dev::lock is held. + */ +static inline void +msm_gpio_write(struct msm_gpio_dev *dev, unsigned n, unsigned on) +{ + if (on) + set_gpio_bit(n, dev->regs.out); + else + clr_gpio_bit(n, dev->regs.out); +} + +static int gpio_chip_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct msm_gpio_dev *msm_gpio = to_msm_gpio_dev(chip); + unsigned long irq_flags; + + spin_lock_irqsave(&msm_gpio->lock, irq_flags); + clr_gpio_bit(offset, msm_gpio->regs.oe); + spin_unlock_irqrestore(&msm_gpio->lock, irq_flags); + + return 0; +} + +static int +gpio_chip_direction_output(struct gpio_chip *chip, unsigned offset, int value) +{ + struct msm_gpio_dev *msm_gpio = to_msm_gpio_dev(chip); + unsigned long irq_flags; + + spin_lock_irqsave(&msm_gpio->lock, irq_flags); + msm_gpio_write(msm_gpio, offset, value); + set_gpio_bit(offset, msm_gpio->regs.oe); + spin_unlock_irqrestore(&msm_gpio->lock, irq_flags); + + return 0; +} + +static int gpio_chip_get(struct gpio_chip *chip, unsigned offset) +{ + struct msm_gpio_dev *msm_gpio = to_msm_gpio_dev(chip); + + return readl(msm_gpio->regs.in) & BIT(offset) ? 1 : 0; +} + +static void gpio_chip_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct msm_gpio_dev *msm_gpio = to_msm_gpio_dev(chip); + unsigned long irq_flags; + + spin_lock_irqsave(&msm_gpio->lock, irq_flags); + msm_gpio_write(msm_gpio, offset, value); + spin_unlock_irqrestore(&msm_gpio->lock, irq_flags); +} + +static int gpio_chip_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct msm_gpio_dev *msm_gpio = to_msm_gpio_dev(chip); + return msm_gpio->irq_base + offset; +} + +static void forget_level_irq(struct msm_gpio_dev *msm_gpio, unsigned offset) +{ + if (!msm_gpio->latch_level_irqs) { + unsigned v = readl(msm_gpio->regs.int_edge); + unsigned b = BIT(offset); + + if (!(v & b)) + writel(b, msm_gpio->regs.int_clear); + } +} + +static void msm_gpio_irq_mask(unsigned int irq) +{ + unsigned long irq_flags; + struct msm_gpio_dev *msm_gpio = get_irq_chip_data(irq); + unsigned offset = irq - msm_gpio->irq_base; + + spin_lock_irqsave(&msm_gpio->lock, irq_flags); + forget_level_irq(msm_gpio, offset); + msm_gpio->irq_masks[IRQ_MASK_NORMAL] &= ~BIT(offset); + writel(msm_gpio->irq_masks[IRQ_MASK_NORMAL], msm_gpio->regs.int_en); + spin_unlock_irqrestore(&msm_gpio->lock, irq_flags); +} + +static void msm_gpio_irq_unmask(unsigned int irq) +{ + unsigned long irq_flags; + struct msm_gpio_dev *msm_gpio = get_irq_chip_data(irq); + unsigned offset = irq - msm_gpio->irq_base; + + spin_lock_irqsave(&msm_gpio->lock, irq_flags); + forget_level_irq(msm_gpio, offset); + msm_gpio->irq_masks[IRQ_MASK_NORMAL] |= BIT(offset); + writel(msm_gpio->irq_masks[IRQ_MASK_NORMAL], msm_gpio->regs.int_en); + spin_unlock_irqrestore(&msm_gpio->lock, irq_flags); +} + +static int msm_gpio_irq_set_type(unsigned int irq, unsigned int flow_type) +{ + unsigned long irq_flags; + struct msm_gpio_dev *msm_gpio = get_irq_chip_data(irq); + unsigned offset = irq - msm_gpio->irq_base; + + if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) + return -EINVAL; + + if ((flow_type & (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW)) == + (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW)) + return -EINVAL; + + spin_lock_irqsave(&msm_gpio->lock, irq_flags); + + if (flow_type & IRQ_TYPE_EDGE_BOTH) { + set_gpio_bit(offset, msm_gpio->regs.int_edge); + irq_desc[irq].handle_irq = handle_edge_irq; + } else { + clr_gpio_bit(offset, msm_gpio->regs.int_edge); + irq_desc[irq].handle_irq = handle_level_irq; + } + + if (flow_type & (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_RISING)) + set_gpio_bit(offset, msm_gpio->regs.int_pos); + else + clr_gpio_bit(offset, msm_gpio->regs.int_pos); + + spin_unlock_irqrestore(&msm_gpio->lock, irq_flags); + + return 0; +} + +static void msm_gpio_irq_mask_ack(unsigned int irq) +{ + msm_gpio_irq_mask(irq); +} + +static int msm_gpio_irq_set_wake(unsigned int irq, unsigned int on) +{ + struct msm_gpio_dev *msm_gpio = get_irq_chip_data(irq); + unsigned offset = irq - msm_gpio->irq_base; + unsigned long irq_flags; + + spin_lock_irqsave(&msm_gpio->lock, irq_flags); + if (on) + msm_gpio->irq_masks[IRQ_MASK_WAKE_ON] |= BIT(offset); + else + msm_gpio->irq_masks[IRQ_MASK_WAKE_ON] &= ~BIT(offset); + spin_unlock_irqrestore(&msm_gpio->lock, irq_flags); + + return set_irq_wake(msm_gpio->irq_summary, on); +} + +static irqreturn_t msm_gpio_irq_handler(int irq, void *dev) +{ + struct msm_gpio_dev *msm_gpio = dev; + unsigned e, s, triggered_irqs; + int b; + + /* + * The int_status register latches trigger events whether or not + * the gpio line is enabled as an interrupt source. Therefore, + * the set of pins which defines the interrupts which need to fire + * is the intersection of int_status and int_en - int_status + * alone provides an incomplete picture. + */ + spin_lock(&msm_gpio->lock); + s = readl(msm_gpio->regs.int_status); + e = readl(msm_gpio->regs.int_en); + triggered_irqs = s & e; + if (triggered_irqs) + writel(triggered_irqs, msm_gpio->regs.int_clear); + spin_unlock(&msm_gpio->lock); + + if (!triggered_irqs) + return IRQ_NONE; + + while (triggered_irqs) { + b = ffs(triggered_irqs) - 1; + triggered_irqs &= ~BIT(b); + generic_handle_irq(msm_gpio->irq_base + b); + } + return IRQ_HANDLED; +} + +static struct irq_chip msm_gpio_irq_chip = { + .name = "msm_gpio", + .mask = msm_gpio_irq_mask, + .mask_ack = msm_gpio_irq_mask_ack, + .unmask = msm_gpio_irq_unmask, + .set_type = msm_gpio_irq_set_type, + .set_wake = msm_gpio_irq_set_wake, +}; + +static int __devinit msm_gpio_probe(struct platform_device *dev) +{ + struct msm_gpio_dev *msm_gpio; + struct msm7200a_gpio_platform_data *pdata = dev->dev.platform_data; + int i, irq, ret; + + if (!pdata) + return -EINVAL; + + msm_gpio = kzalloc(sizeof(struct msm_gpio_dev), GFP_KERNEL); + if (!msm_gpio) + return -ENOMEM; + + spin_lock_init(&msm_gpio->lock); + platform_set_drvdata(dev, msm_gpio); + + msm_gpio->regs = pdata->regs; + msm_gpio->gpio_chip.label = dev->name; + msm_gpio->gpio_chip.base = pdata->gpio_base; + msm_gpio->gpio_chip.ngpio = pdata->ngpio; + msm_gpio->gpio_chip.direction_input = gpio_chip_direction_input; + msm_gpio->gpio_chip.direction_output = gpio_chip_direction_output; + msm_gpio->gpio_chip.get = gpio_chip_get; + msm_gpio->gpio_chip.set = gpio_chip_set; + msm_gpio->gpio_chip.to_irq = gpio_chip_to_irq; + msm_gpio->irq_base = pdata->irq_base; + msm_gpio->irq_summary = pdata->irq_summary; + msm_gpio->latch_level_irqs = pdata->latch_level_irqs; + + ret = gpiochip_add(&msm_gpio->gpio_chip); + if (ret < 0) + goto err_post_malloc; + + for (i = 0; i < msm_gpio->gpio_chip.ngpio; ++i) { + irq = msm_gpio->irq_base + i; + set_irq_chip_data(irq, msm_gpio); + set_irq_chip(irq, &msm_gpio_irq_chip); + set_irq_handler(irq, handle_level_irq); + set_irq_flags(irq, IRQF_VALID); + } + + /* + * We use a level-triggered interrupt because of the nature + * of the shared GPIO-group interrupt. + * + * Many GPIO chips may be sharing the same group IRQ line, and + * it is possible for GPIO interrupt to re-occur while the system + * is still servicing the group interrupt associated with it. + * The group IRQ line would not de-assert and re-assert, and + * we'd get no second edge to cause the group IRQ to be handled again. + * + * Using a level interrupt guarantees that the group IRQ handlers + * will continue to be called as long as any GPIO chip in the group + * is asserting, even if the condition began while the group + * handler was in mid-pass. + */ + ret = request_irq(msm_gpio->irq_summary, + msm_gpio_irq_handler, + IRQF_SHARED | IRQF_TRIGGER_HIGH, + dev_name(&dev->dev), + msm_gpio); + if (ret < 0) + goto err_post_gpiochip_add; + + return ret; +err_post_gpiochip_add: + /* + * Under no circumstances should a line be held on a gpiochip + * which hasn't finished probing. + */ + BUG_ON(gpiochip_remove(&msm_gpio->gpio_chip) < 0); +err_post_malloc: + platform_set_drvdata(dev, NULL); + kfree(msm_gpio); + return ret; +} + +static int __devexit msm_gpio_remove(struct platform_device *dev) +{ + struct msm_gpio_dev *msm_gpio = platform_get_drvdata(dev); + int ret = gpiochip_remove(&msm_gpio->gpio_chip); + + if (ret < 0) + return ret; + + free_irq(msm_gpio->irq_summary, msm_gpio); + kfree(msm_gpio); + + return 0; +} + +#ifdef CONFIG_PM +static int msm_gpio_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct msm_gpio_dev *msm_gpio = platform_get_drvdata(pdev); + unsigned long irq_flags; + unsigned long irq_mask; + + spin_lock_irqsave(&msm_gpio->lock, irq_flags); + if ((msm_gpio->nsuspend)++ == 0) { + irq_mask = msm_gpio->irq_masks[IRQ_MASK_NORMAL] & + msm_gpio->irq_masks[IRQ_MASK_WAKE_ON]; + writel(irq_mask, msm_gpio->regs.int_en); + } + spin_unlock_irqrestore(&msm_gpio->lock, irq_flags); + + return 0; +} + +static int msm_gpio_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct msm_gpio_dev *msm_gpio = platform_get_drvdata(pdev); + unsigned long irq_flags; + + spin_lock_irqsave(&msm_gpio->lock, irq_flags); + if (--(msm_gpio->nsuspend) == 0) + writel(msm_gpio->irq_masks[IRQ_MASK_NORMAL], + msm_gpio->regs.int_en); + spin_unlock_irqrestore(&msm_gpio->lock, irq_flags); + + return 0; +} +#else +#define msm_gpio_suspend NULL +#define msm_gpio_resume NULL +#endif + +static SIMPLE_DEV_PM_OPS(msm_gpio_pm_ops, msm_gpio_suspend, msm_gpio_resume); + +static struct platform_driver msm_gpio_driver = { + .probe = msm_gpio_probe, + .remove = __devexit_p(msm_gpio_remove), + .driver = { + .name = "msm7200a-gpio", + .owner = THIS_MODULE, + .pm = &msm_gpio_pm_ops, + }, +}; + +static int __init msm_gpio_init(void) +{ + return platform_driver_register(&msm_gpio_driver); +} + +static void __exit msm_gpio_exit(void) +{ + platform_driver_unregister(&msm_gpio_driver); +} + +postcore_initcall(msm_gpio_init); +module_exit(msm_gpio_exit); + +MODULE_AUTHOR("Gregory Bean <gbean@codeaurora.org>"); +MODULE_DESCRIPTION("Driver for Qualcomm MSM 7200a-family SoC GPIOs"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:msm7200a-gpio"); diff --git a/arch/arm/mach-msm/msm7200a-gpio.h b/arch/arm/mach-msm/msm7200a-gpio.h new file mode 100644 index 000000000000..317c1fd22ecc --- /dev/null +++ b/arch/arm/mach-msm/msm7200a-gpio.h @@ -0,0 +1,87 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef __LINUX_MSM7200A_GPIO_H +#define __LINUX_MSM7200A_GPIO_H + +/** + * struct msm7200a_gpio_regs - addresess of iomapped GPIO registers + * @in: GPIO_IN_n + * @out: GPIO_OUT_n + * @oe: GPIO_OE_n + * @int_status: GPIO_INT_STATUS_n + * @int_clear: GPIO_INT_CLEAR_n + * @int_en: GPIO_INT_EN_n + * @int_edge: GPIO_INT_EDGE_n + * @int_pos: GPIO_INT_POS_n + * + * Registers are not guaranteed to be packed in memory, or even + * located in a predictable pattern. + */ +struct msm7200a_gpio_regs { + void __iomem *in; + void __iomem *out; + void __iomem *oe; + void __iomem *int_status; + void __iomem *int_clear; + void __iomem *int_en; + void __iomem *int_edge; + void __iomem *int_pos; +}; + +/** + * struct msm7200a_gpio_platform_data - configuration for msm7200a-gpio + * @gpio_base: The first gpio to be assigned to the device. Corresponds + * directly to gpio_chip.base. + * @ngpio: The number of gpio lines to be managed by the device. + * Must be <= 32. Corresponds directly to gpio_chip.ngpio. + * @irq_base: The first irq to be assigned to the device. The gpio + * at 'gpio_base' will be assigned irq 'irq_base', + * gpio 'gpio_base + 1' will receive irq 'irq_base + 1', + * and so on. + * @irq_summary: The summary irq line which will be used by the device + * to notify the kernel when an interrupt occurs on + * any of its gpio lines. Most MSM SoCs have more + * than one gpio device sharing each of these. + * @latch_level_irqs: The MSM gpio hardware latches level interrupts, + * which is atypical. Setting this flag to false + * makes the driver compensate for this and produce + * the traditional unlatched behavior for level irqs. + * @regs: Addresses of the registers which control the gpios + * to be managed by the device. + */ +struct msm7200a_gpio_platform_data { + unsigned gpio_base; + unsigned ngpio; + unsigned irq_base; + unsigned irq_summary; + bool latch_level_irqs; + struct msm7200a_gpio_regs regs; +}; + +#endif |