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 
1544b024225SYueHaibing 	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