diff options
Diffstat (limited to 'drivers/pinctrl/pinctrl-samsung.c')
-rw-r--r-- | drivers/pinctrl/pinctrl-samsung.c | 105 |
1 files changed, 66 insertions, 39 deletions
diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c index 3d5cf639aa46..976366899f68 100644 --- a/drivers/pinctrl/pinctrl-samsung.c +++ b/drivers/pinctrl/pinctrl-samsung.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <linux/err.h> | 27 | #include <linux/err.h> |
28 | #include <linux/gpio.h> | 28 | #include <linux/gpio.h> |
29 | #include <linux/irqdomain.h> | 29 | #include <linux/irqdomain.h> |
30 | #include <linux/spinlock.h> | ||
30 | 31 | ||
31 | #include "core.h" | 32 | #include "core.h" |
32 | #include "pinctrl-samsung.h" | 33 | #include "pinctrl-samsung.h" |
@@ -214,7 +215,7 @@ static void samsung_dt_free_map(struct pinctrl_dev *pctldev, | |||
214 | } | 215 | } |
215 | 216 | ||
216 | /* list of pinctrl callbacks for the pinctrl core */ | 217 | /* list of pinctrl callbacks for the pinctrl core */ |
217 | static struct pinctrl_ops samsung_pctrl_ops = { | 218 | static const struct pinctrl_ops samsung_pctrl_ops = { |
218 | .get_groups_count = samsung_get_group_count, | 219 | .get_groups_count = samsung_get_group_count, |
219 | .get_group_name = samsung_get_group_name, | 220 | .get_group_name = samsung_get_group_name, |
220 | .get_group_pins = samsung_get_group_pins, | 221 | .get_group_pins = samsung_get_group_pins, |
@@ -274,10 +275,6 @@ static void pin_to_reg_bank(struct samsung_pinctrl_drv_data *drvdata, | |||
274 | *offset = pin - b->pin_base; | 275 | *offset = pin - b->pin_base; |
275 | if (bank) | 276 | if (bank) |
276 | *bank = b; | 277 | *bank = b; |
277 | |||
278 | /* some banks have two config registers in a single bank */ | ||
279 | if (*offset * b->func_width > BITS_PER_LONG) | ||
280 | *reg += 4; | ||
281 | } | 278 | } |
282 | 279 | ||
283 | /* enable or disable a pinmux function */ | 280 | /* enable or disable a pinmux function */ |
@@ -289,6 +286,7 @@ static void samsung_pinmux_setup(struct pinctrl_dev *pctldev, unsigned selector, | |||
289 | struct samsung_pin_bank *bank; | 286 | struct samsung_pin_bank *bank; |
290 | void __iomem *reg; | 287 | void __iomem *reg; |
291 | u32 mask, shift, data, pin_offset, cnt; | 288 | u32 mask, shift, data, pin_offset, cnt; |
289 | unsigned long flags; | ||
292 | 290 | ||
293 | drvdata = pinctrl_dev_get_drvdata(pctldev); | 291 | drvdata = pinctrl_dev_get_drvdata(pctldev); |
294 | pins = drvdata->pin_groups[group].pins; | 292 | pins = drvdata->pin_groups[group].pins; |
@@ -298,16 +296,28 @@ static void samsung_pinmux_setup(struct pinctrl_dev *pctldev, unsigned selector, | |||
298 | * pin function number in the config register. | 296 | * pin function number in the config register. |
299 | */ | 297 | */ |
300 | for (cnt = 0; cnt < drvdata->pin_groups[group].num_pins; cnt++) { | 298 | for (cnt = 0; cnt < drvdata->pin_groups[group].num_pins; cnt++) { |
299 | struct samsung_pin_bank_type *type; | ||
300 | |||
301 | pin_to_reg_bank(drvdata, pins[cnt] - drvdata->ctrl->base, | 301 | pin_to_reg_bank(drvdata, pins[cnt] - drvdata->ctrl->base, |
302 | ®, &pin_offset, &bank); | 302 | ®, &pin_offset, &bank); |
303 | mask = (1 << bank->func_width) - 1; | 303 | type = bank->type; |
304 | shift = pin_offset * bank->func_width; | 304 | mask = (1 << type->fld_width[PINCFG_TYPE_FUNC]) - 1; |
305 | shift = pin_offset * type->fld_width[PINCFG_TYPE_FUNC]; | ||
306 | if (shift >= 32) { | ||
307 | /* Some banks have two config registers */ | ||
308 | shift -= 32; | ||
309 | reg += 4; | ||
310 | } | ||
311 | |||
312 | spin_lock_irqsave(&bank->slock, flags); | ||
305 | 313 | ||
306 | data = readl(reg); | 314 | data = readl(reg + type->reg_offset[PINCFG_TYPE_FUNC]); |
307 | data &= ~(mask << shift); | 315 | data &= ~(mask << shift); |
308 | if (enable) | 316 | if (enable) |
309 | data |= drvdata->pin_groups[group].func << shift; | 317 | data |= drvdata->pin_groups[group].func << shift; |
310 | writel(data, reg); | 318 | writel(data, reg + type->reg_offset[PINCFG_TYPE_FUNC]); |
319 | |||
320 | spin_unlock_irqrestore(&bank->slock, flags); | ||
311 | } | 321 | } |
312 | } | 322 | } |
313 | 323 | ||
@@ -334,30 +344,44 @@ static void samsung_pinmux_disable(struct pinctrl_dev *pctldev, | |||
334 | static int samsung_pinmux_gpio_set_direction(struct pinctrl_dev *pctldev, | 344 | static int samsung_pinmux_gpio_set_direction(struct pinctrl_dev *pctldev, |
335 | struct pinctrl_gpio_range *range, unsigned offset, bool input) | 345 | struct pinctrl_gpio_range *range, unsigned offset, bool input) |
336 | { | 346 | { |
347 | struct samsung_pin_bank_type *type; | ||
337 | struct samsung_pin_bank *bank; | 348 | struct samsung_pin_bank *bank; |
338 | struct samsung_pinctrl_drv_data *drvdata; | 349 | struct samsung_pinctrl_drv_data *drvdata; |
339 | void __iomem *reg; | 350 | void __iomem *reg; |
340 | u32 data, pin_offset, mask, shift; | 351 | u32 data, pin_offset, mask, shift; |
352 | unsigned long flags; | ||
341 | 353 | ||
342 | bank = gc_to_pin_bank(range->gc); | 354 | bank = gc_to_pin_bank(range->gc); |
355 | type = bank->type; | ||
343 | drvdata = pinctrl_dev_get_drvdata(pctldev); | 356 | drvdata = pinctrl_dev_get_drvdata(pctldev); |
344 | 357 | ||
345 | pin_offset = offset - bank->pin_base; | 358 | pin_offset = offset - bank->pin_base; |
346 | reg = drvdata->virt_base + bank->pctl_offset; | 359 | reg = drvdata->virt_base + bank->pctl_offset + |
360 | type->reg_offset[PINCFG_TYPE_FUNC]; | ||
361 | |||
362 | mask = (1 << type->fld_width[PINCFG_TYPE_FUNC]) - 1; | ||
363 | shift = pin_offset * type->fld_width[PINCFG_TYPE_FUNC]; | ||
364 | if (shift >= 32) { | ||
365 | /* Some banks have two config registers */ | ||
366 | shift -= 32; | ||
367 | reg += 4; | ||
368 | } | ||
347 | 369 | ||
348 | mask = (1 << bank->func_width) - 1; | 370 | spin_lock_irqsave(&bank->slock, flags); |
349 | shift = pin_offset * bank->func_width; | ||
350 | 371 | ||
351 | data = readl(reg); | 372 | data = readl(reg); |
352 | data &= ~(mask << shift); | 373 | data &= ~(mask << shift); |
353 | if (!input) | 374 | if (!input) |
354 | data |= FUNC_OUTPUT << shift; | 375 | data |= FUNC_OUTPUT << shift; |
355 | writel(data, reg); | 376 | writel(data, reg); |
377 | |||
378 | spin_unlock_irqrestore(&bank->slock, flags); | ||
379 | |||
356 | return 0; | 380 | return 0; |
357 | } | 381 | } |
358 | 382 | ||
359 | /* list of pinmux callbacks for the pinmux vertical in pinctrl core */ | 383 | /* list of pinmux callbacks for the pinmux vertical in pinctrl core */ |
360 | static struct pinmux_ops samsung_pinmux_ops = { | 384 | static const struct pinmux_ops samsung_pinmux_ops = { |
361 | .get_functions_count = samsung_get_functions_count, | 385 | .get_functions_count = samsung_get_functions_count, |
362 | .get_function_name = samsung_pinmux_get_fname, | 386 | .get_function_name = samsung_pinmux_get_fname, |
363 | .get_function_groups = samsung_pinmux_get_groups, | 387 | .get_function_groups = samsung_pinmux_get_groups, |
@@ -371,40 +395,26 @@ static int samsung_pinconf_rw(struct pinctrl_dev *pctldev, unsigned int pin, | |||
371 | unsigned long *config, bool set) | 395 | unsigned long *config, bool set) |
372 | { | 396 | { |
373 | struct samsung_pinctrl_drv_data *drvdata; | 397 | struct samsung_pinctrl_drv_data *drvdata; |
398 | struct samsung_pin_bank_type *type; | ||
374 | struct samsung_pin_bank *bank; | 399 | struct samsung_pin_bank *bank; |
375 | void __iomem *reg_base; | 400 | void __iomem *reg_base; |
376 | enum pincfg_type cfg_type = PINCFG_UNPACK_TYPE(*config); | 401 | enum pincfg_type cfg_type = PINCFG_UNPACK_TYPE(*config); |
377 | u32 data, width, pin_offset, mask, shift; | 402 | u32 data, width, pin_offset, mask, shift; |
378 | u32 cfg_value, cfg_reg; | 403 | u32 cfg_value, cfg_reg; |
404 | unsigned long flags; | ||
379 | 405 | ||
380 | drvdata = pinctrl_dev_get_drvdata(pctldev); | 406 | drvdata = pinctrl_dev_get_drvdata(pctldev); |
381 | pin_to_reg_bank(drvdata, pin - drvdata->ctrl->base, ®_base, | 407 | pin_to_reg_bank(drvdata, pin - drvdata->ctrl->base, ®_base, |
382 | &pin_offset, &bank); | 408 | &pin_offset, &bank); |
409 | type = bank->type; | ||
383 | 410 | ||
384 | switch (cfg_type) { | 411 | if (cfg_type >= PINCFG_TYPE_NUM || !type->fld_width[cfg_type]) |
385 | case PINCFG_TYPE_PUD: | ||
386 | width = bank->pud_width; | ||
387 | cfg_reg = PUD_REG; | ||
388 | break; | ||
389 | case PINCFG_TYPE_DRV: | ||
390 | width = bank->drv_width; | ||
391 | cfg_reg = DRV_REG; | ||
392 | break; | ||
393 | case PINCFG_TYPE_CON_PDN: | ||
394 | width = bank->conpdn_width; | ||
395 | cfg_reg = CONPDN_REG; | ||
396 | break; | ||
397 | case PINCFG_TYPE_PUD_PDN: | ||
398 | width = bank->pudpdn_width; | ||
399 | cfg_reg = PUDPDN_REG; | ||
400 | break; | ||
401 | default: | ||
402 | WARN_ON(1); | ||
403 | return -EINVAL; | 412 | return -EINVAL; |
404 | } | ||
405 | 413 | ||
406 | if (!width) | 414 | width = type->fld_width[cfg_type]; |
407 | return -EINVAL; | 415 | cfg_reg = type->reg_offset[cfg_type]; |
416 | |||
417 | spin_lock_irqsave(&bank->slock, flags); | ||
408 | 418 | ||
409 | mask = (1 << width) - 1; | 419 | mask = (1 << width) - 1; |
410 | shift = pin_offset * width; | 420 | shift = pin_offset * width; |
@@ -420,6 +430,9 @@ static int samsung_pinconf_rw(struct pinctrl_dev *pctldev, unsigned int pin, | |||
420 | data &= mask; | 430 | data &= mask; |
421 | *config = PINCFG_PACK(cfg_type, data); | 431 | *config = PINCFG_PACK(cfg_type, data); |
422 | } | 432 | } |
433 | |||
434 | spin_unlock_irqrestore(&bank->slock, flags); | ||
435 | |||
423 | return 0; | 436 | return 0; |
424 | } | 437 | } |
425 | 438 | ||
@@ -468,7 +481,7 @@ static int samsung_pinconf_group_get(struct pinctrl_dev *pctldev, | |||
468 | } | 481 | } |
469 | 482 | ||
470 | /* list of pinconfig callbacks for pinconfig vertical in the pinctrl code */ | 483 | /* list of pinconfig callbacks for pinconfig vertical in the pinctrl code */ |
471 | static struct pinconf_ops samsung_pinconf_ops = { | 484 | static const struct pinconf_ops samsung_pinconf_ops = { |
472 | .pin_config_get = samsung_pinconf_get, | 485 | .pin_config_get = samsung_pinconf_get, |
473 | .pin_config_set = samsung_pinconf_set, | 486 | .pin_config_set = samsung_pinconf_set, |
474 | .pin_config_group_get = samsung_pinconf_group_get, | 487 | .pin_config_group_get = samsung_pinconf_group_get, |
@@ -479,16 +492,22 @@ static struct pinconf_ops samsung_pinconf_ops = { | |||
479 | static void samsung_gpio_set(struct gpio_chip *gc, unsigned offset, int value) | 492 | static void samsung_gpio_set(struct gpio_chip *gc, unsigned offset, int value) |
480 | { | 493 | { |
481 | struct samsung_pin_bank *bank = gc_to_pin_bank(gc); | 494 | struct samsung_pin_bank *bank = gc_to_pin_bank(gc); |
495 | struct samsung_pin_bank_type *type = bank->type; | ||
496 | unsigned long flags; | ||
482 | void __iomem *reg; | 497 | void __iomem *reg; |
483 | u32 data; | 498 | u32 data; |
484 | 499 | ||
485 | reg = bank->drvdata->virt_base + bank->pctl_offset; | 500 | reg = bank->drvdata->virt_base + bank->pctl_offset; |
486 | 501 | ||
487 | data = readl(reg + DAT_REG); | 502 | spin_lock_irqsave(&bank->slock, flags); |
503 | |||
504 | data = readl(reg + type->reg_offset[PINCFG_TYPE_DAT]); | ||
488 | data &= ~(1 << offset); | 505 | data &= ~(1 << offset); |
489 | if (value) | 506 | if (value) |
490 | data |= 1 << offset; | 507 | data |= 1 << offset; |
491 | writel(data, reg + DAT_REG); | 508 | writel(data, reg + type->reg_offset[PINCFG_TYPE_DAT]); |
509 | |||
510 | spin_unlock_irqrestore(&bank->slock, flags); | ||
492 | } | 511 | } |
493 | 512 | ||
494 | /* gpiolib gpio_get callback function */ | 513 | /* gpiolib gpio_get callback function */ |
@@ -497,10 +516,11 @@ static int samsung_gpio_get(struct gpio_chip *gc, unsigned offset) | |||
497 | void __iomem *reg; | 516 | void __iomem *reg; |
498 | u32 data; | 517 | u32 data; |
499 | struct samsung_pin_bank *bank = gc_to_pin_bank(gc); | 518 | struct samsung_pin_bank *bank = gc_to_pin_bank(gc); |
519 | struct samsung_pin_bank_type *type = bank->type; | ||
500 | 520 | ||
501 | reg = bank->drvdata->virt_base + bank->pctl_offset; | 521 | reg = bank->drvdata->virt_base + bank->pctl_offset; |
502 | 522 | ||
503 | data = readl(reg + DAT_REG); | 523 | data = readl(reg + type->reg_offset[PINCFG_TYPE_DAT]); |
504 | data >>= offset; | 524 | data >>= offset; |
505 | data &= 1; | 525 | data &= 1; |
506 | return data; | 526 | return data; |
@@ -859,6 +879,7 @@ static struct samsung_pin_ctrl *samsung_pinctrl_get_soc_data( | |||
859 | 879 | ||
860 | bank = ctrl->pin_banks; | 880 | bank = ctrl->pin_banks; |
861 | for (i = 0; i < ctrl->nr_banks; ++i, ++bank) { | 881 | for (i = 0; i < ctrl->nr_banks; ++i, ++bank) { |
882 | spin_lock_init(&bank->slock); | ||
862 | bank->drvdata = d; | 883 | bank->drvdata = d; |
863 | bank->pin_base = ctrl->nr_pins; | 884 | bank->pin_base = ctrl->nr_pins; |
864 | ctrl->nr_pins += bank->nr_pins; | 885 | ctrl->nr_pins += bank->nr_pins; |
@@ -944,12 +965,18 @@ static int samsung_pinctrl_probe(struct platform_device *pdev) | |||
944 | } | 965 | } |
945 | 966 | ||
946 | static const struct of_device_id samsung_pinctrl_dt_match[] = { | 967 | static const struct of_device_id samsung_pinctrl_dt_match[] = { |
968 | #ifdef CONFIG_PINCTRL_EXYNOS | ||
947 | { .compatible = "samsung,exynos4210-pinctrl", | 969 | { .compatible = "samsung,exynos4210-pinctrl", |
948 | .data = (void *)exynos4210_pin_ctrl }, | 970 | .data = (void *)exynos4210_pin_ctrl }, |
949 | { .compatible = "samsung,exynos4x12-pinctrl", | 971 | { .compatible = "samsung,exynos4x12-pinctrl", |
950 | .data = (void *)exynos4x12_pin_ctrl }, | 972 | .data = (void *)exynos4x12_pin_ctrl }, |
951 | { .compatible = "samsung,exynos5250-pinctrl", | 973 | { .compatible = "samsung,exynos5250-pinctrl", |
952 | .data = (void *)exynos5250_pin_ctrl }, | 974 | .data = (void *)exynos5250_pin_ctrl }, |
975 | #endif | ||
976 | #ifdef CONFIG_PINCTRL_S3C64XX | ||
977 | { .compatible = "samsung,s3c64xx-pinctrl", | ||
978 | .data = s3c64xx_pin_ctrl }, | ||
979 | #endif | ||
953 | {}, | 980 | {}, |
954 | }; | 981 | }; |
955 | MODULE_DEVICE_TABLE(of, samsung_pinctrl_dt_match); | 982 | MODULE_DEVICE_TABLE(of, samsung_pinctrl_dt_match); |