// SPDX-License-Identifier: GPL-2.0-only /* * Spreadtrum pin controller driver * Copyright (C) 2017 Spreadtrum - http://www.spreadtrum.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../core.h" #include "../pinmux.h" #include "../pinconf.h" #include "../pinctrl-utils.h" #include "pinctrl-sprd.h" #define PINCTRL_BIT_MASK(width) (~(~0UL << (width))) #define PINCTRL_REG_OFFSET 0x20 #define PINCTRL_REG_MISC_OFFSET 0x4020 #define PINCTRL_REG_LEN 0x4 #define PIN_FUNC_MASK (BIT(4) | BIT(5)) #define PIN_FUNC_SEL_1 ~PIN_FUNC_MASK #define PIN_FUNC_SEL_2 BIT(4) #define PIN_FUNC_SEL_3 BIT(5) #define PIN_FUNC_SEL_4 PIN_FUNC_MASK #define AP_SLEEP_MODE BIT(13) #define PUBCP_SLEEP_MODE BIT(14) #define TGLDSP_SLEEP_MODE BIT(15) #define AGDSP_SLEEP_MODE BIT(16) #define SLEEP_MODE_MASK GENMASK(3, 0) #define SLEEP_MODE_SHIFT 13 #define SLEEP_INPUT BIT(1) #define SLEEP_INPUT_MASK 0x1 #define SLEEP_INPUT_SHIFT 1 #define SLEEP_OUTPUT BIT(0) #define SLEEP_OUTPUT_MASK 0x1 #define SLEEP_OUTPUT_SHIFT 0 #define DRIVE_STRENGTH_MASK GENMASK(3, 0) #define DRIVE_STRENGTH_SHIFT 19 #define SLEEP_PULL_DOWN BIT(2) #define SLEEP_PULL_DOWN_MASK 0x1 #define SLEEP_PULL_DOWN_SHIFT 2 #define PULL_DOWN BIT(6) #define PULL_DOWN_MASK 0x1 #define PULL_DOWN_SHIFT 6 #define SLEEP_PULL_UP BIT(3) #define SLEEP_PULL_UP_MASK 0x1 #define SLEEP_PULL_UP_SHIFT 3 #define PULL_UP_20K (BIT(12) | BIT(7)) #define PULL_UP_4_7K BIT(12) #define PULL_UP_MASK 0x21 #define PULL_UP_SHIFT 7 #define INPUT_SCHMITT BIT(11) #define INPUT_SCHMITT_MASK 0x1 #define INPUT_SCHMITT_SHIFT 11 enum pin_sleep_mode { AP_SLEEP = BIT(0), PUBCP_SLEEP = BIT(1), TGLDSP_SLEEP = BIT(2), AGDSP_SLEEP = BIT(3), }; enum pin_func_sel { PIN_FUNC_1, PIN_FUNC_2, PIN_FUNC_3, PIN_FUNC_4, PIN_FUNC_MAX, }; /** * struct sprd_pin: represent one pin's description * @name: pin name * @number: pin number * @type: pin type, can be GLOBAL_CTRL_PIN/COMMON_PIN/MISC_PIN * @reg: pin register address * @bit_offset: bit offset in pin register * @bit_width: bit width in pin register */ struct sprd_pin { const char *name; unsigned int number; enum pin_type type; unsigned long reg; unsigned long bit_offset; unsigned long bit_width; }; /** * struct sprd_pin_group: represent one group's description * @name: group name * @npins: pin numbers of this group * @pins: pointer to pins array */ struct sprd_pin_group { const char *name; unsigned int npins; unsigned int *pins; }; /** * struct sprd_pinctrl_soc_info: represent the SoC's pins description * @groups: pointer to groups of pins * @ngroups: group numbers of the whole SoC * @pins: pointer to pins description * @npins: pin numbers of the whole SoC * @grp_names: pointer to group names array */ struct sprd_pinctrl_soc_info { struct sprd_pin_group *groups; unsigned int ngroups; struct sprd_pin *pins; unsigned int npins; const char **grp_names; }; /** * struct sprd_pinctrl: represent the pin controller device * @dev: pointer to the device structure * @pctl: pointer to the pinctrl handle * @base: base address of the controller * @info: pointer to SoC's pins description information */ struct sprd_pinctrl { struct device *dev; struct pinctrl_dev *pctl; void __iomem *base; struct sprd_pinctrl_soc_info *info; }; #define SPRD_PIN_CONFIG_CONTROL (PIN_CONFIG_END + 1) #define SPRD_PIN_CONFIG_SLEEP_MODE (PIN_CONFIG_END + 2) static int sprd_pinctrl_get_id_by_name(struct sprd_pinctrl *sprd_pctl, const char *name) { struct sprd_pinctrl_soc_info *info = sprd_pctl->info; int i; for (i = 0; i < info->npins; i++) { if (!strcmp(info->pins[i].name, name)) return info->pins[i].number; } return -ENODEV; } static struct sprd_pin * sprd_pinctrl_get_pin_by_id(struct sprd_pinctrl *sprd_pctl, unsigned int id) { struct sprd_pinctrl_soc_info *info = sprd_pctl->info; struct sprd_pin *pin = NULL; int i; for (i = 0; i < info->npins; i++) { if (info->pins[i].number == id) { pin = &info->pins[i]; break; } } return pin; } static const struct sprd_pin_group * sprd_pinctrl_find_group_by_name(struct sprd_pinctrl *sprd_pctl, const char *name) { struct sprd_pinctrl_soc_info *info = sprd_pctl->info; const struct sprd_pin_group *grp = NULL; int i; for (i = 0; i < info->ngroups; i++) { if (!strcmp(info->groups[i].name, name)) { grp = &info->groups[i]; break; } } return grp; } static int sprd_pctrl_group_count(struct pinctrl_dev *pctldev) { struct sprd_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); struct sprd_pinctrl_soc_info *info = pctl->info; return info->ngroups; } static const char *sprd_pctrl_group_name(struct pinctrl_dev *pctldev, unsigned int selector) { struct sprd_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); struct sprd_pinctrl_soc_info *info = pctl->info; return info->groups[selector].name; } static int sprd_pctrl_group_pins(struct pinctrl_dev *pctldev, unsigned int selector, const unsigned int **pins, unsigned int *npins) { struct sprd_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); struct sprd_pinctrl_soc_info *info = pctl->info; if (selector >= info->ngroups) return -EINVAL; *pins = info->groups[selector].pins; *npins = info->groups[selector].npins; return 0; } static int sprd_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node *np, struct pinctrl_map **map, unsigned int *num_maps) { struct sprd_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); const struct sprd_pin_group *grp; unsigned long *configs = NULL; unsigned int num_configs = 0; unsigned int reserved_maps = 0; unsigned int reserve = 0; const char *function; enum pinctrl_map_type type; int ret; grp = sprd_pinctrl_find_group_by_name(pctl, np->name); if (!grp) { dev_err(pctl->dev, "unable to find group for node %s\n", of_node_full_name(np)); return -EINVAL; } ret = of_property_count_strings(np, "pins"); if (ret < 0) return ret; if (ret == 1) type = PIN_MAP_TYPE_CONFIGS_PIN; else type = PIN_MAP_TYPE_CONFIGS_GROUP; ret = of_property_read_string(np, "function", &function); if (ret < 0) { if (ret != -EINVAL) dev_err(pctl->dev, "%s: could not parse property function\n", of_node_full_name(np)); function = NULL; } ret = pinconf_generic_parse_dt_config(np, pctldev, &configs, &num_configs); if (ret < 0) { dev_err(pctl->dev, "%s: could not parse node property\n", of_node_full_name(np)); return ret; } *map = NULL; *num_maps = 0; if (function != NULL) reserve++; if (num_configs) reserve++; ret = pinctrl_utils_reserve_map(pctldev, map, &reserved_maps, num_maps, reserve); if (ret < 0) goto out; if (function) { ret = pinctrl_utils_add_map_mux(pctldev, map, &reserved_maps, num_maps, grp->name, function); if (ret < 0) goto out; } if (num_configs) { const char *group_or_pin; unsigned int pin_id; if (type == PIN_MAP_TYPE_CONFIGS_PIN) { pin_id = grp->pins[0]; group_or_pin = pin_get_name(pctldev, pin_id); } else { group_or_pin = grp->name; } ret = pinctrl_utils_add_map_configs(pctldev, map, &reserved_maps, num_maps, group_or_pin, configs, num_configs, type); } out: kfree(configs); return ret; } static void sprd_pctrl_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned int offset) { seq_printf(s, "%s", dev_name(pctldev->dev)); } static const struct pinctrl_ops sprd_pctrl_ops = { .get_groups_count = sprd_pctrl_group_count, .get_group_name = sprd_pctrl_group_name, .get_group_pins = sprd_pctrl_group_pins, .pin_dbg_show = sprd_pctrl_dbg_show, .dt_node_to_map = sprd_dt_node_to_map, .dt_free_map = pinctrl_utils_free_map, }; static int sprd_pmx_get_function_count(struct pinctrl_dev *pctldev) { return PIN_FUNC_MAX; } static const char *sprd_pmx_get_function_name(struct pinctrl_dev *pctldev, unsigned int selector) { switch (selector) { case PIN_FUNC_1: return "func1"; case PIN_FUNC_2: return "func2"; case PIN_FUNC_3: return "func3"; case PIN_FUNC_4: return "func4"; default: return "null"; } } static int sprd_pmx_get_function_groups(struct pinctrl_dev *pctldev, unsigned int selector, const char * const **groups, unsigned int * const num_groups) { struct sprd_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); struct sprd_pinctrl_soc_info *info = pctl->info; *groups = info->grp_names; *num_groups = info->ngroups; return 0; } static int sprd_pmx_set_mux(struct pinctrl_dev *pctldev, unsigned int func_selector, unsigned int group_selector) { struct sprd_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); struct sprd_pinctrl_soc_info *info = pctl->info; struct sprd_pin_group *grp = &info->groups[group_selector]; unsigned int i, grp_pins = grp->npins; unsigned long reg; unsigned int val = 0; if (group_selector >= info->ngroups) return -EINVAL; switch (func_selector) { case PIN_FUNC_1: val &= PIN_FUNC_SEL_1; break; case PIN_FUNC_2: val |= PIN_FUNC_SEL_2; break; case PIN_FUNC_3: val |= PIN_FUNC_SEL_3; break; case PIN_FUNC_4: val |= PIN_FUNC_SEL_4; break; default: break; } for (i = 0; i < grp_pins; i++) { unsigned int pin_id = grp->pins[i]; struct sprd_pin *pin = sprd_pinctrl_get_pin_by_id(pctl, pin_id); if (!pin || pin->type != COMMON_PIN) continue; reg = readl((void __iomem *)pin->reg); reg &= ~PIN_FUNC_MASK; reg |= val; writel(reg, (void __iomem *)pin->reg); } return 0; } static const struct pinmux_ops sprd_pmx_ops = { .get_functions_count = sprd_pmx_get_function_count, .get_function_name = sprd_pmx_get_function_name, .get_function_groups = sprd_pmx_get_function_groups, .set_mux = sprd_pmx_set_mux, }; static int sprd_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin_id, unsigned long *config) { struct sprd_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); struct sprd_pin *pin = sprd_pinctrl_get_pin_by_id(pctl, pin_id); unsigned int param = pinconf_to_config_param(*config); unsigned int reg, arg; if (!pin) return -EINVAL; if (pin->type == GLOBAL_CTRL_PIN) { reg = (readl((void __iomem *)pin->reg) >> pin->bit_offset) & PINCTRL_BIT_MASK(pin->bit_width); } else { reg = readl((void __iomem *)pin->reg); } if (pin->type == GLOBAL_CTRL_PIN && param == SPRD_PIN_CONFIG_CONTROL) { arg = reg; } else if (pin->type == COMMON_PIN || pin->type == MISC_PIN) { switch (param) { case SPRD_PIN_CONFIG_SLEEP_MODE: arg = (reg >> SLEEP_MODE_SHIFT) & SLEEP_MODE_MASK; break; case PIN_CONFIG_INPUT_ENABLE: arg = (reg >> SLEEP_INPUT_SHIFT) & SLEEP_INPUT_MASK; break; case PIN_CONFIG_OUTPUT: arg = reg & SLEEP_OUTPUT_MASK; break; case PIN_CONFIG_DRIVE_STRENGTH: arg = (reg >> DRIVE_STRENGTH_SHIFT) & DRIVE_STRENGTH_MASK; break; case PIN_CONFIG_BIAS_PULL_DOWN: /* combine sleep pull down and pull down config */ arg = ((reg >> SLEEP_PULL_DOWN_SHIFT) & SLEEP_PULL_DOWN_MASK) << 16; arg |= (reg >> PULL_DOWN_SHIFT) & PULL_DOWN_MASK; break; case PIN_CONFIG_INPUT_SCHMITT_ENABLE: arg = (reg >> INPUT_SCHMITT_SHIFT) & INPUT_SCHMITT_MASK; break; case PIN_CONFIG_BIAS_PULL_UP: /* combine sleep pull up and pull up config */ arg = ((reg >> SLEEP_PULL_UP_SHIFT) & SLEEP_PULL_UP_MASK) << 16; arg |= (reg >> PULL_UP_SHIFT) & PULL_UP_MASK; break; case PIN_CONFIG_SLEEP_HARDWARE_STATE: arg = 0; break; default: return -ENOTSUPP; } } else { return -ENOTSUPP; } *config = pinconf_to_config_packed(param, arg); return 0; } static unsigned int sprd_pinconf_drive(unsigned int mA) { unsigned int val = 0; switch (mA) { case 2: break; case 4: val |= BIT(19); break; case 6: val |= BIT(20); break; case 8: val |= BIT(19) | BIT(20); break; case 10: val |= BIT(21); break; case 12: val |= BIT(21) | BIT(19); break; case 14: val |= BIT(21) | BIT(20); break; case 16: val |= BIT(19) | BIT(20) | BIT(21); break; case 20: val |= BIT(22); break; case 21: val |= BIT(22) | BIT(19); break; case 24: val |= BIT(22) | BIT(20); break; case 25: val |= BIT(22) | BIT(20) | BIT(19); break; case 27: val |= BIT(22) | BIT(21); break; case 29: val |= BIT(22) | BIT(21) | BIT(19); break; case 31: val |= BIT(22) | BIT(21) | BIT(20); break; case 33: val |= BIT(22) | BIT(21) | BIT(20) | BIT(19); break; default: break; } return val; } static bool sprd_pinctrl_check_sleep_config(unsigned long *configs, unsigned int num_configs) { unsigned int param; int i; for (i = 0; i < num_configs; i++) { param = pinconf_to_config_param(configs[i]); if (param == PIN_CONFIG_SLEEP_HARDWARE_STATE) return true; } return false; } static int sprd_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin_id, unsigned long *configs, unsigned int num_configs) { struct sprd_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); struct sprd_pin *pin = sprd_pinctrl_get_pin_by_id(pctl, pin_id); bool is_sleep_config; unsigned long reg; int i; if (!pin) return -EINVAL; is_sleep_config = sprd_pinctrl_check_sleep_config(configs, num_configs); for (i = 0; i < num_configs; i++) { unsigned int param, arg, shift, mask, val; param = pinconf_to_config_param(configs[i]); arg = pinconf_to_config_argument(configs[i]); val = 0; shift = 0; mask = 0; if (pin->type == GLOBAL_CTRL_PIN && param == SPRD_PIN_CONFIG_CONTROL) { val = arg; } else if (pin->type == COMMON_PIN || pin->type == MISC_PIN) { switch (param) { case SPRD_PIN_CONFIG_SLEEP_MODE: if (arg & AP_SLEEP) val |= AP_SLEEP_MODE; if (arg & PUBCP_SLEEP) val |= PUBCP_SLEEP_MODE; if (arg & TGLDSP_SLEEP) val |= TGLDSP_SLEEP_MODE; if (arg & AGDSP_SLEEP) val |= AGDSP_SLEEP_MODE; mask = SLEEP_MODE_MASK; shift = SLEEP_MODE_SHIFT; break; case PIN_CONFIG_INPUT_ENABLE: if (is_sleep_config == true) { if (arg > 0) val |= SLEEP_INPUT; else val &= ~SLEEP_INPUT; mask = SLEEP_INPUT_MASK; shift = SLEEP_INPUT_SHIFT; } break; case PIN_CONFIG_OUTPUT: if (is_sleep_config == true) { val |= SLEEP_OUTPUT; mask = SLEEP_OUTPUT_MASK; shift = SLEEP_OUTPUT_SHIFT; } break; case PIN_CONFIG_DRIVE_STRENGTH: if (arg < 2 || arg > 60) return -EINVAL; val = sprd_pinconf_drive(arg); mask = DRIVE_STRENGTH_MASK; shift = DRIVE_STRENGTH_SHIFT; break; case PIN_CONFIG_BIAS_PULL_DOWN: if (is_sleep_config == true) { val |= SLEEP_PULL_DOWN; mask = SLEEP_PULL_DOWN_MASK; shift = SLEEP_PULL_DOWN_SHIFT; } else { val |= PULL_DOWN; mask = PULL_DOWN_MASK; shift = PULL_DOWN_SHIFT; } break; case PIN_CONFIG_INPUT_SCHMITT_ENABLE: if (arg > 0) val |= INPUT_SCHMITT; else val &= ~INPUT_SCHMITT; mask = INPUT_SCHMITT_MASK; shift = INPUT_SCHMITT_SHIFT; break; case PIN_CONFIG_BIAS_PULL_UP: if (is_sleep_config == true) { val |= SLEEP_PULL_UP; mask = SLEEP_PULL_UP_MASK; shift = SLEEP_PULL_UP_SHIFT; } else { if (arg == 20000) val |= PULL_UP_20K; else if (arg == 4700) val |= PULL_UP_4_7K; mask = PULL_UP_MASK; shift = PULL_UP_SHIFT; } break; case PIN_CONFIG_SLEEP_HARDWARE_STATE: continue; default: return -ENOTSUPP; } } else { return -ENOTSUPP; } if (pin->type == GLOBAL_CTRL_PIN) { reg = readl((void __iomem *)pin->reg); reg &= ~(PINCTRL_BIT_MASK(pin->bit_width) << pin->bit_offset); reg |= (val & PINCTRL_BIT_MASK(pin->bit_width)) << pin->bit_offset; writel(reg, (void __iomem *)pin->reg); } else { reg = readl((void __iomem *)pin->reg); reg &= ~(mask << shift); reg |= val; writel(reg, (void __iomem *)pin->reg); } } return 0; } static int sprd_pinconf_group_get(struct pinctrl_dev *pctldev, unsigned int selector, unsigned long *config) { struct sprd_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); struct sprd_pinctrl_soc_info *info = pctl->info; struct sprd_pin_group *grp; unsigned int pin_id; if (selector >= info->ngroups) return -EINVAL; grp = &info->groups[selector]; pin_id = grp->pins[0]; return sprd_pinconf_get(pctldev, pin_id, config); } static int sprd_pinconf_group_set(struct pinctrl_dev *pctldev, unsigned int selector, unsigned long *configs, unsigned int num_configs) { struct sprd_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); struct sprd_pinctrl_soc_info *info = pctl->info; struct sprd_pin_group *grp; int ret, i; if (selector >= info->ngroups) return -EINVAL; grp = &info->groups[selector]; for (i = 0; i < grp->npins; i++) { unsigned int pin_id = grp->pins[i]; ret = sprd_pinconf_set(pctldev, pin_id, configs, num_configs); if (ret) return ret; } return 0; } static int sprd_pinconf_get_config(struct pinctrl_dev *pctldev, unsigned int pin_id, unsigned long *config) { struct sprd_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); struct sprd_pin *pin = sprd_pinctrl_get_pin_by_id(pctl, pin_id); if (!pin) return -EINVAL; if (pin->type == GLOBAL_CTRL_PIN) { *config = (readl((void __iomem *)pin->reg) >> pin->bit_offset) & PINCTRL_BIT_MASK(pin->bit_width); } else { *config = readl((void __iomem *)pin->reg); } return 0; } static void sprd_pinconf_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned int pin_id) { unsigned long config; int ret; ret = sprd_pinconf_get_config(pctldev, pin_id, &config); if (ret) return; seq_printf(s, "0x%lx", config); } static void sprd_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned int selector) { struct sprd_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); struct sprd_pinctrl_soc_info *info = pctl->info; struct sprd_pin_group *grp; unsigned long config; const char *name; int i, ret; if (selector >= info->ngroups) return; grp = &info->groups[selector]; seq_putc(s, '\n'); for (i = 0; i < grp->npins; i++, config++) { unsigned int pin_id = grp->pins[i]; name = pin_get_name(pctldev, pin_id); ret = sprd_pinconf_get_config(pctldev, pin_id, &config); if (ret) return; seq_printf(s, "%s: 0x%lx ", name, config); } } static const struct pinconf_ops sprd_pinconf_ops = { .is_generic = true, .pin_config_get = sprd_pinconf_get, .pin_config_set = sprd_pinconf_set, .pin_config_group_get = sprd_pinconf_group_get, .pin_config_group_set = sprd_pinconf_group_set, .pin_config_dbg_show = sprd_pinconf_dbg_show, .pin_config_group_dbg_show = sprd_pinconf_group_dbg_show, }; static const struct pinconf_generic_params sprd_dt_params[] = { {"sprd,control", SPRD_PIN_CONFIG_CONTROL, 0}, {"sprd,sleep-mode", SPRD_PIN_CONFIG_SLEEP_MODE, 0}, }; #ifdef CONFIG_DEBUG_FS static const struct pin_config_item sprd_conf_items[] = { PCONFDUMP(SPRD_PIN_CONFIG_CONTROL, "global control", NULL, true), PCONFDUMP(SPRD_PIN_CONFIG_SLEEP_MODE, "sleep mode", NULL, true), }; #endif static struct pinctrl_desc sprd_pinctrl_desc = { .pctlops = &sprd_pctrl_ops, .pmxops = &sprd_pmx_ops, .confops = &sprd_pinconf_ops, .num_custom_params = ARRAY_SIZE(sprd_dt_params), .custom_params = sprd_dt_params, #ifdef CONFIG_DEBUG_FS .custom_conf_items = sprd_conf_items, #endif .owner = THIS_MODULE, }; static int sprd_pinctrl_parse_groups(struct device_node *np, struct sprd_pinctrl *sprd_pctl, struct sprd_pin_group *grp) { struct property *prop; const char *pin_name; int ret, i = 0; ret = of_property_count_strings(np, "pins"); if (ret < 0) return ret; grp->name = np->name; grp->npins = ret; grp->pins = devm_kcalloc(sprd_pctl->dev, grp->npins, sizeof(unsigned int), GFP_KERNEL); if (!grp->pins) return -ENOMEM; of_property_for_each_string(np, "pins", prop, pin_name) { ret = sprd_pinctrl_get_id_by_name(sprd_pctl, pin_name); if (ret >= 0) grp->pins[i++] = ret; } for (i = 0; i < grp->npins; i++) { dev_dbg(sprd_pctl->dev, "Group[%s] contains [%d] pins: id = %d\n", grp->name, grp->npins, grp->pins[i]); } return 0; } static unsigned int sprd_pinctrl_get_groups(struct device_node *np) { struct device_node *child; unsigned int group_cnt, cnt; group_cnt = of_get_child_count(np); for_each_child_of_node(np, child) { cnt = of_get_child_count(child); if (cnt > 0) group_cnt += cnt; } return group_cnt; } static int sprd_pinctrl_parse_dt(struct sprd_pinctrl *sprd_pctl) { struct sprd_pinctrl_soc_info *info = sprd_pctl->info; struct device_node *np = sprd_pctl->dev->of_node; struct device_node *child, *sub_child; struct sprd_pin_group *grp; const char **temp; int ret; if (!np) return -ENODEV; info->ngroups = sprd_pinctrl_get_groups(np); if (!info->ngroups) return 0; info->groups = devm_kcalloc(sprd_pctl->dev, info->ngroups, sizeof(struct sprd_pin_group), GFP_KERNEL); if (!info->groups) return -ENOMEM; info->grp_names = devm_kcalloc(sprd_pctl->dev, info->ngroups, sizeof(char *), GFP_KERNEL); if (!info->grp_names) return -ENOMEM; temp = info->grp_names; grp = info->groups; for_each_child_of_node(np, child) { ret = sprd_pinctrl_parse_groups(child, sprd_pctl, grp); if (ret) { of_node_put(child); return ret; } *temp++ = grp->name; grp++; if (of_get_child_count(child) > 0) { for_each_child_of_node(child, sub_child) { ret = sprd_pinctrl_parse_groups(sub_child, sprd_pctl, grp); if (ret) { of_node_put(sub_child); of_node_put(child); return ret; } *temp++ = grp->name; grp++; } } } return 0; } static int sprd_pinctrl_add_pins(struct sprd_pinctrl *sprd_pctl, struct sprd_pins_info *sprd_soc_pin_info, int pins_cnt) { struct sprd_pinctrl_soc_info *info = sprd_pctl->info; unsigned int ctrl_pin = 0, com_pin = 0; struct sprd_pin *pin; int i; info->npins = pins_cnt; info->pins = devm_kcalloc(sprd_pctl->dev, info->npins, sizeof(struct sprd_pin), GFP_KERNEL); if (!info->pins) return -ENOMEM; for (i = 0, pin = info->pins; i < info->npins; i++, pin++) { unsigned int reg; pin->name = sprd_soc_pin_info[i].name; pin->type = sprd_soc_pin_info[i].type; pin->number = sprd_soc_pin_info[i].num; reg = sprd_soc_pin_info[i].reg; if (pin->type == GLOBAL_CTRL_PIN) { pin->reg = (unsigned long)sprd_pctl->base + PINCTRL_REG_LEN * reg; pin->bit_offset = sprd_soc_pin_info[i].bit_offset; pin->bit_width = sprd_soc_pin_info[i].bit_width; ctrl_pin++; } else if (pin->type == COMMON_PIN) { pin->reg = (unsigned long)sprd_pctl->base + PINCTRL_REG_OFFSET + PINCTRL_REG_LEN * (i - ctrl_pin); com_pin++; } else if (pin->type == MISC_PIN) { pin->reg = (unsigned long)sprd_pctl->base + PINCTRL_REG_MISC_OFFSET + PINCTRL_REG_LEN * (i - ctrl_pin - com_pin); } } for (i = 0, pin = info->pins; i < info->npins; pin++, i++) { dev_dbg(sprd_pctl->dev, "pin name[%s-%d], type = %d, " "bit offset = %ld, bit width = %ld, reg = 0x%lx\n", pin->name, pin->number, pin->type, pin->bit_offset, pin->bit_width, pin->reg); } return 0; } int sprd_pinctrl_core_probe(struct platform_device *pdev, struct sprd_pins_info *sprd_soc_pin_info, int pins_cnt) { struct sprd_pinctrl *sprd_pctl; struct sprd_pinctrl_soc_info *pinctrl_info; struct pinctrl_pin_desc *pin_desc; int ret, i; sprd_pctl = devm_kzalloc(&pdev->dev, sizeof(struct sprd_pinctrl), GFP_KERNEL); if (!sprd_pctl) return -ENOMEM; sprd_pctl->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(sprd_pctl->base)) return PTR_ERR(sprd_pctl->base); pinctrl_info = devm_kzalloc(&pdev->dev, sizeof(struct sprd_pinctrl_soc_info), GFP_KERNEL); if (!pinctrl_info) return -ENOMEM; sprd_pctl->info = pinctrl_info; sprd_pctl->dev = &pdev->dev; platform_set_drvdata(pdev, sprd_pctl); ret = sprd_pinctrl_add_pins(sprd_pctl, sprd_soc_pin_info, pins_cnt); if (ret) { dev_err(&pdev->dev, "fail to add pins information\n"); return ret; } ret = sprd_pinctrl_parse_dt(sprd_pctl); if (ret) { dev_err(&pdev->dev, "fail to parse dt properties\n"); return ret; } pin_desc = devm_kcalloc(&pdev->dev, pinctrl_info->npins, sizeof(struct pinctrl_pin_desc), GFP_KERNEL); if (!pin_desc) return -ENOMEM; for (i = 0; i < pinctrl_info->npins; i++) { pin_desc[i].number = pinctrl_info->pins[i].number; pin_desc[i].name = pinctrl_info->pins[i].name; pin_desc[i].drv_data = pinctrl_info; } sprd_pinctrl_desc.pins = pin_desc; sprd_pinctrl_desc.name = dev_name(&pdev->dev); sprd_pinctrl_desc.npins = pinctrl_info->npins; sprd_pctl->pctl = pinctrl_register(&sprd_pinctrl_desc, &pdev->dev, (void *)sprd_pctl); if (IS_ERR(sprd_pctl->pctl)) { dev_err(&pdev->dev, "could not register pinctrl driver\n"); return PTR_ERR(sprd_pctl->pctl); } return 0; } int sprd_pinctrl_remove(struct platform_device *pdev) { struct sprd_pinctrl *sprd_pctl = platform_get_drvdata(pdev); pinctrl_unregister(sprd_pctl->pctl); return 0; } void sprd_pinctrl_shutdown(struct platform_device *pdev) { struct pinctrl *pinctl; struct pinctrl_state *state; pinctl = devm_pinctrl_get(&pdev->dev); if (IS_ERR(pinctl)) return; state = pinctrl_lookup_state(pinctl, "shutdown"); if (IS_ERR(state)) return; pinctrl_select_state(pinctl, state); } MODULE_DESCRIPTION("SPREADTRUM Pin Controller Driver"); MODULE_AUTHOR("Baolin Wang "); MODULE_LICENSE("GPL v2");