/* * Copyright (C) 2015-2016 Socionext Inc. * Author: Masahiro Yamada * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include #include #include "pinctrl-uniphier.h" #define UNIPHIER_PINCTRL_PINMUX_BASE 0x1000 #define UNIPHIER_PINCTRL_LOAD_PINMUX 0x1700 #define UNIPHIER_PINCTRL_PUPDCTRL_BASE 0x1a00 #define UNIPHIER_PINCTRL_IECTRL 0x1d00 static const char *uniphier_pinctrl_dummy_name = "_dummy"; static int uniphier_pinctrl_get_groups_count(struct udevice *dev) { struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); return priv->socdata->groups_count; } static const char *uniphier_pinctrl_get_group_name(struct udevice *dev, unsigned selector) { struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); if (!priv->socdata->groups[selector].name) return uniphier_pinctrl_dummy_name; return priv->socdata->groups[selector].name; } static int uniphier_pinmux_get_functions_count(struct udevice *dev) { struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); return priv->socdata->functions_count; } static const char *uniphier_pinmux_get_function_name(struct udevice *dev, unsigned selector) { struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); if (!priv->socdata->functions[selector]) return uniphier_pinctrl_dummy_name; return priv->socdata->functions[selector]; } static int uniphier_pinconf_input_enable_perpin(struct udevice *dev, unsigned int pin, int enable) { struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); unsigned reg; u32 mask, tmp; reg = UNIPHIER_PINCTRL_IECTRL + pin / 32 * 4; mask = BIT(pin % 32); tmp = readl(priv->base + reg); if (enable) tmp |= mask; else tmp &= ~mask; writel(tmp, priv->base + reg); return 0; } static int uniphier_pinconf_input_enable_legacy(struct udevice *dev, unsigned int pin, int enable) { struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); int pins_count = priv->socdata->pins_count; const struct uniphier_pinctrl_pin *pins = priv->socdata->pins; int i; /* * Multiple pins share one input enable, per-pin disabling is * impossible. */ if (!enable) return -EINVAL; for (i = 0; i < pins_count; i++) { if (pins[i].number == pin) { unsigned int iectrl; u32 tmp; iectrl = uniphier_pin_get_iectrl(pins[i].data); tmp = readl(priv->base + UNIPHIER_PINCTRL_IECTRL); tmp |= 1 << iectrl; writel(tmp, priv->base + UNIPHIER_PINCTRL_IECTRL); } } return 0; } static int uniphier_pinconf_input_enable(struct udevice *dev, unsigned int pin, int enable) { struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); if (priv->socdata->caps & UNIPHIER_PINCTRL_CAPS_PERPIN_IECTRL) return uniphier_pinconf_input_enable_perpin(dev, pin, enable); else return uniphier_pinconf_input_enable_legacy(dev, pin, enable); } #if CONFIG_IS_ENABLED(PINCONF) static const struct pinconf_param uniphier_pinconf_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 }, { "bias-pull-pin-default", PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, 1 }, { "input-enable", PIN_CONFIG_INPUT_ENABLE, 1 }, { "input-disable", PIN_CONFIG_INPUT_ENABLE, 0 }, }; static int uniphier_pinconf_bias_set(struct udevice *dev, unsigned int pin, unsigned int param, unsigned int arg) { struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); unsigned int enable = 1; unsigned int reg; u32 mask, tmp; if (!(priv->socdata->caps & UNIPHIER_PINCTRL_CAPS_PUPD_SIMPLE)) return -ENOTSUPP; switch (param) { case PIN_CONFIG_BIAS_DISABLE: enable = 0; break; case PIN_CONFIG_BIAS_PULL_UP: case PIN_CONFIG_BIAS_PULL_DOWN: if (arg == 0) /* total bias is not supported */ return -EINVAL; break; case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: if (arg == 0) /* configuration ignored */ return 0; default: BUG(); } reg = UNIPHIER_PINCTRL_PUPDCTRL_BASE + pin / 32 * 4; mask = BIT(pin % 32); tmp = readl(priv->base + reg); if (enable) tmp |= mask; else tmp &= ~mask; writel(tmp, priv->base + reg); return 0; } static int uniphier_pinconf_set_one(struct udevice *dev, unsigned int pin, unsigned int param, unsigned int arg) { int ret; switch (param) { case PIN_CONFIG_BIAS_DISABLE: case PIN_CONFIG_BIAS_PULL_UP: case PIN_CONFIG_BIAS_PULL_DOWN: case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: ret = uniphier_pinconf_bias_set(dev, pin, param, arg); break; case PIN_CONFIG_INPUT_ENABLE: ret = uniphier_pinconf_input_enable(dev, pin, arg); break; default: printf("unsupported configuration parameter %u\n", param); return -EINVAL; } return ret; } static int uniphier_pinconf_group_set(struct udevice *dev, unsigned int group_selector, unsigned int param, unsigned int arg) { struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); const struct uniphier_pinctrl_group *grp = &priv->socdata->groups[group_selector]; int i, ret; for (i = 0; i < grp->num_pins; i++) { ret = uniphier_pinconf_set_one(dev, grp->pins[i], param, arg); if (ret) return ret; } return 0; } #endif /* CONFIG_IS_ENABLED(PINCONF) */ static void uniphier_pinmux_set_one(struct udevice *dev, unsigned pin, int muxval) { struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); unsigned reg, reg_end, shift, mask; unsigned mux_bits = 8; unsigned reg_stride = 4; bool load_pinctrl = false; u32 tmp; /* some pins need input-enabling */ uniphier_pinconf_input_enable(dev, pin, 1); if (muxval < 0) return; /* dedicated pin; nothing to do for pin-mux */ if (priv->socdata->caps & UNIPHIER_PINCTRL_CAPS_MUX_4BIT) mux_bits = 4; if (priv->socdata->caps & UNIPHIER_PINCTRL_CAPS_DBGMUX_SEPARATE) { /* * Mode offset bit * Normal 4 * n shift+3:shift * Debug 4 * n shift+7:shift+4 */ mux_bits /= 2; reg_stride = 8; load_pinctrl = true; } reg = UNIPHIER_PINCTRL_PINMUX_BASE + pin * mux_bits / 32 * reg_stride; reg_end = reg + reg_stride; shift = pin * mux_bits % 32; mask = (1U << mux_bits) - 1; /* * If reg_stride is greater than 4, the MSB of each pinsel shall be * stored in the offset+4. */ for (; reg < reg_end; reg += 4) { tmp = readl(priv->base + reg); tmp &= ~(mask << shift); tmp |= (mask & muxval) << shift; writel(tmp, priv->base + reg); muxval >>= mux_bits; } if (load_pinctrl) writel(1, priv->base + UNIPHIER_PINCTRL_LOAD_PINMUX); } static int uniphier_pinmux_group_set(struct udevice *dev, unsigned group_selector, unsigned func_selector) { struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); const struct uniphier_pinctrl_group *grp = &priv->socdata->groups[group_selector]; int i; for (i = 0; i < grp->num_pins; i++) uniphier_pinmux_set_one(dev, grp->pins[i], grp->muxvals[i]); return 0; } const struct pinctrl_ops uniphier_pinctrl_ops = { .get_groups_count = uniphier_pinctrl_get_groups_count, .get_group_name = uniphier_pinctrl_get_group_name, .get_functions_count = uniphier_pinmux_get_functions_count, .get_function_name = uniphier_pinmux_get_function_name, .pinmux_group_set = uniphier_pinmux_group_set, #if CONFIG_IS_ENABLED(PINCONF) .pinconf_num_params = ARRAY_SIZE(uniphier_pinconf_params), .pinconf_params = uniphier_pinconf_params, .pinconf_group_set = uniphier_pinconf_group_set, #endif .set_state = pinctrl_generic_set_state, }; int uniphier_pinctrl_probe(struct udevice *dev, struct uniphier_pinctrl_socdata *socdata) { struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); fdt_addr_t addr; addr = dev_get_addr(dev->parent); if (addr == FDT_ADDR_T_NONE) return -EINVAL; priv->base = devm_ioremap(dev, addr, SZ_4K); if (!priv->base) return -ENOMEM; priv->socdata = socdata; return 0; }