diff options
author | Gabriel Fernandez <gabriel.fernandez@stericsson.com> | 2012-12-17 09:53:24 -0500 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2013-01-11 15:49:21 -0500 |
commit | e32af889458837d11a1ec5ec98934a1e711b049d (patch) | |
tree | 764ca31b5ca3fc5ed6443f3077c4890d499e9f66 /drivers/pinctrl/pinctrl-nomadik.c | |
parent | 50f690d85e4e26ca31082e9943d36b618e7566fe (diff) |
pinctrl/nomadik: add device tree support
This implements pin multiplexing and pin configuration for
the Nomadik pin controller using the device tree.
Signed-off-by: Gabriel Fernandez <gabriel.fernandez@stericsson.com>
Reviewed-by: Stephen Warren <swarren@nvidia.com>
Reviewed-by: Philippe Langlais <philippe.langlais@stericsson.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/pinctrl/pinctrl-nomadik.c')
-rw-r--r-- | drivers/pinctrl/pinctrl-nomadik.c | 277 |
1 files changed, 276 insertions, 1 deletions
diff --git a/drivers/pinctrl/pinctrl-nomadik.c b/drivers/pinctrl/pinctrl-nomadik.c index f404e644a16e..ec3a5742e413 100644 --- a/drivers/pinctrl/pinctrl-nomadik.c +++ b/drivers/pinctrl/pinctrl-nomadik.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/irqdomain.h> | 25 | #include <linux/irqdomain.h> |
26 | #include <linux/slab.h> | 26 | #include <linux/slab.h> |
27 | #include <linux/of_device.h> | 27 | #include <linux/of_device.h> |
28 | #include <linux/pinctrl/machine.h> | ||
28 | #include <linux/pinctrl/pinctrl.h> | 29 | #include <linux/pinctrl/pinctrl.h> |
29 | #include <linux/pinctrl/pinmux.h> | 30 | #include <linux/pinctrl/pinmux.h> |
30 | #include <linux/pinctrl/pinconf.h> | 31 | #include <linux/pinctrl/pinconf.h> |
@@ -1490,11 +1491,285 @@ static void nmk_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, | |||
1490 | nmk_gpio_dbg_show_one(s, pctldev, chip, offset - chip->base, offset); | 1491 | nmk_gpio_dbg_show_one(s, pctldev, chip, offset - chip->base, offset); |
1491 | } | 1492 | } |
1492 | 1493 | ||
1494 | static void nmk_pinctrl_dt_free_map(struct pinctrl_dev *pctldev, | ||
1495 | struct pinctrl_map *map, unsigned num_maps) | ||
1496 | { | ||
1497 | int i; | ||
1498 | |||
1499 | for (i = 0; i < num_maps; i++) | ||
1500 | if (map[i].type == PIN_MAP_TYPE_CONFIGS_PIN) | ||
1501 | kfree(map[i].data.configs.configs); | ||
1502 | kfree(map); | ||
1503 | } | ||
1504 | |||
1505 | static int nmk_dt_reserve_map(struct pinctrl_map **map, unsigned *reserved_maps, | ||
1506 | unsigned *num_maps, unsigned reserve) | ||
1507 | { | ||
1508 | unsigned old_num = *reserved_maps; | ||
1509 | unsigned new_num = *num_maps + reserve; | ||
1510 | struct pinctrl_map *new_map; | ||
1511 | |||
1512 | if (old_num >= new_num) | ||
1513 | return 0; | ||
1514 | |||
1515 | new_map = krealloc(*map, sizeof(*new_map) * new_num, GFP_KERNEL); | ||
1516 | if (!new_map) | ||
1517 | return -ENOMEM; | ||
1518 | |||
1519 | memset(new_map + old_num, 0, (new_num - old_num) * sizeof(*new_map)); | ||
1520 | |||
1521 | *map = new_map; | ||
1522 | *reserved_maps = new_num; | ||
1523 | |||
1524 | return 0; | ||
1525 | } | ||
1526 | |||
1527 | static int nmk_dt_add_map_mux(struct pinctrl_map **map, unsigned *reserved_maps, | ||
1528 | unsigned *num_maps, const char *group, | ||
1529 | const char *function) | ||
1530 | { | ||
1531 | if (*num_maps == *reserved_maps) | ||
1532 | return -ENOSPC; | ||
1533 | |||
1534 | (*map)[*num_maps].type = PIN_MAP_TYPE_MUX_GROUP; | ||
1535 | (*map)[*num_maps].data.mux.group = group; | ||
1536 | (*map)[*num_maps].data.mux.function = function; | ||
1537 | (*num_maps)++; | ||
1538 | |||
1539 | return 0; | ||
1540 | } | ||
1541 | |||
1542 | static int nmk_dt_add_map_configs(struct pinctrl_map **map, | ||
1543 | unsigned *reserved_maps, | ||
1544 | unsigned *num_maps, const char *group, | ||
1545 | unsigned long *configs, unsigned num_configs) | ||
1546 | { | ||
1547 | unsigned long *dup_configs; | ||
1548 | |||
1549 | if (*num_maps == *reserved_maps) | ||
1550 | return -ENOSPC; | ||
1551 | |||
1552 | dup_configs = kmemdup(configs, num_configs * sizeof(*dup_configs), | ||
1553 | GFP_KERNEL); | ||
1554 | if (!dup_configs) | ||
1555 | return -ENOMEM; | ||
1556 | |||
1557 | (*map)[*num_maps].type = PIN_MAP_TYPE_CONFIGS_PIN; | ||
1558 | |||
1559 | (*map)[*num_maps].data.configs.group_or_pin = group; | ||
1560 | (*map)[*num_maps].data.configs.configs = dup_configs; | ||
1561 | (*map)[*num_maps].data.configs.num_configs = num_configs; | ||
1562 | (*num_maps)++; | ||
1563 | |||
1564 | return 0; | ||
1565 | } | ||
1566 | |||
1567 | #define NMK_CONFIG_PIN(x,y) { .property = x, .config = y, } | ||
1568 | #define NMK_CONFIG_PIN_ARRAY(x,y) { .property = x, .choice = y, \ | ||
1569 | .size = ARRAY_SIZE(y), } | ||
1570 | |||
1571 | static const unsigned long nmk_pin_input_modes[] = { | ||
1572 | PIN_INPUT_NOPULL, | ||
1573 | PIN_INPUT_PULLUP, | ||
1574 | PIN_INPUT_PULLDOWN, | ||
1575 | }; | ||
1576 | |||
1577 | static const unsigned long nmk_pin_output_modes[] = { | ||
1578 | PIN_OUTPUT_LOW, | ||
1579 | PIN_OUTPUT_HIGH, | ||
1580 | PIN_DIR_OUTPUT, | ||
1581 | }; | ||
1582 | |||
1583 | static const unsigned long nmk_pin_sleep_modes[] = { | ||
1584 | PIN_SLEEPMODE_DISABLED, | ||
1585 | PIN_SLEEPMODE_ENABLED, | ||
1586 | }; | ||
1587 | |||
1588 | static const unsigned long nmk_pin_sleep_input_modes[] = { | ||
1589 | PIN_SLPM_INPUT_NOPULL, | ||
1590 | PIN_SLPM_INPUT_PULLUP, | ||
1591 | PIN_SLPM_INPUT_PULLDOWN, | ||
1592 | PIN_SLPM_DIR_INPUT, | ||
1593 | }; | ||
1594 | |||
1595 | static const unsigned long nmk_pin_sleep_output_modes[] = { | ||
1596 | PIN_SLPM_OUTPUT_LOW, | ||
1597 | PIN_SLPM_OUTPUT_HIGH, | ||
1598 | PIN_SLPM_DIR_OUTPUT, | ||
1599 | }; | ||
1600 | |||
1601 | static const unsigned long nmk_pin_sleep_wakeup_modes[] = { | ||
1602 | PIN_SLPM_WAKEUP_DISABLE, | ||
1603 | PIN_SLPM_WAKEUP_ENABLE, | ||
1604 | }; | ||
1605 | |||
1606 | static const unsigned long nmk_pin_gpio_modes[] = { | ||
1607 | PIN_GPIOMODE_DISABLED, | ||
1608 | PIN_GPIOMODE_ENABLED, | ||
1609 | }; | ||
1610 | |||
1611 | static const unsigned long nmk_pin_sleep_pdis_modes[] = { | ||
1612 | PIN_SLPM_PDIS_DISABLED, | ||
1613 | PIN_SLPM_PDIS_ENABLED, | ||
1614 | }; | ||
1615 | |||
1616 | struct nmk_cfg_param { | ||
1617 | const char *property; | ||
1618 | unsigned long config; | ||
1619 | const unsigned long *choice; | ||
1620 | int size; | ||
1621 | }; | ||
1622 | |||
1623 | static const struct nmk_cfg_param nmk_cfg_params[] = { | ||
1624 | NMK_CONFIG_PIN_ARRAY("ste,input", nmk_pin_input_modes), | ||
1625 | NMK_CONFIG_PIN_ARRAY("ste,output", nmk_pin_output_modes), | ||
1626 | NMK_CONFIG_PIN_ARRAY("ste,sleep", nmk_pin_sleep_modes), | ||
1627 | NMK_CONFIG_PIN_ARRAY("ste,sleep-input", nmk_pin_sleep_input_modes), | ||
1628 | NMK_CONFIG_PIN_ARRAY("ste,sleep-output", nmk_pin_sleep_output_modes), | ||
1629 | NMK_CONFIG_PIN_ARRAY("ste,sleep-wakeup", nmk_pin_sleep_wakeup_modes), | ||
1630 | NMK_CONFIG_PIN_ARRAY("ste,gpio", nmk_pin_gpio_modes), | ||
1631 | NMK_CONFIG_PIN_ARRAY("ste,sleep-pull-disable", nmk_pin_sleep_pdis_modes), | ||
1632 | }; | ||
1633 | |||
1634 | static int nmk_dt_pin_config(int index, int val, unsigned long *config) | ||
1635 | { | ||
1636 | int ret = 0; | ||
1637 | |||
1638 | if (nmk_cfg_params[index].choice == NULL) | ||
1639 | *config = nmk_cfg_params[index].config; | ||
1640 | else { | ||
1641 | /* test if out of range */ | ||
1642 | if (val < nmk_cfg_params[index].size) { | ||
1643 | *config = nmk_cfg_params[index].config | | ||
1644 | nmk_cfg_params[index].choice[val]; | ||
1645 | } | ||
1646 | } | ||
1647 | return ret; | ||
1648 | } | ||
1649 | |||
1650 | static const char *nmk_find_pin_name(struct pinctrl_dev *pctldev, const char *pin_name) | ||
1651 | { | ||
1652 | int i, pin_number; | ||
1653 | struct nmk_pinctrl *npct = pinctrl_dev_get_drvdata(pctldev); | ||
1654 | |||
1655 | if (sscanf((char *)pin_name, "GPIO%d", &pin_number) == 1) | ||
1656 | for (i = 0; i < npct->soc->npins; i++) | ||
1657 | if (npct->soc->pins[i].number == pin_number) | ||
1658 | return npct->soc->pins[i].name; | ||
1659 | return NULL; | ||
1660 | } | ||
1661 | |||
1662 | static bool nmk_pinctrl_dt_get_config(struct device_node *np, | ||
1663 | unsigned long *configs) | ||
1664 | { | ||
1665 | bool has_config = 0; | ||
1666 | unsigned long cfg = 0; | ||
1667 | int i, val, ret; | ||
1668 | |||
1669 | for (i = 0; i < ARRAY_SIZE(nmk_cfg_params); i++) { | ||
1670 | ret = of_property_read_u32(np, | ||
1671 | nmk_cfg_params[i].property, &val); | ||
1672 | if (ret != -EINVAL) { | ||
1673 | if (nmk_dt_pin_config(i, val, &cfg) == 0) { | ||
1674 | *configs |= cfg; | ||
1675 | has_config = 1; | ||
1676 | } | ||
1677 | } | ||
1678 | } | ||
1679 | |||
1680 | return has_config; | ||
1681 | } | ||
1682 | |||
1683 | int nmk_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev, | ||
1684 | struct device_node *np, | ||
1685 | struct pinctrl_map **map, | ||
1686 | unsigned *reserved_maps, | ||
1687 | unsigned *num_maps) | ||
1688 | { | ||
1689 | int ret; | ||
1690 | const char *function = NULL; | ||
1691 | unsigned long configs = 0; | ||
1692 | bool has_config = 0; | ||
1693 | unsigned reserve = 0; | ||
1694 | struct property *prop; | ||
1695 | const char *group, *gpio_name; | ||
1696 | struct device_node *np_config; | ||
1697 | |||
1698 | ret = of_property_read_string(np, "ste,function", &function); | ||
1699 | if (ret >= 0) | ||
1700 | reserve = 1; | ||
1701 | |||
1702 | has_config = nmk_pinctrl_dt_get_config(np, &configs); | ||
1703 | |||
1704 | np_config = of_parse_phandle(np, "ste,config", 0); | ||
1705 | if (np_config) | ||
1706 | has_config |= nmk_pinctrl_dt_get_config(np_config, &configs); | ||
1707 | |||
1708 | ret = of_property_count_strings(np, "ste,pins"); | ||
1709 | if (ret < 0) | ||
1710 | goto exit; | ||
1711 | |||
1712 | if (has_config) | ||
1713 | reserve++; | ||
1714 | |||
1715 | reserve *= ret; | ||
1716 | |||
1717 | ret = nmk_dt_reserve_map(map, reserved_maps, num_maps, reserve); | ||
1718 | if (ret < 0) | ||
1719 | goto exit; | ||
1720 | |||
1721 | of_property_for_each_string(np, "ste,pins", prop, group) { | ||
1722 | if (function) { | ||
1723 | ret = nmk_dt_add_map_mux(map, reserved_maps, num_maps, | ||
1724 | group, function); | ||
1725 | if (ret < 0) | ||
1726 | goto exit; | ||
1727 | } | ||
1728 | if (has_config) { | ||
1729 | gpio_name = nmk_find_pin_name(pctldev, group); | ||
1730 | |||
1731 | ret = nmk_dt_add_map_configs(map, reserved_maps, num_maps, | ||
1732 | gpio_name, &configs, 1); | ||
1733 | if (ret < 0) | ||
1734 | goto exit; | ||
1735 | } | ||
1736 | |||
1737 | } | ||
1738 | exit: | ||
1739 | return ret; | ||
1740 | } | ||
1741 | |||
1742 | int nmk_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, | ||
1743 | struct device_node *np_config, | ||
1744 | struct pinctrl_map **map, unsigned *num_maps) | ||
1745 | { | ||
1746 | unsigned reserved_maps; | ||
1747 | struct device_node *np; | ||
1748 | int ret; | ||
1749 | |||
1750 | reserved_maps = 0; | ||
1751 | *map = NULL; | ||
1752 | *num_maps = 0; | ||
1753 | |||
1754 | for_each_child_of_node(np_config, np) { | ||
1755 | ret = nmk_pinctrl_dt_subnode_to_map(pctldev, np, map, | ||
1756 | &reserved_maps, num_maps); | ||
1757 | if (ret < 0) { | ||
1758 | nmk_pinctrl_dt_free_map(pctldev, *map, *num_maps); | ||
1759 | return ret; | ||
1760 | } | ||
1761 | } | ||
1762 | |||
1763 | return 0; | ||
1764 | } | ||
1765 | |||
1493 | static struct pinctrl_ops nmk_pinctrl_ops = { | 1766 | static struct pinctrl_ops nmk_pinctrl_ops = { |
1494 | .get_groups_count = nmk_get_groups_cnt, | 1767 | .get_groups_count = nmk_get_groups_cnt, |
1495 | .get_group_name = nmk_get_group_name, | 1768 | .get_group_name = nmk_get_group_name, |
1496 | .get_group_pins = nmk_get_group_pins, | 1769 | .get_group_pins = nmk_get_group_pins, |
1497 | .pin_dbg_show = nmk_pin_dbg_show, | 1770 | .pin_dbg_show = nmk_pin_dbg_show, |
1771 | .dt_node_to_map = nmk_pinctrl_dt_node_to_map, | ||
1772 | .dt_free_map = nmk_pinctrl_dt_free_map, | ||
1498 | }; | 1773 | }; |
1499 | 1774 | ||
1500 | static int nmk_pmx_get_funcs_cnt(struct pinctrl_dev *pctldev) | 1775 | static int nmk_pmx_get_funcs_cnt(struct pinctrl_dev *pctldev) |
@@ -1828,7 +2103,7 @@ static struct pinctrl_desc nmk_pinctrl_desc = { | |||
1828 | 2103 | ||
1829 | static const struct of_device_id nmk_pinctrl_match[] = { | 2104 | static const struct of_device_id nmk_pinctrl_match[] = { |
1830 | { | 2105 | { |
1831 | .compatible = "stericsson,nmk_pinctrl", | 2106 | .compatible = "stericsson,nmk-pinctrl", |
1832 | .data = (void *)PINCTRL_NMK_DB8500, | 2107 | .data = (void *)PINCTRL_NMK_DB8500, |
1833 | }, | 2108 | }, |
1834 | {}, | 2109 | {}, |