diff options
Diffstat (limited to '')
-rw-r--r-- | drivers/pinctrl/visconti/pinctrl-common.c | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/drivers/pinctrl/visconti/pinctrl-common.c b/drivers/pinctrl/visconti/pinctrl-common.c new file mode 100644 index 000000000..21c7e0d18 --- /dev/null +++ b/drivers/pinctrl/visconti/pinctrl-common.c @@ -0,0 +1,328 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2020 TOSHIBA CORPORATION + * Copyright (c) 2020 Toshiba Electronic Devices & Storage Corporation + * Copyright (c) 2020 Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp> + */ + +#include <linux/init.h> +#include <linux/of.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinmux.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinconf-generic.h> +#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 int visconti_gpio_request_enable(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int pin) +{ + struct visconti_pinctrl *priv = pinctrl_dev_get_drvdata(pctldev); + const struct visconti_mux *gpio_mux = &priv->devdata->gpio_mux[pin]; + unsigned long flags; + unsigned int val; + + dev_dbg(priv->dev, "%s: pin = %d\n", __func__, pin); + + /* update mux */ + spin_lock_irqsave(&priv->lock, flags); + val = readl(priv->base + gpio_mux->offset); + val &= ~gpio_mux->mask; + val |= gpio_mux->val; + writel(val, priv->base + gpio_mux->offset); + spin_unlock_irqrestore(&priv->lock, flags); + + 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, + .gpio_request_enable = visconti_gpio_request_enable, + .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); +} |