diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed.c b/drivers/pinctrl/aspeed/pinctrl-aspeed.c index 5b49952e5fad..a86a4d66099c 100644 --- a/drivers/pinctrl/aspeed/pinctrl-aspeed.c +++ b/drivers/pinctrl/aspeed/pinctrl-aspeed.c @@ -547,3 +547,214 @@ int aspeed_pinctrl_probe(struct platform_device *pdev, return 0; } + +static inline bool pin_in_config_range(unsigned int offset, + const struct aspeed_pin_config *config) +{ + return offset >= config->pins[0] && offset <= config->pins[1]; +} + +static inline const struct aspeed_pin_config *find_pinconf_config( + const struct aspeed_pinctrl_data *pdata, + unsigned int offset, + enum pin_config_param param) +{ + unsigned int i; + + for (i = 0; i < pdata->nconfigs; i++) { + if (param == pdata->configs[i].param && + pin_in_config_range(offset, &pdata->configs[i])) + return &pdata->configs[i]; + } + + return NULL; +} + +/** + * @param: pinconf configuration parameter + * @arg: The supported argument for @param, or -1 if any value is supported + * @value: The register value to write to configure @arg for @param + * + * The map is to be used in conjunction with the configuration array supplied + * by the driver implementation. + */ +struct aspeed_pin_config_map { + enum pin_config_param param; + s32 arg; + u32 val; +}; + +enum aspeed_pin_config_map_type { MAP_TYPE_ARG, MAP_TYPE_VAL }; + +/* Aspeed consistently both: + * + * 1. Defines "disable bits" for internal pull-downs + * 2. Uses 8mA or 16mA drive strengths + */ +static const struct aspeed_pin_config_map pin_config_map[] = { + { PIN_CONFIG_BIAS_PULL_DOWN, 0, 1 }, + { PIN_CONFIG_BIAS_PULL_DOWN, -1, 0 }, + { PIN_CONFIG_BIAS_DISABLE, -1, 1 }, + { PIN_CONFIG_DRIVE_STRENGTH, 8, 0 }, + { PIN_CONFIG_DRIVE_STRENGTH, 16, 1 }, +}; + +static const struct aspeed_pin_config_map *find_pinconf_map( + enum pin_config_param param, + enum aspeed_pin_config_map_type type, + s64 value) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pin_config_map); i++) { + const struct aspeed_pin_config_map *elem; + bool match; + + elem = &pin_config_map[i]; + + switch (type) { + case MAP_TYPE_ARG: + match = (elem->arg == -1 || elem->arg == value); + break; + case MAP_TYPE_VAL: + match = (elem->val == value); + break; + } + + if (param == elem->param && match) + return elem; + } + + return NULL; +} + +int aspeed_pin_config_get(struct pinctrl_dev *pctldev, unsigned int offset, + unsigned long *config) +{ + const enum pin_config_param param = pinconf_to_config_param(*config); + const struct aspeed_pin_config_map *pmap; + const struct aspeed_pinctrl_data *pdata; + const struct aspeed_pin_config *pconf; + unsigned int val; + int rc = 0; + u32 arg; + + pdata = pinctrl_dev_get_drvdata(pctldev); + pconf = find_pinconf_config(pdata, offset, param); + if (!pconf) + return -ENOTSUPP; + + rc = regmap_read(pdata->maps[ASPEED_IP_SCU], pconf->reg, &val); + if (rc < 0) + return rc; + + pmap = find_pinconf_map(param, MAP_TYPE_VAL, + (val & BIT(pconf->bit)) >> pconf->bit); + + if (!pmap) + return -EINVAL; + + if (param == PIN_CONFIG_DRIVE_STRENGTH) + arg = (u32) pmap->arg; + else if (param == PIN_CONFIG_BIAS_PULL_DOWN) + arg = !!pmap->arg; + else + arg = 1; + + if (!arg) + return -EINVAL; + + *config = pinconf_to_config_packed(param, arg); + + return 0; +} + +int aspeed_pin_config_set(struct pinctrl_dev *pctldev, unsigned int offset, + unsigned long *configs, unsigned int num_configs) +{ + const struct aspeed_pinctrl_data *pdata; + unsigned int i; + int rc = 0; + + pdata = pinctrl_dev_get_drvdata(pctldev); + + for (i = 0; i < num_configs; i++) { + const struct aspeed_pin_config_map *pmap; + const struct aspeed_pin_config *pconf; + enum pin_config_param param; + unsigned int val; + u32 arg; + + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + + pconf = find_pinconf_config(pdata, offset, param); + if (!pconf) + return -ENOTSUPP; + + pmap = find_pinconf_map(param, MAP_TYPE_ARG, arg); + + if (unlikely(WARN_ON(!pmap))) + return -EINVAL; + + val = pmap->val << pconf->bit; + + rc = regmap_update_bits(pdata->maps[ASPEED_IP_SCU], pconf->reg, + BIT(pconf->bit), val); + + if (rc < 0) + return rc; + + pr_debug("%s: Set SCU%02X[%d]=%d for param %d(=%d) on pin %d\n", + __func__, pconf->reg, pconf->bit, pmap->val, + param, arg, offset); + } + + return 0; +} + +int aspeed_pin_config_group_get(struct pinctrl_dev *pctldev, + unsigned int selector, + unsigned long *config) +{ + const unsigned int *pins; + unsigned int npins; + int rc; + + rc = aspeed_pinctrl_get_group_pins(pctldev, selector, &pins, &npins); + if (rc < 0) + return rc; + + if (!npins) + return -ENODEV; + + rc = aspeed_pin_config_get(pctldev, pins[0], config); + + return rc; +} + +int aspeed_pin_config_group_set(struct pinctrl_dev *pctldev, + unsigned int selector, + unsigned long *configs, + unsigned int num_configs) +{ + const unsigned int *pins; + unsigned int npins; + int rc; + int i; + + pr_debug("%s: Fetching pins for group selector %d\n", + __func__, selector); + rc = aspeed_pinctrl_get_group_pins(pctldev, selector, &pins, &npins); + if (rc < 0) + return rc; + + for (i = 0; i < npins; i++) { + rc = aspeed_pin_config_set(pctldev, pins[i], configs, + num_configs); + if (rc < 0) + return rc; + } + + return 0; +} diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed.h b/drivers/pinctrl/aspeed/pinctrl-aspeed.h index 08a10d4db229..fa125db828f5 100644 --- a/drivers/pinctrl/aspeed/pinctrl-aspeed.h +++ b/drivers/pinctrl/aspeed/pinctrl-aspeed.h @@ -514,6 +514,20 @@ struct aspeed_pin_desc { SIG_EXPR_LIST_DECL_SINGLE(gpio, gpio); \ MS_PIN_DECL_(pin, SIG_EXPR_LIST_PTR(gpio)) +/** + * @param The pinconf parameter type + * @pins The pin range this config struct covers, [low, high] + * @reg The register housing the configuration bits + * @mask The mask to select the bits of interest in @reg + */ +struct aspeed_pin_config { + enum pin_config_param param; + unsigned int pins[2]; + unsigned int reg; + u8 bit; + u8 value; +}; + struct aspeed_pinctrl_data { struct regmap *maps[ASPEED_NR_PINMUX_IPS]; @@ -525,6 +539,9 @@ struct aspeed_pinctrl_data { const struct aspeed_pin_function *functions; const unsigned int nfunctions; + + const struct aspeed_pin_config *configs; + const unsigned int nconfigs; }; #define ASPEED_PINCTRL_PIN(name_) \ @@ -580,5 +597,16 @@ int aspeed_gpio_request_enable(struct pinctrl_dev *pctldev, int aspeed_pinctrl_probe(struct platform_device *pdev, struct pinctrl_desc *pdesc, struct aspeed_pinctrl_data *pdata); +int aspeed_pin_config_get(struct pinctrl_dev *pctldev, unsigned int offset, + unsigned long *config); +int aspeed_pin_config_set(struct pinctrl_dev *pctldev, unsigned int offset, + unsigned long *configs, unsigned int num_configs); +int aspeed_pin_config_group_get(struct pinctrl_dev *pctldev, + unsigned int selector, + unsigned long *config); +int aspeed_pin_config_group_set(struct pinctrl_dev *pctldev, + unsigned int selector, + unsigned long *configs, + unsigned int num_configs); #endif /* PINCTRL_ASPEED */