| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 1 | /* | 
|  | 2 | * Pinmuxed GPIO support for SuperH. | 
|  | 3 | * | 
|  | 4 | * Copyright (C) 2008 Magnus Damm | 
|  | 5 | * | 
|  | 6 | * This file is subject to the terms and conditions of the GNU General Public | 
|  | 7 | * License.  See the file "COPYING" in the main directory of this archive | 
|  | 8 | * for more details. | 
|  | 9 | */ | 
| Paul Mundt | b72421d | 2010-10-04 03:54:56 +0900 | [diff] [blame] | 10 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 
|  | 11 |  | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 12 | #include <linux/errno.h> | 
|  | 13 | #include <linux/kernel.h> | 
|  | 14 | #include <linux/list.h> | 
|  | 15 | #include <linux/module.h> | 
|  | 16 | #include <linux/clk.h> | 
|  | 17 | #include <linux/err.h> | 
|  | 18 | #include <linux/io.h> | 
|  | 19 | #include <linux/irq.h> | 
|  | 20 | #include <linux/bitops.h> | 
|  | 21 | #include <linux/gpio.h> | 
|  | 22 |  | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 23 | static int enum_in_range(pinmux_enum_t enum_id, struct pinmux_range *r) | 
|  | 24 | { | 
|  | 25 | if (enum_id < r->begin) | 
|  | 26 | return 0; | 
|  | 27 |  | 
|  | 28 | if (enum_id > r->end) | 
|  | 29 | return 0; | 
|  | 30 |  | 
|  | 31 | return 1; | 
|  | 32 | } | 
|  | 33 |  | 
| Magnus Damm | 3292094 | 2008-12-25 18:17:26 +0900 | [diff] [blame] | 34 | static unsigned long gpio_read_raw_reg(unsigned long reg, | 
|  | 35 | unsigned long reg_width) | 
|  | 36 | { | 
|  | 37 | switch (reg_width) { | 
|  | 38 | case 8: | 
| Paul Mundt | 9cdae91 | 2009-11-30 12:10:41 +0900 | [diff] [blame] | 39 | return __raw_readb(reg); | 
| Magnus Damm | 3292094 | 2008-12-25 18:17:26 +0900 | [diff] [blame] | 40 | case 16: | 
| Paul Mundt | 9cdae91 | 2009-11-30 12:10:41 +0900 | [diff] [blame] | 41 | return __raw_readw(reg); | 
| Magnus Damm | 3292094 | 2008-12-25 18:17:26 +0900 | [diff] [blame] | 42 | case 32: | 
| Paul Mundt | 9cdae91 | 2009-11-30 12:10:41 +0900 | [diff] [blame] | 43 | return __raw_readl(reg); | 
| Magnus Damm | 3292094 | 2008-12-25 18:17:26 +0900 | [diff] [blame] | 44 | } | 
|  | 45 |  | 
|  | 46 | BUG(); | 
|  | 47 | return 0; | 
|  | 48 | } | 
|  | 49 |  | 
|  | 50 | static void gpio_write_raw_reg(unsigned long reg, | 
|  | 51 | unsigned long reg_width, | 
|  | 52 | unsigned long data) | 
|  | 53 | { | 
|  | 54 | switch (reg_width) { | 
|  | 55 | case 8: | 
| Paul Mundt | 9cdae91 | 2009-11-30 12:10:41 +0900 | [diff] [blame] | 56 | __raw_writeb(data, reg); | 
| Magnus Damm | 3292094 | 2008-12-25 18:17:26 +0900 | [diff] [blame] | 57 | return; | 
|  | 58 | case 16: | 
| Paul Mundt | 9cdae91 | 2009-11-30 12:10:41 +0900 | [diff] [blame] | 59 | __raw_writew(data, reg); | 
| Magnus Damm | 3292094 | 2008-12-25 18:17:26 +0900 | [diff] [blame] | 60 | return; | 
|  | 61 | case 32: | 
| Paul Mundt | 9cdae91 | 2009-11-30 12:10:41 +0900 | [diff] [blame] | 62 | __raw_writel(data, reg); | 
| Magnus Damm | 3292094 | 2008-12-25 18:17:26 +0900 | [diff] [blame] | 63 | return; | 
|  | 64 | } | 
|  | 65 |  | 
|  | 66 | BUG(); | 
|  | 67 | } | 
|  | 68 |  | 
|  | 69 | static void gpio_write_bit(struct pinmux_data_reg *dr, | 
|  | 70 | unsigned long in_pos, unsigned long value) | 
|  | 71 | { | 
|  | 72 | unsigned long pos; | 
|  | 73 |  | 
|  | 74 | pos = dr->reg_width - (in_pos + 1); | 
|  | 75 |  | 
| Paul Mundt | ca6f2d7 | 2009-12-09 15:51:27 +0900 | [diff] [blame] | 76 | pr_debug("write_bit addr = %lx, value = %d, pos = %ld, " | 
| Paul Mundt | fd2cb0c | 2009-11-30 12:15:04 +0900 | [diff] [blame] | 77 | "r_width = %ld\n", | 
|  | 78 | dr->reg, !!value, pos, dr->reg_width); | 
| Magnus Damm | 3292094 | 2008-12-25 18:17:26 +0900 | [diff] [blame] | 79 |  | 
|  | 80 | if (value) | 
|  | 81 | set_bit(pos, &dr->reg_shadow); | 
|  | 82 | else | 
|  | 83 | clear_bit(pos, &dr->reg_shadow); | 
|  | 84 |  | 
|  | 85 | gpio_write_raw_reg(dr->reg, dr->reg_width, dr->reg_shadow); | 
|  | 86 | } | 
|  | 87 |  | 
| Magnus Damm | 0fc64cc | 2008-12-25 18:17:18 +0900 | [diff] [blame] | 88 | static int gpio_read_reg(unsigned long reg, unsigned long reg_width, | 
|  | 89 | unsigned long field_width, unsigned long in_pos) | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 90 | { | 
|  | 91 | unsigned long data, mask, pos; | 
|  | 92 |  | 
|  | 93 | data = 0; | 
|  | 94 | mask = (1 << field_width) - 1; | 
|  | 95 | pos = reg_width - ((in_pos + 1) * field_width); | 
|  | 96 |  | 
| Paul Mundt | fd2cb0c | 2009-11-30 12:15:04 +0900 | [diff] [blame] | 97 | pr_debug("read_reg: addr = %lx, pos = %ld, " | 
|  | 98 | "r_width = %ld, f_width = %ld\n", | 
|  | 99 | reg, pos, reg_width, field_width); | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 100 |  | 
| Magnus Damm | 3292094 | 2008-12-25 18:17:26 +0900 | [diff] [blame] | 101 | data = gpio_read_raw_reg(reg, reg_width); | 
| Magnus Damm | 0fc64cc | 2008-12-25 18:17:18 +0900 | [diff] [blame] | 102 | return (data >> pos) & mask; | 
|  | 103 | } | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 104 |  | 
| Magnus Damm | 0fc64cc | 2008-12-25 18:17:18 +0900 | [diff] [blame] | 105 | static void gpio_write_reg(unsigned long reg, unsigned long reg_width, | 
|  | 106 | unsigned long field_width, unsigned long in_pos, | 
|  | 107 | unsigned long value) | 
|  | 108 | { | 
|  | 109 | unsigned long mask, pos; | 
|  | 110 |  | 
|  | 111 | mask = (1 << field_width) - 1; | 
|  | 112 | pos = reg_width - ((in_pos + 1) * field_width); | 
|  | 113 |  | 
| Paul Mundt | fd2cb0c | 2009-11-30 12:15:04 +0900 | [diff] [blame] | 114 | pr_debug("write_reg addr = %lx, value = %ld, pos = %ld, " | 
|  | 115 | "r_width = %ld, f_width = %ld\n", | 
|  | 116 | reg, value, pos, reg_width, field_width); | 
| Magnus Damm | 0fc64cc | 2008-12-25 18:17:18 +0900 | [diff] [blame] | 117 |  | 
|  | 118 | mask = ~(mask << pos); | 
|  | 119 | value = value << pos; | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 120 |  | 
|  | 121 | switch (reg_width) { | 
|  | 122 | case 8: | 
| Paul Mundt | 9cdae91 | 2009-11-30 12:10:41 +0900 | [diff] [blame] | 123 | __raw_writeb((__raw_readb(reg) & mask) | value, reg); | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 124 | break; | 
|  | 125 | case 16: | 
| Paul Mundt | 9cdae91 | 2009-11-30 12:10:41 +0900 | [diff] [blame] | 126 | __raw_writew((__raw_readw(reg) & mask) | value, reg); | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 127 | break; | 
|  | 128 | case 32: | 
| Paul Mundt | 9cdae91 | 2009-11-30 12:10:41 +0900 | [diff] [blame] | 129 | __raw_writel((__raw_readl(reg) & mask) | value, reg); | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 130 | break; | 
|  | 131 | } | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 132 | } | 
|  | 133 |  | 
| Magnus Damm | 18801be | 2008-12-25 18:17:09 +0900 | [diff] [blame] | 134 | static int setup_data_reg(struct pinmux_info *gpioc, unsigned gpio) | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 135 | { | 
| Magnus Damm | 18801be | 2008-12-25 18:17:09 +0900 | [diff] [blame] | 136 | struct pinmux_gpio *gpiop = &gpioc->gpios[gpio]; | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 137 | struct pinmux_data_reg *data_reg; | 
|  | 138 | int k, n; | 
|  | 139 |  | 
| Magnus Damm | 18801be | 2008-12-25 18:17:09 +0900 | [diff] [blame] | 140 | if (!enum_in_range(gpiop->enum_id, &gpioc->data)) | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 141 | return -1; | 
|  | 142 |  | 
|  | 143 | k = 0; | 
|  | 144 | while (1) { | 
|  | 145 | data_reg = gpioc->data_regs + k; | 
|  | 146 |  | 
|  | 147 | if (!data_reg->reg_width) | 
|  | 148 | break; | 
|  | 149 |  | 
|  | 150 | for (n = 0; n < data_reg->reg_width; n++) { | 
| Magnus Damm | 18801be | 2008-12-25 18:17:09 +0900 | [diff] [blame] | 151 | if (data_reg->enum_ids[n] == gpiop->enum_id) { | 
|  | 152 | gpiop->flags &= ~PINMUX_FLAG_DREG; | 
|  | 153 | gpiop->flags |= (k << PINMUX_FLAG_DREG_SHIFT); | 
|  | 154 | gpiop->flags &= ~PINMUX_FLAG_DBIT; | 
|  | 155 | gpiop->flags |= (n << PINMUX_FLAG_DBIT_SHIFT); | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 156 | return 0; | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 157 | } | 
|  | 158 | } | 
|  | 159 | k++; | 
|  | 160 | } | 
|  | 161 |  | 
| Magnus Damm | 18801be | 2008-12-25 18:17:09 +0900 | [diff] [blame] | 162 | BUG(); | 
|  | 163 |  | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 164 | return -1; | 
|  | 165 | } | 
|  | 166 |  | 
| Magnus Damm | 3292094 | 2008-12-25 18:17:26 +0900 | [diff] [blame] | 167 | static void setup_data_regs(struct pinmux_info *gpioc) | 
|  | 168 | { | 
|  | 169 | struct pinmux_data_reg *drp; | 
|  | 170 | int k; | 
|  | 171 |  | 
|  | 172 | for (k = gpioc->first_gpio; k <= gpioc->last_gpio; k++) | 
|  | 173 | setup_data_reg(gpioc, k); | 
|  | 174 |  | 
|  | 175 | k = 0; | 
|  | 176 | while (1) { | 
|  | 177 | drp = gpioc->data_regs + k; | 
|  | 178 |  | 
|  | 179 | if (!drp->reg_width) | 
|  | 180 | break; | 
|  | 181 |  | 
|  | 182 | drp->reg_shadow = gpio_read_raw_reg(drp->reg, drp->reg_width); | 
|  | 183 | k++; | 
|  | 184 | } | 
|  | 185 | } | 
|  | 186 |  | 
| Magnus Damm | 18801be | 2008-12-25 18:17:09 +0900 | [diff] [blame] | 187 | static int get_data_reg(struct pinmux_info *gpioc, unsigned gpio, | 
|  | 188 | struct pinmux_data_reg **drp, int *bitp) | 
|  | 189 | { | 
|  | 190 | struct pinmux_gpio *gpiop = &gpioc->gpios[gpio]; | 
|  | 191 | int k, n; | 
|  | 192 |  | 
|  | 193 | if (!enum_in_range(gpiop->enum_id, &gpioc->data)) | 
|  | 194 | return -1; | 
|  | 195 |  | 
|  | 196 | k = (gpiop->flags & PINMUX_FLAG_DREG) >> PINMUX_FLAG_DREG_SHIFT; | 
|  | 197 | n = (gpiop->flags & PINMUX_FLAG_DBIT) >> PINMUX_FLAG_DBIT_SHIFT; | 
|  | 198 | *drp = gpioc->data_regs + k; | 
|  | 199 | *bitp = n; | 
|  | 200 | return 0; | 
|  | 201 | } | 
|  | 202 |  | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 203 | static int get_config_reg(struct pinmux_info *gpioc, pinmux_enum_t enum_id, | 
|  | 204 | struct pinmux_cfg_reg **crp, int *indexp, | 
|  | 205 | unsigned long **cntp) | 
|  | 206 | { | 
|  | 207 | struct pinmux_cfg_reg *config_reg; | 
|  | 208 | unsigned long r_width, f_width; | 
|  | 209 | int k, n; | 
|  | 210 |  | 
|  | 211 | k = 0; | 
|  | 212 | while (1) { | 
|  | 213 | config_reg = gpioc->cfg_regs + k; | 
|  | 214 |  | 
|  | 215 | r_width = config_reg->reg_width; | 
|  | 216 | f_width = config_reg->field_width; | 
|  | 217 |  | 
|  | 218 | if (!r_width) | 
|  | 219 | break; | 
| Magnus Damm | c63bcc6 | 2011-10-17 18:01:19 +0900 | [diff] [blame] | 220 | for (n = 0; n < (r_width / f_width) * (1 << f_width); n++) { | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 221 | if (config_reg->enum_ids[n] == enum_id) { | 
|  | 222 | *crp = config_reg; | 
|  | 223 | *indexp = n; | 
|  | 224 | *cntp = &config_reg->cnt[n / (1 << f_width)]; | 
|  | 225 | return 0; | 
|  | 226 | } | 
|  | 227 | } | 
|  | 228 | k++; | 
|  | 229 | } | 
|  | 230 |  | 
|  | 231 | return -1; | 
|  | 232 | } | 
|  | 233 |  | 
|  | 234 | static int get_gpio_enum_id(struct pinmux_info *gpioc, unsigned gpio, | 
|  | 235 | int pos, pinmux_enum_t *enum_idp) | 
|  | 236 | { | 
|  | 237 | pinmux_enum_t enum_id = gpioc->gpios[gpio].enum_id; | 
|  | 238 | pinmux_enum_t *data = gpioc->gpio_data; | 
|  | 239 | int k; | 
|  | 240 |  | 
|  | 241 | if (!enum_in_range(enum_id, &gpioc->data)) { | 
|  | 242 | if (!enum_in_range(enum_id, &gpioc->mark)) { | 
|  | 243 | pr_err("non data/mark enum_id for gpio %d\n", gpio); | 
|  | 244 | return -1; | 
|  | 245 | } | 
|  | 246 | } | 
|  | 247 |  | 
|  | 248 | if (pos) { | 
|  | 249 | *enum_idp = data[pos + 1]; | 
|  | 250 | return pos + 1; | 
|  | 251 | } | 
|  | 252 |  | 
|  | 253 | for (k = 0; k < gpioc->gpio_data_size; k++) { | 
|  | 254 | if (data[k] == enum_id) { | 
|  | 255 | *enum_idp = data[k + 1]; | 
|  | 256 | return k + 1; | 
|  | 257 | } | 
|  | 258 | } | 
|  | 259 |  | 
|  | 260 | pr_err("cannot locate data/mark enum_id for gpio %d\n", gpio); | 
|  | 261 | return -1; | 
|  | 262 | } | 
|  | 263 |  | 
| Magnus Damm | 0fc64cc | 2008-12-25 18:17:18 +0900 | [diff] [blame] | 264 | static void write_config_reg(struct pinmux_info *gpioc, | 
|  | 265 | struct pinmux_cfg_reg *crp, | 
|  | 266 | int index) | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 267 | { | 
|  | 268 | unsigned long ncomb, pos, value; | 
|  | 269 |  | 
|  | 270 | ncomb = 1 << crp->field_width; | 
|  | 271 | pos = index / ncomb; | 
|  | 272 | value = index % ncomb; | 
|  | 273 |  | 
| Magnus Damm | 0fc64cc | 2008-12-25 18:17:18 +0900 | [diff] [blame] | 274 | gpio_write_reg(crp->reg, crp->reg_width, crp->field_width, pos, value); | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 275 | } | 
|  | 276 |  | 
|  | 277 | static int check_config_reg(struct pinmux_info *gpioc, | 
|  | 278 | struct pinmux_cfg_reg *crp, | 
|  | 279 | int index) | 
|  | 280 | { | 
|  | 281 | unsigned long ncomb, pos, value; | 
|  | 282 |  | 
|  | 283 | ncomb = 1 << crp->field_width; | 
|  | 284 | pos = index / ncomb; | 
|  | 285 | value = index % ncomb; | 
|  | 286 |  | 
| Magnus Damm | 0fc64cc | 2008-12-25 18:17:18 +0900 | [diff] [blame] | 287 | if (gpio_read_reg(crp->reg, crp->reg_width, | 
|  | 288 | crp->field_width, pos) == value) | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 289 | return 0; | 
|  | 290 |  | 
|  | 291 | return -1; | 
|  | 292 | } | 
|  | 293 |  | 
|  | 294 | enum { GPIO_CFG_DRYRUN, GPIO_CFG_REQ, GPIO_CFG_FREE }; | 
|  | 295 |  | 
| Magnus Damm | 0fc64cc | 2008-12-25 18:17:18 +0900 | [diff] [blame] | 296 | static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, | 
|  | 297 | int pinmux_type, int cfg_mode) | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 298 | { | 
|  | 299 | struct pinmux_cfg_reg *cr = NULL; | 
|  | 300 | pinmux_enum_t enum_id; | 
|  | 301 | struct pinmux_range *range; | 
|  | 302 | int in_range, pos, index; | 
|  | 303 | unsigned long *cntp; | 
|  | 304 |  | 
|  | 305 | switch (pinmux_type) { | 
|  | 306 |  | 
|  | 307 | case PINMUX_TYPE_FUNCTION: | 
|  | 308 | range = NULL; | 
|  | 309 | break; | 
|  | 310 |  | 
|  | 311 | case PINMUX_TYPE_OUTPUT: | 
|  | 312 | range = &gpioc->output; | 
|  | 313 | break; | 
|  | 314 |  | 
|  | 315 | case PINMUX_TYPE_INPUT: | 
|  | 316 | range = &gpioc->input; | 
|  | 317 | break; | 
|  | 318 |  | 
|  | 319 | case PINMUX_TYPE_INPUT_PULLUP: | 
|  | 320 | range = &gpioc->input_pu; | 
|  | 321 | break; | 
|  | 322 |  | 
|  | 323 | case PINMUX_TYPE_INPUT_PULLDOWN: | 
|  | 324 | range = &gpioc->input_pd; | 
|  | 325 | break; | 
|  | 326 |  | 
|  | 327 | default: | 
|  | 328 | goto out_err; | 
|  | 329 | } | 
|  | 330 |  | 
|  | 331 | pos = 0; | 
|  | 332 | enum_id = 0; | 
|  | 333 | index = 0; | 
|  | 334 | while (1) { | 
|  | 335 | pos = get_gpio_enum_id(gpioc, gpio, pos, &enum_id); | 
|  | 336 | if (pos <= 0) | 
|  | 337 | goto out_err; | 
|  | 338 |  | 
|  | 339 | if (!enum_id) | 
|  | 340 | break; | 
|  | 341 |  | 
| Magnus Damm | 50dd314 | 2010-01-19 13:52:28 +0000 | [diff] [blame] | 342 | /* first check if this is a function enum */ | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 343 | in_range = enum_in_range(enum_id, &gpioc->function); | 
| Magnus Damm | 50dd314 | 2010-01-19 13:52:28 +0000 | [diff] [blame] | 344 | if (!in_range) { | 
|  | 345 | /* not a function enum */ | 
|  | 346 | if (range) { | 
|  | 347 | /* | 
|  | 348 | * other range exists, so this pin is | 
|  | 349 | * a regular GPIO pin that now is being | 
|  | 350 | * bound to a specific direction. | 
|  | 351 | * | 
|  | 352 | * for this case we only allow function enums | 
|  | 353 | * and the enums that match the other range. | 
|  | 354 | */ | 
|  | 355 | in_range = enum_in_range(enum_id, range); | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 356 |  | 
| Magnus Damm | 50dd314 | 2010-01-19 13:52:28 +0000 | [diff] [blame] | 357 | /* | 
|  | 358 | * special case pass through for fixed | 
|  | 359 | * input-only or output-only pins without | 
|  | 360 | * function enum register association. | 
|  | 361 | */ | 
|  | 362 | if (in_range && enum_id == range->force) | 
|  | 363 | continue; | 
|  | 364 | } else { | 
|  | 365 | /* | 
|  | 366 | * no other range exists, so this pin | 
|  | 367 | * must then be of the function type. | 
|  | 368 | * | 
|  | 369 | * allow function type pins to select | 
|  | 370 | * any combination of function/in/out | 
|  | 371 | * in their MARK lists. | 
|  | 372 | */ | 
|  | 373 | in_range = 1; | 
|  | 374 | } | 
| Magnus Damm | 42eed42 | 2008-10-22 18:29:17 +0900 | [diff] [blame] | 375 | } | 
|  | 376 |  | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 377 | if (!in_range) | 
|  | 378 | continue; | 
|  | 379 |  | 
|  | 380 | if (get_config_reg(gpioc, enum_id, &cr, &index, &cntp) != 0) | 
|  | 381 | goto out_err; | 
|  | 382 |  | 
|  | 383 | switch (cfg_mode) { | 
|  | 384 | case GPIO_CFG_DRYRUN: | 
|  | 385 | if (!*cntp || !check_config_reg(gpioc, cr, index)) | 
|  | 386 | continue; | 
|  | 387 | break; | 
|  | 388 |  | 
|  | 389 | case GPIO_CFG_REQ: | 
| Magnus Damm | 0fc64cc | 2008-12-25 18:17:18 +0900 | [diff] [blame] | 390 | write_config_reg(gpioc, cr, index); | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 391 | *cntp = *cntp + 1; | 
|  | 392 | break; | 
|  | 393 |  | 
|  | 394 | case GPIO_CFG_FREE: | 
|  | 395 | *cntp = *cntp - 1; | 
|  | 396 | break; | 
|  | 397 | } | 
|  | 398 | } | 
|  | 399 |  | 
|  | 400 | return 0; | 
|  | 401 | out_err: | 
|  | 402 | return -1; | 
|  | 403 | } | 
|  | 404 |  | 
|  | 405 | static DEFINE_SPINLOCK(gpio_lock); | 
|  | 406 |  | 
| Magnus Damm | 69edbba | 2008-12-25 18:17:34 +0900 | [diff] [blame] | 407 | static struct pinmux_info *chip_to_pinmux(struct gpio_chip *chip) | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 408 | { | 
| Magnus Damm | 69edbba | 2008-12-25 18:17:34 +0900 | [diff] [blame] | 409 | return container_of(chip, struct pinmux_info, chip); | 
|  | 410 | } | 
|  | 411 |  | 
|  | 412 | static int sh_gpio_request(struct gpio_chip *chip, unsigned offset) | 
|  | 413 | { | 
|  | 414 | struct pinmux_info *gpioc = chip_to_pinmux(chip); | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 415 | struct pinmux_data_reg *dummy; | 
|  | 416 | unsigned long flags; | 
|  | 417 | int i, ret, pinmux_type; | 
|  | 418 |  | 
|  | 419 | ret = -EINVAL; | 
|  | 420 |  | 
|  | 421 | if (!gpioc) | 
|  | 422 | goto err_out; | 
|  | 423 |  | 
|  | 424 | spin_lock_irqsave(&gpio_lock, flags); | 
|  | 425 |  | 
| Magnus Damm | 69edbba | 2008-12-25 18:17:34 +0900 | [diff] [blame] | 426 | if ((gpioc->gpios[offset].flags & PINMUX_FLAG_TYPE) != PINMUX_TYPE_NONE) | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 427 | goto err_unlock; | 
|  | 428 |  | 
|  | 429 | /* setup pin function here if no data is associated with pin */ | 
|  | 430 |  | 
| Magnus Damm | 69edbba | 2008-12-25 18:17:34 +0900 | [diff] [blame] | 431 | if (get_data_reg(gpioc, offset, &dummy, &i) != 0) | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 432 | pinmux_type = PINMUX_TYPE_FUNCTION; | 
|  | 433 | else | 
|  | 434 | pinmux_type = PINMUX_TYPE_GPIO; | 
|  | 435 |  | 
|  | 436 | if (pinmux_type == PINMUX_TYPE_FUNCTION) { | 
| Magnus Damm | 69edbba | 2008-12-25 18:17:34 +0900 | [diff] [blame] | 437 | if (pinmux_config_gpio(gpioc, offset, | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 438 | pinmux_type, | 
|  | 439 | GPIO_CFG_DRYRUN) != 0) | 
|  | 440 | goto err_unlock; | 
|  | 441 |  | 
| Magnus Damm | 69edbba | 2008-12-25 18:17:34 +0900 | [diff] [blame] | 442 | if (pinmux_config_gpio(gpioc, offset, | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 443 | pinmux_type, | 
|  | 444 | GPIO_CFG_REQ) != 0) | 
|  | 445 | BUG(); | 
|  | 446 | } | 
|  | 447 |  | 
| Magnus Damm | 69edbba | 2008-12-25 18:17:34 +0900 | [diff] [blame] | 448 | gpioc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; | 
|  | 449 | gpioc->gpios[offset].flags |= pinmux_type; | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 450 |  | 
|  | 451 | ret = 0; | 
|  | 452 | err_unlock: | 
|  | 453 | spin_unlock_irqrestore(&gpio_lock, flags); | 
|  | 454 | err_out: | 
|  | 455 | return ret; | 
|  | 456 | } | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 457 |  | 
| Magnus Damm | 69edbba | 2008-12-25 18:17:34 +0900 | [diff] [blame] | 458 | static void sh_gpio_free(struct gpio_chip *chip, unsigned offset) | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 459 | { | 
| Magnus Damm | 69edbba | 2008-12-25 18:17:34 +0900 | [diff] [blame] | 460 | struct pinmux_info *gpioc = chip_to_pinmux(chip); | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 461 | unsigned long flags; | 
|  | 462 | int pinmux_type; | 
|  | 463 |  | 
|  | 464 | if (!gpioc) | 
|  | 465 | return; | 
|  | 466 |  | 
|  | 467 | spin_lock_irqsave(&gpio_lock, flags); | 
|  | 468 |  | 
| Magnus Damm | 69edbba | 2008-12-25 18:17:34 +0900 | [diff] [blame] | 469 | pinmux_type = gpioc->gpios[offset].flags & PINMUX_FLAG_TYPE; | 
|  | 470 | pinmux_config_gpio(gpioc, offset, pinmux_type, GPIO_CFG_FREE); | 
|  | 471 | gpioc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; | 
|  | 472 | gpioc->gpios[offset].flags |= PINMUX_TYPE_NONE; | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 473 |  | 
|  | 474 | spin_unlock_irqrestore(&gpio_lock, flags); | 
|  | 475 | } | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 476 |  | 
|  | 477 | static int pinmux_direction(struct pinmux_info *gpioc, | 
|  | 478 | unsigned gpio, int new_pinmux_type) | 
|  | 479 | { | 
| Magnus Damm | 0fc64cc | 2008-12-25 18:17:18 +0900 | [diff] [blame] | 480 | int pinmux_type; | 
|  | 481 | int ret = -EINVAL; | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 482 |  | 
| Magnus Damm | 0fc64cc | 2008-12-25 18:17:18 +0900 | [diff] [blame] | 483 | if (!gpioc) | 
|  | 484 | goto err_out; | 
|  | 485 |  | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 486 | pinmux_type = gpioc->gpios[gpio].flags & PINMUX_FLAG_TYPE; | 
|  | 487 |  | 
|  | 488 | switch (pinmux_type) { | 
|  | 489 | case PINMUX_TYPE_GPIO: | 
|  | 490 | break; | 
|  | 491 | case PINMUX_TYPE_OUTPUT: | 
|  | 492 | case PINMUX_TYPE_INPUT: | 
|  | 493 | case PINMUX_TYPE_INPUT_PULLUP: | 
|  | 494 | case PINMUX_TYPE_INPUT_PULLDOWN: | 
|  | 495 | pinmux_config_gpio(gpioc, gpio, pinmux_type, GPIO_CFG_FREE); | 
|  | 496 | break; | 
|  | 497 | default: | 
|  | 498 | goto err_out; | 
|  | 499 | } | 
|  | 500 |  | 
|  | 501 | if (pinmux_config_gpio(gpioc, gpio, | 
|  | 502 | new_pinmux_type, | 
|  | 503 | GPIO_CFG_DRYRUN) != 0) | 
|  | 504 | goto err_out; | 
|  | 505 |  | 
|  | 506 | if (pinmux_config_gpio(gpioc, gpio, | 
|  | 507 | new_pinmux_type, | 
|  | 508 | GPIO_CFG_REQ) != 0) | 
|  | 509 | BUG(); | 
|  | 510 |  | 
| Magnus Damm | 18801be | 2008-12-25 18:17:09 +0900 | [diff] [blame] | 511 | gpioc->gpios[gpio].flags &= ~PINMUX_FLAG_TYPE; | 
|  | 512 | gpioc->gpios[gpio].flags |= new_pinmux_type; | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 513 |  | 
|  | 514 | ret = 0; | 
|  | 515 | err_out: | 
|  | 516 | return ret; | 
|  | 517 | } | 
|  | 518 |  | 
| Magnus Damm | 69edbba | 2008-12-25 18:17:34 +0900 | [diff] [blame] | 519 | static int sh_gpio_direction_input(struct gpio_chip *chip, unsigned offset) | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 520 | { | 
| Magnus Damm | 69edbba | 2008-12-25 18:17:34 +0900 | [diff] [blame] | 521 | struct pinmux_info *gpioc = chip_to_pinmux(chip); | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 522 | unsigned long flags; | 
| Magnus Damm | 0fc64cc | 2008-12-25 18:17:18 +0900 | [diff] [blame] | 523 | int ret; | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 524 |  | 
|  | 525 | spin_lock_irqsave(&gpio_lock, flags); | 
| Magnus Damm | 69edbba | 2008-12-25 18:17:34 +0900 | [diff] [blame] | 526 | ret = pinmux_direction(gpioc, offset, PINMUX_TYPE_INPUT); | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 527 | spin_unlock_irqrestore(&gpio_lock, flags); | 
| Magnus Damm | 0fc64cc | 2008-12-25 18:17:18 +0900 | [diff] [blame] | 528 |  | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 529 | return ret; | 
|  | 530 | } | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 531 |  | 
| Magnus Damm | 69edbba | 2008-12-25 18:17:34 +0900 | [diff] [blame] | 532 | static void sh_gpio_set_value(struct pinmux_info *gpioc, | 
| Magnus Damm | 0fc64cc | 2008-12-25 18:17:18 +0900 | [diff] [blame] | 533 | unsigned gpio, int value) | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 534 | { | 
|  | 535 | struct pinmux_data_reg *dr = NULL; | 
|  | 536 | int bit = 0; | 
|  | 537 |  | 
| Magnus Damm | 0fc64cc | 2008-12-25 18:17:18 +0900 | [diff] [blame] | 538 | if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0) | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 539 | BUG(); | 
|  | 540 | else | 
| Magnus Damm | 3292094 | 2008-12-25 18:17:26 +0900 | [diff] [blame] | 541 | gpio_write_bit(dr, bit, value); | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 542 | } | 
|  | 543 |  | 
| Magnus Damm | 69edbba | 2008-12-25 18:17:34 +0900 | [diff] [blame] | 544 | static int sh_gpio_direction_output(struct gpio_chip *chip, unsigned offset, | 
|  | 545 | int value) | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 546 | { | 
| Magnus Damm | 69edbba | 2008-12-25 18:17:34 +0900 | [diff] [blame] | 547 | struct pinmux_info *gpioc = chip_to_pinmux(chip); | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 548 | unsigned long flags; | 
| Magnus Damm | 0fc64cc | 2008-12-25 18:17:18 +0900 | [diff] [blame] | 549 | int ret; | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 550 |  | 
| Magnus Damm | 69edbba | 2008-12-25 18:17:34 +0900 | [diff] [blame] | 551 | sh_gpio_set_value(gpioc, offset, value); | 
| Magnus Damm | 3292094 | 2008-12-25 18:17:26 +0900 | [diff] [blame] | 552 | spin_lock_irqsave(&gpio_lock, flags); | 
| Magnus Damm | 69edbba | 2008-12-25 18:17:34 +0900 | [diff] [blame] | 553 | ret = pinmux_direction(gpioc, offset, PINMUX_TYPE_OUTPUT); | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 554 | spin_unlock_irqrestore(&gpio_lock, flags); | 
| Magnus Damm | 0fc64cc | 2008-12-25 18:17:18 +0900 | [diff] [blame] | 555 |  | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 556 | return ret; | 
|  | 557 | } | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 558 |  | 
| Magnus Damm | 69edbba | 2008-12-25 18:17:34 +0900 | [diff] [blame] | 559 | static int sh_gpio_get_value(struct pinmux_info *gpioc, unsigned gpio) | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 560 | { | 
| Magnus Damm | 0fc64cc | 2008-12-25 18:17:18 +0900 | [diff] [blame] | 561 | struct pinmux_data_reg *dr = NULL; | 
|  | 562 | int bit = 0; | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 563 |  | 
| Paul Mundt | e8184a4 | 2010-10-04 05:15:20 +0900 | [diff] [blame] | 564 | if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0) | 
|  | 565 | return -EINVAL; | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 566 |  | 
| Magnus Damm | 0fc64cc | 2008-12-25 18:17:18 +0900 | [diff] [blame] | 567 | return gpio_read_reg(dr->reg, dr->reg_width, 1, bit); | 
|  | 568 | } | 
|  | 569 |  | 
| Magnus Damm | 69edbba | 2008-12-25 18:17:34 +0900 | [diff] [blame] | 570 | static int sh_gpio_get(struct gpio_chip *chip, unsigned offset) | 
| Magnus Damm | 0fc64cc | 2008-12-25 18:17:18 +0900 | [diff] [blame] | 571 | { | 
| Magnus Damm | 69edbba | 2008-12-25 18:17:34 +0900 | [diff] [blame] | 572 | return sh_gpio_get_value(chip_to_pinmux(chip), offset); | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 573 | } | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 574 |  | 
| Magnus Damm | 69edbba | 2008-12-25 18:17:34 +0900 | [diff] [blame] | 575 | static void sh_gpio_set(struct gpio_chip *chip, unsigned offset, int value) | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 576 | { | 
| Magnus Damm | 69edbba | 2008-12-25 18:17:34 +0900 | [diff] [blame] | 577 | sh_gpio_set_value(chip_to_pinmux(chip), offset, value); | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 578 | } | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 579 |  | 
| Magnus Damm | ad2a8e7 | 2011-09-28 16:50:58 +0900 | [diff] [blame] | 580 | static int sh_gpio_to_irq(struct gpio_chip *chip, unsigned offset) | 
|  | 581 | { | 
|  | 582 | struct pinmux_info *gpioc = chip_to_pinmux(chip); | 
|  | 583 | pinmux_enum_t enum_id; | 
|  | 584 | pinmux_enum_t *enum_ids; | 
|  | 585 | int i, k, pos; | 
|  | 586 |  | 
|  | 587 | pos = 0; | 
|  | 588 | enum_id = 0; | 
|  | 589 | while (1) { | 
|  | 590 | pos = get_gpio_enum_id(gpioc, offset, pos, &enum_id); | 
|  | 591 | if (pos <= 0 || !enum_id) | 
|  | 592 | break; | 
|  | 593 |  | 
|  | 594 | for (i = 0; i < gpioc->gpio_irq_size; i++) { | 
|  | 595 | enum_ids = gpioc->gpio_irq[i].enum_ids; | 
|  | 596 | for (k = 0; enum_ids[k]; k++) { | 
|  | 597 | if (enum_ids[k] == enum_id) | 
|  | 598 | return gpioc->gpio_irq[i].irq; | 
|  | 599 | } | 
|  | 600 | } | 
|  | 601 | } | 
|  | 602 |  | 
|  | 603 | return -ENOSYS; | 
|  | 604 | } | 
|  | 605 |  | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 606 | int register_pinmux(struct pinmux_info *pip) | 
|  | 607 | { | 
| Magnus Damm | 69edbba | 2008-12-25 18:17:34 +0900 | [diff] [blame] | 608 | struct gpio_chip *chip = &pip->chip; | 
|  | 609 |  | 
| Paul Mundt | b72421d | 2010-10-04 03:54:56 +0900 | [diff] [blame] | 610 | pr_info("%s handling gpio %d -> %d\n", | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 611 | pip->name, pip->first_gpio, pip->last_gpio); | 
|  | 612 |  | 
| Magnus Damm | 69edbba | 2008-12-25 18:17:34 +0900 | [diff] [blame] | 613 | setup_data_regs(pip); | 
|  | 614 |  | 
|  | 615 | chip->request = sh_gpio_request; | 
|  | 616 | chip->free = sh_gpio_free; | 
|  | 617 | chip->direction_input = sh_gpio_direction_input; | 
|  | 618 | chip->get = sh_gpio_get; | 
|  | 619 | chip->direction_output = sh_gpio_direction_output; | 
|  | 620 | chip->set = sh_gpio_set; | 
| Magnus Damm | ad2a8e7 | 2011-09-28 16:50:58 +0900 | [diff] [blame] | 621 | chip->to_irq = sh_gpio_to_irq; | 
| Magnus Damm | 69edbba | 2008-12-25 18:17:34 +0900 | [diff] [blame] | 622 |  | 
|  | 623 | WARN_ON(pip->first_gpio != 0); /* needs testing */ | 
|  | 624 |  | 
|  | 625 | chip->label = pip->name; | 
|  | 626 | chip->owner = THIS_MODULE; | 
|  | 627 | chip->base = pip->first_gpio; | 
|  | 628 | chip->ngpio = (pip->last_gpio - pip->first_gpio) + 1; | 
|  | 629 |  | 
|  | 630 | return gpiochip_add(chip); | 
| Magnus Damm | 2967dab | 2008-10-08 20:41:43 +0900 | [diff] [blame] | 631 | } | 
| Paul Mundt | b72421d | 2010-10-04 03:54:56 +0900 | [diff] [blame] | 632 |  | 
|  | 633 | int unregister_pinmux(struct pinmux_info *pip) | 
|  | 634 | { | 
|  | 635 | pr_info("%s deregistering\n", pip->name); | 
|  | 636 |  | 
|  | 637 | return gpiochip_remove(&pip->chip); | 
|  | 638 | } |