| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 1 | /* | 
|  | 2 | * driver/mfd/asic3.c | 
|  | 3 | * | 
|  | 4 | * Compaq ASIC3 support. | 
|  | 5 | * | 
|  | 6 | * This program is free software; you can redistribute it and/or modify | 
|  | 7 | * it under the terms of the GNU General Public License version 2 as | 
|  | 8 | * published by the Free Software Foundation. | 
|  | 9 | * | 
|  | 10 | * Copyright 2001 Compaq Computer Corporation. | 
|  | 11 | * Copyright 2004-2005 Phil Blundell | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 12 | * Copyright 2007-2008 OpenedHand Ltd. | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 13 | * | 
|  | 14 | * Authors: Phil Blundell <pb@handhelds.org>, | 
|  | 15 | *	    Samuel Ortiz <sameo@openedhand.com> | 
|  | 16 | * | 
|  | 17 | */ | 
|  | 18 |  | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 19 | #include <linux/kernel.h> | 
| Philipp Zabel | 9461f65 | 2009-06-15 12:10:24 +0200 | [diff] [blame] | 20 | #include <linux/delay.h> | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 21 | #include <linux/irq.h> | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 22 | #include <linux/gpio.h> | 
| Paul Gortmaker | 5d4a357 | 2011-07-10 12:41:10 -0400 | [diff] [blame] | 23 | #include <linux/export.h> | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 24 | #include <linux/io.h> | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 25 | #include <linux/slab.h> | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 26 | #include <linux/spinlock.h> | 
|  | 27 | #include <linux/platform_device.h> | 
|  | 28 |  | 
|  | 29 | #include <linux/mfd/asic3.h> | 
| Philipp Zabel | 9461f65 | 2009-06-15 12:10:24 +0200 | [diff] [blame] | 30 | #include <linux/mfd/core.h> | 
|  | 31 | #include <linux/mfd/ds1wm.h> | 
| Philipp Zabel | 09f05ce | 2009-06-15 12:10:25 +0200 | [diff] [blame] | 32 | #include <linux/mfd/tmio.h> | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 33 |  | 
| Philipp Zabel | e956a2a | 2009-06-05 18:31:02 +0200 | [diff] [blame] | 34 | enum { | 
|  | 35 | ASIC3_CLOCK_SPI, | 
|  | 36 | ASIC3_CLOCK_OWM, | 
|  | 37 | ASIC3_CLOCK_PWM0, | 
|  | 38 | ASIC3_CLOCK_PWM1, | 
|  | 39 | ASIC3_CLOCK_LED0, | 
|  | 40 | ASIC3_CLOCK_LED1, | 
|  | 41 | ASIC3_CLOCK_LED2, | 
|  | 42 | ASIC3_CLOCK_SD_HOST, | 
|  | 43 | ASIC3_CLOCK_SD_BUS, | 
|  | 44 | ASIC3_CLOCK_SMBUS, | 
|  | 45 | ASIC3_CLOCK_EX0, | 
|  | 46 | ASIC3_CLOCK_EX1, | 
|  | 47 | }; | 
|  | 48 |  | 
|  | 49 | struct asic3_clk { | 
|  | 50 | int enabled; | 
|  | 51 | unsigned int cdex; | 
|  | 52 | unsigned long rate; | 
|  | 53 | }; | 
|  | 54 |  | 
|  | 55 | #define INIT_CDEX(_name, _rate)	\ | 
|  | 56 | [ASIC3_CLOCK_##_name] = {		\ | 
|  | 57 | .cdex = CLOCK_CDEX_##_name,	\ | 
|  | 58 | .rate = _rate,			\ | 
|  | 59 | } | 
|  | 60 |  | 
| Mark Brown | 59f2ad2 | 2010-12-11 12:59:35 +0000 | [diff] [blame] | 61 | static struct asic3_clk asic3_clk_init[] __initdata = { | 
| Philipp Zabel | e956a2a | 2009-06-05 18:31:02 +0200 | [diff] [blame] | 62 | INIT_CDEX(SPI, 0), | 
|  | 63 | INIT_CDEX(OWM, 5000000), | 
|  | 64 | INIT_CDEX(PWM0, 0), | 
|  | 65 | INIT_CDEX(PWM1, 0), | 
|  | 66 | INIT_CDEX(LED0, 0), | 
|  | 67 | INIT_CDEX(LED1, 0), | 
|  | 68 | INIT_CDEX(LED2, 0), | 
|  | 69 | INIT_CDEX(SD_HOST, 24576000), | 
|  | 70 | INIT_CDEX(SD_BUS, 12288000), | 
|  | 71 | INIT_CDEX(SMBUS, 0), | 
|  | 72 | INIT_CDEX(EX0, 32768), | 
|  | 73 | INIT_CDEX(EX1, 24576000), | 
|  | 74 | }; | 
|  | 75 |  | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 76 | struct asic3 { | 
|  | 77 | void __iomem *mapping; | 
|  | 78 | unsigned int bus_shift; | 
|  | 79 | unsigned int irq_nr; | 
|  | 80 | unsigned int irq_base; | 
|  | 81 | spinlock_t lock; | 
|  | 82 | u16 irq_bothedge[4]; | 
|  | 83 | struct gpio_chip gpio; | 
|  | 84 | struct device *dev; | 
| Ian Molton | 64e8867 | 2010-01-06 13:51:48 +0100 | [diff] [blame] | 85 | void __iomem *tmio_cnf; | 
| Philipp Zabel | e956a2a | 2009-06-05 18:31:02 +0200 | [diff] [blame] | 86 |  | 
|  | 87 | struct asic3_clk clocks[ARRAY_SIZE(asic3_clk_init)]; | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 88 | }; | 
|  | 89 |  | 
|  | 90 | static int asic3_gpio_get(struct gpio_chip *chip, unsigned offset); | 
|  | 91 |  | 
| Paul Parsons | 13ca4f6 | 2011-05-13 18:53:03 +0000 | [diff] [blame] | 92 | void asic3_write_register(struct asic3 *asic, unsigned int reg, u32 value) | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 93 | { | 
| Al Viro | b32661e | 2008-03-29 03:10:58 +0000 | [diff] [blame] | 94 | iowrite16(value, asic->mapping + | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 95 | (reg >> asic->bus_shift)); | 
|  | 96 | } | 
| Paul Parsons | 13ca4f6 | 2011-05-13 18:53:03 +0000 | [diff] [blame] | 97 | EXPORT_SYMBOL_GPL(asic3_write_register); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 98 |  | 
| Paul Parsons | 13ca4f6 | 2011-05-13 18:53:03 +0000 | [diff] [blame] | 99 | u32 asic3_read_register(struct asic3 *asic, unsigned int reg) | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 100 | { | 
| Al Viro | b32661e | 2008-03-29 03:10:58 +0000 | [diff] [blame] | 101 | return ioread16(asic->mapping + | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 102 | (reg >> asic->bus_shift)); | 
|  | 103 | } | 
| Paul Parsons | 13ca4f6 | 2011-05-13 18:53:03 +0000 | [diff] [blame] | 104 | EXPORT_SYMBOL_GPL(asic3_read_register); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 105 |  | 
| Mark Brown | 59f2ad2 | 2010-12-11 12:59:35 +0000 | [diff] [blame] | 106 | static void asic3_set_register(struct asic3 *asic, u32 reg, u32 bits, bool set) | 
| Philipp Zabel | 6483c1b | 2009-06-05 18:31:01 +0200 | [diff] [blame] | 107 | { | 
|  | 108 | unsigned long flags; | 
|  | 109 | u32 val; | 
|  | 110 |  | 
|  | 111 | spin_lock_irqsave(&asic->lock, flags); | 
|  | 112 | val = asic3_read_register(asic, reg); | 
|  | 113 | if (set) | 
|  | 114 | val |= bits; | 
|  | 115 | else | 
|  | 116 | val &= ~bits; | 
|  | 117 | asic3_write_register(asic, reg, val); | 
|  | 118 | spin_unlock_irqrestore(&asic->lock, flags); | 
|  | 119 | } | 
|  | 120 |  | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 121 | /* IRQs */ | 
|  | 122 | #define MAX_ASIC_ISR_LOOPS    20 | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 123 | #define ASIC3_GPIO_BASE_INCR \ | 
|  | 124 | (ASIC3_GPIO_B_BASE - ASIC3_GPIO_A_BASE) | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 125 |  | 
|  | 126 | static void asic3_irq_flip_edge(struct asic3 *asic, | 
|  | 127 | u32 base, int bit) | 
|  | 128 | { | 
|  | 129 | u16 edge; | 
|  | 130 | unsigned long flags; | 
|  | 131 |  | 
|  | 132 | spin_lock_irqsave(&asic->lock, flags); | 
|  | 133 | edge = asic3_read_register(asic, | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 134 | base + ASIC3_GPIO_EDGE_TRIGGER); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 135 | edge ^= bit; | 
|  | 136 | asic3_write_register(asic, | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 137 | base + ASIC3_GPIO_EDGE_TRIGGER, edge); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 138 | spin_unlock_irqrestore(&asic->lock, flags); | 
|  | 139 | } | 
|  | 140 |  | 
|  | 141 | static void asic3_irq_demux(unsigned int irq, struct irq_desc *desc) | 
|  | 142 | { | 
| Thomas Gleixner | 52a7d60 | 2011-03-25 11:12:26 +0000 | [diff] [blame] | 143 | struct asic3 *asic = irq_desc_get_handler_data(desc); | 
|  | 144 | struct irq_data *data = irq_desc_get_irq_data(desc); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 145 | int iter, i; | 
|  | 146 | unsigned long flags; | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 147 |  | 
| Axel Lin | a09aee8 | 2011-04-14 22:43:47 +0800 | [diff] [blame] | 148 | data->chip->irq_ack(data); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 149 |  | 
|  | 150 | for (iter = 0 ; iter < MAX_ASIC_ISR_LOOPS; iter++) { | 
|  | 151 | u32 status; | 
|  | 152 | int bank; | 
|  | 153 |  | 
|  | 154 | spin_lock_irqsave(&asic->lock, flags); | 
|  | 155 | status = asic3_read_register(asic, | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 156 | ASIC3_OFFSET(INTR, P_INT_STAT)); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 157 | spin_unlock_irqrestore(&asic->lock, flags); | 
|  | 158 |  | 
|  | 159 | /* Check all ten register bits */ | 
|  | 160 | if ((status & 0x3ff) == 0) | 
|  | 161 | break; | 
|  | 162 |  | 
|  | 163 | /* Handle GPIO IRQs */ | 
|  | 164 | for (bank = 0; bank < ASIC3_NUM_GPIO_BANKS; bank++) { | 
|  | 165 | if (status & (1 << bank)) { | 
|  | 166 | unsigned long base, istat; | 
|  | 167 |  | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 168 | base = ASIC3_GPIO_A_BASE | 
|  | 169 | + bank * ASIC3_GPIO_BASE_INCR; | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 170 |  | 
|  | 171 | spin_lock_irqsave(&asic->lock, flags); | 
|  | 172 | istat = asic3_read_register(asic, | 
|  | 173 | base + | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 174 | ASIC3_GPIO_INT_STATUS); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 175 | /* Clearing IntStatus */ | 
|  | 176 | asic3_write_register(asic, | 
|  | 177 | base + | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 178 | ASIC3_GPIO_INT_STATUS, 0); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 179 | spin_unlock_irqrestore(&asic->lock, flags); | 
|  | 180 |  | 
|  | 181 | for (i = 0; i < ASIC3_GPIOS_PER_BANK; i++) { | 
|  | 182 | int bit = (1 << i); | 
|  | 183 | unsigned int irqnr; | 
|  | 184 |  | 
|  | 185 | if (!(istat & bit)) | 
|  | 186 | continue; | 
|  | 187 |  | 
|  | 188 | irqnr = asic->irq_base + | 
|  | 189 | (ASIC3_GPIOS_PER_BANK * bank) | 
|  | 190 | + i; | 
| Thomas Gleixner | 52a7d60 | 2011-03-25 11:12:26 +0000 | [diff] [blame] | 191 | generic_handle_irq(irqnr); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 192 | if (asic->irq_bothedge[bank] & bit) | 
|  | 193 | asic3_irq_flip_edge(asic, base, | 
|  | 194 | bit); | 
|  | 195 | } | 
|  | 196 | } | 
|  | 197 | } | 
|  | 198 |  | 
|  | 199 | /* Handle remaining IRQs in the status register */ | 
|  | 200 | for (i = ASIC3_NUM_GPIOS; i < ASIC3_NR_IRQS; i++) { | 
|  | 201 | /* They start at bit 4 and go up */ | 
| Thomas Gleixner | 52a7d60 | 2011-03-25 11:12:26 +0000 | [diff] [blame] | 202 | if (status & (1 << (i - ASIC3_NUM_GPIOS + 4))) | 
|  | 203 | generic_handle_irq(asic->irq_base + i); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 204 | } | 
|  | 205 | } | 
|  | 206 |  | 
|  | 207 | if (iter >= MAX_ASIC_ISR_LOOPS) | 
| Samuel Ortiz | 24f4f2e | 2008-06-20 11:11:19 +0200 | [diff] [blame] | 208 | dev_err(asic->dev, "interrupt processing overrun\n"); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 209 | } | 
|  | 210 |  | 
|  | 211 | static inline int asic3_irq_to_bank(struct asic3 *asic, int irq) | 
|  | 212 | { | 
|  | 213 | int n; | 
|  | 214 |  | 
|  | 215 | n = (irq - asic->irq_base) >> 4; | 
|  | 216 |  | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 217 | return (n * (ASIC3_GPIO_B_BASE - ASIC3_GPIO_A_BASE)); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 218 | } | 
|  | 219 |  | 
|  | 220 | static inline int asic3_irq_to_index(struct asic3 *asic, int irq) | 
|  | 221 | { | 
|  | 222 | return (irq - asic->irq_base) & 0xf; | 
|  | 223 | } | 
|  | 224 |  | 
| Mark Brown | 0f76aae | 2010-12-11 13:08:57 +0000 | [diff] [blame] | 225 | static void asic3_mask_gpio_irq(struct irq_data *data) | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 226 | { | 
| Mark Brown | 0f76aae | 2010-12-11 13:08:57 +0000 | [diff] [blame] | 227 | struct asic3 *asic = irq_data_get_irq_chip_data(data); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 228 | u32 val, bank, index; | 
|  | 229 | unsigned long flags; | 
|  | 230 |  | 
| Mark Brown | 0f76aae | 2010-12-11 13:08:57 +0000 | [diff] [blame] | 231 | bank = asic3_irq_to_bank(asic, data->irq); | 
|  | 232 | index = asic3_irq_to_index(asic, data->irq); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 233 |  | 
|  | 234 | spin_lock_irqsave(&asic->lock, flags); | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 235 | val = asic3_read_register(asic, bank + ASIC3_GPIO_MASK); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 236 | val |= 1 << index; | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 237 | asic3_write_register(asic, bank + ASIC3_GPIO_MASK, val); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 238 | spin_unlock_irqrestore(&asic->lock, flags); | 
|  | 239 | } | 
|  | 240 |  | 
| Mark Brown | 0f76aae | 2010-12-11 13:08:57 +0000 | [diff] [blame] | 241 | static void asic3_mask_irq(struct irq_data *data) | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 242 | { | 
| Mark Brown | 0f76aae | 2010-12-11 13:08:57 +0000 | [diff] [blame] | 243 | struct asic3 *asic = irq_data_get_irq_chip_data(data); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 244 | int regval; | 
|  | 245 | unsigned long flags; | 
|  | 246 |  | 
|  | 247 | spin_lock_irqsave(&asic->lock, flags); | 
|  | 248 | regval = asic3_read_register(asic, | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 249 | ASIC3_INTR_BASE + | 
|  | 250 | ASIC3_INTR_INT_MASK); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 251 |  | 
|  | 252 | regval &= ~(ASIC3_INTMASK_MASK0 << | 
| Mark Brown | 0f76aae | 2010-12-11 13:08:57 +0000 | [diff] [blame] | 253 | (data->irq - (asic->irq_base + ASIC3_NUM_GPIOS))); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 254 |  | 
|  | 255 | asic3_write_register(asic, | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 256 | ASIC3_INTR_BASE + | 
|  | 257 | ASIC3_INTR_INT_MASK, | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 258 | regval); | 
|  | 259 | spin_unlock_irqrestore(&asic->lock, flags); | 
|  | 260 | } | 
|  | 261 |  | 
| Mark Brown | 0f76aae | 2010-12-11 13:08:57 +0000 | [diff] [blame] | 262 | static void asic3_unmask_gpio_irq(struct irq_data *data) | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 263 | { | 
| Mark Brown | 0f76aae | 2010-12-11 13:08:57 +0000 | [diff] [blame] | 264 | struct asic3 *asic = irq_data_get_irq_chip_data(data); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 265 | u32 val, bank, index; | 
|  | 266 | unsigned long flags; | 
|  | 267 |  | 
| Mark Brown | 0f76aae | 2010-12-11 13:08:57 +0000 | [diff] [blame] | 268 | bank = asic3_irq_to_bank(asic, data->irq); | 
|  | 269 | index = asic3_irq_to_index(asic, data->irq); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 270 |  | 
|  | 271 | spin_lock_irqsave(&asic->lock, flags); | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 272 | val = asic3_read_register(asic, bank + ASIC3_GPIO_MASK); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 273 | val &= ~(1 << index); | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 274 | asic3_write_register(asic, bank + ASIC3_GPIO_MASK, val); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 275 | spin_unlock_irqrestore(&asic->lock, flags); | 
|  | 276 | } | 
|  | 277 |  | 
| Mark Brown | 0f76aae | 2010-12-11 13:08:57 +0000 | [diff] [blame] | 278 | static void asic3_unmask_irq(struct irq_data *data) | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 279 | { | 
| Mark Brown | 0f76aae | 2010-12-11 13:08:57 +0000 | [diff] [blame] | 280 | struct asic3 *asic = irq_data_get_irq_chip_data(data); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 281 | int regval; | 
|  | 282 | unsigned long flags; | 
|  | 283 |  | 
|  | 284 | spin_lock_irqsave(&asic->lock, flags); | 
|  | 285 | regval = asic3_read_register(asic, | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 286 | ASIC3_INTR_BASE + | 
|  | 287 | ASIC3_INTR_INT_MASK); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 288 |  | 
|  | 289 | regval |= (ASIC3_INTMASK_MASK0 << | 
| Mark Brown | 0f76aae | 2010-12-11 13:08:57 +0000 | [diff] [blame] | 290 | (data->irq - (asic->irq_base + ASIC3_NUM_GPIOS))); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 291 |  | 
|  | 292 | asic3_write_register(asic, | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 293 | ASIC3_INTR_BASE + | 
|  | 294 | ASIC3_INTR_INT_MASK, | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 295 | regval); | 
|  | 296 | spin_unlock_irqrestore(&asic->lock, flags); | 
|  | 297 | } | 
|  | 298 |  | 
| Mark Brown | 0f76aae | 2010-12-11 13:08:57 +0000 | [diff] [blame] | 299 | static int asic3_gpio_irq_type(struct irq_data *data, unsigned int type) | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 300 | { | 
| Mark Brown | 0f76aae | 2010-12-11 13:08:57 +0000 | [diff] [blame] | 301 | struct asic3 *asic = irq_data_get_irq_chip_data(data); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 302 | u32 bank, index; | 
|  | 303 | u16 trigger, level, edge, bit; | 
|  | 304 | unsigned long flags; | 
|  | 305 |  | 
| Mark Brown | 0f76aae | 2010-12-11 13:08:57 +0000 | [diff] [blame] | 306 | bank = asic3_irq_to_bank(asic, data->irq); | 
|  | 307 | index = asic3_irq_to_index(asic, data->irq); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 308 | bit = 1<<index; | 
|  | 309 |  | 
|  | 310 | spin_lock_irqsave(&asic->lock, flags); | 
|  | 311 | level = asic3_read_register(asic, | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 312 | bank + ASIC3_GPIO_LEVEL_TRIGGER); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 313 | edge = asic3_read_register(asic, | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 314 | bank + ASIC3_GPIO_EDGE_TRIGGER); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 315 | trigger = asic3_read_register(asic, | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 316 | bank + ASIC3_GPIO_TRIGGER_TYPE); | 
| Mark Brown | 0f76aae | 2010-12-11 13:08:57 +0000 | [diff] [blame] | 317 | asic->irq_bothedge[(data->irq - asic->irq_base) >> 4] &= ~bit; | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 318 |  | 
| Dmitry Baryshkov | 6cab486 | 2008-07-27 04:23:31 +0100 | [diff] [blame] | 319 | if (type == IRQ_TYPE_EDGE_RISING) { | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 320 | trigger |= bit; | 
|  | 321 | edge |= bit; | 
| Dmitry Baryshkov | 6cab486 | 2008-07-27 04:23:31 +0100 | [diff] [blame] | 322 | } else if (type == IRQ_TYPE_EDGE_FALLING) { | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 323 | trigger |= bit; | 
|  | 324 | edge &= ~bit; | 
| Dmitry Baryshkov | 6cab486 | 2008-07-27 04:23:31 +0100 | [diff] [blame] | 325 | } else if (type == IRQ_TYPE_EDGE_BOTH) { | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 326 | trigger |= bit; | 
| Mark Brown | 0f76aae | 2010-12-11 13:08:57 +0000 | [diff] [blame] | 327 | if (asic3_gpio_get(&asic->gpio, data->irq - asic->irq_base)) | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 328 | edge &= ~bit; | 
|  | 329 | else | 
|  | 330 | edge |= bit; | 
| Mark Brown | 0f76aae | 2010-12-11 13:08:57 +0000 | [diff] [blame] | 331 | asic->irq_bothedge[(data->irq - asic->irq_base) >> 4] |= bit; | 
| Dmitry Baryshkov | 6cab486 | 2008-07-27 04:23:31 +0100 | [diff] [blame] | 332 | } else if (type == IRQ_TYPE_LEVEL_LOW) { | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 333 | trigger &= ~bit; | 
|  | 334 | level &= ~bit; | 
| Dmitry Baryshkov | 6cab486 | 2008-07-27 04:23:31 +0100 | [diff] [blame] | 335 | } else if (type == IRQ_TYPE_LEVEL_HIGH) { | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 336 | trigger &= ~bit; | 
|  | 337 | level |= bit; | 
|  | 338 | } else { | 
|  | 339 | /* | 
| Dmitry Baryshkov | 6cab486 | 2008-07-27 04:23:31 +0100 | [diff] [blame] | 340 | * if type == IRQ_TYPE_NONE, we should mask interrupts, but | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 341 | * be careful to not unmask them if mask was also called. | 
|  | 342 | * Probably need internal state for mask. | 
|  | 343 | */ | 
| Samuel Ortiz | 24f4f2e | 2008-06-20 11:11:19 +0200 | [diff] [blame] | 344 | dev_notice(asic->dev, "irq type not changed\n"); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 345 | } | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 346 | asic3_write_register(asic, bank + ASIC3_GPIO_LEVEL_TRIGGER, | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 347 | level); | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 348 | asic3_write_register(asic, bank + ASIC3_GPIO_EDGE_TRIGGER, | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 349 | edge); | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 350 | asic3_write_register(asic, bank + ASIC3_GPIO_TRIGGER_TYPE, | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 351 | trigger); | 
|  | 352 | spin_unlock_irqrestore(&asic->lock, flags); | 
|  | 353 | return 0; | 
|  | 354 | } | 
|  | 355 |  | 
|  | 356 | static struct irq_chip asic3_gpio_irq_chip = { | 
|  | 357 | .name		= "ASIC3-GPIO", | 
| Mark Brown | 0f76aae | 2010-12-11 13:08:57 +0000 | [diff] [blame] | 358 | .irq_ack	= asic3_mask_gpio_irq, | 
|  | 359 | .irq_mask	= asic3_mask_gpio_irq, | 
|  | 360 | .irq_unmask	= asic3_unmask_gpio_irq, | 
|  | 361 | .irq_set_type	= asic3_gpio_irq_type, | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 362 | }; | 
|  | 363 |  | 
|  | 364 | static struct irq_chip asic3_irq_chip = { | 
|  | 365 | .name		= "ASIC3", | 
| Mark Brown | 0f76aae | 2010-12-11 13:08:57 +0000 | [diff] [blame] | 366 | .irq_ack	= asic3_mask_irq, | 
|  | 367 | .irq_mask	= asic3_mask_irq, | 
|  | 368 | .irq_unmask	= asic3_unmask_irq, | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 369 | }; | 
|  | 370 |  | 
| Philipp Zabel | 065032f | 2008-06-21 00:51:38 +0200 | [diff] [blame] | 371 | static int __init asic3_irq_probe(struct platform_device *pdev) | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 372 | { | 
|  | 373 | struct asic3 *asic = platform_get_drvdata(pdev); | 
|  | 374 | unsigned long clksel = 0; | 
|  | 375 | unsigned int irq, irq_base; | 
| Roel Kluin | c491b2f | 2008-07-25 19:44:41 -0700 | [diff] [blame] | 376 | int ret; | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 377 |  | 
| Roel Kluin | c491b2f | 2008-07-25 19:44:41 -0700 | [diff] [blame] | 378 | ret = platform_get_irq(pdev, 0); | 
|  | 379 | if (ret < 0) | 
|  | 380 | return ret; | 
|  | 381 | asic->irq_nr = ret; | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 382 |  | 
|  | 383 | /* turn on clock to IRQ controller */ | 
|  | 384 | clksel |= CLOCK_SEL_CX; | 
|  | 385 | asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL), | 
|  | 386 | clksel); | 
|  | 387 |  | 
|  | 388 | irq_base = asic->irq_base; | 
|  | 389 |  | 
|  | 390 | for (irq = irq_base; irq < irq_base + ASIC3_NR_IRQS; irq++) { | 
|  | 391 | if (irq < asic->irq_base + ASIC3_NUM_GPIOS) | 
| Thomas Gleixner | d5bb122 | 2011-03-25 11:12:32 +0000 | [diff] [blame] | 392 | irq_set_chip(irq, &asic3_gpio_irq_chip); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 393 | else | 
| Thomas Gleixner | d5bb122 | 2011-03-25 11:12:32 +0000 | [diff] [blame] | 394 | irq_set_chip(irq, &asic3_irq_chip); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 395 |  | 
| Thomas Gleixner | d5bb122 | 2011-03-25 11:12:32 +0000 | [diff] [blame] | 396 | irq_set_chip_data(irq, asic); | 
|  | 397 | irq_set_handler(irq, handle_level_irq); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 398 | set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); | 
|  | 399 | } | 
|  | 400 |  | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 401 | asic3_write_register(asic, ASIC3_OFFSET(INTR, INT_MASK), | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 402 | ASIC3_INTMASK_GINTMASK); | 
|  | 403 |  | 
| Thomas Gleixner | d5bb122 | 2011-03-25 11:12:32 +0000 | [diff] [blame] | 404 | irq_set_chained_handler(asic->irq_nr, asic3_irq_demux); | 
|  | 405 | irq_set_irq_type(asic->irq_nr, IRQ_TYPE_EDGE_RISING); | 
|  | 406 | irq_set_handler_data(asic->irq_nr, asic); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 407 |  | 
|  | 408 | return 0; | 
|  | 409 | } | 
|  | 410 |  | 
|  | 411 | static void asic3_irq_remove(struct platform_device *pdev) | 
|  | 412 | { | 
|  | 413 | struct asic3 *asic = platform_get_drvdata(pdev); | 
|  | 414 | unsigned int irq, irq_base; | 
|  | 415 |  | 
|  | 416 | irq_base = asic->irq_base; | 
|  | 417 |  | 
|  | 418 | for (irq = irq_base; irq < irq_base + ASIC3_NR_IRQS; irq++) { | 
|  | 419 | set_irq_flags(irq, 0); | 
| Thomas Gleixner | d6f7ce9f | 2011-03-25 11:12:35 +0000 | [diff] [blame] | 420 | irq_set_chip_and_handler(irq, NULL, NULL); | 
| Thomas Gleixner | d5bb122 | 2011-03-25 11:12:32 +0000 | [diff] [blame] | 421 | irq_set_chip_data(irq, NULL); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 422 | } | 
| Thomas Gleixner | d5bb122 | 2011-03-25 11:12:32 +0000 | [diff] [blame] | 423 | irq_set_chained_handler(asic->irq_nr, NULL); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 424 | } | 
|  | 425 |  | 
|  | 426 | /* GPIOs */ | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 427 | static int asic3_gpio_direction(struct gpio_chip *chip, | 
|  | 428 | unsigned offset, int out) | 
|  | 429 | { | 
|  | 430 | u32 mask = ASIC3_GPIO_TO_MASK(offset), out_reg; | 
|  | 431 | unsigned int gpio_base; | 
|  | 432 | unsigned long flags; | 
|  | 433 | struct asic3 *asic; | 
|  | 434 |  | 
|  | 435 | asic = container_of(chip, struct asic3, gpio); | 
|  | 436 | gpio_base = ASIC3_GPIO_TO_BASE(offset); | 
|  | 437 |  | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 438 | if (gpio_base > ASIC3_GPIO_D_BASE) { | 
| Samuel Ortiz | 24f4f2e | 2008-06-20 11:11:19 +0200 | [diff] [blame] | 439 | dev_err(asic->dev, "Invalid base (0x%x) for gpio %d\n", | 
|  | 440 | gpio_base, offset); | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 441 | return -EINVAL; | 
|  | 442 | } | 
|  | 443 |  | 
|  | 444 | spin_lock_irqsave(&asic->lock, flags); | 
|  | 445 |  | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 446 | out_reg = asic3_read_register(asic, gpio_base + ASIC3_GPIO_DIRECTION); | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 447 |  | 
|  | 448 | /* Input is 0, Output is 1 */ | 
|  | 449 | if (out) | 
|  | 450 | out_reg |= mask; | 
|  | 451 | else | 
|  | 452 | out_reg &= ~mask; | 
|  | 453 |  | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 454 | asic3_write_register(asic, gpio_base + ASIC3_GPIO_DIRECTION, out_reg); | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 455 |  | 
|  | 456 | spin_unlock_irqrestore(&asic->lock, flags); | 
|  | 457 |  | 
|  | 458 | return 0; | 
|  | 459 |  | 
|  | 460 | } | 
|  | 461 |  | 
|  | 462 | static int asic3_gpio_direction_input(struct gpio_chip *chip, | 
|  | 463 | unsigned offset) | 
|  | 464 | { | 
|  | 465 | return asic3_gpio_direction(chip, offset, 0); | 
|  | 466 | } | 
|  | 467 |  | 
|  | 468 | static int asic3_gpio_direction_output(struct gpio_chip *chip, | 
|  | 469 | unsigned offset, int value) | 
|  | 470 | { | 
|  | 471 | return asic3_gpio_direction(chip, offset, 1); | 
|  | 472 | } | 
|  | 473 |  | 
|  | 474 | static int asic3_gpio_get(struct gpio_chip *chip, | 
|  | 475 | unsigned offset) | 
|  | 476 | { | 
|  | 477 | unsigned int gpio_base; | 
|  | 478 | u32 mask = ASIC3_GPIO_TO_MASK(offset); | 
|  | 479 | struct asic3 *asic; | 
|  | 480 |  | 
|  | 481 | asic = container_of(chip, struct asic3, gpio); | 
|  | 482 | gpio_base = ASIC3_GPIO_TO_BASE(offset); | 
|  | 483 |  | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 484 | if (gpio_base > ASIC3_GPIO_D_BASE) { | 
| Samuel Ortiz | 24f4f2e | 2008-06-20 11:11:19 +0200 | [diff] [blame] | 485 | dev_err(asic->dev, "Invalid base (0x%x) for gpio %d\n", | 
|  | 486 | gpio_base, offset); | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 487 | return -EINVAL; | 
|  | 488 | } | 
|  | 489 |  | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 490 | return asic3_read_register(asic, gpio_base + ASIC3_GPIO_STATUS) & mask; | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 491 | } | 
|  | 492 |  | 
|  | 493 | static void asic3_gpio_set(struct gpio_chip *chip, | 
|  | 494 | unsigned offset, int value) | 
|  | 495 | { | 
|  | 496 | u32 mask, out_reg; | 
|  | 497 | unsigned int gpio_base; | 
|  | 498 | unsigned long flags; | 
|  | 499 | struct asic3 *asic; | 
|  | 500 |  | 
|  | 501 | asic = container_of(chip, struct asic3, gpio); | 
|  | 502 | gpio_base = ASIC3_GPIO_TO_BASE(offset); | 
|  | 503 |  | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 504 | if (gpio_base > ASIC3_GPIO_D_BASE) { | 
| Samuel Ortiz | 24f4f2e | 2008-06-20 11:11:19 +0200 | [diff] [blame] | 505 | dev_err(asic->dev, "Invalid base (0x%x) for gpio %d\n", | 
|  | 506 | gpio_base, offset); | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 507 | return; | 
|  | 508 | } | 
|  | 509 |  | 
|  | 510 | mask = ASIC3_GPIO_TO_MASK(offset); | 
|  | 511 |  | 
|  | 512 | spin_lock_irqsave(&asic->lock, flags); | 
|  | 513 |  | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 514 | out_reg = asic3_read_register(asic, gpio_base + ASIC3_GPIO_OUT); | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 515 |  | 
|  | 516 | if (value) | 
|  | 517 | out_reg |= mask; | 
|  | 518 | else | 
|  | 519 | out_reg &= ~mask; | 
|  | 520 |  | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 521 | asic3_write_register(asic, gpio_base + ASIC3_GPIO_OUT, out_reg); | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 522 |  | 
|  | 523 | spin_unlock_irqrestore(&asic->lock, flags); | 
|  | 524 |  | 
|  | 525 | return; | 
|  | 526 | } | 
|  | 527 |  | 
| Philipp Zabel | 065032f | 2008-06-21 00:51:38 +0200 | [diff] [blame] | 528 | static __init int asic3_gpio_probe(struct platform_device *pdev, | 
|  | 529 | u16 *gpio_config, int num) | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 530 | { | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 531 | struct asic3 *asic = platform_get_drvdata(pdev); | 
| Samuel Ortiz | 3b26bf1 | 2008-06-20 11:09:51 +0200 | [diff] [blame] | 532 | u16 alt_reg[ASIC3_NUM_GPIO_BANKS]; | 
|  | 533 | u16 out_reg[ASIC3_NUM_GPIO_BANKS]; | 
|  | 534 | u16 dir_reg[ASIC3_NUM_GPIO_BANKS]; | 
|  | 535 | int i; | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 536 |  | 
| Russell King | 59f0cb0 | 2008-10-27 11:24:09 +0000 | [diff] [blame] | 537 | memset(alt_reg, 0, ASIC3_NUM_GPIO_BANKS * sizeof(u16)); | 
|  | 538 | memset(out_reg, 0, ASIC3_NUM_GPIO_BANKS * sizeof(u16)); | 
|  | 539 | memset(dir_reg, 0, ASIC3_NUM_GPIO_BANKS * sizeof(u16)); | 
| Samuel Ortiz | 3b26bf1 | 2008-06-20 11:09:51 +0200 | [diff] [blame] | 540 |  | 
|  | 541 | /* Enable all GPIOs */ | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 542 | asic3_write_register(asic, ASIC3_GPIO_OFFSET(A, MASK), 0xffff); | 
|  | 543 | asic3_write_register(asic, ASIC3_GPIO_OFFSET(B, MASK), 0xffff); | 
|  | 544 | asic3_write_register(asic, ASIC3_GPIO_OFFSET(C, MASK), 0xffff); | 
|  | 545 | asic3_write_register(asic, ASIC3_GPIO_OFFSET(D, MASK), 0xffff); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 546 |  | 
| Samuel Ortiz | 3b26bf1 | 2008-06-20 11:09:51 +0200 | [diff] [blame] | 547 | for (i = 0; i < num; i++) { | 
|  | 548 | u8 alt, pin, dir, init, bank_num, bit_num; | 
|  | 549 | u16 config = gpio_config[i]; | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 550 |  | 
| Samuel Ortiz | 3b26bf1 | 2008-06-20 11:09:51 +0200 | [diff] [blame] | 551 | pin = ASIC3_CONFIG_GPIO_PIN(config); | 
|  | 552 | alt = ASIC3_CONFIG_GPIO_ALT(config); | 
|  | 553 | dir = ASIC3_CONFIG_GPIO_DIR(config); | 
|  | 554 | init = ASIC3_CONFIG_GPIO_INIT(config); | 
|  | 555 |  | 
|  | 556 | bank_num = ASIC3_GPIO_TO_BANK(pin); | 
|  | 557 | bit_num = ASIC3_GPIO_TO_BIT(pin); | 
|  | 558 |  | 
|  | 559 | alt_reg[bank_num] |= (alt << bit_num); | 
|  | 560 | out_reg[bank_num] |= (init << bit_num); | 
|  | 561 | dir_reg[bank_num] |= (dir << bit_num); | 
|  | 562 | } | 
|  | 563 |  | 
|  | 564 | for (i = 0; i < ASIC3_NUM_GPIO_BANKS; i++) { | 
|  | 565 | asic3_write_register(asic, | 
|  | 566 | ASIC3_BANK_TO_BASE(i) + | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 567 | ASIC3_GPIO_DIRECTION, | 
| Samuel Ortiz | 3b26bf1 | 2008-06-20 11:09:51 +0200 | [diff] [blame] | 568 | dir_reg[i]); | 
|  | 569 | asic3_write_register(asic, | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 570 | ASIC3_BANK_TO_BASE(i) + ASIC3_GPIO_OUT, | 
| Samuel Ortiz | 3b26bf1 | 2008-06-20 11:09:51 +0200 | [diff] [blame] | 571 | out_reg[i]); | 
|  | 572 | asic3_write_register(asic, | 
|  | 573 | ASIC3_BANK_TO_BASE(i) + | 
| Samuel Ortiz | 3b8139f | 2008-06-20 11:12:21 +0200 | [diff] [blame] | 574 | ASIC3_GPIO_ALT_FUNCTION, | 
| Samuel Ortiz | 3b26bf1 | 2008-06-20 11:09:51 +0200 | [diff] [blame] | 575 | alt_reg[i]); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 576 | } | 
|  | 577 |  | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 578 | return gpiochip_add(&asic->gpio); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 579 | } | 
|  | 580 |  | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 581 | static int asic3_gpio_remove(struct platform_device *pdev) | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 582 | { | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 583 | struct asic3 *asic = platform_get_drvdata(pdev); | 
|  | 584 |  | 
|  | 585 | return gpiochip_remove(&asic->gpio); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 586 | } | 
|  | 587 |  | 
| Paul Parsons | c29a812 | 2011-08-09 16:27:43 +0000 | [diff] [blame] | 588 | static void asic3_clk_enable(struct asic3 *asic, struct asic3_clk *clk) | 
| Philipp Zabel | e956a2a | 2009-06-05 18:31:02 +0200 | [diff] [blame] | 589 | { | 
|  | 590 | unsigned long flags; | 
|  | 591 | u32 cdex; | 
|  | 592 |  | 
|  | 593 | spin_lock_irqsave(&asic->lock, flags); | 
|  | 594 | if (clk->enabled++ == 0) { | 
|  | 595 | cdex = asic3_read_register(asic, ASIC3_OFFSET(CLOCK, CDEX)); | 
|  | 596 | cdex |= clk->cdex; | 
|  | 597 | asic3_write_register(asic, ASIC3_OFFSET(CLOCK, CDEX), cdex); | 
|  | 598 | } | 
|  | 599 | spin_unlock_irqrestore(&asic->lock, flags); | 
| Philipp Zabel | e956a2a | 2009-06-05 18:31:02 +0200 | [diff] [blame] | 600 | } | 
|  | 601 |  | 
|  | 602 | static void asic3_clk_disable(struct asic3 *asic, struct asic3_clk *clk) | 
|  | 603 | { | 
|  | 604 | unsigned long flags; | 
|  | 605 | u32 cdex; | 
|  | 606 |  | 
|  | 607 | WARN_ON(clk->enabled == 0); | 
|  | 608 |  | 
|  | 609 | spin_lock_irqsave(&asic->lock, flags); | 
|  | 610 | if (--clk->enabled == 0) { | 
|  | 611 | cdex = asic3_read_register(asic, ASIC3_OFFSET(CLOCK, CDEX)); | 
|  | 612 | cdex &= ~clk->cdex; | 
|  | 613 | asic3_write_register(asic, ASIC3_OFFSET(CLOCK, CDEX), cdex); | 
|  | 614 | } | 
|  | 615 | spin_unlock_irqrestore(&asic->lock, flags); | 
|  | 616 | } | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 617 |  | 
| Philipp Zabel | 9461f65 | 2009-06-15 12:10:24 +0200 | [diff] [blame] | 618 | /* MFD cells (SPI, PWM, LED, DS1WM, MMC) */ | 
|  | 619 | static struct ds1wm_driver_data ds1wm_pdata = { | 
|  | 620 | .active_high = 1, | 
| Jean-François Dagenais | f607e7f | 2011-07-08 15:39:44 -0700 | [diff] [blame] | 621 | .reset_recover_delay = 1, | 
| Philipp Zabel | 9461f65 | 2009-06-15 12:10:24 +0200 | [diff] [blame] | 622 | }; | 
|  | 623 |  | 
|  | 624 | static struct resource ds1wm_resources[] = { | 
|  | 625 | { | 
|  | 626 | .start = ASIC3_OWM_BASE, | 
|  | 627 | .end   = ASIC3_OWM_BASE + 0x13, | 
|  | 628 | .flags = IORESOURCE_MEM, | 
|  | 629 | }, | 
|  | 630 | { | 
|  | 631 | .start = ASIC3_IRQ_OWM, | 
| Mark Brown | fe42142 | 2010-12-11 13:00:34 +0000 | [diff] [blame] | 632 | .end   = ASIC3_IRQ_OWM, | 
| Philipp Zabel | 9461f65 | 2009-06-15 12:10:24 +0200 | [diff] [blame] | 633 | .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE, | 
|  | 634 | }, | 
|  | 635 | }; | 
|  | 636 |  | 
|  | 637 | static int ds1wm_enable(struct platform_device *pdev) | 
|  | 638 | { | 
|  | 639 | struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); | 
|  | 640 |  | 
|  | 641 | /* Turn on external clocks and the OWM clock */ | 
|  | 642 | asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_EX0]); | 
|  | 643 | asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_EX1]); | 
|  | 644 | asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_OWM]); | 
|  | 645 | msleep(1); | 
|  | 646 |  | 
|  | 647 | /* Reset and enable DS1WM */ | 
|  | 648 | asic3_set_register(asic, ASIC3_OFFSET(EXTCF, RESET), | 
|  | 649 | ASIC3_EXTCF_OWM_RESET, 1); | 
|  | 650 | msleep(1); | 
|  | 651 | asic3_set_register(asic, ASIC3_OFFSET(EXTCF, RESET), | 
|  | 652 | ASIC3_EXTCF_OWM_RESET, 0); | 
|  | 653 | msleep(1); | 
|  | 654 | asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT), | 
|  | 655 | ASIC3_EXTCF_OWM_EN, 1); | 
|  | 656 | msleep(1); | 
|  | 657 |  | 
|  | 658 | return 0; | 
|  | 659 | } | 
|  | 660 |  | 
|  | 661 | static int ds1wm_disable(struct platform_device *pdev) | 
|  | 662 | { | 
|  | 663 | struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); | 
|  | 664 |  | 
|  | 665 | asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT), | 
|  | 666 | ASIC3_EXTCF_OWM_EN, 0); | 
|  | 667 |  | 
|  | 668 | asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_OWM]); | 
|  | 669 | asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_EX0]); | 
|  | 670 | asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_EX1]); | 
|  | 671 |  | 
|  | 672 | return 0; | 
|  | 673 | } | 
|  | 674 |  | 
|  | 675 | static struct mfd_cell asic3_cell_ds1wm = { | 
|  | 676 | .name          = "ds1wm", | 
|  | 677 | .enable        = ds1wm_enable, | 
|  | 678 | .disable       = ds1wm_disable, | 
| Samuel Ortiz | 121ea57 | 2011-04-06 11:41:03 +0200 | [diff] [blame] | 679 | .platform_data = &ds1wm_pdata, | 
|  | 680 | .pdata_size    = sizeof(ds1wm_pdata), | 
| Philipp Zabel | 9461f65 | 2009-06-15 12:10:24 +0200 | [diff] [blame] | 681 | .num_resources = ARRAY_SIZE(ds1wm_resources), | 
|  | 682 | .resources     = ds1wm_resources, | 
|  | 683 | }; | 
|  | 684 |  | 
| Ian Molton | 64e8867 | 2010-01-06 13:51:48 +0100 | [diff] [blame] | 685 | static void asic3_mmc_pwr(struct platform_device *pdev, int state) | 
|  | 686 | { | 
|  | 687 | struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); | 
|  | 688 |  | 
|  | 689 | tmio_core_mmc_pwr(asic->tmio_cnf, 1 - asic->bus_shift, state); | 
|  | 690 | } | 
|  | 691 |  | 
|  | 692 | static void asic3_mmc_clk_div(struct platform_device *pdev, int state) | 
|  | 693 | { | 
|  | 694 | struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); | 
|  | 695 |  | 
|  | 696 | tmio_core_mmc_clk_div(asic->tmio_cnf, 1 - asic->bus_shift, state); | 
|  | 697 | } | 
|  | 698 |  | 
| Philipp Zabel | 09f05ce | 2009-06-15 12:10:25 +0200 | [diff] [blame] | 699 | static struct tmio_mmc_data asic3_mmc_data = { | 
| Ian Molton | 64e8867 | 2010-01-06 13:51:48 +0100 | [diff] [blame] | 700 | .hclk           = 24576000, | 
|  | 701 | .set_pwr        = asic3_mmc_pwr, | 
|  | 702 | .set_clk_div    = asic3_mmc_clk_div, | 
| Philipp Zabel | 09f05ce | 2009-06-15 12:10:25 +0200 | [diff] [blame] | 703 | }; | 
|  | 704 |  | 
|  | 705 | static struct resource asic3_mmc_resources[] = { | 
|  | 706 | { | 
|  | 707 | .start = ASIC3_SD_CTRL_BASE, | 
|  | 708 | .end   = ASIC3_SD_CTRL_BASE + 0x3ff, | 
|  | 709 | .flags = IORESOURCE_MEM, | 
|  | 710 | }, | 
|  | 711 | { | 
| Philipp Zabel | 09f05ce | 2009-06-15 12:10:25 +0200 | [diff] [blame] | 712 | .start = 0, | 
|  | 713 | .end   = 0, | 
|  | 714 | .flags = IORESOURCE_IRQ, | 
|  | 715 | }, | 
|  | 716 | }; | 
|  | 717 |  | 
|  | 718 | static int asic3_mmc_enable(struct platform_device *pdev) | 
|  | 719 | { | 
|  | 720 | struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); | 
|  | 721 |  | 
|  | 722 | /* Not sure if it must be done bit by bit, but leaving as-is */ | 
|  | 723 | asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF), | 
|  | 724 | ASIC3_SDHWCTRL_LEVCD, 1); | 
|  | 725 | asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF), | 
|  | 726 | ASIC3_SDHWCTRL_LEVWP, 1); | 
|  | 727 | asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF), | 
|  | 728 | ASIC3_SDHWCTRL_SUSPEND, 0); | 
|  | 729 | asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF), | 
|  | 730 | ASIC3_SDHWCTRL_PCLR, 0); | 
|  | 731 |  | 
|  | 732 | asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_EX0]); | 
|  | 733 | /* CLK32 used for card detection and for interruption detection | 
|  | 734 | * when HCLK is stopped. | 
|  | 735 | */ | 
|  | 736 | asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_EX1]); | 
|  | 737 | msleep(1); | 
|  | 738 |  | 
|  | 739 | /* HCLK 24.576 MHz, BCLK 12.288 MHz: */ | 
|  | 740 | asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL), | 
|  | 741 | CLOCK_SEL_CX | CLOCK_SEL_SD_HCLK_SEL); | 
|  | 742 |  | 
|  | 743 | asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_SD_HOST]); | 
|  | 744 | asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_SD_BUS]); | 
|  | 745 | msleep(1); | 
|  | 746 |  | 
|  | 747 | asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT), | 
|  | 748 | ASIC3_EXTCF_SD_MEM_ENABLE, 1); | 
|  | 749 |  | 
|  | 750 | /* Enable SD card slot 3.3V power supply */ | 
|  | 751 | asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF), | 
|  | 752 | ASIC3_SDHWCTRL_SDPWR, 1); | 
|  | 753 |  | 
| Ian Molton | 64e8867 | 2010-01-06 13:51:48 +0100 | [diff] [blame] | 754 | /* ASIC3_SD_CTRL_BASE assumes 32-bit addressing, TMIO is 16-bit */ | 
|  | 755 | tmio_core_mmc_enable(asic->tmio_cnf, 1 - asic->bus_shift, | 
|  | 756 | ASIC3_SD_CTRL_BASE >> 1); | 
|  | 757 |  | 
| Philipp Zabel | 09f05ce | 2009-06-15 12:10:25 +0200 | [diff] [blame] | 758 | return 0; | 
|  | 759 | } | 
|  | 760 |  | 
|  | 761 | static int asic3_mmc_disable(struct platform_device *pdev) | 
|  | 762 | { | 
|  | 763 | struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); | 
|  | 764 |  | 
|  | 765 | /* Put in suspend mode */ | 
|  | 766 | asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF), | 
|  | 767 | ASIC3_SDHWCTRL_SUSPEND, 1); | 
|  | 768 |  | 
|  | 769 | /* Disable clocks */ | 
|  | 770 | asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_SD_HOST]); | 
|  | 771 | asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_SD_BUS]); | 
|  | 772 | asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_EX0]); | 
|  | 773 | asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_EX1]); | 
|  | 774 | return 0; | 
|  | 775 | } | 
|  | 776 |  | 
|  | 777 | static struct mfd_cell asic3_cell_mmc = { | 
|  | 778 | .name          = "tmio-mmc", | 
|  | 779 | .enable        = asic3_mmc_enable, | 
|  | 780 | .disable       = asic3_mmc_disable, | 
| Paul Parsons | 3c6e365 | 2011-08-09 16:27:24 +0000 | [diff] [blame] | 781 | .suspend       = asic3_mmc_disable, | 
|  | 782 | .resume        = asic3_mmc_enable, | 
| Samuel Ortiz | ec71974 | 2011-04-06 11:38:14 +0200 | [diff] [blame] | 783 | .platform_data = &asic3_mmc_data, | 
|  | 784 | .pdata_size    = sizeof(asic3_mmc_data), | 
| Philipp Zabel | 09f05ce | 2009-06-15 12:10:25 +0200 | [diff] [blame] | 785 | .num_resources = ARRAY_SIZE(asic3_mmc_resources), | 
|  | 786 | .resources     = asic3_mmc_resources, | 
|  | 787 | }; | 
|  | 788 |  | 
| Paul Parsons | 13ca4f6 | 2011-05-13 18:53:03 +0000 | [diff] [blame] | 789 | static const int clock_ledn[ASIC3_NUM_LEDS] = { | 
|  | 790 | [0] = ASIC3_CLOCK_LED0, | 
|  | 791 | [1] = ASIC3_CLOCK_LED1, | 
|  | 792 | [2] = ASIC3_CLOCK_LED2, | 
|  | 793 | }; | 
|  | 794 |  | 
|  | 795 | static int asic3_leds_enable(struct platform_device *pdev) | 
|  | 796 | { | 
|  | 797 | const struct mfd_cell *cell = mfd_get_cell(pdev); | 
|  | 798 | struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); | 
|  | 799 |  | 
|  | 800 | asic3_clk_enable(asic, &asic->clocks[clock_ledn[cell->id]]); | 
|  | 801 |  | 
|  | 802 | return 0; | 
|  | 803 | } | 
|  | 804 |  | 
|  | 805 | static int asic3_leds_disable(struct platform_device *pdev) | 
|  | 806 | { | 
|  | 807 | const struct mfd_cell *cell = mfd_get_cell(pdev); | 
|  | 808 | struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); | 
|  | 809 |  | 
|  | 810 | asic3_clk_disable(asic, &asic->clocks[clock_ledn[cell->id]]); | 
|  | 811 |  | 
|  | 812 | return 0; | 
|  | 813 | } | 
|  | 814 |  | 
| Paul Parsons | e0b13b5 | 2011-08-09 16:27:33 +0000 | [diff] [blame] | 815 | static int asic3_leds_suspend(struct platform_device *pdev) | 
|  | 816 | { | 
|  | 817 | const struct mfd_cell *cell = mfd_get_cell(pdev); | 
|  | 818 | struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); | 
|  | 819 |  | 
|  | 820 | while (asic3_gpio_get(&asic->gpio, ASIC3_GPIO(C, cell->id)) != 0) | 
|  | 821 | msleep(1); | 
|  | 822 |  | 
|  | 823 | asic3_clk_disable(asic, &asic->clocks[clock_ledn[cell->id]]); | 
|  | 824 |  | 
|  | 825 | return 0; | 
|  | 826 | } | 
|  | 827 |  | 
| Paul Parsons | 13ca4f6 | 2011-05-13 18:53:03 +0000 | [diff] [blame] | 828 | static struct mfd_cell asic3_cell_leds[ASIC3_NUM_LEDS] = { | 
|  | 829 | [0] = { | 
|  | 830 | .name          = "leds-asic3", | 
|  | 831 | .id            = 0, | 
|  | 832 | .enable        = asic3_leds_enable, | 
|  | 833 | .disable       = asic3_leds_disable, | 
| Paul Parsons | e0b13b5 | 2011-08-09 16:27:33 +0000 | [diff] [blame] | 834 | .suspend       = asic3_leds_suspend, | 
|  | 835 | .resume        = asic3_leds_enable, | 
| Paul Parsons | 13ca4f6 | 2011-05-13 18:53:03 +0000 | [diff] [blame] | 836 | }, | 
|  | 837 | [1] = { | 
|  | 838 | .name          = "leds-asic3", | 
|  | 839 | .id            = 1, | 
|  | 840 | .enable        = asic3_leds_enable, | 
|  | 841 | .disable       = asic3_leds_disable, | 
| Paul Parsons | e0b13b5 | 2011-08-09 16:27:33 +0000 | [diff] [blame] | 842 | .suspend       = asic3_leds_suspend, | 
|  | 843 | .resume        = asic3_leds_enable, | 
| Paul Parsons | 13ca4f6 | 2011-05-13 18:53:03 +0000 | [diff] [blame] | 844 | }, | 
|  | 845 | [2] = { | 
|  | 846 | .name          = "leds-asic3", | 
|  | 847 | .id            = 2, | 
|  | 848 | .enable        = asic3_leds_enable, | 
|  | 849 | .disable       = asic3_leds_disable, | 
| Paul Parsons | e0b13b5 | 2011-08-09 16:27:33 +0000 | [diff] [blame] | 850 | .suspend       = asic3_leds_suspend, | 
|  | 851 | .resume        = asic3_leds_enable, | 
| Paul Parsons | 13ca4f6 | 2011-05-13 18:53:03 +0000 | [diff] [blame] | 852 | }, | 
|  | 853 | }; | 
|  | 854 |  | 
| Philipp Zabel | 9461f65 | 2009-06-15 12:10:24 +0200 | [diff] [blame] | 855 | static int __init asic3_mfd_probe(struct platform_device *pdev, | 
| Paul Parsons | 13ca4f6 | 2011-05-13 18:53:03 +0000 | [diff] [blame] | 856 | struct asic3_platform_data *pdata, | 
| Philipp Zabel | 9461f65 | 2009-06-15 12:10:24 +0200 | [diff] [blame] | 857 | struct resource *mem) | 
|  | 858 | { | 
|  | 859 | struct asic3 *asic = platform_get_drvdata(pdev); | 
| Philipp Zabel | 09f05ce | 2009-06-15 12:10:25 +0200 | [diff] [blame] | 860 | struct resource *mem_sdio; | 
|  | 861 | int irq, ret; | 
|  | 862 |  | 
|  | 863 | mem_sdio = platform_get_resource(pdev, IORESOURCE_MEM, 1); | 
|  | 864 | if (!mem_sdio) | 
|  | 865 | dev_dbg(asic->dev, "no SDIO MEM resource\n"); | 
|  | 866 |  | 
|  | 867 | irq = platform_get_irq(pdev, 1); | 
|  | 868 | if (irq < 0) | 
|  | 869 | dev_dbg(asic->dev, "no SDIO IRQ resource\n"); | 
| Philipp Zabel | 9461f65 | 2009-06-15 12:10:24 +0200 | [diff] [blame] | 870 |  | 
|  | 871 | /* DS1WM */ | 
|  | 872 | asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT), | 
|  | 873 | ASIC3_EXTCF_OWM_SMB, 0); | 
|  | 874 |  | 
|  | 875 | ds1wm_resources[0].start >>= asic->bus_shift; | 
|  | 876 | ds1wm_resources[0].end   >>= asic->bus_shift; | 
|  | 877 |  | 
| Philipp Zabel | 09f05ce | 2009-06-15 12:10:25 +0200 | [diff] [blame] | 878 | /* MMC */ | 
| Ian Molton | 64e8867 | 2010-01-06 13:51:48 +0100 | [diff] [blame] | 879 | asic->tmio_cnf = ioremap((ASIC3_SD_CONFIG_BASE >> asic->bus_shift) + | 
| Paul Parsons | 74e32d1 | 2011-05-15 14:13:11 +0000 | [diff] [blame] | 880 | mem_sdio->start, | 
|  | 881 | ASIC3_SD_CONFIG_SIZE >> asic->bus_shift); | 
| Ian Molton | 64e8867 | 2010-01-06 13:51:48 +0100 | [diff] [blame] | 882 | if (!asic->tmio_cnf) { | 
|  | 883 | ret = -ENOMEM; | 
|  | 884 | dev_dbg(asic->dev, "Couldn't ioremap SD_CONFIG\n"); | 
|  | 885 | goto out; | 
|  | 886 | } | 
| Philipp Zabel | 09f05ce | 2009-06-15 12:10:25 +0200 | [diff] [blame] | 887 | asic3_mmc_resources[0].start >>= asic->bus_shift; | 
|  | 888 | asic3_mmc_resources[0].end   >>= asic->bus_shift; | 
| Philipp Zabel | 09f05ce | 2009-06-15 12:10:25 +0200 | [diff] [blame] | 889 |  | 
| Philipp Zabel | 9461f65 | 2009-06-15 12:10:24 +0200 | [diff] [blame] | 890 | ret = mfd_add_devices(&pdev->dev, pdev->id, | 
|  | 891 | &asic3_cell_ds1wm, 1, mem, asic->irq_base); | 
| Philipp Zabel | 09f05ce | 2009-06-15 12:10:25 +0200 | [diff] [blame] | 892 | if (ret < 0) | 
|  | 893 | goto out; | 
| Philipp Zabel | 9461f65 | 2009-06-15 12:10:24 +0200 | [diff] [blame] | 894 |  | 
| Paul Parsons | 13ca4f6 | 2011-05-13 18:53:03 +0000 | [diff] [blame] | 895 | if (mem_sdio && (irq >= 0)) { | 
| Philipp Zabel | 09f05ce | 2009-06-15 12:10:25 +0200 | [diff] [blame] | 896 | ret = mfd_add_devices(&pdev->dev, pdev->id, | 
|  | 897 | &asic3_cell_mmc, 1, mem_sdio, irq); | 
| Paul Parsons | 13ca4f6 | 2011-05-13 18:53:03 +0000 | [diff] [blame] | 898 | if (ret < 0) | 
|  | 899 | goto out; | 
|  | 900 | } | 
|  | 901 |  | 
|  | 902 | if (pdata->leds) { | 
|  | 903 | int i; | 
|  | 904 |  | 
|  | 905 | for (i = 0; i < ASIC3_NUM_LEDS; ++i) { | 
|  | 906 | asic3_cell_leds[i].platform_data = &pdata->leds[i]; | 
|  | 907 | asic3_cell_leds[i].pdata_size = sizeof(pdata->leds[i]); | 
|  | 908 | } | 
|  | 909 | ret = mfd_add_devices(&pdev->dev, 0, | 
|  | 910 | asic3_cell_leds, ASIC3_NUM_LEDS, NULL, 0); | 
|  | 911 | } | 
| Philipp Zabel | 09f05ce | 2009-06-15 12:10:25 +0200 | [diff] [blame] | 912 |  | 
|  | 913 | out: | 
| Philipp Zabel | 9461f65 | 2009-06-15 12:10:24 +0200 | [diff] [blame] | 914 | return ret; | 
|  | 915 | } | 
|  | 916 |  | 
|  | 917 | static void asic3_mfd_remove(struct platform_device *pdev) | 
|  | 918 | { | 
| Ian Molton | 64e8867 | 2010-01-06 13:51:48 +0100 | [diff] [blame] | 919 | struct asic3 *asic = platform_get_drvdata(pdev); | 
|  | 920 |  | 
| Philipp Zabel | 9461f65 | 2009-06-15 12:10:24 +0200 | [diff] [blame] | 921 | mfd_remove_devices(&pdev->dev); | 
| Ian Molton | 64e8867 | 2010-01-06 13:51:48 +0100 | [diff] [blame] | 922 | iounmap(asic->tmio_cnf); | 
| Philipp Zabel | 9461f65 | 2009-06-15 12:10:24 +0200 | [diff] [blame] | 923 | } | 
|  | 924 |  | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 925 | /* Core */ | 
| Philipp Zabel | 065032f | 2008-06-21 00:51:38 +0200 | [diff] [blame] | 926 | static int __init asic3_probe(struct platform_device *pdev) | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 927 | { | 
|  | 928 | struct asic3_platform_data *pdata = pdev->dev.platform_data; | 
|  | 929 | struct asic3 *asic; | 
|  | 930 | struct resource *mem; | 
|  | 931 | unsigned long clksel; | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 932 | int ret = 0; | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 933 |  | 
|  | 934 | asic = kzalloc(sizeof(struct asic3), GFP_KERNEL); | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 935 | if (asic == NULL) { | 
|  | 936 | printk(KERN_ERR "kzalloc failed\n"); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 937 | return -ENOMEM; | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 938 | } | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 939 |  | 
|  | 940 | spin_lock_init(&asic->lock); | 
|  | 941 | platform_set_drvdata(pdev, asic); | 
|  | 942 | asic->dev = &pdev->dev; | 
|  | 943 |  | 
|  | 944 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
|  | 945 | if (!mem) { | 
|  | 946 | ret = -ENOMEM; | 
| Samuel Ortiz | 24f4f2e | 2008-06-20 11:11:19 +0200 | [diff] [blame] | 947 | dev_err(asic->dev, "no MEM resource\n"); | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 948 | goto out_free; | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 949 | } | 
|  | 950 |  | 
| Philipp Zabel | be584bd | 2009-06-05 18:31:04 +0200 | [diff] [blame] | 951 | asic->mapping = ioremap(mem->start, resource_size(mem)); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 952 | if (!asic->mapping) { | 
|  | 953 | ret = -ENOMEM; | 
| Samuel Ortiz | 24f4f2e | 2008-06-20 11:11:19 +0200 | [diff] [blame] | 954 | dev_err(asic->dev, "Couldn't ioremap\n"); | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 955 | goto out_free; | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 956 | } | 
|  | 957 |  | 
|  | 958 | asic->irq_base = pdata->irq_base; | 
|  | 959 |  | 
| Philipp Zabel | 99cdb0c | 2008-07-10 02:17:02 +0200 | [diff] [blame] | 960 | /* calculate bus shift from mem resource */ | 
| Philipp Zabel | be584bd | 2009-06-05 18:31:04 +0200 | [diff] [blame] | 961 | asic->bus_shift = 2 - (resource_size(mem) >> 12); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 962 |  | 
|  | 963 | clksel = 0; | 
|  | 964 | asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL), clksel); | 
|  | 965 |  | 
|  | 966 | ret = asic3_irq_probe(pdev); | 
|  | 967 | if (ret < 0) { | 
| Samuel Ortiz | 24f4f2e | 2008-06-20 11:11:19 +0200 | [diff] [blame] | 968 | dev_err(asic->dev, "Couldn't probe IRQs\n"); | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 969 | goto out_unmap; | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 970 | } | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 971 |  | 
| Paul Parsons | d8e4a88 | 2011-08-09 16:27:50 +0000 | [diff] [blame] | 972 | asic->gpio.label = "asic3"; | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 973 | asic->gpio.base = pdata->gpio_base; | 
|  | 974 | asic->gpio.ngpio = ASIC3_NUM_GPIOS; | 
|  | 975 | asic->gpio.get = asic3_gpio_get; | 
|  | 976 | asic->gpio.set = asic3_gpio_set; | 
|  | 977 | asic->gpio.direction_input = asic3_gpio_direction_input; | 
|  | 978 | asic->gpio.direction_output = asic3_gpio_direction_output; | 
|  | 979 |  | 
| Samuel Ortiz | 3b26bf1 | 2008-06-20 11:09:51 +0200 | [diff] [blame] | 980 | ret = asic3_gpio_probe(pdev, | 
|  | 981 | pdata->gpio_config, | 
|  | 982 | pdata->gpio_config_num); | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 983 | if (ret < 0) { | 
| Samuel Ortiz | 24f4f2e | 2008-06-20 11:11:19 +0200 | [diff] [blame] | 984 | dev_err(asic->dev, "GPIO probe failed\n"); | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 985 | goto out_irq; | 
|  | 986 | } | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 987 |  | 
| Philipp Zabel | e956a2a | 2009-06-05 18:31:02 +0200 | [diff] [blame] | 988 | /* Making a per-device copy is only needed for the | 
|  | 989 | * theoretical case of multiple ASIC3s on one board: | 
|  | 990 | */ | 
|  | 991 | memcpy(asic->clocks, asic3_clk_init, sizeof(asic3_clk_init)); | 
|  | 992 |  | 
| Paul Parsons | 13ca4f6 | 2011-05-13 18:53:03 +0000 | [diff] [blame] | 993 | asic3_mfd_probe(pdev, pdata, mem); | 
| Philipp Zabel | 9461f65 | 2009-06-15 12:10:24 +0200 | [diff] [blame] | 994 |  | 
| Samuel Ortiz | 24f4f2e | 2008-06-20 11:11:19 +0200 | [diff] [blame] | 995 | dev_info(asic->dev, "ASIC3 Core driver\n"); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 996 |  | 
|  | 997 | return 0; | 
|  | 998 |  | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 999 | out_irq: | 
|  | 1000 | asic3_irq_remove(pdev); | 
|  | 1001 |  | 
|  | 1002 | out_unmap: | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 1003 | iounmap(asic->mapping); | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 1004 |  | 
|  | 1005 | out_free: | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 1006 | kfree(asic); | 
|  | 1007 |  | 
|  | 1008 | return ret; | 
|  | 1009 | } | 
|  | 1010 |  | 
| Uwe Kleine-König | 1e3edaf | 2009-10-01 10:28:05 +0200 | [diff] [blame] | 1011 | static int __devexit asic3_remove(struct platform_device *pdev) | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 1012 | { | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 1013 | int ret; | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 1014 | struct asic3 *asic = platform_get_drvdata(pdev); | 
|  | 1015 |  | 
| Philipp Zabel | 9461f65 | 2009-06-15 12:10:24 +0200 | [diff] [blame] | 1016 | asic3_mfd_remove(pdev); | 
|  | 1017 |  | 
| Samuel Ortiz | 6f2384c | 2008-06-20 11:02:19 +0200 | [diff] [blame] | 1018 | ret = asic3_gpio_remove(pdev); | 
|  | 1019 | if (ret < 0) | 
|  | 1020 | return ret; | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 1021 | asic3_irq_remove(pdev); | 
|  | 1022 |  | 
|  | 1023 | asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL), 0); | 
|  | 1024 |  | 
|  | 1025 | iounmap(asic->mapping); | 
|  | 1026 |  | 
|  | 1027 | kfree(asic); | 
|  | 1028 |  | 
|  | 1029 | return 0; | 
|  | 1030 | } | 
|  | 1031 |  | 
|  | 1032 | static void asic3_shutdown(struct platform_device *pdev) | 
|  | 1033 | { | 
|  | 1034 | } | 
|  | 1035 |  | 
|  | 1036 | static struct platform_driver asic3_device_driver = { | 
|  | 1037 | .driver		= { | 
|  | 1038 | .name	= "asic3", | 
|  | 1039 | }, | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 1040 | .remove		= __devexit_p(asic3_remove), | 
|  | 1041 | .shutdown	= asic3_shutdown, | 
|  | 1042 | }; | 
|  | 1043 |  | 
|  | 1044 | static int __init asic3_init(void) | 
|  | 1045 | { | 
|  | 1046 | int retval = 0; | 
| Philipp Zabel | 065032f | 2008-06-21 00:51:38 +0200 | [diff] [blame] | 1047 | retval = platform_driver_probe(&asic3_device_driver, asic3_probe); | 
| Samuel Ortiz | fa9ff4b | 2008-02-07 00:14:49 -0800 | [diff] [blame] | 1048 | return retval; | 
|  | 1049 | } | 
|  | 1050 |  | 
|  | 1051 | subsys_initcall(asic3_init); |