Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 | // SPDX-License-Identifier: GPL-2.0+ /* * Atmel PIO4 pinctrl driver * * Copyright (C) 2016 Atmel Corporation * Wenyou.Yang <wenyou.yang@atmel.com> */ #include <dm.h> #include <asm/global_data.h> #include <dm/device-internal.h> #include <dm/lists.h> #include <dm/pinctrl.h> #include <linux/bitops.h> #include <linux/io.h> #include <linux/err.h> #include <dm/uclass-internal.h> #include <mach/atmel_pio4.h> DECLARE_GLOBAL_DATA_PTR; /* * Warning: * In order to not introduce confusion between Atmel PIO groups and pinctrl * framework groups, Atmel PIO groups will be called banks. */ struct atmel_pio4_plat { struct atmel_pio4_port *reg_base; unsigned int slew_rate_support; }; /* * Table keeping track of the pinctrl driver's slew rate support and the * corresponding index into the struct udevice_id of the gpio_atmel_pio4 GPIO * driver. This has been done in order to align the DT of U-Boot with the DT of * Linux. In Linux, a phandle from a '-gpio' DT property is linked to the * pinctrl driver, unlike U-Boot which redirects this phandle to a corresponding * UCLASS_GPIO driver. Thus, in order to link the two, a hook to the bind method * of the pinctrl driver in U-Boot has been added. This bind method will attach * the GPIO driver to the pinctrl DT node using this table. * @slew_rate_support pinctrl driver's slew rate support * @gdidx index into the GPIO driver's struct udevide_id * (needed in order to properly bind with driver_data) */ struct atmel_pinctrl_data { unsigned int slew_rate_support; int gdidx; }; static const struct pinconf_param conf_params[] = { { "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 }, { "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 }, { "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 }, { "drive-open-drain", PIN_CONFIG_DRIVE_OPEN_DRAIN, 0 }, { "input-schmitt-disable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 0 }, { "input-schmitt-enable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 1 }, { "input-debounce", PIN_CONFIG_INPUT_DEBOUNCE, 0 }, { "atmel,drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 0 }, { "slew-rate", PIN_CONFIG_SLEW_RATE, 0}, }; static u32 atmel_pinctrl_get_pinconf(struct udevice *config, struct atmel_pio4_plat *plat) { const struct pinconf_param *params; u32 param, arg, conf = 0; u32 i; u32 val; for (i = 0; i < ARRAY_SIZE(conf_params); i++) { params = &conf_params[i]; if (!dev_read_prop(config, params->property, NULL)) continue; param = params->param; arg = params->default_value; /* Keep slew rate enabled by default. */ if (plat->slew_rate_support) conf |= ATMEL_PIO_SR; switch (param) { case PIN_CONFIG_BIAS_DISABLE: conf &= (~ATMEL_PIO_PUEN_MASK); conf &= (~ATMEL_PIO_PDEN_MASK); break; case PIN_CONFIG_BIAS_PULL_UP: conf |= ATMEL_PIO_PUEN_MASK; break; case PIN_CONFIG_BIAS_PULL_DOWN: conf |= ATMEL_PIO_PDEN_MASK; break; case PIN_CONFIG_DRIVE_OPEN_DRAIN: if (arg == 0) conf &= (~ATMEL_PIO_OPD_MASK); else conf |= ATMEL_PIO_OPD_MASK; break; case PIN_CONFIG_INPUT_SCHMITT_ENABLE: if (arg == 0) conf |= ATMEL_PIO_SCHMITT_MASK; else conf &= (~ATMEL_PIO_SCHMITT_MASK); break; case PIN_CONFIG_INPUT_DEBOUNCE: if (arg == 0) { conf &= (~ATMEL_PIO_IFEN_MASK); conf &= (~ATMEL_PIO_IFSCEN_MASK); } else { conf |= ATMEL_PIO_IFEN_MASK; conf |= ATMEL_PIO_IFSCEN_MASK; } break; case PIN_CONFIG_DRIVE_STRENGTH: dev_read_u32(config, params->property, &val); conf &= (~ATMEL_PIO_DRVSTR_MASK); conf |= (val << ATMEL_PIO_DRVSTR_OFFSET) & ATMEL_PIO_DRVSTR_MASK; break; case PIN_CONFIG_SLEW_RATE: if (!plat->slew_rate_support) break; dev_read_u32(config, params->property, &val); /* And disable it if requested. */ if (val == 0) conf &= ~ATMEL_PIO_SR; break; default: printf("%s: Unsupported configuration parameter: %u\n", __func__, param); break; } } return conf; } static inline struct atmel_pio4_port *atmel_pio4_bank_base(struct udevice *dev, u32 bank) { struct atmel_pio4_plat *plat = dev_get_plat(dev); struct atmel_pio4_port *bank_base = (struct atmel_pio4_port *)((u32)plat->reg_base + ATMEL_PIO_BANK_OFFSET * bank); return bank_base; } #define MAX_PINMUX_ENTRIES 40 static int atmel_process_config_dev(struct udevice *dev, struct udevice *config) { struct atmel_pio4_plat *plat = dev_get_plat(dev); int node = dev_of_offset(config); struct atmel_pio4_port *bank_base; u32 offset, func, bank, line; u32 cells[MAX_PINMUX_ENTRIES]; u32 i, conf; int count; conf = atmel_pinctrl_get_pinconf(config, plat); /* * The only case where this function returns a negative error value * is when there is no "pinmux" property attached to this node */ count = fdtdec_get_int_array_count(gd->fdt_blob, node, "pinmux", cells, ARRAY_SIZE(cells)); if (count < 0) return count; if (count > MAX_PINMUX_ENTRIES) return -EINVAL; for (i = 0 ; i < count; i++) { offset = ATMEL_GET_PIN_NO(cells[i]); func = ATMEL_GET_PIN_FUNC(cells[i]); bank = ATMEL_PIO_BANK(offset); line = ATMEL_PIO_LINE(offset); bank_base = atmel_pio4_bank_base(dev, bank); writel(BIT(line), &bank_base->mskr); conf &= (~ATMEL_PIO_CFGR_FUNC_MASK); conf |= (func & ATMEL_PIO_CFGR_FUNC_MASK); writel(conf, &bank_base->cfgr); } return 0; } static int atmel_pinctrl_set_state(struct udevice *dev, struct udevice *config) { int node = dev_of_offset(config); struct udevice *subconfig; int subnode, subnode_count = 0, ret; /* * If this function returns a negative error code then that means * that either the "pinmux" property of the node is missing, which is * the case for pinctrl nodes that do not have all the pins with the * same configuration and are split in multiple subnodes, or something * else went wrong and we have to stop. For the latter case, it would * mean that the node failed even though it has no subnodes. */ ret = atmel_process_config_dev(dev, config); if (!ret) return ret; /* * If we reach here, it means that the subnode pinctrl's DT has multiple * subnodes. If it does not, then something else went wrong in the * previous call to atmel_process_config_dev. */ fdt_for_each_subnode(subnode, gd->fdt_blob, node) { /* Get subnode as an udevice */ ret = uclass_find_device_by_of_offset(UCLASS_PINCONFIG, subnode, &subconfig); if (ret) return ret; /* * If this time the function returns an error code on a subnode * then something is totally wrong so abort. */ ret = atmel_process_config_dev(dev, subconfig); if (ret) return ret; subnode_count++; } /* * If we somehow got here and we do not have any subnodes, abort. */ if (!subnode_count) return -EINVAL; return 0; } const struct pinctrl_ops atmel_pinctrl_ops = { .set_state = atmel_pinctrl_set_state, }; static int atmel_pinctrl_probe(struct udevice *dev) { struct atmel_pio4_plat *plat = dev_get_plat(dev); struct atmel_pinctrl_data *priv = (struct atmel_pinctrl_data *)dev_get_driver_data(dev); fdt_addr_t addr_base; addr_base = dev_read_addr(dev); if (addr_base == FDT_ADDR_T_NONE) return -EINVAL; plat->reg_base = (struct atmel_pio4_port *)addr_base; plat->slew_rate_support = priv->slew_rate_support; return 0; } static int atmel_pinctrl_bind(struct udevice *dev) { struct udevice *g; struct driver *drv; ofnode node = dev_ofnode(dev); struct atmel_pinctrl_data *priv = (struct atmel_pinctrl_data *)dev_get_driver_data(dev); if (!IS_ENABLED(CONFIG_ATMEL_PIO4)) return 0; /* Obtain a handle to the GPIO driver */ drv = lists_driver_lookup_name("gpio_atmel_pio4"); if (!drv) return -ENOENT; /* * Bind the GPIO driver to the pinctrl DT node, together * with its corresponding driver_data. */ return device_bind_with_driver_data(dev, drv, drv->name, drv->of_match[priv->gdidx].data, node, &g); } static const struct atmel_pinctrl_data atmel_sama5d2_pinctrl_data = { .gdidx = 0, }; static const struct atmel_pinctrl_data microchip_sama7g5_pinctrl_data = { .slew_rate_support = 1, .gdidx = 1, }; static const struct udevice_id atmel_pinctrl_match[] = { { .compatible = "atmel,sama5d2-pinctrl", .data = (ulong)&atmel_sama5d2_pinctrl_data, }, { .compatible = "microchip,sama7g5-pinctrl", .data = (ulong)µchip_sama7g5_pinctrl_data, }, {} }; U_BOOT_DRIVER(atmel_pinctrl) = { .name = "pinctrl_atmel_pio4", .id = UCLASS_PINCTRL, .of_match = atmel_pinctrl_match, .bind = atmel_pinctrl_bind, .probe = atmel_pinctrl_probe, .plat_auto = sizeof(struct atmel_pio4_plat), .ops = &atmel_pinctrl_ops, }; |