1 /* 2 * Pinconf driver for TI DA850/OMAP-L138/AM18XX pullup/pulldown groups 3 * 4 * Copyright (C) 2016 David Lechner 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License as published by the Free 8 * Software Foundation; version 2 of the License. 9 * 10 * This program is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 * more details. 14 */ 15 16 #include <linux/bitops.h> 17 #include <linux/device.h> 18 #include <linux/io.h> 19 #include <linux/ioport.h> 20 #include <linux/mod_devicetable.h> 21 #include <linux/module.h> 22 #include <linux/pinctrl/pinconf.h> 23 #include <linux/pinctrl/pinconf-generic.h> 24 #include <linux/pinctrl/pinctrl.h> 25 #include <linux/platform_device.h> 26 27 #define DA850_PUPD_ENA 0x00 28 #define DA850_PUPD_SEL 0x04 29 30 struct da850_pupd_data { 31 void __iomem *base; 32 struct pinctrl_desc desc; 33 struct pinctrl_dev *pinctrl; 34 }; 35 36 static const char * const da850_pupd_group_names[] = { 37 "cp0", "cp1", "cp2", "cp3", "cp4", "cp5", "cp6", "cp7", 38 "cp8", "cp9", "cp10", "cp11", "cp12", "cp13", "cp14", "cp15", 39 "cp16", "cp17", "cp18", "cp19", "cp20", "cp21", "cp22", "cp23", 40 "cp24", "cp25", "cp26", "cp27", "cp28", "cp29", "cp30", "cp31", 41 }; 42 43 static int da850_pupd_get_groups_count(struct pinctrl_dev *pctldev) 44 { 45 return ARRAY_SIZE(da850_pupd_group_names); 46 } 47 48 static const char *da850_pupd_get_group_name(struct pinctrl_dev *pctldev, 49 unsigned int selector) 50 { 51 return da850_pupd_group_names[selector]; 52 } 53 54 static int da850_pupd_get_group_pins(struct pinctrl_dev *pctldev, 55 unsigned int selector, 56 const unsigned int **pins, 57 unsigned int *num_pins) 58 { 59 *num_pins = 0; 60 61 return 0; 62 } 63 64 static const struct pinctrl_ops da850_pupd_pctlops = { 65 .get_groups_count = da850_pupd_get_groups_count, 66 .get_group_name = da850_pupd_get_group_name, 67 .get_group_pins = da850_pupd_get_group_pins, 68 .dt_node_to_map = pinconf_generic_dt_node_to_map_group, 69 .dt_free_map = pinconf_generic_dt_free_map, 70 }; 71 72 static int da850_pupd_pin_config_group_get(struct pinctrl_dev *pctldev, 73 unsigned int selector, 74 unsigned long *config) 75 { 76 struct da850_pupd_data *data = pinctrl_dev_get_drvdata(pctldev); 77 enum pin_config_param param = pinconf_to_config_param(*config); 78 u32 val; 79 u16 arg; 80 81 val = readl(data->base + DA850_PUPD_ENA); 82 arg = !!(~val & BIT(selector)); 83 84 switch (param) { 85 case PIN_CONFIG_BIAS_DISABLE: 86 break; 87 case PIN_CONFIG_BIAS_PULL_UP: 88 case PIN_CONFIG_BIAS_PULL_DOWN: 89 if (arg) { 90 /* bias is disabled */ 91 arg = 0; 92 break; 93 } 94 val = readl(data->base + DA850_PUPD_SEL); 95 if (param == PIN_CONFIG_BIAS_PULL_DOWN) 96 val = ~val; 97 arg = !!(val & BIT(selector)); 98 break; 99 default: 100 return -EINVAL; 101 } 102 103 *config = pinconf_to_config_packed(param, arg); 104 105 return 0; 106 } 107 108 static int da850_pupd_pin_config_group_set(struct pinctrl_dev *pctldev, 109 unsigned int selector, 110 unsigned long *configs, 111 unsigned int num_configs) 112 { 113 struct da850_pupd_data *data = pinctrl_dev_get_drvdata(pctldev); 114 u32 ena, sel; 115 enum pin_config_param param; 116 u16 arg; 117 int i; 118 119 ena = readl(data->base + DA850_PUPD_ENA); 120 sel = readl(data->base + DA850_PUPD_SEL); 121 122 for (i = 0; i < num_configs; i++) { 123 param = pinconf_to_config_param(configs[i]); 124 arg = pinconf_to_config_argument(configs[i]); 125 126 switch (param) { 127 case PIN_CONFIG_BIAS_DISABLE: 128 ena &= ~BIT(selector); 129 break; 130 case PIN_CONFIG_BIAS_PULL_UP: 131 ena |= BIT(selector); 132 sel |= BIT(selector); 133 break; 134 case PIN_CONFIG_BIAS_PULL_DOWN: 135 ena |= BIT(selector); 136 sel &= ~BIT(selector); 137 break; 138 default: 139 return -EINVAL; 140 } 141 } 142 143 writel(sel, data->base + DA850_PUPD_SEL); 144 writel(ena, data->base + DA850_PUPD_ENA); 145 146 return 0; 147 } 148 149 static const struct pinconf_ops da850_pupd_confops = { 150 .is_generic = true, 151 .pin_config_group_get = da850_pupd_pin_config_group_get, 152 .pin_config_group_set = da850_pupd_pin_config_group_set, 153 }; 154 155 static int da850_pupd_probe(struct platform_device *pdev) 156 { 157 struct device *dev = &pdev->dev; 158 struct da850_pupd_data *data; 159 struct resource *res; 160 161 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 162 if (!data) 163 return -ENOMEM; 164 165 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 166 data->base = devm_ioremap_resource(dev, res); 167 if (IS_ERR(data->base)) { 168 dev_err(dev, "Could not map resource\n"); 169 return PTR_ERR(data->base); 170 } 171 172 data->desc.name = dev_name(dev); 173 data->desc.pctlops = &da850_pupd_pctlops; 174 data->desc.confops = &da850_pupd_confops; 175 data->desc.owner = THIS_MODULE; 176 177 data->pinctrl = devm_pinctrl_register(dev, &data->desc, data); 178 if (IS_ERR(data->pinctrl)) { 179 dev_err(dev, "Failed to register pinctrl\n"); 180 return PTR_ERR(data->pinctrl); 181 } 182 183 platform_set_drvdata(pdev, data); 184 185 return 0; 186 } 187 188 static int da850_pupd_remove(struct platform_device *pdev) 189 { 190 return 0; 191 } 192 193 static const struct of_device_id da850_pupd_of_match[] = { 194 { .compatible = "ti,da850-pupd" }, 195 { } 196 }; 197 198 static struct platform_driver da850_pupd_driver = { 199 .driver = { 200 .name = "ti-da850-pupd", 201 .of_match_table = da850_pupd_of_match, 202 }, 203 .probe = da850_pupd_probe, 204 .remove = da850_pupd_remove, 205 }; 206 module_platform_driver(da850_pupd_driver); 207 208 MODULE_AUTHOR("David Lechner <david@lechnology.com>"); 209 MODULE_DESCRIPTION("TI DA850/OMAP-L138/AM18XX pullup/pulldown configuration"); 210 MODULE_LICENSE("GPL"); 211