GPIO support for HTC Dream
Ryan Mallon
ryan at bluewatersys.com
Tue Dec 8 15:22:21 EST 2009
Pavel Machek wrote:
> Add GPIO support for HTC Dream.
>
> Signed-off-by: Pavel Machek <pavel at ucw.cz>
You might want to run this through checkpatch, I suspect it is going to
give you several errors. Some other comments inline.
> diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
> index f780086..774c50e 100644
> --- a/arch/arm/mach-msm/Kconfig
> +++ b/arch/arm/mach-msm/Kconfig
> @@ -40,4 +40,8 @@ config MACH_TROUT
> help
> Support for the HTC Dream, T-Mobile G1, Android ADP1 devices.
>
> +config GENERIC_GPIO
> + bool
> + default y
> +
> endif
> diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
> index 91e6f5c..4c2567e 100644
> --- a/arch/arm/mach-msm/Makefile
> +++ b/arch/arm/mach-msm/Makefile
> @@ -6,4 +6,4 @@ obj-y += clock.o clock-7x01a.o
>
> obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o
>
> -obj-$(CONFIG_MACH_TROUT) += board-dream.o
> +obj-$(CONFIG_MACH_TROUT) += board-dream.o board-dream-gpio.o generic_gpio.o
> diff --git a/arch/arm/mach-msm/board-dream-gpio.c b/arch/arm/mach-msm/board-dream-gpio.c
> new file mode 100644
> index 0000000..1b23a84
> --- /dev/null
> +++ b/arch/arm/mach-msm/board-dream-gpio.c
> @@ -0,0 +1,301 @@
> +/* arch/arm/mach-msm/board-dream-gpio.c
> + *
> + * Copyright (C) 2008 Google, Inc.
> + *
> + * 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.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/irq.h>
> +#include <linux/pm.h>
> +#include <linux/sysdev.h>
> +#include <linux/io.h>
> +
> +#include <asm/gpio.h>
> +#include <asm/mach-types.h>
> +
> +#include "board-dream.h"
> +#include "gpio_chip.h"
> +
> +#undef MODULE_PARAM_PREFIX
> +#define MODULE_PARAM_PREFIX "board_dream."
This looks a bit dodgy. Is this
(http://lkml.indiana.edu/hypermail/linux/kernel/0903.0/02768.html) the
preferred way?
> +static uint cpld_usb_h2w_sw;
> +module_param_named(usb_h2w_sw, cpld_usb_h2w_sw, uint, 0);
> +
> +static uint8_t dream_cpld_shadow[4] = {
> +#if defined(CONFIG_MSM_DEBUG_UART1)
> + /* H2W pins <-> UART1 */
> + [0] = 0x40, // for serial debug, low current
> +#else
> + /* H2W pins <-> UART3, Bluetooth <-> UART1 */
> + [0] = 0x80, // for serial debug, low current
> +#endif
> + [1] = 0x04, // I2C_PULL
> + [3] = 0x04, // mmdi 32k en
> +};
> +static uint8_t dream_int_mask[2] = {
> + [0] = 0xff, /* mask all interrupts */
> + [1] = 0xff,
> +};
> +static uint8_t dream_sleep_int_mask[] = {
> + [0] = 0xff,
> + [1] = 0xff,
> +};
> +static int dream_suspended;
> +
> +static int dream_gpio_read(struct gpio_chip *chip, unsigned n)
> +{
> + uint8_t b;
> + int reg;
> + if (n >= DREAM_GPIO_VIRTUAL_BASE)
> + n += DREAM_GPIO_VIRTUAL_TO_REAL_OFFSET;
> + b = 1U << (n & 7);
> + reg = (n & 0x78) >> 2; // assumes base is 128
> + return !!(readb(DREAM_CPLD_BASE + reg) & b);
> +}
> +
> +static void update_pwrsink(unsigned gpio, unsigned on)
> +{
> + switch(gpio) {
> + case DREAM_GPIO_UI_LED_EN:
> + break;
> + case DREAM_GPIO_QTKEY_LED_EN:
> + break;
> + }
> +}
What is this function for?
> +static uint8_t dream_gpio_write_shadow(unsigned n, unsigned on)
> +{
> + uint8_t b = 1U << (n & 7);
> + int reg = (n & 0x78) >> 2; // assumes base is 128
> +
> + if(on)
> + return dream_cpld_shadow[reg >> 1] |= b;
> + else
> + return dream_cpld_shadow[reg >> 1] &= ~b;
> +}
> +
> +static int dream_gpio_write(struct gpio_chip *chip, unsigned n, unsigned on)
> +{
> + int reg = (n & 0x78) >> 2; // assumes base is 128
> + unsigned long flags;
> + uint8_t reg_val;
> +
> + if ((reg >> 1) >= ARRAY_SIZE(dream_cpld_shadow)) {
> + printk(KERN_ERR "dream_gpio_write called on input %d\n", n);
> + return -ENOTSUPP;
> + }
> +
> + local_irq_save(flags);
> + update_pwrsink(n, on);
> + reg_val = dream_gpio_write_shadow(n, on);
> + writeb(reg_val, DREAM_CPLD_BASE + reg);
> + local_irq_restore(flags);
> + return 0;
> +}
> +
> +static int dream_gpio_configure(struct gpio_chip *chip, unsigned int gpio, unsigned long flags)
> +{
> + if(flags & (GPIOF_OUTPUT_LOW | GPIOF_OUTPUT_HIGH))
> + dream_gpio_write(chip, gpio, flags & GPIOF_OUTPUT_HIGH);
> + return 0;
> +}
> +
> +static int dream_gpio_get_irq_num(struct gpio_chip *chip, unsigned int gpio, unsigned int *irqp, unsigned long *irqnumflagsp)
> +{
> + if ((gpio < DREAM_GPIO_BANK0_FIRST_INT_SOURCE ||
> + gpio > DREAM_GPIO_BANK0_LAST_INT_SOURCE) &&
> + (gpio < DREAM_GPIO_BANK1_FIRST_INT_SOURCE ||
> + gpio > DREAM_GPIO_BANK1_LAST_INT_SOURCE))
> + return -ENOENT;
> + *irqp = DREAM_GPIO_TO_INT(gpio);
> + if(irqnumflagsp)
> + *irqnumflagsp = 0;
> + return 0;
> +}
> +
> +static void dream_gpio_irq_ack(unsigned int irq)
> +{
> + int bank = DREAM_INT_TO_BANK(irq);
> + uint8_t mask = DREAM_INT_TO_MASK(irq);
> + int reg = DREAM_BANK_TO_STAT_REG(bank);
> + /*printk(KERN_INFO "dream_gpio_irq_ack irq %d\n", irq);*/
pr_debug, or just remove it?
> + writeb(mask, DREAM_CPLD_BASE + reg);
> +}
> +
> +static void dream_gpio_irq_mask(unsigned int irq)
> +{
> + unsigned long flags;
> + uint8_t reg_val;
> + int bank = DREAM_INT_TO_BANK(irq);
> + uint8_t mask = DREAM_INT_TO_MASK(irq);
> + int reg = DREAM_BANK_TO_MASK_REG(bank);
> +
> + local_irq_save(flags);
> + reg_val = dream_int_mask[bank] |= mask;
> + /*printk(KERN_INFO "dream_gpio_irq_mask irq %d => %d:%02x\n",
> + irq, bank, reg_val);*/
> + if (!dream_suspended)
> + writeb(reg_val, DREAM_CPLD_BASE + reg);
> + local_irq_restore(flags);
> +}
> +
> +static void dream_gpio_irq_unmask(unsigned int irq)
> +{
> + unsigned long flags;
> + uint8_t reg_val;
> + int bank = DREAM_INT_TO_BANK(irq);
> + uint8_t mask = DREAM_INT_TO_MASK(irq);
> + int reg = DREAM_BANK_TO_MASK_REG(bank);
> +
> + local_irq_save(flags);
> + reg_val = dream_int_mask[bank] &= ~mask;
> + /*printk(KERN_INFO "dream_gpio_irq_unmask irq %d => %d:%02x\n",
> + irq, bank, reg_val);*/
> + if (!dream_suspended)
> + writeb(reg_val, DREAM_CPLD_BASE + reg);
> + local_irq_restore(flags);
> +}
> +
> +int dream_gpio_irq_set_wake(unsigned int irq, unsigned int on)
> +{
> + unsigned long flags;
> + int bank = DREAM_INT_TO_BANK(irq);
> + uint8_t mask = DREAM_INT_TO_MASK(irq);
> +
> + local_irq_save(flags);
> + if(on)
> + dream_sleep_int_mask[bank] &= ~mask;
> + else
> + dream_sleep_int_mask[bank] |= mask;
> + local_irq_restore(flags);
> + return 0;
> +}
> +
> +static void dream_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
> +{
> + int j, m;
> + unsigned v;
> + int bank;
> + int stat_reg;
> + int int_base = DREAM_INT_START;
> + uint8_t int_mask;
> +
> + for (bank = 0; bank < 2; bank++) {
> + stat_reg = DREAM_BANK_TO_STAT_REG(bank);
> + v = readb(DREAM_CPLD_BASE + stat_reg);
> + int_mask = dream_int_mask[bank];
> + if (v & int_mask) {
> + writeb(v & int_mask, DREAM_CPLD_BASE + stat_reg);
> + printk(KERN_ERR "dream_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 += DREAM_INT_BANK0_COUNT;
> + }
> + desc->chip->ack(irq);
> +}
Some blank lines here and there would make this more readable. All your
code is wedged together :-).
> +static int dream_sysdev_suspend(struct sys_device *dev, pm_message_t state)
> +{
> + dream_suspended = 1;
> + writeb(dream_sleep_int_mask[0],
> + DREAM_CPLD_BASE + DREAM_GPIO_INT_MASK0_REG);
> + writeb(dream_sleep_int_mask[1],
> + DREAM_CPLD_BASE + DREAM_GPIO_INT_MASK1_REG);
> + writeb(dream_sleep_int_mask[0],
> + DREAM_CPLD_BASE + DREAM_GPIO_INT_STAT0_REG);
> + writeb(dream_sleep_int_mask[1],
> + DREAM_CPLD_BASE + DREAM_GPIO_INT_STAT1_REG);
> + return 0;
> +}
> +
> +int dream_sysdev_resume(struct sys_device *dev)
> +{
> + writeb(dream_int_mask[0], DREAM_CPLD_BASE + DREAM_GPIO_INT_MASK0_REG);
> + writeb(dream_int_mask[1], DREAM_CPLD_BASE + DREAM_GPIO_INT_MASK1_REG);
> + dream_suspended = 0;
> + return 0;
> +}
> +
> +static struct irq_chip dream_gpio_irq_chip = {
> + .name = "dreamgpio",
> + .ack = dream_gpio_irq_ack,
> + .mask = dream_gpio_irq_mask,
> + .unmask = dream_gpio_irq_unmask,
> + .set_wake = dream_gpio_irq_set_wake,
> + //.set_type = dream_gpio_irq_set_type,
> +};
> +
> +static struct gpio_chip dream_gpio_chip = {
> + .start = DREAM_GPIO_START,
> + .end = DREAM_GPIO_END,
> + .configure = dream_gpio_configure,
> + .get_irq_num = dream_gpio_get_irq_num,
> + .read = dream_gpio_read,
> + .write = dream_gpio_write,
> +// .read_detect_status = dream_gpio_read_detect_status,
> +// .clear_detect_status = dream_gpio_clear_detect_status
> +};
> +
> +struct sysdev_class dream_sysdev_class = {
> + .name = "dreamgpio_irq",
> + .suspend = dream_sysdev_suspend,
> + .resume = dream_sysdev_resume,
> +};
> +
> +static struct sys_device dream_irq_device = {
> + .cls = &dream_sysdev_class,
> +};
> +
> +static int __init dream_init_gpio(void)
> +{
> + int i;
> +
> + if (!machine_is_trout())
> + return 0;
> +
> + /* adjust GPIOs based on bootloader request */
> + pr_info("dream_init_gpio: cpld_usb_hw2_sw = %d\n", cpld_usb_h2w_sw);
> + dream_gpio_write_shadow(DREAM_GPIO_USB_H2W_SW, cpld_usb_h2w_sw);
> +
> + for(i = 0; i < ARRAY_SIZE(dream_cpld_shadow); i++)
> + writeb(dream_cpld_shadow[i], DREAM_CPLD_BASE + i * 2);
> +
> + for(i = DREAM_INT_START; i <= DREAM_INT_END; i++) {
> + set_irq_chip(i, &dream_gpio_irq_chip);
> + set_irq_handler(i, handle_edge_irq);
> + set_irq_flags(i, IRQF_VALID);
> + }
> +
> + register_gpio_chip(&dream_gpio_chip);
> +
> + set_irq_type(MSM_GPIO_TO_INT(17), IRQF_TRIGGER_HIGH);
> + set_irq_chained_handler(MSM_GPIO_TO_INT(17), dream_gpio_irq_handler);
> + set_irq_wake(MSM_GPIO_TO_INT(17), 1);
> +
> + if(sysdev_class_register(&dream_sysdev_class) == 0)
> + sysdev_register(&dream_irq_device);
> +
> + return 0;
> +}
> +
> +postcore_initcall(dream_init_gpio);
> diff --git a/arch/arm/mach-msm/board-dream.c b/arch/arm/mach-msm/board-dream.c
> index d238e2c..4758957 100644
> --- a/arch/arm/mach-msm/board-dream.c
> +++ b/arch/arm/mach-msm/board-dream.c
> @@ -18,11 +18,13 @@
> #include <linux/kernel.h>
> #include <linux/init.h>
> #include <linux/platform_device.h>
> +#include <linux/delay.h>
>
> #include <asm/mach-types.h>
> #include <asm/mach/arch.h>
> #include <asm/mach/map.h>
> #include <asm/setup.h>
> +#include <asm/gpio.h>
>
> #include <mach/board.h>
> #include <mach/hardware.h>
> @@ -62,9 +64,9 @@ static void __init dream_init(void)
>
> static struct map_desc dream_io_desc[] __initdata = {
> {
> - .virtual = TROUT_CPLD_BASE,
> - .pfn = __phys_to_pfn(TROUT_CPLD_START),
> - .length = TROUT_CPLD_SIZE,
> + .virtual = DREAM_CPLD_BASE,
> + .pfn = __phys_to_pfn(DREAM_CPLD_START),
> + .length = DREAM_CPLD_SIZE,
> .type = MT_DEVICE_NONSHARED
> }
> };
> @@ -76,7 +78,7 @@ static void __init dream_map_io(void)
>
> #ifdef CONFIG_MSM_DEBUG_UART3
> /* route UART3 to the "H2W" extended usb connector */
> - writeb(0x80, TROUT_CPLD_BASE + 0x00);
> + writeb(0x80, DREAM_CPLD_BASE + 0x00);
> #endif
>
> msm_clock_init();
> diff --git a/arch/arm/mach-msm/board-dream.h b/arch/arm/mach-msm/board-dream.h
> index 4f345a5..eadfd17 100644
> --- a/arch/arm/mach-msm/board-dream.h
> +++ b/arch/arm/mach-msm/board-dream.h
> @@ -1,5 +1,153 @@
>
> -#define TROUT_CPLD_BASE 0xE8100000
> -#define TROUT_CPLD_START 0x98000000
> -#define TROUT_CPLD_SIZE SZ_4K
> +#define MSM_SMI_BASE 0x00000000
> +#define MSM_SMI_SIZE 0x00800000
>
> +#define MSM_EBI_BASE 0x10000000
> +#define MSM_EBI_SIZE 0x06e00000
> +
> +#define MSM_PMEM_GPU0_BASE 0x00000000
> +#define MSM_PMEM_GPU0_SIZE 0x00700000
> +
> +#define MSM_PMEM_MDP_BASE 0x02000000
> +#define MSM_PMEM_MDP_SIZE 0x00800000
> +
> +#define MSM_PMEM_ADSP_BASE 0x02800000
> +#define MSM_PMEM_ADSP_SIZE 0x00800000
> +
> +#define MSM_PMEM_CAMERA_BASE 0x03000000
> +#define MSM_PMEM_CAMERA_SIZE 0x00800000
> +
> +#define MSM_FB_BASE 0x03800000
> +#define MSM_FB_SIZE 0x00100000
> +
> +#define MSM_LINUX_BASE MSM_EBI_BASE
> +#define MSM_LINUX_SIZE 0x06500000
> +
> +#define MSM_PMEM_GPU1_SIZE 0x800000
> +#define MSM_PMEM_GPU1_BASE MSM_RAM_CONSOLE_BASE - MSM_PMEM_GPU1_SIZE
> +
> +#define MSM_RAM_CONSOLE_BASE MSM_EBI_BASE + 0x6d00000
> +#define MSM_RAM_CONSOLE_SIZE 128 * SZ_1K
> +
> +#if (MSM_FB_BASE + MSM_FB_SIZE) >= (MSM_PMEM_GPU1_BASE)
> +#error invalid memory map
> +#endif
> +
> +#define DECLARE_MSM_IOMAP
> +#include <mach/msm_iomap.h>
> +
> +#define DREAM_4_BALL_UP_0 1
> +#define DREAM_4_BALL_LEFT_0 18
> +#define DREAM_4_BALL_DOWN_0 57
> +#define DREAM_4_BALL_RIGHT_0 91
> +
> +#define DREAM_5_BALL_UP_0 94
> +#define DREAM_5_BALL_LEFT_0 18
> +#define DREAM_5_BALL_DOWN_0 90
> +#define DREAM_5_BALL_RIGHT_0 19
> +
> +#define DREAM_POWER_KEY 20
> +
> +#define DREAM_4_TP_LS_EN 19
> +#define DREAM_5_TP_LS_EN 1
> +
> +#define DREAM_CPLD_BASE 0xE8100000
> +#define DREAM_CPLD_START 0x98000000
> +#define DREAM_CPLD_SIZE SZ_4K
> +
> +#define DREAM_GPIO_CABLE_IN1 (83)
> +#define DREAM_GPIO_CABLE_IN2 (49)
> +
> +#define DREAM_GPIO_START (128)
> +
> +#define DREAM_GPIO_INT_MASK0_REG (0x0c)
> +#define DREAM_GPIO_INT_STAT0_REG (0x0e)
> +#define DREAM_GPIO_INT_MASK1_REG (0x14)
> +#define DREAM_GPIO_INT_STAT1_REG (0x10)
> +
> +#define DREAM_GPIO_HAPTIC_PWM (28)
> +#define DREAM_GPIO_PS_HOLD (25)
> +
> +#define DREAM_GPIO_MISC2_BASE (DREAM_GPIO_START + 0x00)
> +#define DREAM_GPIO_MISC3_BASE (DREAM_GPIO_START + 0x08)
> +#define DREAM_GPIO_MISC4_BASE (DREAM_GPIO_START + 0x10)
> +#define DREAM_GPIO_MISC5_BASE (DREAM_GPIO_START + 0x18)
> +#define DREAM_GPIO_INT2_BASE (DREAM_GPIO_START + 0x20)
> +#define DREAM_GPIO_MISC1_BASE (DREAM_GPIO_START + 0x28)
> +#define DREAM_GPIO_VIRTUAL_BASE (DREAM_GPIO_START + 0x30)
> +#define DREAM_GPIO_INT5_BASE (DREAM_GPIO_START + 0x48)
> +
> +#define DREAM_GPIO_CHARGER_EN (DREAM_GPIO_MISC2_BASE + 0)
> +#define DREAM_GPIO_ISET (DREAM_GPIO_MISC2_BASE + 1)
> +#define DREAM_GPIO_H2W_DAT_DIR (DREAM_GPIO_MISC2_BASE + 2)
> +#define DREAM_GPIO_H2W_CLK_DIR (DREAM_GPIO_MISC2_BASE + 3)
> +#define DREAM_GPIO_H2W_DAT_GPO (DREAM_GPIO_MISC2_BASE + 4)
> +#define DREAM_GPIO_H2W_CLK_GPO (DREAM_GPIO_MISC2_BASE + 5)
> +#define DREAM_GPIO_H2W_SEL0 (DREAM_GPIO_MISC2_BASE + 6)
> +#define DREAM_GPIO_H2W_SEL1 (DREAM_GPIO_MISC2_BASE + 7)
> +
> +#define DREAM_GPIO_SPOTLIGHT_EN (DREAM_GPIO_MISC3_BASE + 0)
> +#define DREAM_GPIO_FLASH_EN (DREAM_GPIO_MISC3_BASE + 1)
> +#define DREAM_GPIO_I2C_PULL (DREAM_GPIO_MISC3_BASE + 2)
> +#define DREAM_GPIO_TP_I2C_PULL (DREAM_GPIO_MISC3_BASE + 3)
> +#define DREAM_GPIO_TP_EN (DREAM_GPIO_MISC3_BASE + 4)
> +#define DREAM_GPIO_JOG_EN (DREAM_GPIO_MISC3_BASE + 5)
> +#define DREAM_GPIO_UI_LED_EN (DREAM_GPIO_MISC3_BASE + 6)
> +#define DREAM_GPIO_QTKEY_LED_EN (DREAM_GPIO_MISC3_BASE + 7)
> +
> +#define DREAM_GPIO_VCM_PWDN (DREAM_GPIO_MISC4_BASE + 0)
> +#define DREAM_GPIO_USB_H2W_SW (DREAM_GPIO_MISC4_BASE + 1)
> +#define DREAM_GPIO_COMPASS_RST_N (DREAM_GPIO_MISC4_BASE + 2)
> +#define DREAM_GPIO_HAPTIC_EN_UP (DREAM_GPIO_MISC4_BASE + 3)
> +#define DREAM_GPIO_HAPTIC_EN_MAIN (DREAM_GPIO_MISC4_BASE + 4)
> +#define DREAM_GPIO_USB_PHY_RST_N (DREAM_GPIO_MISC4_BASE + 5)
> +#define DREAM_GPIO_WIFI_PA_RESETX (DREAM_GPIO_MISC4_BASE + 6)
> +#define DREAM_GPIO_WIFI_EN (DREAM_GPIO_MISC4_BASE + 7)
> +
> +#define DREAM_GPIO_BT_32K_EN (DREAM_GPIO_MISC5_BASE + 0)
> +#define DREAM_GPIO_MAC_32K_EN (DREAM_GPIO_MISC5_BASE + 1)
> +#define DREAM_GPIO_MDDI_32K_EN (DREAM_GPIO_MISC5_BASE + 2)
> +#define DREAM_GPIO_COMPASS_32K_EN (DREAM_GPIO_MISC5_BASE + 3)
> +
> +#define DREAM_GPIO_NAVI_ACT_N (DREAM_GPIO_INT2_BASE + 0)
> +#define DREAM_GPIO_COMPASS_IRQ (DREAM_GPIO_INT2_BASE + 1)
> +#define DREAM_GPIO_SLIDING_DET (DREAM_GPIO_INT2_BASE + 2)
> +#define DREAM_GPIO_AUD_HSMIC_DET_N (DREAM_GPIO_INT2_BASE + 3)
> +#define DREAM_GPIO_SD_DOOR_N (DREAM_GPIO_INT2_BASE + 4)
> +#define DREAM_GPIO_CAM_BTN_STEP1_N (DREAM_GPIO_INT2_BASE + 5)
> +#define DREAM_GPIO_CAM_BTN_STEP2_N (DREAM_GPIO_INT2_BASE + 6)
> +#define DREAM_GPIO_TP_ATT_N (DREAM_GPIO_INT2_BASE + 7)
> +#define DREAM_GPIO_BANK0_FIRST_INT_SOURCE (DREAM_GPIO_NAVI_ACT_N)
> +#define DREAM_GPIO_BANK0_LAST_INT_SOURCE (DREAM_GPIO_TP_ATT_N)
> +
> +#define DREAM_GPIO_H2W_DAT_GPI (DREAM_GPIO_MISC1_BASE + 0)
> +#define DREAM_GPIO_H2W_CLK_GPI (DREAM_GPIO_MISC1_BASE + 1)
> +#define DREAM_GPIO_CPLD128_VER_0 (DREAM_GPIO_MISC1_BASE + 4)
> +#define DREAM_GPIO_CPLD128_VER_1 (DREAM_GPIO_MISC1_BASE + 5)
> +#define DREAM_GPIO_CPLD128_VER_2 (DREAM_GPIO_MISC1_BASE + 6)
> +#define DREAM_GPIO_CPLD128_VER_3 (DREAM_GPIO_MISC1_BASE + 7)
> +
> +#define DREAM_GPIO_SDMC_CD_N (DREAM_GPIO_VIRTUAL_BASE + 0)
> +#define DREAM_GPIO_END (DREAM_GPIO_SDMC_CD_N)
> +#define DREAM_GPIO_BANK1_FIRST_INT_SOURCE (DREAM_GPIO_SDMC_CD_N)
> +#define DREAM_GPIO_BANK1_LAST_INT_SOURCE (DREAM_GPIO_SDMC_CD_N)
> +
> +#define DREAM_GPIO_VIRTUAL_TO_REAL_OFFSET \
> + (DREAM_GPIO_INT5_BASE - DREAM_GPIO_VIRTUAL_BASE)
> +
> +#define DREAM_INT_START (NR_MSM_IRQS + NR_GPIO_IRQS)
> +#define DREAM_INT_BANK0_COUNT (8)
> +#define DREAM_INT_BANK1_START (DREAM_INT_START + DREAM_INT_BANK0_COUNT)
> +#define DREAM_INT_BANK1_COUNT (1)
> +#define DREAM_INT_END (DREAM_INT_START + DREAM_INT_BANK0_COUNT + \
> + DREAM_INT_BANK1_COUNT - 1)
> +#define DREAM_GPIO_TO_INT(n) (((n) <= DREAM_GPIO_BANK0_LAST_INT_SOURCE) ? \
> + (DREAM_INT_START - DREAM_GPIO_BANK0_FIRST_INT_SOURCE + (n)) : \
> + (DREAM_INT_BANK1_START - DREAM_GPIO_BANK1_FIRST_INT_SOURCE + (n)))
> +
> +#define DREAM_INT_TO_BANK(n) ((n - DREAM_INT_START) / DREAM_INT_BANK0_COUNT)
> +#define DREAM_INT_TO_MASK(n) (1U << ((n - DREAM_INT_START) & 7))
> +#define DREAM_BANK_TO_MASK_REG(bank) \
> + (bank ? DREAM_GPIO_INT_MASK1_REG : DREAM_GPIO_INT_MASK0_REG)
> +#define DREAM_BANK_TO_STAT_REG(bank) \
> + (bank ? DREAM_GPIO_INT_STAT1_REG : DREAM_GPIO_INT_STAT0_REG)
Are these needed outside of the gpiolib code? If not, they should be
moved there. I also think they should have lower case names since they
act like a function, and make the code where they are used nicer to read.
> diff --git a/arch/arm/mach-msm/generic_gpio.c b/arch/arm/mach-msm/generic_gpio.c
> new file mode 100644
> index 0000000..fe24d38
> --- /dev/null
> +++ b/arch/arm/mach-msm/generic_gpio.c
> @@ -0,0 +1,274 @@
> +/* arch/arm/mach-msm/generic_gpio.c
> + *
> + * Copyright (C) 2007 Google, Inc.
> + *
> + * 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.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/errno.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <asm/gpio.h>
> +#include "gpio_chip.h"
> +
> +#define GPIO_NUM_TO_CHIP_INDEX(gpio) ((gpio)>>5)
> +
> +struct gpio_state {
> + unsigned long flags;
> + int refcount;
> +};
> +
> +static DEFINE_SPINLOCK(gpio_chips_lock);
> +static LIST_HEAD(gpio_chip_list);
> +static struct gpio_chip **gpio_chip_array;
> +static unsigned long gpio_chip_array_size;
> +
> +int register_gpio_chip(struct gpio_chip *new_gpio_chip)
> +{
> + int err = 0;
> + struct gpio_chip *gpio_chip;
> + int i;
> + unsigned long irq_flags;
> + unsigned int chip_array_start_index, chip_array_end_index;
> +
> + new_gpio_chip->state = kzalloc((new_gpio_chip->end + 1 - new_gpio_chip->start) * sizeof(new_gpio_chip->state[0]), GFP_KERNEL);
> + if (new_gpio_chip->state == NULL) {
> + printk(KERN_ERR "register_gpio_chip: failed to allocate state\n");
> + return -ENOMEM;
> + }
> +
> + spin_lock_irqsave(&gpio_chips_lock, irq_flags);
> + chip_array_start_index = GPIO_NUM_TO_CHIP_INDEX(new_gpio_chip->start);
> + chip_array_end_index = GPIO_NUM_TO_CHIP_INDEX(new_gpio_chip->end);
> + if (chip_array_end_index >= gpio_chip_array_size) {
> + struct gpio_chip **new_gpio_chip_array;
> + unsigned long new_gpio_chip_array_size = chip_array_end_index + 1;
> +
> + new_gpio_chip_array = kmalloc(new_gpio_chip_array_size * sizeof(new_gpio_chip_array[0]), GFP_ATOMIC);
> + if (new_gpio_chip_array == NULL) {
> + printk(KERN_ERR "register_gpio_chip: failed to allocate array\n");
> + err = -ENOMEM;
> + goto failed;
> + }
> + for (i = 0; i < gpio_chip_array_size; i++)
> + new_gpio_chip_array[i] = gpio_chip_array[i];
> + for (i = gpio_chip_array_size; i < new_gpio_chip_array_size; i++)
> + new_gpio_chip_array[i] = NULL;
> + gpio_chip_array = new_gpio_chip_array;
> + gpio_chip_array_size = new_gpio_chip_array_size;
> + }
> + list_for_each_entry(gpio_chip, &gpio_chip_list, list) {
> + if (gpio_chip->start > new_gpio_chip->end) {
> + list_add_tail(&new_gpio_chip->list, &gpio_chip->list);
> + goto added;
> + }
> + if (gpio_chip->end >= new_gpio_chip->start) {
> + printk(KERN_ERR "register_gpio_source %u-%u overlaps with %u-%u\n",
> + new_gpio_chip->start, new_gpio_chip->end,
> + gpio_chip->start, gpio_chip->end);
> + err = -EBUSY;
> + goto failed;
> + }
> + }
> + list_add_tail(&new_gpio_chip->list, &gpio_chip_list);
> +added:
> + for (i = chip_array_start_index; i <= chip_array_end_index; i++) {
> + if (gpio_chip_array[i] == NULL || gpio_chip_array[i]->start > new_gpio_chip->start)
> + gpio_chip_array[i] = new_gpio_chip;
> + }
> +failed:
> + spin_unlock_irqrestore(&gpio_chips_lock, irq_flags);
> + if (err)
> + kfree(new_gpio_chip->state);
> + return err;
> +}
Thats big, hard to read, has about 3 blank lines total and no comments.
> +
> +static struct gpio_chip *get_gpio_chip_locked(unsigned int gpio)
> +{
> + unsigned long i;
> + struct gpio_chip *chip;
> +
> + i = GPIO_NUM_TO_CHIP_INDEX(gpio);
> + if (i >= gpio_chip_array_size)
> + return NULL;
> + chip = gpio_chip_array[i];
> + if (chip == NULL)
> + return NULL;
> + list_for_each_entry_from(chip, &gpio_chip_list, list) {
> + if (gpio < chip->start)
> + return NULL;
> + if (gpio <= chip->end)
> + return chip;
> + }
> + return NULL;
> +}
> +
> +static int request_gpio(unsigned int gpio, unsigned long flags)
> +{
> + int err = 0;
> + struct gpio_chip *chip;
> + unsigned long irq_flags;
> + unsigned long chip_index;
> +
> + spin_lock_irqsave(&gpio_chips_lock, irq_flags);
> + chip = get_gpio_chip_locked(gpio);
> + if (chip == NULL) {
> + err = -EINVAL;
> + goto err;
> + }
> + chip_index = gpio - chip->start;
> + if (chip->state[chip_index].refcount == 0) {
> + chip->configure(chip, gpio, flags);
> + chip->state[chip_index].flags = flags;
> + chip->state[chip_index].refcount++;
> + } else if ((flags & IRQF_SHARED) && (chip->state[chip_index].flags & IRQF_SHARED))
> + chip->state[chip_index].refcount++;
> + else
> + err = -EBUSY;
> +err:
> + spin_unlock_irqrestore(&gpio_chips_lock, irq_flags);
> + return err;
> +}
> +
> +int gpio_request(unsigned gpio, const char *label)
> +{
> + return request_gpio(gpio, 0);
> +}
> +EXPORT_SYMBOL(gpio_request);
> +
> +void gpio_free(unsigned gpio)
> +{
> + struct gpio_chip *chip;
> + unsigned long irq_flags;
> + unsigned long chip_index;
> +
> + spin_lock_irqsave(&gpio_chips_lock, irq_flags);
> + chip = get_gpio_chip_locked(gpio);
> + if (chip) {
> + chip_index = gpio - chip->start;
> + chip->state[chip_index].refcount--;
> + }
> + spin_unlock_irqrestore(&gpio_chips_lock, irq_flags);
> +}
> +EXPORT_SYMBOL(gpio_free);
> +
> +static int gpio_get_irq_num(unsigned int gpio, unsigned int *irqp, unsigned long *irqnumflagsp)
> +{
> + int ret = -ENOTSUPP;
> + struct gpio_chip *chip;
> + unsigned long irq_flags;
> +
> + spin_lock_irqsave(&gpio_chips_lock, irq_flags);
> + chip = get_gpio_chip_locked(gpio);
> + if (chip && chip->get_irq_num)
> + ret = chip->get_irq_num(chip, gpio, irqp, irqnumflagsp);
> + spin_unlock_irqrestore(&gpio_chips_lock, irq_flags);
> + return ret;
> +}
> +
> +int gpio_to_irq(unsigned gpio)
> +{
> + int ret, irq;
> + ret = gpio_get_irq_num(gpio, &irq, NULL);
> + if (ret)
> + return ret;
> + return irq;
> +}
> +EXPORT_SYMBOL(gpio_to_irq);
> +
> +int gpio_configure(unsigned int gpio, unsigned long flags)
> +{
> + int ret = -ENOTSUPP;
> + struct gpio_chip *chip;
> + unsigned long irq_flags;
> +
> + spin_lock_irqsave(&gpio_chips_lock, irq_flags);
> + chip = get_gpio_chip_locked(gpio);
> + if (chip)
> + ret = chip->configure(chip, gpio, flags);
> + spin_unlock_irqrestore(&gpio_chips_lock, irq_flags);
> + return ret;
> +}
> +EXPORT_SYMBOL(gpio_configure);
> +
> +int gpio_direction_input(unsigned gpio)
> +{
> + return gpio_configure(gpio, GPIOF_INPUT);
> +}
> +EXPORT_SYMBOL(gpio_direction_input);
> +
> +int gpio_direction_output(unsigned gpio, int value)
> +{
> + gpio_set_value(gpio, value);
> + return gpio_configure(gpio, GPIOF_DRIVE_OUTPUT);
> +}
> +EXPORT_SYMBOL(gpio_direction_output);
> +
> +int gpio_get_value(unsigned gpio)
> +{
> + int ret = -ENOTSUPP;
> + struct gpio_chip *chip;
> + unsigned long irq_flags;
> +
> + spin_lock_irqsave(&gpio_chips_lock, irq_flags);
> + chip = get_gpio_chip_locked(gpio);
> + if (chip && chip->read)
> + ret = chip->read(chip, gpio);
> + spin_unlock_irqrestore(&gpio_chips_lock, irq_flags);
> + return ret;
> +}
> +EXPORT_SYMBOL(gpio_get_value);
> +
> +void gpio_set_value(unsigned gpio, int on)
> +{
> + int ret = -ENOTSUPP;
> + struct gpio_chip *chip;
> + unsigned long irq_flags;
> +
> + spin_lock_irqsave(&gpio_chips_lock, irq_flags);
> + chip = get_gpio_chip_locked(gpio);
> + if (chip && chip->write)
> + ret = chip->write(chip, gpio, on);
> + spin_unlock_irqrestore(&gpio_chips_lock, irq_flags);
> +}
> +EXPORT_SYMBOL(gpio_set_value);
> +
> +int gpio_read_detect_status(unsigned int gpio)
> +{
> + int ret = -ENOTSUPP;
> + struct gpio_chip *chip;
> + unsigned long irq_flags;
> +
> + spin_lock_irqsave(&gpio_chips_lock, irq_flags);
> + chip = get_gpio_chip_locked(gpio);
> + if (chip && chip->read_detect_status)
> + ret = chip->read_detect_status(chip, gpio);
> + spin_unlock_irqrestore(&gpio_chips_lock, irq_flags);
> + return ret;
> +}
> +EXPORT_SYMBOL(gpio_read_detect_status);
> +
> +int gpio_clear_detect_status(unsigned int gpio)
> +{
> + int ret = -ENOTSUPP;
> + struct gpio_chip *chip;
> + unsigned long irq_flags;
> +
> + spin_lock_irqsave(&gpio_chips_lock, irq_flags);
> + chip = get_gpio_chip_locked(gpio);
> + if (chip && chip->clear_detect_status)
> + ret = chip->clear_detect_status(chip, gpio);
> + spin_unlock_irqrestore(&gpio_chips_lock, irq_flags);
> + return ret;
> +}
> +EXPORT_SYMBOL(gpio_clear_detect_status);
> diff --git a/arch/arm/mach-msm/gpio_chip.h b/arch/arm/mach-msm/gpio_chip.h
> new file mode 100644
> index 0000000..ee4eddc
> --- /dev/null
> +++ b/arch/arm/mach-msm/gpio_chip.h
> @@ -0,0 +1,50 @@
> +/* arch/arm/mach-msm/gpio_chip.h
> + *
> + * Copyright (C) 2007 Google, Inc.
> + *
> + * 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 _LINUX_GPIO_CHIP_H
> +#define _LINUX_GPIO_CHIP_H
> +
> +#include <linux/list.h>
> +
> +struct gpio_chip {
> + struct list_head list;
> + struct gpio_state *state;
> +
> + unsigned int start;
> + unsigned int end;
> +
> + int (*configure)(struct gpio_chip *chip, unsigned int gpio, unsigned long flags);
> + int (*get_irq_num)(struct gpio_chip *chip, unsigned int gpio, unsigned int *irqp, unsigned long *irqnumflagsp);
> + int (*read)(struct gpio_chip *chip, unsigned int gpio);
> + int (*write)(struct gpio_chip *chip, unsigned int gpio, unsigned on);
> + int (*read_detect_status)(struct gpio_chip *chip, unsigned int gpio);
> + int (*clear_detect_status)(struct gpio_chip *chip, unsigned int gpio);
> +};
> +
> +int register_gpio_chip(struct gpio_chip *gpio_chip);
> +
> +/* extended gpio api */
> +
> +#define GPIOF_IRQF_MASK 0x0000ffff /* use to specify edge detection without */
> +#define GPIOF_IRQF_TRIGGER_NONE 0x00010000 /* IRQF_TRIGGER_NONE is 0 which also means "as already configured" */
> +#define GPIOF_INPUT 0x00020000
> +#define GPIOF_DRIVE_OUTPUT 0x00040000
> +#define GPIOF_OUTPUT_LOW 0x00080000
> +#define GPIOF_OUTPUT_HIGH 0x00100000
> +
> +#define GPIOIRQF_SHARED 0x00000001 /* the irq line is shared with other inputs */
> +
> +
> +#endif
> diff --git a/arch/arm/mach-msm/include/mach/gpio.h b/arch/arm/mach-msm/include/mach/gpio.h
> new file mode 100644
> index 0000000..92ce18d
> --- /dev/null
> +++ b/arch/arm/mach-msm/include/mach/gpio.h
> @@ -0,0 +1,36 @@
> +/* linux/include/asm-arm/arch-msm/gpio.h
> + *
> + * Copyright (C) 2007 Google, Inc.
> + * Author: Mike Lockwood <lockwood at android.com>
> + *
> + * 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 __ASM_ARCH_MSM_GPIO_H
> +#define __ASM_ARCH_MSM_GPIO_H
> +
> +#include <linux/interrupt.h>
> +
> +int gpio_request(unsigned gpio, const char *label);
> +void gpio_free(unsigned gpio);
> +int gpio_direction_input(unsigned gpio);
> +int gpio_direction_output(unsigned gpio, int value);
> +int gpio_get_value(unsigned gpio);
> +void gpio_set_value(unsigned gpio, int value);
> +int gpio_to_irq(unsigned gpio);
> +
> +#include <asm-generic/gpio.h>
> +
> +extern int gpio_configure(unsigned int gpio, unsigned long flags);
> +extern int gpio_read_detect_status(unsigned int gpio);
> +extern int gpio_clear_detect_status(unsigned int gpio);
> +
> +#endif
>
>
--
Bluewater Systems Ltd - ARM Technology Solution Centre
Ryan Mallon 5 Amuri Park, 404 Barbadoes St
ryan at bluewatersys.com PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com New Zealand
Phone: +64 3 3779127 Freecall: Australia 1800 148 751
Fax: +64 3 3779135 USA 1800 261 2934
More information about the linux-arm-kernel
mailing list