| Author | Tokens | Token Proportion | Commits | Commit Proportion |
|---|---|---|---|---|
| Tzuyi Chang | 3706 | 97.76% | 4 | 30.77% |
| Yu-Chun Lin | 36 | 0.95% | 5 | 38.46% |
| Linus Walleij | 20 | 0.53% | 1 | 7.69% |
| Chen Ni | 19 | 0.50% | 1 | 7.69% |
| Dan Carpenter | 9 | 0.24% | 1 | 7.69% |
| Javier Carrasco | 1 | 0.03% | 1 | 7.69% |
| Total | 3791 | 13 |
// 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; u32 **saved_regs; }; /* 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) #define RTD_HIGH_VIL (PIN_CONFIG_END + 4) 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}, {"realtek,high-vil-microvolt", RTD_HIGH_VIL, 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, unsigned int 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, input_volt_off, sr_off, hvil_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, "Pin config unsupported 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, "Input schmitt unsupported 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, "Push pull unsupported 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, "Bias disable unsupported 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, "Bias pull up unsupported 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, "Bias pull down unsupported 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, "Drive strength unsupported 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, "Power source unsupported 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 PIN_CONFIG_SLEW_RATE: if (config_desc->slew_rate_offset == NA) { dev_err(data->dev, "Slew rate setting unsupported for pin: %s\n", name); return -ENOTSUPP; } switch (arg) { case 1: set_val = 0; break; case 10: set_val = 1; break; case 20: set_val = 2; break; case 30: set_val = 3; break; default: return -EINVAL; } sr_off = config_desc->base_bit + config_desc->slew_rate_offset; reg_off = config_desc->reg_offset; mask = 0x3 << sr_off; val = arg << sr_off; break; case PIN_CONFIG_INPUT_VOLTAGE_UV: if (config_desc->input_volt_offset == NA) { dev_err(data->dev, "Input voltage level setting unsupported for pin:%s\n", name); return -ENOTSUPP; } if (arg == 3300000) set_val = 1; else if (arg == 1800000) set_val = 0; else return -EINVAL; input_volt_off = config_desc->base_bit + config_desc->input_volt_offset; reg_off = config_desc->reg_offset; mask = BIT(input_volt_off); val = set_val ? BIT(input_volt_off) : 0; break; case RTD_HIGH_VIL: if (config_desc->hvil_offset == NA) { dev_err(data->dev, "High vil setting unsupported for pin:%s\n", name); return -ENOTSUPP; } hvil_off = config_desc->base_bit + config_desc->hvil_offset; reg_off = config_desc->reg_offset; mask = BIT(hvil_off); val = 1; break; case RTD_DRIVE_STRENGH_P: sconfig_desc = rtd_pinctrl_find_sconfig(data, pinnr); if (!sconfig_desc) { dev_err(data->dev, "P driving unsupported 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, "N driving unsupported 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, "Duty cycle unsupported 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_dbg(data->dev, "unsupported pinconf: %d\n", (u32)param); return -ENOTSUPP; } 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, }; static int rtd_pinctrl_init_pm(struct rtd_pinctrl *data) { const struct rtd_pin_range *pin_range = data->info->pin_range; struct device *dev = data->pcdev->dev; const struct rtd_reg_range *range; size_t num_entries; int i; data->saved_regs = devm_kcalloc(dev, pin_range->num_ranges, sizeof(u32 *), GFP_KERNEL); if (!data->saved_regs) return -ENOMEM; for (i = 0; i < pin_range->num_ranges; i++) { range = &pin_range->ranges[i]; num_entries = range->len / 4; data->saved_regs[i] = devm_kzalloc(dev, num_entries * sizeof(u32), GFP_KERNEL); if (!data->saved_regs[i]) return -ENOMEM; } return 0; } int rtd_pinctrl_probe(struct platform_device *pdev, const struct rtd_pinctrl_desc *desc) { struct rtd_pinctrl *data; data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; data->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(data->base)) return dev_err_probe(&pdev->dev, PTR_ERR(data->base), "Failed to ioremap resource\n"); 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)) return dev_err_probe(data->dev, PTR_ERR(data->regmap_pinctrl), "Failed to init regmap\n"); data->pcdev = devm_pinctrl_register(&pdev->dev, &data->desc, data); if (IS_ERR(data->pcdev)) return dev_err_probe(data->dev, PTR_ERR(data->pcdev), "Failed to register pinctrl\n"); platform_set_drvdata(pdev, data); dev_dbg(&pdev->dev, "probed\n"); if (data->info->pin_range) { if (rtd_pinctrl_init_pm(data)) return -ENOMEM; } return 0; } EXPORT_SYMBOL(rtd_pinctrl_probe); static int realtek_pinctrl_suspend(struct device *dev) { struct rtd_pinctrl *data = dev_get_drvdata(dev); const struct rtd_pin_range *pin_range = data->info->pin_range; const struct rtd_reg_range *range; u32 *range_regs; int count; int i, j; int ret; if (!data->saved_regs) return 0; for (i = 0; i < pin_range->num_ranges; i++) { range = &pin_range->ranges[i]; range_regs = data->saved_regs[i]; count = range->len / 4; for (j = 0; j < count; j++) { ret = regmap_read(data->regmap_pinctrl, range->offset + (j * 4), &range_regs[j]); if (ret) { dev_err(dev, "failed to store register 0x%x: %d\n", range->offset + (j * 4), ret); return ret; } } } return 0; } static int realtek_pinctrl_resume(struct device *dev) { struct rtd_pinctrl *data = dev_get_drvdata(dev); const struct rtd_pin_range *pin_range = data->info->pin_range; const struct rtd_reg_range *range; u32 *range_regs; int count; int i, j; int ret; if (!data->saved_regs) return 0; for (i = 0; i < pin_range->num_ranges; i++) { range = &pin_range->ranges[i]; range_regs = data->saved_regs[i]; count = range->len / 4; for (j = 0; j < count; j++) { ret = regmap_write(data->regmap_pinctrl, range->offset + (j * 4), range_regs[j]); if (ret) { dev_err(dev, "failed to restore register 0x%x: %d\n", range->offset + (j * 4), ret); return ret; } } } return 0; } const struct dev_pm_ops realtek_pinctrl_pm_ops = { NOIRQ_SYSTEM_SLEEP_PM_OPS(realtek_pinctrl_suspend, realtek_pinctrl_resume) }; EXPORT_SYMBOL_GPL(realtek_pinctrl_pm_ops); MODULE_DESCRIPTION("Realtek DHC SoC pinctrl driver"); MODULE_LICENSE("GPL");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1