// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2020 TOSHIBA CORPORATION * Copyright (c) 2020 Toshiba Electronic Devices & Storage Corporation * Copyright (c) 2020 Nobuhiro Iwamatsu */ #include #include #include #include #include #include #include #include #include "pinctrl-common.h" #include "../core.h" #include "../pinconf.h" #include "../pinctrl-utils.h" #define DSEL_MASK GENMASK(3, 0) /* private data */ struct visconti_pinctrl { void __iomem *base; struct device *dev; struct pinctrl_dev *pctl; struct pinctrl_desc pctl_desc; const struct visconti_pinctrl_devdata *devdata; spinlock_t lock; /* protect pinctrl register */ }; /* pinconf */ static int visconti_pin_config_set(struct pinctrl_dev *pctldev, unsigned int _pin, unsigned long *configs, unsigned int num_configs) { struct visconti_pinctrl *priv = pinctrl_dev_get_drvdata(pctldev); const struct visconti_desc_pin *pin = &priv->devdata->pins[_pin]; enum pin_config_param param; unsigned int arg; int i, ret = 0; unsigned int val, set_val, pude_val; unsigned long flags; dev_dbg(priv->dev, "%s: pin = %d (%s)\n", __func__, _pin, pin->pin.name); spin_lock_irqsave(&priv->lock, flags); for (i = 0; i < num_configs; i++) { set_val = 0; pude_val = 0; param = pinconf_to_config_param(configs[i]); switch (param) { case PIN_CONFIG_BIAS_PULL_UP: set_val = 1; fallthrough; case PIN_CONFIG_BIAS_PULL_DOWN: /* update pudsel setting */ val = readl(priv->base + pin->pudsel_offset); val &= ~BIT(pin->pud_shift); val |= set_val << pin->pud_shift; writel(val, priv->base + pin->pudsel_offset); pude_val = 1; fallthrough; case PIN_CONFIG_BIAS_DISABLE: /* update pude setting */ val = readl(priv->base + pin->pude_offset); val &= ~BIT(pin->pud_shift); val |= pude_val << pin->pud_shift; writel(val, priv->base + pin->pude_offset); dev_dbg(priv->dev, "BIAS(%d): off = 0x%x val = 0x%x\n", param, pin->pude_offset, val); break; case PIN_CONFIG_DRIVE_STRENGTH: arg = pinconf_to_config_argument(configs[i]); dev_dbg(priv->dev, "DRV_STR arg = %d\n", arg); switch (arg) { case 2: case 4: case 8: case 16: case 24: case 32: /* * I/O drive capacity setting: * 2mA: 0 * 4mA: 1 * 8mA: 3 * 16mA: 7 * 24mA: 11 * 32mA: 15 */ set_val = DIV_ROUND_CLOSEST(arg, 2) - 1; break; default: ret = -EINVAL; goto err; } /* update drive setting */ val = readl(priv->base + pin->dsel_offset); val &= ~(DSEL_MASK << pin->dsel_shift); val |= set_val << pin->dsel_shift; writel(val, priv->base + pin->dsel_offset); break; default: ret = -EOPNOTSUPP; goto err; } } err: spin_unlock_irqrestore(&priv->lock, flags); return ret; } static int visconti_pin_config_group_set(struct pinctrl_dev *pctldev, unsigned int selector, unsigned long *configs, unsigned int num_configs) { struct visconti_pinctrl *priv = pinctrl_dev_get_drvdata(pctldev); const unsigned int *pins; unsigned int num_pins; int i, ret; pins = priv->devdata->groups[selector].pins; num_pins = priv->devdata->groups[selector].nr_pins; dev_dbg(priv->dev, "%s: select = %d, n_pin = %d, n_config = %d\n", __func__, selector, num_pins, num_configs); for (i = 0; i < num_pins; i++) { ret = visconti_pin_config_set(pctldev, pins[i], configs, num_configs); if (ret) return ret; } return 0; } static const struct pinconf_ops visconti_pinconf_ops = { .is_generic = true, .pin_config_set = visconti_pin_config_set, .pin_config_group_set = visconti_pin_config_group_set, .pin_config_config_dbg_show = pinconf_generic_dump_config, }; /* pinctrl */ static int visconti_get_groups_count(struct pinctrl_dev *pctldev) { struct visconti_pinctrl *priv = pinctrl_dev_get_drvdata(pctldev); return priv->devdata->nr_groups; } static const char *visconti_get_group_name(struct pinctrl_dev *pctldev, unsigned int selector) { struct visconti_pinctrl *priv = pinctrl_dev_get_drvdata(pctldev); return priv->devdata->groups[selector].name; } static int visconti_get_group_pins(struct pinctrl_dev *pctldev, unsigned int selector, const unsigned int **pins, unsigned int *num_pins) { struct visconti_pinctrl *priv = pinctrl_dev_get_drvdata(pctldev); *pins = priv->devdata->groups[selector].pins; *num_pins = priv->devdata->groups[selector].nr_pins; return 0; } static const struct pinctrl_ops visconti_pinctrl_ops = { .get_groups_count = visconti_get_groups_count, .get_group_name = visconti_get_group_name, .get_group_pins = visconti_get_group_pins, .dt_node_to_map = pinconf_generic_dt_node_to_map_group, .dt_free_map = pinctrl_utils_free_map, }; /* pinmux */ static int visconti_get_functions_count(struct pinctrl_dev *pctldev) { struct visconti_pinctrl *priv = pinctrl_dev_get_drvdata(pctldev); return priv->devdata->nr_functions; } static const char *visconti_get_function_name(struct pinctrl_dev *pctldev, unsigned int selector) { struct visconti_pinctrl *priv = pinctrl_dev_get_drvdata(pctldev); return priv->devdata->functions[selector].name; } static int visconti_get_function_groups(struct pinctrl_dev *pctldev, unsigned int selector, const char * const **groups, unsigned * const num_groups) { struct visconti_pinctrl *priv = pinctrl_dev_get_drvdata(pctldev); *groups = priv->devdata->functions[selector].groups; *num_groups = priv->devdata->functions[selector].nr_groups; return 0; } static int visconti_set_mux(struct pinctrl_dev *pctldev, unsigned int function, unsigned int group) { struct visconti_pinctrl *priv = pinctrl_dev_get_drvdata(pctldev); const struct visconti_pin_function *func = &priv->devdata->functions[function]; const struct visconti_pin_group *grp = &priv->devdata->groups[group]; const struct visconti_mux *mux = &grp->mux; unsigned int val; unsigned long flags; dev_dbg(priv->dev, "%s: function = %d(%s) group = %d(%s)\n", __func__, function, func->name, group, grp->name); spin_lock_irqsave(&priv->lock, flags); /* update mux */ val = readl(priv->base + mux->offset); val &= ~mux->mask; val |= mux->val; writel(val, priv->base + mux->offset); spin_unlock_irqrestore(&priv->lock, flags); dev_dbg(priv->dev, "[%x]: 0x%x\n", mux->offset, val); return 0; } static const struct pinmux_ops visconti_pinmux_ops = { .get_functions_count = visconti_get_functions_count, .get_function_name = visconti_get_function_name, .get_function_groups = visconti_get_function_groups, .set_mux = visconti_set_mux, .strict = true, }; int visconti_pinctrl_probe(struct platform_device *pdev, const struct visconti_pinctrl_devdata *devdata) { struct device *dev = &pdev->dev; struct visconti_pinctrl *priv; struct pinctrl_pin_desc *pins; int i, ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; priv->dev = dev; priv->devdata = devdata; spin_lock_init(&priv->lock); priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) { dev_err(dev, "unable to map I/O space\n"); return PTR_ERR(priv->base); } pins = devm_kcalloc(dev, devdata->nr_pins, sizeof(*pins), GFP_KERNEL); if (!pins) return -ENOMEM; for (i = 0; i < devdata->nr_pins; i++) pins[i] = devdata->pins[i].pin; priv->pctl_desc.name = dev_name(dev); priv->pctl_desc.owner = THIS_MODULE; priv->pctl_desc.pins = pins; priv->pctl_desc.npins = devdata->nr_pins; priv->pctl_desc.confops = &visconti_pinconf_ops; priv->pctl_desc.pctlops = &visconti_pinctrl_ops; priv->pctl_desc.pmxops = &visconti_pinmux_ops; ret = devm_pinctrl_register_and_init(dev, &priv->pctl_desc, priv, &priv->pctl); if (ret) { dev_err(dev, "couldn't register pinctrl: %d\n", ret); return ret; } if (devdata->unlock) devdata->unlock(priv->base); return pinctrl_enable(priv->pctl); }