// SPDX-License-Identifier: GPL-2.0-only /* * Author: Lorenzo Bianconi * Author: Benjamin Larsson * Author: Markus Gothe * Author: Mikhail Kshevetskiy */ #include #include #include #include #include #include #include #include #include #include #include "airoha-common.h" #define airoha_pinctrl_get_pullup_conf(pinctrl, pin, val) \ airoha_pinctrl_get_conf((pinctrl), AIROHA_PINCTRL_CONFS_PULLUP, \ (pin), (val)) #define airoha_pinctrl_get_pulldown_conf(pinctrl, pin, val) \ airoha_pinctrl_get_conf((pinctrl), AIROHA_PINCTRL_CONFS_PULLDOWN, \ (pin), (val)) #define airoha_pinctrl_get_drive_e2_conf(pinctrl, pin, val) \ airoha_pinctrl_get_conf((pinctrl), AIROHA_PINCTRL_CONFS_DRIVE_E2, \ (pin), (val)) #define airoha_pinctrl_get_drive_e4_conf(pinctrl, pin, val) \ airoha_pinctrl_get_conf((pinctrl), AIROHA_PINCTRL_CONFS_DRIVE_E4, \ (pin), (val)) #define airoha_pinctrl_get_pcie_rst_od_conf(pinctrl, pin, val) \ airoha_pinctrl_get_conf((pinctrl), AIROHA_PINCTRL_CONFS_PCIE_RST_OD, \ (pin), (val)) #define airoha_pinctrl_set_pullup_conf(pinctrl, pin, val) \ airoha_pinctrl_set_conf((pinctrl), AIROHA_PINCTRL_CONFS_PULLUP, \ (pin), (val)) #define airoha_pinctrl_set_pulldown_conf(pinctrl, pin, val) \ airoha_pinctrl_set_conf((pinctrl), AIROHA_PINCTRL_CONFS_PULLDOWN, \ (pin), (val)) #define airoha_pinctrl_set_drive_e2_conf(pinctrl, pin, val) \ airoha_pinctrl_set_conf((pinctrl), AIROHA_PINCTRL_CONFS_DRIVE_E2, \ (pin), (val)) #define airoha_pinctrl_set_drive_e4_conf(pinctrl, pin, val) \ airoha_pinctrl_set_conf((pinctrl), AIROHA_PINCTRL_CONFS_DRIVE_E4, \ (pin), (val)) #define airoha_pinctrl_set_pcie_rst_od_conf(pinctrl, pin, val) \ airoha_pinctrl_set_conf((pinctrl), AIROHA_PINCTRL_CONFS_PCIE_RST_OD, \ (pin), (val)) static const u32 gpio_data_regs[] = { REG_GPIO_DATA, REG_GPIO_DATA1 }; static const u32 gpio_out_regs[] = { REG_GPIO_OE, REG_GPIO_OE1 }; static const u32 gpio_dir_regs[] = { REG_GPIO_CTRL, REG_GPIO_CTRL1, REG_GPIO_CTRL2, REG_GPIO_CTRL3 }; static const u32 irq_status_regs[] = { REG_GPIO_INT, REG_GPIO_INT1 }; static const u32 irq_level_regs[] = { REG_GPIO_INT_LEVEL, REG_GPIO_INT_LEVEL1, REG_GPIO_INT_LEVEL2, REG_GPIO_INT_LEVEL3 }; static const u32 irq_edge_regs[] = { REG_GPIO_INT_EDGE, REG_GPIO_INT_EDGE1, REG_GPIO_INT_EDGE2, REG_GPIO_INT_EDGE3 }; static int pin_in_group(unsigned int pin, const struct pingroup *grp) { for (int i = 0; i < grp->npins; i++) { if (grp->pins[i] == pin) return 1; } return 0; } static int pin_to_gpio(struct airoha_pinctrl *pinctrl, unsigned int pin) { struct airoha_pinctrl_match_data *data = pinctrl->data; if (pin < data->gpio_offs || pin >= data->gpio_offs + data->gpio_pin_cnt) return -EINVAL; return pin - data->gpio_offs; } /* gpio callbacks */ static int airoha_gpio_set(struct airoha_pinctrl *pinctrl, unsigned int gpio, int value) { u32 offset = gpio % AIROHA_PIN_BANK_SIZE; u8 index = gpio / AIROHA_PIN_BANK_SIZE; return regmap_update_bits(pinctrl->regmap, pinctrl->gpiochip.data[index], BIT(offset), value ? BIT(offset) : 0); } static int airoha_gpio_get(struct airoha_pinctrl *pinctrl, unsigned int gpio) { u32 val, pin = gpio % AIROHA_PIN_BANK_SIZE; u8 index = gpio / AIROHA_PIN_BANK_SIZE; int err; err = regmap_read(pinctrl->regmap, pinctrl->gpiochip.data[index], &val); return err ? err : !!(val & BIT(pin)); } static int airoha_gpio_get_direction(struct airoha_pinctrl *pinctrl, unsigned int gpio) { u32 mask, index, val; int err, field_shift; field_shift = 2 * (gpio % AIROHA_REG_GPIOCTRL_NUM_PIN); mask = GENMASK(field_shift + 1, field_shift); index = gpio / AIROHA_REG_GPIOCTRL_NUM_PIN; err = regmap_read(pinctrl->regmap, pinctrl->gpiochip.dir[index], &val); if (err) return err; if ((val & mask) > BIT(field_shift)) return -EINVAL; return (val & mask) ? GPIOF_OUTPUT : GPIOF_INPUT; } static int airoha_gpio_set_direction(struct airoha_pinctrl *pinctrl, unsigned int gpio, bool input) { u32 mask, index; int err, field_shift; /* set output enable */ mask = BIT(gpio % AIROHA_PIN_BANK_SIZE); index = gpio / AIROHA_PIN_BANK_SIZE; err = regmap_update_bits(pinctrl->regmap, pinctrl->gpiochip.out[index], mask, !input ? mask : 0); if (err) return err; /* set direction */ field_shift = 2 * (gpio % AIROHA_REG_GPIOCTRL_NUM_PIN); mask = GENMASK(field_shift + 1, field_shift); index = gpio / AIROHA_REG_GPIOCTRL_NUM_PIN; return regmap_update_bits(pinctrl->regmap, pinctrl->gpiochip.dir[index], mask, !input ? BIT(field_shift) : 0); } /* pinmux callbacks */ static int airoha_pinmux_set_mux(struct airoha_pinctrl *pinctrl, unsigned int func_selector, unsigned int group_selector) { const struct airoha_pinctrl_func *func; const struct pingroup *grp; int i; func = &pinctrl->data->funcs[func_selector]; grp = &pinctrl->data->grps[group_selector]; dev_dbg(pinctrl->dev, "enable function %s group %s\n", func->desc.name, grp->name); for (i = 0; i < func->group_size; i++) { const struct airoha_pinctrl_func_group *group; int j; group = &func->groups[i]; if (strcmp(group->name, grp->name)) continue; for (j = 0; j < group->regmap_size; j++) { switch (group->regmap[j].mux) { case AIROHA_FUNC_PWM_EXT_MUX: case AIROHA_FUNC_PWM_MUX: regmap_update_bits(pinctrl->regmap, group->regmap[j].offset, group->regmap[j].mask, group->regmap[j].val); break; default: regmap_update_bits(pinctrl->chip_scu, group->regmap[j].offset, group->regmap[j].mask, group->regmap[j].val); break; } } return 0; } return -EINVAL; } static int airoha_pinmux_set_direction(struct airoha_pinctrl *pinctrl, unsigned int p, bool input) { int gpio; gpio = pin_to_gpio(pinctrl, p); if (gpio < 0) return gpio; return airoha_gpio_set_direction(pinctrl, gpio, input); } /* pinconf callbacks */ static const struct airoha_pinctrl_reg * airoha_pinctrl_get_conf_reg(const struct airoha_pinctrl_conf *conf, int conf_size, int pin) { int i; for (i = 0; i < conf_size; i++) { if (conf[i].pin == pin) return &conf[i].reg; } return NULL; } static int airoha_pinctrl_get_conf(struct airoha_pinctrl *pinctrl, enum airoha_pinctrl_confs_type conf_type, int pin, u32 *val) { const struct airoha_pinctrl_confs_info *confs_info; const struct airoha_pinctrl_reg *reg; confs_info = &pinctrl->data->confs_info[conf_type]; reg = airoha_pinctrl_get_conf_reg(confs_info->confs, confs_info->num_confs, pin); if (!reg) return -EINVAL; if (regmap_read(pinctrl->chip_scu, reg->offset, val)) return -EINVAL; *val = field_get(reg->mask, *val); return 0; } static int airoha_pinctrl_set_conf(struct airoha_pinctrl *pinctrl, enum airoha_pinctrl_confs_type conf_type, int pin, u32 val) { const struct airoha_pinctrl_confs_info *confs_info; const struct airoha_pinctrl_reg *reg = NULL; confs_info = &pinctrl->data->confs_info[conf_type]; reg = airoha_pinctrl_get_conf_reg(confs_info->confs, confs_info->num_confs, pin); if (!reg) return -EINVAL; if (regmap_update_bits(pinctrl->chip_scu, reg->offset, reg->mask, field_prep(reg->mask, val))) return -EINVAL; return 0; } static int airoha_pinconf_get_direction(struct airoha_pinctrl *pinctrl, u32 p) { int gpio; gpio = pin_to_gpio(pinctrl, p); if (gpio < 0) return gpio; return airoha_gpio_get_direction(pinctrl, gpio); } static int airoha_pinconf_get(struct airoha_pinctrl *pinctrl, unsigned int pin, unsigned long *config) { enum pin_config_param param = pinconf_to_config_param(*config); u32 arg; switch (param) { case PIN_CONFIG_BIAS_PULL_DOWN: case PIN_CONFIG_BIAS_DISABLE: case PIN_CONFIG_BIAS_PULL_UP: { u32 pull_up, pull_down; if (airoha_pinctrl_get_pullup_conf(pinctrl, pin, &pull_up) || airoha_pinctrl_get_pulldown_conf(pinctrl, pin, &pull_down)) return -EINVAL; if (param == PIN_CONFIG_BIAS_PULL_UP && !(pull_up && !pull_down)) return -EINVAL; else if (param == PIN_CONFIG_BIAS_PULL_DOWN && !(pull_down && !pull_up)) return -EINVAL; else if (pull_up || pull_down) return -EINVAL; arg = 1; break; } case PIN_CONFIG_DRIVE_STRENGTH: { u32 e2, e4; if (airoha_pinctrl_get_drive_e2_conf(pinctrl, pin, &e2) || airoha_pinctrl_get_drive_e4_conf(pinctrl, pin, &e4)) return -EINVAL; arg = e4 << 1 | e2; break; } case PIN_CONFIG_DRIVE_OPEN_DRAIN: if (airoha_pinctrl_get_pcie_rst_od_conf(pinctrl, pin, &arg)) return -EINVAL; break; case PIN_CONFIG_OUTPUT_ENABLE: case PIN_CONFIG_INPUT_ENABLE: arg = airoha_pinconf_get_direction(pinctrl, pin); if ((param != PIN_CONFIG_OUTPUT_ENABLE || arg != GPIOF_OUTPUT) && (param != PIN_CONFIG_INPUT_ENABLE || arg != GPIOF_INPUT)) return -EINVAL; arg = 1; break; default: return -EOPNOTSUPP; } *config = pinconf_to_config_packed(param, arg); return 0; } static int airoha_pinconf_set_pin_value(struct airoha_pinctrl *pinctrl, unsigned int p, bool value) { int gpio; gpio = pin_to_gpio(pinctrl, p); if (gpio < 0) return gpio; return airoha_gpio_set(pinctrl, gpio, value); } static int airoha_pinconf_set(struct airoha_pinctrl *pinctrl, unsigned int pin, unsigned long *configs, unsigned int num_configs) { int i, err; for (i = 0; i < num_configs; i++) { u32 param = pinconf_to_config_param(configs[i]); u32 arg = pinconf_to_config_argument(configs[i]); switch (param) { case PIN_CONFIG_BIAS_DISABLE: err = airoha_pinctrl_set_pulldown_conf(pinctrl, pin, 0); if (err) return err; err = airoha_pinctrl_set_pullup_conf(pinctrl, pin, 0); if (err) return err; break; case PIN_CONFIG_BIAS_PULL_UP: err = airoha_pinctrl_set_pulldown_conf(pinctrl, pin, 0); if (err) return err; err = airoha_pinctrl_set_pullup_conf(pinctrl, pin, 1); if (err) return err; break; case PIN_CONFIG_BIAS_PULL_DOWN: err = airoha_pinctrl_set_pulldown_conf(pinctrl, pin, 1); if (err) return err; err = airoha_pinctrl_set_pullup_conf(pinctrl, pin, 0); if (err) return err; break; case PIN_CONFIG_DRIVE_STRENGTH: { u32 e2 = 0, e4 = 0; switch (arg) { case MTK_DRIVE_2mA: break; case MTK_DRIVE_4mA: e2 = 1; break; case MTK_DRIVE_6mA: e4 = 1; break; case MTK_DRIVE_8mA: e2 = 1; e4 = 1; break; default: return -EINVAL; } err = airoha_pinctrl_set_drive_e2_conf(pinctrl, pin, e2); if (err) return err; err = airoha_pinctrl_set_drive_e4_conf(pinctrl, pin, e4); if (err) return err; break; } case PIN_CONFIG_DRIVE_OPEN_DRAIN: err = airoha_pinctrl_set_pcie_rst_od_conf(pinctrl, pin, !!arg); if (err) return err; break; case PIN_CONFIG_OUTPUT_ENABLE: case PIN_CONFIG_INPUT_ENABLE: case PIN_CONFIG_OUTPUT: { bool input = param == PIN_CONFIG_INPUT_ENABLE; err = airoha_pinmux_set_direction(pinctrl, pin, input); if (err) return err; if (param == PIN_CONFIG_OUTPUT) { err = airoha_pinconf_set_pin_value(pinctrl, pin, !!arg); if (err) return err; } break; } default: return -EOPNOTSUPP; } } return 0; } static int airoha_pinconf_group_set(struct airoha_pinctrl *pinctrl, unsigned int group, unsigned long *configs, unsigned int num_configs) { int i; for (i = 0; i < pinctrl->data->grps[group].npins; i++) { int err; err = airoha_pinconf_set(pinctrl, pinctrl->data->grps[group].pins[i], configs, num_configs); if (err) return err; } return 0; } static int func_grp_active(struct airoha_pinctrl *pinctrl, const struct airoha_pinctrl_func *func, const char *grp_name) { const struct airoha_pinctrl_func_group *func_grp; u32 val, match; int ret; for (int i = 0; i < func->group_size; i++) { if (strcmp(func->groups[i].name, grp_name)) continue; match = 0; func_grp = &func->groups[i]; for (int j = 0; j < func_grp->regmap_size; j++) { switch (func_grp->regmap[j].mux) { case AIROHA_FUNC_PWM_EXT_MUX: case AIROHA_FUNC_PWM_MUX: ret = regmap_read(pinctrl->regmap, func_grp->regmap[j].offset, &val); break; default: ret = regmap_read(pinctrl->chip_scu, func_grp->regmap[j].offset, &val); break; } if (ret) break; if ((val & func_grp->regmap[j].mask) != func_grp->regmap[j].val) break; match++; } return match == func->groups[i].regmap_size; } return 0; } /*********************** * gpio driver interface ***********************/ static int airoha_pinctrl_gpio_set(struct udevice *dev, unsigned int gpio, int value) { return airoha_gpio_set(dev_get_priv(dev->parent), gpio, value); } static int airoha_pinctrl_gpio_get(struct udevice *dev, unsigned int gpio) { return airoha_gpio_get(dev_get_priv(dev->parent), gpio); } static int airoha_pinctrl_gpio_get_direction(struct udevice *dev, unsigned int gpio) { return airoha_gpio_get_direction(dev_get_priv(dev->parent), gpio); } static int airoha_pinctrl_gpio_direction_input(struct udevice *dev, unsigned int gpio) { return airoha_gpio_set_direction(dev_get_priv(dev->parent), gpio, true); } static int airoha_pinctrl_gpio_direction_output(struct udevice *dev, unsigned int gpio, int val) { struct airoha_pinctrl *pinctrl = dev_get_priv(dev->parent); int err; err = airoha_gpio_set_direction(pinctrl, gpio, false); if (err) return err; return airoha_gpio_set(pinctrl, gpio, val); } static int airoha_pinctrl_gpio_probe(struct udevice *dev) { struct airoha_pinctrl *pinctrl = dev_get_priv(dev->parent); struct gpio_dev_priv *uc_priv; uc_priv = dev_get_uclass_priv(dev); uc_priv->bank_name = "airoha"; uc_priv->gpio_count = pinctrl->data->gpio_pin_cnt; return 0; } static int airoha_pinctrl_gpio_bind(struct udevice *dev) { dev_or_flags(dev, DM_FLAG_PROBE_AFTER_BIND); return 0; } static const struct dm_gpio_ops airoha_pinctrl_gpio_ops = { .set_value = airoha_pinctrl_gpio_set, .get_value = airoha_pinctrl_gpio_get, .get_function = airoha_pinctrl_gpio_get_direction, .direction_input = airoha_pinctrl_gpio_direction_input, .direction_output = airoha_pinctrl_gpio_direction_output, }; static struct driver airoha_pinctrl_gpio_driver = { .name = "airoha_pinctrl_gpio", .id = UCLASS_GPIO, .probe = airoha_pinctrl_gpio_probe, .bind = airoha_pinctrl_gpio_bind, .ops = &airoha_pinctrl_gpio_ops, }; static int airoha_pinctrl_gpio_register(struct udevice *parent) { struct uclass_driver *drv; ofnode node; int ret; drv = lists_uclass_lookup(UCLASS_GPIO); if (!drv) return -ENOENT; /* * Support upstream linux DTSI that define gpio-controller * in the root node (instead of a dedicated subnode) */ if (dev_read_bool(parent, "gpio-controller")) { /* upstream DTSI, use current node */ node = dev_ofnode(parent); } else { /* legacy DTSI, search for gpio-controller subnode */ ret = -ENOENT; dev_for_each_subnode(node, parent) if (ofnode_read_bool(node, "gpio-controller")) { ret = 0; break; } if (ret) return ret; } return device_bind_with_driver_data(parent, &airoha_pinctrl_gpio_driver, "airoha_pinctrl_gpio", 0, node, NULL); } /************************** * pinctrl driver interface **************************/ static int airoha_get_pins_count(struct udevice *dev) { struct airoha_pinctrl *pinctrl = dev_get_priv(dev); return pinctrl->data->num_pins; } static const char *airoha_get_pin_name(struct udevice *dev, unsigned int selector) { struct airoha_pinctrl *pinctrl = dev_get_priv(dev); return pinctrl->data->pins[selector].name; } static int airoha_get_groups_count(struct udevice *dev) { struct airoha_pinctrl *pinctrl = dev_get_priv(dev); return pinctrl->data->num_grps; } static const char *airoha_get_group_name(struct udevice *dev, unsigned int selector) { struct airoha_pinctrl *pinctrl = dev_get_priv(dev); return pinctrl->data->grps[selector].name; } static int airoha_get_funcs_count(struct udevice *dev) { struct airoha_pinctrl *pinctrl = dev_get_priv(dev); return pinctrl->data->num_funcs; } static const char *airoha_get_func_name(struct udevice *dev, unsigned int selector) { struct airoha_pinctrl *pinctrl = dev_get_priv(dev); return pinctrl->data->funcs[selector].desc.name; } static int airoha_pinmux_group_set(struct udevice *dev, unsigned int group_selector, unsigned int func_selector) { struct airoha_pinctrl *pinctrl = dev_get_priv(dev); dev_dbg(dev, "enabling %s function for pin group %s\n", airoha_get_func_name(dev, func_selector), airoha_get_group_name(dev, group_selector)); return airoha_pinmux_set_mux(pinctrl, func_selector, group_selector); } static int airoha_pinmux_set(struct udevice *dev, unsigned int pin_selector, unsigned int func_selector) { struct airoha_pinctrl *pinctrl = dev_get_priv(dev); const struct airoha_pinctrl_match_data *data = pinctrl->data; const char *pin_name; unsigned int selector; pin_name = data->pins[pin_selector].name; /* find group matching the pin_name */ for (selector = 0; selector < data->num_grps; selector++) { if (!strcmp(pin_name, data->grps[selector].name)) return airoha_pinmux_group_set(dev, selector, func_selector); } return -ENOENT; } static const struct pinconf_param airoha_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 }, { "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 0 }, { "drive-open-drain", PIN_CONFIG_DRIVE_OPEN_DRAIN, 0 }, { "output-enable", PIN_CONFIG_OUTPUT_ENABLE, 1 }, { "input-enable", PIN_CONFIG_INPUT_ENABLE, 1 }, }; static const char *airoha_pinconf_param_name(unsigned int param) { for (int i = 0; i < ARRAY_SIZE(airoha_pinconf_params); i++) { if (airoha_pinconf_params[i].param == param) return airoha_pinconf_params[i].property; } return NULL; } static int airoha_pinconf_set_handler(struct udevice *dev, unsigned pin_selector, unsigned int param, unsigned int argument) { struct airoha_pinctrl *pinctrl = dev_get_priv(dev); unsigned long configs[1] = { pinconf_to_config_packed(param, argument) }; unsigned int pin = pinctrl->data->pins[pin_selector].number; dev_dbg(dev, "enabling %s=%d property for pin %s\n", airoha_pinconf_param_name(param), argument, airoha_get_pin_name(dev, pin_selector)); return airoha_pinconf_set(pinctrl, pin, configs, ARRAY_SIZE(configs)); } static int airoha_pinconf_group_set_handler(struct udevice *dev, unsigned int group_selector, unsigned int param, unsigned int argument) { struct airoha_pinctrl *pinctrl = dev_get_priv(dev); unsigned long configs[1] = { pinconf_to_config_packed(param, argument) }; dev_dbg(dev, "enabling %s=%d property for pin group %s\n", airoha_pinconf_param_name(param), argument, airoha_get_group_name(dev, group_selector)); return airoha_pinconf_group_set(pinctrl, group_selector, configs, ARRAY_SIZE(configs)); } static int airoha_get_pin_muxing(struct udevice *dev, unsigned int selector, char *buf, int size) { struct airoha_pinctrl *pinctrl = dev_get_priv(dev); struct airoha_pinctrl_match_data *data = pinctrl->data; const char *name, *type; int ret, gpio, found = 0; unsigned long config; unsigned int param, pin; u32 val; pin = data->pins[selector].number; for (int i = 0; i < data->num_grps; i++) { if (!pin_in_group(pin, &data->grps[i])) continue; name = data->grps[i].name; for (int j = 0; j < data->num_funcs; j++) { if (!func_grp_active(pinctrl, &data->funcs[j], name)) continue; ret = scnprintf(buf, size, "%s(%s)", data->funcs[j].desc.name, name); if (ret < 0) return -ENOSPC; found = 1; buf += ret; size -= ret; break; } if (found) break; } if (!found) { gpio = pin_to_gpio(pinctrl, pin); if (gpio < 0) { /* * WARNING: non-gpio pin with unknown function. * * This should not have happened, the function group * tables are incomplete. Please fix ASAP. */ ret = scnprintf(buf, size, "default"); } else { /* assume gpio */ val = airoha_gpio_get(pinctrl, gpio); switch (airoha_gpio_get_direction(pinctrl, gpio)) { case GPIOF_INPUT: type = "input"; break; case GPIOF_OUTPUT: type = "output"; break; default: type = "unknown"; break; }; ret = scnprintf(buf, size, "gpio%d, %s(%d)", gpio, type, val); } if (ret < 0) return -ENOSPC; buf += ret; size -= ret; } for (int i = 0; i < ARRAY_SIZE(airoha_pinconf_params); i++) { param = airoha_pinconf_params[i].param; config = pinconf_to_config_packed(param, 0); ret = airoha_pinconf_get(pinctrl, pin, &config); if (ret < 0) continue; name = airoha_pinconf_params[i].property; switch (param) { case PIN_CONFIG_BIAS_DISABLE: case PIN_CONFIG_BIAS_PULL_UP: case PIN_CONFIG_BIAS_PULL_DOWN: ret = scnprintf(buf, size, ", %s", name); break; case PIN_CONFIG_DRIVE_STRENGTH: case PIN_CONFIG_DRIVE_OPEN_DRAIN: val = pinconf_to_config_argument(config); ret = scnprintf(buf, size, ", %s(%d)", name, val); break; default: break; } if (ret < 0) return -ENOSPC; buf += ret; size -= ret; } return 0; } const struct pinctrl_ops airoha_pinctrl_ops = { .get_pins_count = airoha_get_pins_count, .get_pin_name = airoha_get_pin_name, .get_groups_count = airoha_get_groups_count, .get_group_name = airoha_get_group_name, .get_functions_count = airoha_get_funcs_count, .get_function_name = airoha_get_func_name, .pinmux_set = airoha_pinmux_set, .pinmux_group_set = airoha_pinmux_group_set, .pinconf_num_params = ARRAY_SIZE(airoha_pinconf_params), .pinconf_params = airoha_pinconf_params, .pinconf_set = airoha_pinconf_set_handler, .pinconf_group_set = airoha_pinconf_group_set_handler, .set_state = pinctrl_generic_set_state, .get_pin_muxing = airoha_get_pin_muxing, }; int airoha_pinctrl_probe(struct udevice *dev) { struct airoha_pinctrl *pinctrl = dev_get_priv(dev); pinctrl->dev = dev; pinctrl->data = (struct airoha_pinctrl_match_data *)dev_get_driver_data(dev); pinctrl->regmap = syscon_node_to_regmap(dev_ofnode(dev->parent)); if (IS_ERR(pinctrl->regmap)) return PTR_ERR(pinctrl->regmap); pinctrl->chip_scu = airoha_get_chip_scu_regmap(); if (IS_ERR(pinctrl->chip_scu)) return PTR_ERR(pinctrl->chip_scu); pinctrl->gpiochip.data = gpio_data_regs; pinctrl->gpiochip.dir = gpio_dir_regs; pinctrl->gpiochip.out = gpio_out_regs; pinctrl->gpiochip.status = irq_status_regs; pinctrl->gpiochip.level = irq_level_regs; pinctrl->gpiochip.edge = irq_edge_regs; return 0; } int airoha_pinctrl_bind(struct udevice *dev) { if (airoha_pinctrl_gpio_register(dev)) debug("Warning: can't bind gpio driver with device node\n"); /* * Make sure that the pinctrl driver gets probed after binding, * otherwise GPIO interface driver will not be probed as well. * GPIOs of non-probed driver can't be used. */ dev_or_flags(dev, DM_FLAG_PROBE_AFTER_BIND); return 0; }