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 int i; 117 118 ena = readl(data->base + DA850_PUPD_ENA); 119 sel = readl(data->base + DA850_PUPD_SEL); 120 121 for (i = 0; i < num_configs; i++) { 122 param = pinconf_to_config_param(configs[i]); 123 124 switch (param) { 125 case PIN_CONFIG_BIAS_DISABLE: 126 ena &= ~BIT(selector); 127 break; 128 case PIN_CONFIG_BIAS_PULL_UP: 129 ena |= BIT(selector); 130 sel |= BIT(selector); 131 break; 132 case PIN_CONFIG_BIAS_PULL_DOWN: 133 ena |= BIT(selector); 134 sel &= ~BIT(selector); 135 break; 136 default: 137 return -EINVAL; 138 } 139 } 140 141 writel(sel, data->base + DA850_PUPD_SEL); 142 writel(ena, data->base + DA850_PUPD_ENA); 143 144 return 0; 145 } 146 147 static const struct pinconf_ops da850_pupd_confops = { 148 .is_generic = true, 149 .pin_config_group_get = da850_pupd_pin_config_group_get, 150 .pin_config_group_set = da850_pupd_pin_config_group_set, 151 }; 152 153 static int da850_pupd_probe(struct platform_device *pdev) 154 { 155 struct device *dev = &pdev->dev; 156 struct da850_pupd_data *data; 157 struct resource *res; 158 159 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 160 if (!data) 161 return -ENOMEM; 162 163 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 164 data->base = devm_ioremap_resource(dev, res); 165 if (IS_ERR(data->base)) { 166 dev_err(dev, "Could not map resource\n"); 167 return PTR_ERR(data->base); 168 } 169 170 data->desc.name = dev_name(dev); 171 data->desc.pctlops = &da850_pupd_pctlops; 172 data->desc.confops = &da850_pupd_confops; 173 data->desc.owner = THIS_MODULE; 174 175 data->pinctrl = devm_pinctrl_register(dev, &data->desc, data); 176 if (IS_ERR(data->pinctrl)) { 177 dev_err(dev, "Failed to register pinctrl\n"); 178 return PTR_ERR(data->pinctrl); 179 } 180 181 platform_set_drvdata(pdev, data); 182 183 return 0; 184 } 185 186 static int da850_pupd_remove(struct platform_device *pdev) 187 { 188 return 0; 189 } 190 191 static const struct of_device_id da850_pupd_of_match[] = { 192 { .compatible = "ti,da850-pupd" }, 193 { } 194 }; 195 MODULE_DEVICE_TABLE(of, da850_pupd_of_match); 196 197 static struct platform_driver da850_pupd_driver = { 198 .driver = { 199 .name = "ti-da850-pupd", 200 .of_match_table = da850_pupd_of_match, 201 }, 202 .probe = da850_pupd_probe, 203 .remove = da850_pupd_remove, 204 }; 205 module_platform_driver(da850_pupd_driver); 206 207 MODULE_AUTHOR("David Lechner <david@lechnology.com>"); 208 MODULE_DESCRIPTION("TI DA850/OMAP-L138/AM18XX pullup/pulldown configuration"); 209 MODULE_LICENSE("GPL"); 210