| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- * Realtek DHC pin controller driver
- *
- * Copyright (c) 2023 Realtek Semiconductor Corp.
- */
- #include <linux/bitops.h>
- #include <linux/io.h>
- #include <linux/module.h>
- #include <linux/of.h>
- #include <linux/of_address.h>
- #include <linux/pinctrl/machine.h>
- #include <linux/pinctrl/pinconf.h>
- #include <linux/pinctrl/pinconf-generic.h>
- #include <linux/pinctrl/pinctrl.h>
- #include <linux/pinctrl/pinmux.h>
- #include <linux/platform_device.h>
- #include <linux/seq_file.h>
- #include <linux/regmap.h>
- #include <linux/slab.h>
- #include "../core.h"
- #include "../pinctrl-utils.h"
- #include "pinctrl-rtd.h"
- struct rtd_pinctrl {
- struct device *dev;
- struct pinctrl_dev *pcdev;
- void __iomem *base;
- struct pinctrl_desc desc;
- const struct rtd_pinctrl_desc *info;
- struct regmap *regmap_pinctrl;
- };
- /* custom pinconf parameters */
- #define RTD_DRIVE_STRENGH_P (PIN_CONFIG_END + 1)
- #define RTD_DRIVE_STRENGH_N (PIN_CONFIG_END + 2)
- #define RTD_DUTY_CYCLE (PIN_CONFIG_END + 3)
- static const struct pinconf_generic_params rtd_custom_bindings[] = {
- {"realtek,drive-strength-p", RTD_DRIVE_STRENGH_P, 0},
- {"realtek,drive-strength-n", RTD_DRIVE_STRENGH_N, 0},
- {"realtek,duty-cycle", RTD_DUTY_CYCLE, 0},
- };
- static int rtd_pinctrl_get_groups_count(struct pinctrl_dev *pcdev)
- {
- struct rtd_pinctrl *data = pinctrl_dev_get_drvdata(pcdev);
- return data->info->num_groups;
- }
- static const char *rtd_pinctrl_get_group_name(struct pinctrl_dev *pcdev,
- unsigned int selector)
- {
- struct rtd_pinctrl *data = pinctrl_dev_get_drvdata(pcdev);
- return data->info->groups[selector].name;
- }
- static int rtd_pinctrl_get_group_pins(struct pinctrl_dev *pcdev,
- unsigned int selector,
- const unsigned int **pins,
- unsigned int *num_pins)
- {
- struct rtd_pinctrl *data = pinctrl_dev_get_drvdata(pcdev);
- *pins = data->info->groups[selector].pins;
- *num_pins = data->info->groups[selector].num_pins;
- return 0;
- }
- static void rtd_pinctrl_dbg_show(struct pinctrl_dev *pcdev,
- struct seq_file *s,
- unsigned int offset)
- {
- struct rtd_pinctrl *data = pinctrl_dev_get_drvdata(pcdev);
- const struct rtd_pin_desc *mux = &data->info->muxes[offset];
- const struct rtd_pin_mux_desc *func;
- u32 val;
- u32 mask;
- u32 pin_val;
- int is_map;
- if (!mux->name) {
- seq_puts(s, "[not defined]");
- return;
- }
- val = readl_relaxed(data->base + mux->mux_offset);
- mask = mux->mux_mask;
- pin_val = val & mask;
- is_map = 0;
- func = &mux->functions[0];
- seq_puts(s, "function: ");
- while (func->name) {
- if (func->mux_value == pin_val) {
- is_map = 1;
- seq_printf(s, "[%s] ", func->name);
- } else {
- seq_printf(s, "%s ", func->name);
- }
- func++;
- }
- if (!is_map)
- seq_puts(s, "[not defined]");
- }
- static const struct pinctrl_ops rtd_pinctrl_ops = {
- .dt_node_to_map = pinconf_generic_dt_node_to_map_all,
- .dt_free_map = pinctrl_utils_free_map,
- .get_groups_count = rtd_pinctrl_get_groups_count,
- .get_group_name = rtd_pinctrl_get_group_name,
- .get_group_pins = rtd_pinctrl_get_group_pins,
- .pin_dbg_show = rtd_pinctrl_dbg_show,
- };
- static int rtd_pinctrl_get_functions_count(struct pinctrl_dev *pcdev)
- {
- struct rtd_pinctrl *data = pinctrl_dev_get_drvdata(pcdev);
- return data->info->num_functions;
- }
- static const char *rtd_pinctrl_get_function_name(struct pinctrl_dev *pcdev,
- unsigned int selector)
- {
- struct rtd_pinctrl *data = pinctrl_dev_get_drvdata(pcdev);
- return data->info->functions[selector].name;
- }
- static int rtd_pinctrl_get_function_groups(struct pinctrl_dev *pcdev,
- unsigned int selector,
- const char * const **groups,
- unsigned int * const num_groups)
- {
- struct rtd_pinctrl *data = pinctrl_dev_get_drvdata(pcdev);
- *groups = data->info->functions[selector].groups;
- *num_groups = data->info->functions[selector].num_groups;
- return 0;
- }
- static const struct rtd_pin_desc *rtd_pinctrl_find_mux(struct rtd_pinctrl *data, unsigned int pin)
- {
- if (data->info->muxes[pin].name)
- return &data->info->muxes[pin];
- return NULL;
- }
- static int rtd_pinctrl_set_one_mux(struct pinctrl_dev *pcdev,
- unsigned int pin, const char *func_name)
- {
- struct rtd_pinctrl *data = pinctrl_dev_get_drvdata(pcdev);
- const struct rtd_pin_desc *mux;
- int ret = 0;
- int i;
- mux = rtd_pinctrl_find_mux(data, pin);
- if (!mux)
- return 0;
- if (!mux->functions) {
- if (!mux->name)
- dev_err(pcdev->dev, "NULL pin has no functions\n");
- else
- dev_err(pcdev->dev, "No functions available for pin %s\n", mux->name);
- return -ENOTSUPP;
- }
- for (i = 0; mux->functions[i].name; i++) {
- if (strcmp(mux->functions[i].name, func_name) != 0)
- continue;
- ret = regmap_update_bits(data->regmap_pinctrl, mux->mux_offset, mux->mux_mask,
- mux->functions[i].mux_value);
- return ret;
- }
- if (!mux->name) {
- dev_err(pcdev->dev, "NULL pin provided for function %s\n", func_name);
- return -EINVAL;
- }
- dev_err(pcdev->dev, "No function %s available for pin %s\n", func_name, mux->name);
- return -EINVAL;
- }
- static int rtd_pinctrl_set_mux(struct pinctrl_dev *pcdev,
- unsigned int function, unsigned int group)
- {
- struct rtd_pinctrl *data = pinctrl_dev_get_drvdata(pcdev);
- const unsigned int *pins;
- unsigned int num_pins;
- const char *func_name;
- const char *group_name;
- int i, ret;
- func_name = data->info->functions[function].name;
- group_name = data->info->groups[group].name;
- ret = rtd_pinctrl_get_group_pins(pcdev, group, &pins, &num_pins);
- if (ret) {
- dev_err(pcdev->dev, "Getting pins for group %s failed\n", group_name);
- return ret;
- }
- for (i = 0; i < num_pins; i++) {
- ret = rtd_pinctrl_set_one_mux(pcdev, pins[i], func_name);
- if (ret)
- return ret;
- }
- return 0;
- }
- static int rtd_pinctrl_gpio_request_enable(struct pinctrl_dev *pcdev,
- struct pinctrl_gpio_range *range,
- unsigned int offset)
- {
- return rtd_pinctrl_set_one_mux(pcdev, offset, "gpio");
- }
- static const struct pinmux_ops rtd_pinmux_ops = {
- .get_functions_count = rtd_pinctrl_get_functions_count,
- .get_function_name = rtd_pinctrl_get_function_name,
- .get_function_groups = rtd_pinctrl_get_function_groups,
- .set_mux = rtd_pinctrl_set_mux,
- .gpio_request_enable = rtd_pinctrl_gpio_request_enable,
- };
- static const struct pinctrl_pin_desc
- *rtd_pinctrl_get_pin_by_number(struct rtd_pinctrl *data, int number)
- {
- int i;
- for (i = 0; i < data->info->num_pins; i++) {
- if (data->info->pins[i].number == number)
- return &data->info->pins[i];
- }
- return NULL;
- }
- static const struct rtd_pin_config_desc
- *rtd_pinctrl_find_config(struct rtd_pinctrl *data, unsigned int pin)
- {
- if (data->info->configs[pin].name)
- return &data->info->configs[pin];
- return NULL;
- }
- static const struct rtd_pin_sconfig_desc *rtd_pinctrl_find_sconfig(struct rtd_pinctrl *data,
- unsigned int pin)
- {
- int i;
- const struct pinctrl_pin_desc *pin_desc;
- const char *pin_name;
- pin_desc = rtd_pinctrl_get_pin_by_number(data, pin);
- if (!pin_desc)
- return NULL;
- pin_name = pin_desc->name;
- for (i = 0; i < data->info->num_sconfigs; i++) {
- if (strcmp(data->info->sconfigs[i].name, pin_name) == 0)
- return &data->info->sconfigs[i];
- }
- return NULL;
- }
- static int rtd_pconf_parse_conf(struct rtd_pinctrl *data,
- unsigned int pinnr,
- enum pin_config_param param,
- enum pin_config_param arg)
- {
- const struct rtd_pin_config_desc *config_desc;
- const struct rtd_pin_sconfig_desc *sconfig_desc;
- u8 set_val = 0;
- u16 strength;
- u32 val;
- u32 mask;
- u32 pulsel_off, pulen_off, smt_off, curr_off, pow_off, reg_off, p_off, n_off;
- const char *name = data->info->pins[pinnr].name;
- int ret = 0;
- config_desc = rtd_pinctrl_find_config(data, pinnr);
- if (!config_desc) {
- dev_err(data->dev, "Not support pin config for pin: %s\n", name);
- return -ENOTSUPP;
- }
- switch ((u32)param) {
- case PIN_CONFIG_INPUT_SCHMITT:
- case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
- if (config_desc->smt_offset == NA) {
- dev_err(data->dev, "Not support input schmitt for pin: %s\n", name);
- return -ENOTSUPP;
- }
- smt_off = config_desc->base_bit + config_desc->smt_offset;
- reg_off = config_desc->reg_offset;
- set_val = arg;
- mask = BIT(smt_off);
- val = set_val ? BIT(smt_off) : 0;
- break;
- case PIN_CONFIG_DRIVE_PUSH_PULL:
- if (config_desc->pud_en_offset == NA) {
- dev_err(data->dev, "Not support push pull for pin: %s\n", name);
- return -ENOTSUPP;
- }
- pulen_off = config_desc->base_bit + config_desc->pud_en_offset;
- reg_off = config_desc->reg_offset;
- mask = BIT(pulen_off);
- val = 0;
- break;
- case PIN_CONFIG_BIAS_DISABLE:
- if (config_desc->pud_en_offset == NA) {
- dev_err(data->dev, "Not support bias disable for pin: %s\n", name);
- return -ENOTSUPP;
- }
- pulen_off = config_desc->base_bit + config_desc->pud_en_offset;
- reg_off = config_desc->reg_offset;
- mask = BIT(pulen_off);
- val = 0;
- break;
- case PIN_CONFIG_BIAS_PULL_UP:
- if (config_desc->pud_en_offset == NA) {
- dev_err(data->dev, "Not support bias pull up for pin:%s\n", name);
- return -ENOTSUPP;
- }
- pulen_off = config_desc->base_bit + config_desc->pud_en_offset;
- pulsel_off = config_desc->base_bit + config_desc->pud_sel_offset;
- reg_off = config_desc->reg_offset;
- mask = BIT(pulen_off) | BIT(pulsel_off);
- val = mask;
- break;
- case PIN_CONFIG_BIAS_PULL_DOWN:
- if (config_desc->pud_en_offset == NA) {
- dev_err(data->dev, "Not support bias pull down for pin: %s\n", name);
- return -ENOTSUPP;
- }
- pulen_off = config_desc->base_bit + config_desc->pud_en_offset;
- pulsel_off = config_desc->base_bit + config_desc->pud_sel_offset;
- reg_off = config_desc->reg_offset;
- mask = BIT(pulen_off) | BIT(pulsel_off);
- val = BIT(pulen_off);
- break;
- case PIN_CONFIG_DRIVE_STRENGTH:
- curr_off = config_desc->base_bit + config_desc->curr_offset;
- reg_off = config_desc->reg_offset;
- strength = arg;
- val = 0;
- switch (config_desc->curr_type) {
- case PADDRI_4_8:
- if (strength == 4)
- val = 0;
- else if (strength == 8)
- val = BIT(curr_off);
- else
- return -EINVAL;
- break;
- case PADDRI_2_4:
- if (strength == 2)
- val = 0;
- else if (strength == 4)
- val = BIT(curr_off);
- else
- return -EINVAL;
- break;
- case NA:
- dev_err(data->dev, "Not support drive strength for pin: %s\n", name);
- return -ENOTSUPP;
- default:
- return -EINVAL;
- }
- mask = BIT(curr_off);
- break;
- case PIN_CONFIG_POWER_SOURCE:
- if (config_desc->power_offset == NA) {
- dev_err(data->dev, "Not support power source for pin: %s\n", name);
- return -ENOTSUPP;
- }
- reg_off = config_desc->reg_offset;
- pow_off = config_desc->base_bit + config_desc->power_offset;
- if (pow_off >= 32) {
- reg_off += 0x4;
- pow_off -= 32;
- }
- set_val = arg;
- mask = BIT(pow_off);
- val = set_val ? mask : 0;
- break;
- case RTD_DRIVE_STRENGH_P:
- sconfig_desc = rtd_pinctrl_find_sconfig(data, pinnr);
- if (!sconfig_desc) {
- dev_err(data->dev, "Not support P driving for pin: %s\n", name);
- return -ENOTSUPP;
- }
- set_val = arg;
- reg_off = sconfig_desc->reg_offset;
- p_off = sconfig_desc->pdrive_offset;
- if (p_off >= 32) {
- reg_off += 0x4;
- p_off -= 32;
- }
- mask = GENMASK(p_off + sconfig_desc->pdrive_maskbits - 1, p_off);
- val = set_val << p_off;
- break;
- case RTD_DRIVE_STRENGH_N:
- sconfig_desc = rtd_pinctrl_find_sconfig(data, pinnr);
- if (!sconfig_desc) {
- dev_err(data->dev, "Not support N driving for pin: %s\n", name);
- return -ENOTSUPP;
- }
- set_val = arg;
- reg_off = sconfig_desc->reg_offset;
- n_off = sconfig_desc->ndrive_offset;
- if (n_off >= 32) {
- reg_off += 0x4;
- n_off -= 32;
- }
- mask = GENMASK(n_off + sconfig_desc->ndrive_maskbits - 1, n_off);
- val = set_val << n_off;
- break;
- case RTD_DUTY_CYCLE:
- sconfig_desc = rtd_pinctrl_find_sconfig(data, pinnr);
- if (!sconfig_desc || sconfig_desc->dcycle_offset == NA) {
- dev_err(data->dev, "Not support duty cycle for pin: %s\n", name);
- return -ENOTSUPP;
- }
- set_val = arg;
- reg_off = config_desc->reg_offset;
- mask = GENMASK(sconfig_desc->dcycle_offset +
- sconfig_desc->dcycle_maskbits - 1, sconfig_desc->dcycle_offset);
- val = set_val << sconfig_desc->dcycle_offset;
- break;
- default:
- dev_err(data->dev, "unsupported pinconf: %d\n", (u32)param);
- return -EINVAL;
- }
- ret = regmap_update_bits(data->regmap_pinctrl, reg_off, mask, val);
- if (ret)
- dev_err(data->dev, "could not update pinconf(%d) for pin(%s)\n", (u32)param, name);
- return ret;
- }
- static int rtd_pin_config_get(struct pinctrl_dev *pcdev, unsigned int pinnr,
- unsigned long *config)
- {
- unsigned int param = pinconf_to_config_param(*config);
- unsigned int arg = 0;
- switch (param) {
- default:
- return -ENOTSUPP;
- }
- *config = pinconf_to_config_packed(param, arg);
- return 0;
- }
- static int rtd_pin_config_set(struct pinctrl_dev *pcdev, unsigned int pinnr,
- unsigned long *configs, unsigned int num_configs)
- {
- struct rtd_pinctrl *data = pinctrl_dev_get_drvdata(pcdev);
- int i;
- int ret = 0;
- for (i = 0; i < num_configs; i++) {
- ret = rtd_pconf_parse_conf(data, pinnr,
- pinconf_to_config_param(configs[i]),
- pinconf_to_config_argument(configs[i]));
- if (ret < 0)
- return ret;
- }
- return 0;
- }
- static int rtd_pin_config_group_set(struct pinctrl_dev *pcdev, unsigned int group,
- unsigned long *configs, unsigned int num_configs)
- {
- struct rtd_pinctrl *data = pinctrl_dev_get_drvdata(pcdev);
- const unsigned int *pins;
- unsigned int num_pins;
- const char *group_name;
- int i, ret;
- group_name = data->info->groups[group].name;
- ret = rtd_pinctrl_get_group_pins(pcdev, group, &pins, &num_pins);
- if (ret) {
- dev_err(pcdev->dev, "Getting pins for group %s failed\n", group_name);
- return ret;
- }
- for (i = 0; i < num_pins; i++) {
- ret = rtd_pin_config_set(pcdev, pins[i], configs, num_configs);
- if (ret)
- return ret;
- }
- return 0;
- }
- static const struct pinconf_ops rtd_pinconf_ops = {
- .is_generic = true,
- .pin_config_get = rtd_pin_config_get,
- .pin_config_set = rtd_pin_config_set,
- .pin_config_group_set = rtd_pin_config_group_set,
- };
- static const struct regmap_config rtd_pinctrl_regmap_config = {
- .reg_bits = 32,
- .val_bits = 32,
- .reg_stride = 4,
- .use_relaxed_mmio = true,
- };
- int rtd_pinctrl_probe(struct platform_device *pdev, const struct rtd_pinctrl_desc *desc)
- {
- struct rtd_pinctrl *data;
- int ret;
- data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
- data->base = of_iomap(pdev->dev.of_node, 0);
- if (!data->base)
- return -ENOMEM;
- data->dev = &pdev->dev;
- data->info = desc;
- data->desc.name = dev_name(&pdev->dev);
- data->desc.pins = data->info->pins;
- data->desc.npins = data->info->num_pins;
- data->desc.pctlops = &rtd_pinctrl_ops;
- data->desc.pmxops = &rtd_pinmux_ops;
- data->desc.confops = &rtd_pinconf_ops;
- data->desc.custom_params = rtd_custom_bindings;
- data->desc.num_custom_params = ARRAY_SIZE(rtd_custom_bindings);
- data->desc.owner = THIS_MODULE;
- data->regmap_pinctrl = devm_regmap_init_mmio(data->dev, data->base,
- &rtd_pinctrl_regmap_config);
- if (IS_ERR(data->regmap_pinctrl)) {
- dev_err(data->dev, "failed to init regmap: %ld\n",
- PTR_ERR(data->regmap_pinctrl));
- ret = PTR_ERR(data->regmap_pinctrl);
- goto unmap;
- }
- data->pcdev = pinctrl_register(&data->desc, &pdev->dev, data);
- if (IS_ERR(data->pcdev)) {
- ret = PTR_ERR(data->pcdev);
- goto unmap;
- }
- platform_set_drvdata(pdev, data);
- dev_dbg(&pdev->dev, "probed\n");
- return 0;
- unmap:
- iounmap(data->base);
- return ret;
- }
- EXPORT_SYMBOL(rtd_pinctrl_probe);
- MODULE_DESCRIPTION("Realtek DHC SoC pinctrl driver");
- MODULE_LICENSE("GPL v2");
|