18e8e69d6SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21ff91f0aSDavid Lechner /*
31ff91f0aSDavid Lechner * Pinconf driver for TI DA850/OMAP-L138/AM18XX pullup/pulldown groups
41ff91f0aSDavid Lechner *
51ff91f0aSDavid Lechner * Copyright (C) 2016 David Lechner
61ff91f0aSDavid Lechner */
71ff91f0aSDavid Lechner
81ff91f0aSDavid Lechner #include <linux/bitops.h>
91ff91f0aSDavid Lechner #include <linux/device.h>
101ff91f0aSDavid Lechner #include <linux/io.h>
111ff91f0aSDavid Lechner #include <linux/ioport.h>
121ff91f0aSDavid Lechner #include <linux/mod_devicetable.h>
131ff91f0aSDavid Lechner #include <linux/module.h>
141ff91f0aSDavid Lechner #include <linux/pinctrl/pinconf.h>
151ff91f0aSDavid Lechner #include <linux/pinctrl/pinconf-generic.h>
161ff91f0aSDavid Lechner #include <linux/pinctrl/pinctrl.h>
171ff91f0aSDavid Lechner #include <linux/platform_device.h>
181ff91f0aSDavid Lechner
191ff91f0aSDavid Lechner #define DA850_PUPD_ENA 0x00
201ff91f0aSDavid Lechner #define DA850_PUPD_SEL 0x04
211ff91f0aSDavid Lechner
221ff91f0aSDavid Lechner struct da850_pupd_data {
231ff91f0aSDavid Lechner void __iomem *base;
241ff91f0aSDavid Lechner struct pinctrl_desc desc;
251ff91f0aSDavid Lechner struct pinctrl_dev *pinctrl;
261ff91f0aSDavid Lechner };
271ff91f0aSDavid Lechner
281ff91f0aSDavid Lechner static const char * const da850_pupd_group_names[] = {
291ff91f0aSDavid Lechner "cp0", "cp1", "cp2", "cp3", "cp4", "cp5", "cp6", "cp7",
301ff91f0aSDavid Lechner "cp8", "cp9", "cp10", "cp11", "cp12", "cp13", "cp14", "cp15",
311ff91f0aSDavid Lechner "cp16", "cp17", "cp18", "cp19", "cp20", "cp21", "cp22", "cp23",
321ff91f0aSDavid Lechner "cp24", "cp25", "cp26", "cp27", "cp28", "cp29", "cp30", "cp31",
331ff91f0aSDavid Lechner };
341ff91f0aSDavid Lechner
da850_pupd_get_groups_count(struct pinctrl_dev * pctldev)351ff91f0aSDavid Lechner static int da850_pupd_get_groups_count(struct pinctrl_dev *pctldev)
361ff91f0aSDavid Lechner {
371ff91f0aSDavid Lechner return ARRAY_SIZE(da850_pupd_group_names);
381ff91f0aSDavid Lechner }
391ff91f0aSDavid Lechner
da850_pupd_get_group_name(struct pinctrl_dev * pctldev,unsigned int selector)401ff91f0aSDavid Lechner static const char *da850_pupd_get_group_name(struct pinctrl_dev *pctldev,
411ff91f0aSDavid Lechner unsigned int selector)
421ff91f0aSDavid Lechner {
431ff91f0aSDavid Lechner return da850_pupd_group_names[selector];
441ff91f0aSDavid Lechner }
451ff91f0aSDavid Lechner
da850_pupd_get_group_pins(struct pinctrl_dev * pctldev,unsigned int selector,const unsigned int ** pins,unsigned int * num_pins)461ff91f0aSDavid Lechner static int da850_pupd_get_group_pins(struct pinctrl_dev *pctldev,
471ff91f0aSDavid Lechner unsigned int selector,
481ff91f0aSDavid Lechner const unsigned int **pins,
491ff91f0aSDavid Lechner unsigned int *num_pins)
501ff91f0aSDavid Lechner {
511ff91f0aSDavid Lechner *num_pins = 0;
521ff91f0aSDavid Lechner
531ff91f0aSDavid Lechner return 0;
541ff91f0aSDavid Lechner }
551ff91f0aSDavid Lechner
561ff91f0aSDavid Lechner static const struct pinctrl_ops da850_pupd_pctlops = {
571ff91f0aSDavid Lechner .get_groups_count = da850_pupd_get_groups_count,
581ff91f0aSDavid Lechner .get_group_name = da850_pupd_get_group_name,
591ff91f0aSDavid Lechner .get_group_pins = da850_pupd_get_group_pins,
601ff91f0aSDavid Lechner .dt_node_to_map = pinconf_generic_dt_node_to_map_group,
611ff91f0aSDavid Lechner .dt_free_map = pinconf_generic_dt_free_map,
621ff91f0aSDavid Lechner };
631ff91f0aSDavid Lechner
da850_pupd_pin_config_group_get(struct pinctrl_dev * pctldev,unsigned int selector,unsigned long * config)641ff91f0aSDavid Lechner static int da850_pupd_pin_config_group_get(struct pinctrl_dev *pctldev,
651ff91f0aSDavid Lechner unsigned int selector,
661ff91f0aSDavid Lechner unsigned long *config)
671ff91f0aSDavid Lechner {
681ff91f0aSDavid Lechner struct da850_pupd_data *data = pinctrl_dev_get_drvdata(pctldev);
691ff91f0aSDavid Lechner enum pin_config_param param = pinconf_to_config_param(*config);
701ff91f0aSDavid Lechner u32 val;
711ff91f0aSDavid Lechner u16 arg;
721ff91f0aSDavid Lechner
731ff91f0aSDavid Lechner val = readl(data->base + DA850_PUPD_ENA);
741ff91f0aSDavid Lechner arg = !!(~val & BIT(selector));
751ff91f0aSDavid Lechner
761ff91f0aSDavid Lechner switch (param) {
771ff91f0aSDavid Lechner case PIN_CONFIG_BIAS_DISABLE:
781ff91f0aSDavid Lechner break;
791ff91f0aSDavid Lechner case PIN_CONFIG_BIAS_PULL_UP:
801ff91f0aSDavid Lechner case PIN_CONFIG_BIAS_PULL_DOWN:
811ff91f0aSDavid Lechner if (arg) {
821ff91f0aSDavid Lechner /* bias is disabled */
831ff91f0aSDavid Lechner arg = 0;
841ff91f0aSDavid Lechner break;
851ff91f0aSDavid Lechner }
861ff91f0aSDavid Lechner val = readl(data->base + DA850_PUPD_SEL);
871ff91f0aSDavid Lechner if (param == PIN_CONFIG_BIAS_PULL_DOWN)
881ff91f0aSDavid Lechner val = ~val;
891ff91f0aSDavid Lechner arg = !!(val & BIT(selector));
901ff91f0aSDavid Lechner break;
911ff91f0aSDavid Lechner default:
921ff91f0aSDavid Lechner return -EINVAL;
931ff91f0aSDavid Lechner }
941ff91f0aSDavid Lechner
951ff91f0aSDavid Lechner *config = pinconf_to_config_packed(param, arg);
961ff91f0aSDavid Lechner
971ff91f0aSDavid Lechner return 0;
981ff91f0aSDavid Lechner }
991ff91f0aSDavid Lechner
da850_pupd_pin_config_group_set(struct pinctrl_dev * pctldev,unsigned int selector,unsigned long * configs,unsigned int num_configs)1001ff91f0aSDavid Lechner static int da850_pupd_pin_config_group_set(struct pinctrl_dev *pctldev,
1011ff91f0aSDavid Lechner unsigned int selector,
1021ff91f0aSDavid Lechner unsigned long *configs,
1031ff91f0aSDavid Lechner unsigned int num_configs)
1041ff91f0aSDavid Lechner {
1051ff91f0aSDavid Lechner struct da850_pupd_data *data = pinctrl_dev_get_drvdata(pctldev);
1061ff91f0aSDavid Lechner u32 ena, sel;
1071ff91f0aSDavid Lechner enum pin_config_param param;
1081ff91f0aSDavid Lechner int i;
1091ff91f0aSDavid Lechner
1101ff91f0aSDavid Lechner ena = readl(data->base + DA850_PUPD_ENA);
1111ff91f0aSDavid Lechner sel = readl(data->base + DA850_PUPD_SEL);
1121ff91f0aSDavid Lechner
1131ff91f0aSDavid Lechner for (i = 0; i < num_configs; i++) {
1141ff91f0aSDavid Lechner param = pinconf_to_config_param(configs[i]);
1151ff91f0aSDavid Lechner
1161ff91f0aSDavid Lechner switch (param) {
1171ff91f0aSDavid Lechner case PIN_CONFIG_BIAS_DISABLE:
1181ff91f0aSDavid Lechner ena &= ~BIT(selector);
1191ff91f0aSDavid Lechner break;
1201ff91f0aSDavid Lechner case PIN_CONFIG_BIAS_PULL_UP:
1211ff91f0aSDavid Lechner ena |= BIT(selector);
1221ff91f0aSDavid Lechner sel |= BIT(selector);
1231ff91f0aSDavid Lechner break;
1241ff91f0aSDavid Lechner case PIN_CONFIG_BIAS_PULL_DOWN:
1251ff91f0aSDavid Lechner ena |= BIT(selector);
1261ff91f0aSDavid Lechner sel &= ~BIT(selector);
1271ff91f0aSDavid Lechner break;
1281ff91f0aSDavid Lechner default:
1291ff91f0aSDavid Lechner return -EINVAL;
1301ff91f0aSDavid Lechner }
1311ff91f0aSDavid Lechner }
1321ff91f0aSDavid Lechner
1331ff91f0aSDavid Lechner writel(sel, data->base + DA850_PUPD_SEL);
1341ff91f0aSDavid Lechner writel(ena, data->base + DA850_PUPD_ENA);
1351ff91f0aSDavid Lechner
1361ff91f0aSDavid Lechner return 0;
1371ff91f0aSDavid Lechner }
1381ff91f0aSDavid Lechner
1391ff91f0aSDavid Lechner static const struct pinconf_ops da850_pupd_confops = {
1401ff91f0aSDavid Lechner .is_generic = true,
1411ff91f0aSDavid Lechner .pin_config_group_get = da850_pupd_pin_config_group_get,
1421ff91f0aSDavid Lechner .pin_config_group_set = da850_pupd_pin_config_group_set,
1431ff91f0aSDavid Lechner };
1441ff91f0aSDavid Lechner
da850_pupd_probe(struct platform_device * pdev)1451ff91f0aSDavid Lechner static int da850_pupd_probe(struct platform_device *pdev)
1461ff91f0aSDavid Lechner {
1471ff91f0aSDavid Lechner struct device *dev = &pdev->dev;
1481ff91f0aSDavid Lechner struct da850_pupd_data *data;
1491ff91f0aSDavid Lechner
1501ff91f0aSDavid Lechner data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
1511ff91f0aSDavid Lechner if (!data)
1521ff91f0aSDavid Lechner return -ENOMEM;
1531ff91f0aSDavid Lechner
154*4b024225SYueHaibing data->base = devm_platform_ioremap_resource(pdev, 0);
1551ff91f0aSDavid Lechner if (IS_ERR(data->base)) {
1561ff91f0aSDavid Lechner dev_err(dev, "Could not map resource\n");
1571ff91f0aSDavid Lechner return PTR_ERR(data->base);
1581ff91f0aSDavid Lechner }
1591ff91f0aSDavid Lechner
1601ff91f0aSDavid Lechner data->desc.name = dev_name(dev);
1611ff91f0aSDavid Lechner data->desc.pctlops = &da850_pupd_pctlops;
1621ff91f0aSDavid Lechner data->desc.confops = &da850_pupd_confops;
1631ff91f0aSDavid Lechner data->desc.owner = THIS_MODULE;
1641ff91f0aSDavid Lechner
1651ff91f0aSDavid Lechner data->pinctrl = devm_pinctrl_register(dev, &data->desc, data);
1661ff91f0aSDavid Lechner if (IS_ERR(data->pinctrl)) {
1671ff91f0aSDavid Lechner dev_err(dev, "Failed to register pinctrl\n");
1681ff91f0aSDavid Lechner return PTR_ERR(data->pinctrl);
1691ff91f0aSDavid Lechner }
1701ff91f0aSDavid Lechner
1711ff91f0aSDavid Lechner platform_set_drvdata(pdev, data);
1721ff91f0aSDavid Lechner
1731ff91f0aSDavid Lechner return 0;
1741ff91f0aSDavid Lechner }
1751ff91f0aSDavid Lechner
1761ff91f0aSDavid Lechner static const struct of_device_id da850_pupd_of_match[] = {
1771ff91f0aSDavid Lechner { .compatible = "ti,da850-pupd" },
1781ff91f0aSDavid Lechner { }
1791ff91f0aSDavid Lechner };
180476e3e1dSDavid Lechner MODULE_DEVICE_TABLE(of, da850_pupd_of_match);
1811ff91f0aSDavid Lechner
1821ff91f0aSDavid Lechner static struct platform_driver da850_pupd_driver = {
1831ff91f0aSDavid Lechner .driver = {
1841ff91f0aSDavid Lechner .name = "ti-da850-pupd",
1851ff91f0aSDavid Lechner .of_match_table = da850_pupd_of_match,
1861ff91f0aSDavid Lechner },
1871ff91f0aSDavid Lechner .probe = da850_pupd_probe,
1881ff91f0aSDavid Lechner };
1891ff91f0aSDavid Lechner module_platform_driver(da850_pupd_driver);
1901ff91f0aSDavid Lechner
1911ff91f0aSDavid Lechner MODULE_AUTHOR("David Lechner <david@lechnology.com>");
1921ff91f0aSDavid Lechner MODULE_DESCRIPTION("TI DA850/OMAP-L138/AM18XX pullup/pulldown configuration");
1931ff91f0aSDavid Lechner MODULE_LICENSE("GPL");
194