1/* 2 * linux/arch/arm/mach-msm/gpio.c 3 * 4 * Copyright (C) 2005 HP Labs 5 * Copyright (C) 2008 Google, Inc. 6 * Copyright (C) 2009 Pavel Machek <pavel@ucw.cz> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 */ 13 14#include <linux/kernel.h> 15#include <linux/module.h> 16#include <linux/io.h> 17#include <linux/irq.h> 18#include <linux/interrupt.h> 19#include <linux/gpio.h> 20 21#include "board-trout.h" 22 23static uint8_t trout_int_mask[2] = { 24 [0] = 0xff, /* mask all interrupts */ 25 [1] = 0xff, 26}; 27static uint8_t trout_sleep_int_mask[] = { 28 [0] = 0xff, 29 [1] = 0xff, 30}; 31 32struct msm_gpio_chip { 33 struct gpio_chip chip; 34 void __iomem *reg; /* Base of register bank */ 35 u8 shadow; 36}; 37 38#define to_msm_gpio_chip(c) container_of(c, struct msm_gpio_chip, chip) 39 40static int msm_gpiolib_get(struct gpio_chip *chip, unsigned offset) 41{ 42 struct msm_gpio_chip *msm_gpio = to_msm_gpio_chip(chip); 43 unsigned mask = 1 << offset; 44 45 return !!(readb(msm_gpio->reg) & mask); 46} 47 48static void msm_gpiolib_set(struct gpio_chip *chip, unsigned offset, int val) 49{ 50 struct msm_gpio_chip *msm_gpio = to_msm_gpio_chip(chip); 51 unsigned mask = 1 << offset; 52 53 if (val) 54 msm_gpio->shadow |= mask; 55 else 56 msm_gpio->shadow &= ~mask; 57 58 writeb(msm_gpio->shadow, msm_gpio->reg); 59} 60 61static int msm_gpiolib_direction_input(struct gpio_chip *chip, 62 unsigned offset) 63{ 64 msm_gpiolib_set(chip, offset, 0); 65 return 0; 66} 67 68static int msm_gpiolib_direction_output(struct gpio_chip *chip, 69 unsigned offset, int val) 70{ 71 msm_gpiolib_set(chip, offset, val); 72 return 0; 73} 74 75#define TROUT_GPIO_BANK(name, reg_num, base_gpio, shadow_val) \ 76 { \ 77 .chip = { \ 78 .label = name, \ 79 .direction_input = msm_gpiolib_direction_input,\ 80 .direction_output = msm_gpiolib_direction_output, \ 81 .get = msm_gpiolib_get, \ 82 .set = msm_gpiolib_set, \ 83 .base = base_gpio, \ 84 .ngpio = 8, \ 85 }, \ 86 .reg = (void *) reg_num + TROUT_CPLD_BASE, \ 87 .shadow = shadow_val, \ 88 } 89 90static struct msm_gpio_chip msm_gpio_banks[] = { 91#if defined(CONFIG_MSM_DEBUG_UART1) 92 /* H2W pins <-> UART1 */ 93 TROUT_GPIO_BANK("MISC2", 0x00, TROUT_GPIO_MISC2_BASE, 0x40), 94#else 95 /* H2W pins <-> UART3, Bluetooth <-> UART1 */ 96 TROUT_GPIO_BANK("MISC2", 0x00, TROUT_GPIO_MISC2_BASE, 0x80), 97#endif 98 /* I2C pull */ 99 TROUT_GPIO_BANK("MISC3", 0x02, TROUT_GPIO_MISC3_BASE, 0x04), 100 TROUT_GPIO_BANK("MISC4", 0x04, TROUT_GPIO_MISC4_BASE, 0), 101 /* mmdi 32k en */ 102 TROUT_GPIO_BANK("MISC5", 0x06, TROUT_GPIO_MISC5_BASE, 0x04), 103 TROUT_GPIO_BANK("INT2", 0x08, TROUT_GPIO_INT2_BASE, 0), 104 TROUT_GPIO_BANK("MISC1", 0x0a, TROUT_GPIO_MISC1_BASE, 0), 105 TROUT_GPIO_BANK("VIRTUAL", 0x12, TROUT_GPIO_VIRTUAL_BASE, 0), 106}; 107 108static void trout_gpio_irq_ack(unsigned int irq) 109{ 110 int bank = TROUT_INT_TO_BANK(irq); 111 uint8_t mask = TROUT_INT_TO_MASK(irq); 112 int reg = TROUT_BANK_TO_STAT_REG(bank); 113 /*printk(KERN_INFO "trout_gpio_irq_ack irq %d\n", irq);*/ 114 writeb(mask, TROUT_CPLD_BASE + reg); 115} 116 117static void trout_gpio_irq_mask(unsigned int irq) 118{ 119 unsigned long flags; 120 uint8_t reg_val; 121 int bank = TROUT_INT_TO_BANK(irq); 122 uint8_t mask = TROUT_INT_TO_MASK(irq); 123 int reg = TROUT_BANK_TO_MASK_REG(bank); 124 125 local_irq_save(flags); 126 reg_val = trout_int_mask[bank] |= mask; 127 /*printk(KERN_INFO "trout_gpio_irq_mask irq %d => %d:%02x\n", 128 irq, bank, reg_val);*/ 129 writeb(reg_val, TROUT_CPLD_BASE + reg); 130 local_irq_restore(flags); 131} 132 133static void trout_gpio_irq_unmask(unsigned int irq) 134{ 135 unsigned long flags; 136 uint8_t reg_val; 137 int bank = TROUT_INT_TO_BANK(irq); 138 uint8_t mask = TROUT_INT_TO_MASK(irq); 139 int reg = TROUT_BANK_TO_MASK_REG(bank); 140 141 local_irq_save(flags); 142 reg_val = trout_int_mask[bank] &= ~mask; 143 /*printk(KERN_INFO "trout_gpio_irq_unmask irq %d => %d:%02x\n", 144 irq, bank, reg_val);*/ 145 writeb(reg_val, TROUT_CPLD_BASE + reg); 146 local_irq_restore(flags); 147} 148 149int trout_gpio_irq_set_wake(unsigned int irq, unsigned int on) 150{ 151 unsigned long flags; 152 int bank = TROUT_INT_TO_BANK(irq); 153 uint8_t mask = TROUT_INT_TO_MASK(irq); 154 155 local_irq_save(flags); 156 if(on) 157 trout_sleep_int_mask[bank] &= ~mask; 158 else 159 trout_sleep_int_mask[bank] |= mask; 160 local_irq_restore(flags); 161 return 0; 162} 163 164static void trout_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) 165{ 166 int j, m; 167 unsigned v; 168 int bank; 169 int stat_reg; 170 int int_base = TROUT_INT_START; 171 uint8_t int_mask; 172 173 for (bank = 0; bank < 2; bank++) { 174 stat_reg = TROUT_BANK_TO_STAT_REG(bank); 175 v = readb(TROUT_CPLD_BASE + stat_reg); 176 int_mask = trout_int_mask[bank]; 177 if (v & int_mask) { 178 writeb(v & int_mask, TROUT_CPLD_BASE + stat_reg); 179 printk(KERN_ERR "trout_gpio_irq_handler: got masked " 180 "interrupt: %d:%02x\n", bank, v & int_mask); 181 } 182 v &= ~int_mask; 183 while (v) { 184 m = v & -v; 185 j = fls(m) - 1; 186 /*printk(KERN_INFO "msm_gpio_irq_handler %d:%02x %02x b" 187 "it %d irq %d\n", bank, v, m, j, int_base + j);*/ 188 v &= ~m; 189 generic_handle_irq(int_base + j); 190 } 191 int_base += TROUT_INT_BANK0_COUNT; 192 } 193 desc->chip->ack(irq); 194} 195 196static struct irq_chip trout_gpio_irq_chip = { 197 .name = "troutgpio", 198 .ack = trout_gpio_irq_ack, 199 .mask = trout_gpio_irq_mask, 200 .unmask = trout_gpio_irq_unmask, 201 .set_wake = trout_gpio_irq_set_wake, 202}; 203 204/* 205 * Called from the processor-specific init to enable GPIO pin support. 206 */ 207int __init trout_init_gpio(void) 208{ 209 int i; 210 for(i = TROUT_INT_START; i <= TROUT_INT_END; i++) { 211 set_irq_chip(i, &trout_gpio_irq_chip); 212 set_irq_handler(i, handle_edge_irq); 213 set_irq_flags(i, IRQF_VALID); 214 } 215 216 for (i = 0; i < ARRAY_SIZE(msm_gpio_banks); i++) 217 gpiochip_add(&msm_gpio_banks[i].chip); 218 219 set_irq_type(MSM_GPIO_TO_INT(17), IRQF_TRIGGER_HIGH); 220 set_irq_chained_handler(MSM_GPIO_TO_INT(17), trout_gpio_irq_handler); 221 set_irq_wake(MSM_GPIO_TO_INT(17), 1); 222 223 return 0; 224} 225 226postcore_initcall(trout_init_gpio); 227