// SPDX-License-Identifier: GPL-2.0-only /* * Author: Lorenzo Bianconi * Author: Benjamin Larsson * Author: Markus Gothe */ #include "airoha-common.h" /* GPIOs */ #define REG_GPIO_CTRL 0x0000 #define REG_GPIO_DATA 0x0004 #define REG_GPIO_INT 0x0008 #define REG_GPIO_INT_EDGE 0x000c #define REG_GPIO_INT_LEVEL 0x0010 #define REG_GPIO_OE 0x0014 #define REG_GPIO_CTRL1 0x0020 #define REG_GPIO_CTRL2 0x0060 #define REG_GPIO_CTRL3 0x0064 #define REG_GPIO_DATA1 0x0070 #define REG_GPIO_OE1 0x0078 #define REG_GPIO_INT1 0x007c #define REG_GPIO_INT_EDGE1 0x0080 #define REG_GPIO_INT_EDGE2 0x0084 #define REG_GPIO_INT_EDGE3 0x0088 #define REG_GPIO_INT_LEVEL1 0x008c #define REG_GPIO_INT_LEVEL2 0x0090 #define REG_GPIO_INT_LEVEL3 0x0094 #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 struct airoha_gpiochip_regs airoha_gpiochip_regs = { .data = gpio_data_regs, .dir = gpio_dir_regs, .out = gpio_out_regs, .status = irq_status_regs, .level = irq_level_regs, .edge = irq_edge_regs, }; static int airoha_convert_pin_to_reg_offset(struct pinctrl_dev *pctrl_dev, struct pinctrl_gpio_range *range, int pin) { if (!range) range = pinctrl_find_gpio_range_from_pin_nolock(pctrl_dev, pin); if (!range) return -EINVAL; return pin - range->pin_base; } /* gpio callbacks */ static int airoha_gpio_set(struct gpio_chip *chip, unsigned int gpio, int value) { struct airoha_pinctrl *pinctrl = gpiochip_get_data(chip); u32 offset = gpio % AIROHA_PIN_BANK_SIZE; u8 index = gpio / AIROHA_PIN_BANK_SIZE; return regmap_update_bits(pinctrl->regmap, pinctrl->gpio_regs->data[index], BIT(offset), value ? BIT(offset) : 0); } static int airoha_gpio_get(struct gpio_chip *chip, unsigned int gpio) { struct airoha_pinctrl *pinctrl = gpiochip_get_data(chip); u32 val, pin = gpio % AIROHA_PIN_BANK_SIZE; u8 index = gpio / AIROHA_PIN_BANK_SIZE; int err; err = regmap_read(pinctrl->regmap, pinctrl->gpio_regs->data[index], &val); return err ? err : !!(val & BIT(pin)); } static int airoha_gpio_get_direction(struct gpio_chip *chip, unsigned int gpio) { struct airoha_pinctrl *pinctrl = gpiochip_get_data(chip); u32 val, mask; u8 index; int err; index = gpio / AIROHA_REG_GPIOCTRL_NUM_PIN; err = regmap_read(pinctrl->regmap, pinctrl->gpio_regs->dir[index], &val); if (err) return err; mask = BIT(2 * (gpio % AIROHA_REG_GPIOCTRL_NUM_PIN)); return val & mask ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; } static int airoha_gpio_set_direction(struct gpio_chip *chip, unsigned int gpio, bool input) { struct airoha_pinctrl *pinctrl = gpiochip_get_data(chip); u32 mask, index; int err; /* set output enable */ mask = BIT(gpio % AIROHA_PIN_BANK_SIZE); index = gpio / AIROHA_PIN_BANK_SIZE; err = regmap_update_bits(pinctrl->regmap, pinctrl->gpio_regs->out[index], mask, !input ? mask : 0); if (err) return err; /* set direction */ mask = BIT(2 * (gpio % AIROHA_REG_GPIOCTRL_NUM_PIN)); index = gpio / AIROHA_REG_GPIOCTRL_NUM_PIN; return regmap_update_bits(pinctrl->regmap, pinctrl->gpio_regs->dir[index], mask, !input ? mask : 0); } static int airoha_gpio_direction_input(struct gpio_chip *chip, unsigned int gpio) { return airoha_gpio_set_direction(chip, gpio, true); } static int airoha_gpio_direction_output(struct gpio_chip *chip, unsigned int gpio, int value) { int err; err = airoha_gpio_set_direction(chip, gpio, false); if (err) return err; return airoha_gpio_set(chip, gpio, value); } /* irq callbacks */ static void airoha_irq_unmask(struct irq_data *data) { struct gpio_chip *gc = irq_data_get_irq_chip_data(data); struct airoha_pinctrl *pinctrl = gpiochip_get_data(gc); struct airoha_gpiochip_regs *gpio_regs = pinctrl->gpio_regs; u8 offset = data->hwirq % AIROHA_REG_GPIOCTRL_NUM_PIN; u8 index = data->hwirq / AIROHA_REG_GPIOCTRL_NUM_PIN; u32 mask = GENMASK(2 * offset + 1, 2 * offset); u32 val = BIT(2 * offset); if (WARN_ON_ONCE(data->hwirq >= AIROHA_NUM_PINS)) return; gpiochip_enable_irq(gc, irqd_to_hwirq(data)); switch (irqd_get_trigger_type(data)) { case IRQ_TYPE_LEVEL_LOW: val = val << 1; fallthrough; case IRQ_TYPE_LEVEL_HIGH: regmap_update_bits(pinctrl->regmap, gpio_regs->level[index], mask, val); break; case IRQ_TYPE_EDGE_FALLING: val = val << 1; fallthrough; case IRQ_TYPE_EDGE_RISING: regmap_update_bits(pinctrl->regmap, gpio_regs->edge[index], mask, val); break; case IRQ_TYPE_EDGE_BOTH: regmap_set_bits(pinctrl->regmap, gpio_regs->edge[index], mask); break; default: break; } } static void airoha_irq_mask(struct irq_data *data) { struct gpio_chip *gc = irq_data_get_irq_chip_data(data); struct airoha_pinctrl *pinctrl = gpiochip_get_data(gc); struct airoha_gpiochip_regs *gpio_regs = pinctrl->gpio_regs; u8 offset = data->hwirq % AIROHA_REG_GPIOCTRL_NUM_PIN; u8 index = data->hwirq / AIROHA_REG_GPIOCTRL_NUM_PIN; u32 mask = GENMASK(2 * offset + 1, 2 * offset); if (data->hwirq >= AIROHA_NUM_PINS) return; regmap_clear_bits(pinctrl->regmap, gpio_regs->level[index], mask); regmap_clear_bits(pinctrl->regmap, gpio_regs->edge[index], mask); gpiochip_disable_irq(gc, irqd_to_hwirq(data)); } static void airoha_irq_ack(struct irq_data *data) { struct gpio_chip *gc = irq_data_get_irq_chip_data(data); struct airoha_pinctrl *pinctrl = gpiochip_get_data(gc); struct airoha_gpiochip_regs *gpio_regs = pinctrl->gpio_regs; u8 offset = data->hwirq % AIROHA_PIN_BANK_SIZE; u8 index = data->hwirq / AIROHA_PIN_BANK_SIZE; if (data->hwirq >= AIROHA_NUM_PINS) return; regmap_write(pinctrl->regmap, gpio_regs->status[index], BIT(offset)); } static int airoha_irq_type(struct irq_data *data, unsigned int type) { if (data->hwirq >= AIROHA_NUM_PINS) return -EINVAL; if (type == IRQ_TYPE_NONE) { irqd_set_trigger_type(data, type); irq_set_handler_locked(data, handle_bad_irq); return 0; } if (type == IRQ_TYPE_PROBE) { if (irqd_get_trigger_type(data)) return 0; type = IRQ_TYPE_EDGE_BOTH; } irqd_set_trigger_type(data, type); if (type & IRQ_TYPE_EDGE_BOTH) irq_set_handler_locked(data, handle_edge_irq); else irq_set_handler_locked(data, handle_level_irq); return 0; } static irqreturn_t airoha_irq_handler(int irq, void *data) { struct airoha_pinctrl *pinctrl = data; bool handled = false; int i; for (i = 0; i < ARRAY_SIZE(irq_status_regs); i++) { struct gpio_irq_chip *girq = &pinctrl->gpiochip.irq; u32 regmap; unsigned long status; int irq; if (regmap_read(pinctrl->regmap, pinctrl->gpio_regs->status[i], ®map)) continue; status = regmap; for_each_set_bit(irq, &status, AIROHA_PIN_BANK_SIZE) { u32 offset = irq + i * AIROHA_PIN_BANK_SIZE; generic_handle_domain_irq(girq->domain, offset); regmap_write(pinctrl->regmap, pinctrl->gpio_regs->status[i], BIT(irq)); } handled |= !!status; } return handled ? IRQ_HANDLED : IRQ_NONE; } static const struct irq_chip airoha_gpio_irq_chip = { .name = "airoha-gpio-irq", .irq_unmask = airoha_irq_unmask, .irq_mask = airoha_irq_mask, .irq_ack = airoha_irq_ack, .irq_set_type = airoha_irq_type, .flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_IMMUTABLE, GPIOCHIP_IRQ_RESOURCE_HELPERS, }; static int airoha_pinctrl_add_gpiochip(struct airoha_pinctrl *pinctrl, struct platform_device *pdev) { struct gpio_chip *gc = &pinctrl->gpiochip; struct gpio_irq_chip *girq = &gc->irq; struct device *dev = &pdev->dev; int irq, err; gc->parent = dev; gc->label = dev_name(dev); gc->request = gpiochip_generic_request; gc->free = gpiochip_generic_free; gc->direction_input = airoha_gpio_direction_input; gc->direction_output = airoha_gpio_direction_output; gc->get_direction = airoha_gpio_get_direction; gc->set = airoha_gpio_set; gc->get = airoha_gpio_get; gc->base = -1; gc->ngpio = AIROHA_NUM_PINS; girq->default_type = IRQ_TYPE_NONE; girq->handler = handle_bad_irq; gpio_irq_chip_set_chip(girq, &airoha_gpio_irq_chip); irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; err = devm_request_irq(dev, irq, airoha_irq_handler, IRQF_SHARED, dev_name(dev), pinctrl); if (err) return err; return devm_gpiochip_add_data(dev, gc, pinctrl); } /* pinmux callbacks */ static int airoha_pinmux_set_mux(struct pinctrl_dev *pctrl_dev, unsigned int selector, unsigned int group) { struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); const struct airoha_pinctrl_func *func; const struct function_desc *desc; struct group_desc *grp; int i; desc = pinmux_generic_get_function(pctrl_dev, selector); if (!desc) return -EINVAL; grp = pinctrl_generic_get_group(pctrl_dev, group); if (!grp) return -EINVAL; dev_dbg(pctrl_dev->dev, "enable function %s group %s\n", desc->func->name, grp->grp.name); func = desc->data; 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->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 pinctrl_dev *pctrl_dev, struct pinctrl_gpio_range *range, unsigned int p, bool input) { struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); int pin; pin = airoha_convert_pin_to_reg_offset(pctrl_dev, range, p); if (pin < 0) return pin; return airoha_gpio_set_direction(&pinctrl->gpiochip, pin, input); } static const struct pinmux_ops airoha_pmxops = { .get_functions_count = pinmux_generic_get_function_count, .get_function_name = pinmux_generic_get_function_name, .get_function_groups = pinmux_generic_get_function_groups, .gpio_set_direction = airoha_pinmux_set_direction, .set_mux = airoha_pinmux_set_mux, .strict = true, }; /* 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->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->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 pinctrl_dev *pctrl_dev, u32 p) { struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); int ret, pin; pin = airoha_convert_pin_to_reg_offset(pctrl_dev, NULL, p); if (pin < 0) return pin; ret = airoha_gpio_get_direction(&pinctrl->gpiochip, pin); if (ret < 0) return ret; return ret == GPIO_LINE_DIRECTION_OUT ? PIN_CONFIG_OUTPUT_ENABLE : PIN_CONFIG_INPUT_ENABLE; } static int airoha_pinconf_get(struct pinctrl_dev *pctrl_dev, unsigned int pin, unsigned long *config) { struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); 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(pctrl_dev, pin); if (arg != param) return -EINVAL; arg = 1; break; default: return -ENOTSUPP; } *config = pinconf_to_config_packed(param, arg); return 0; } static int airoha_pinconf_set_pin_value(struct pinctrl_dev *pctrl_dev, unsigned int p, bool value) { struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); int pin; pin = airoha_convert_pin_to_reg_offset(pctrl_dev, NULL, p); if (pin < 0) return pin; return airoha_gpio_set(&pinctrl->gpiochip, pin, value); } static int airoha_pinconf_set(struct pinctrl_dev *pctrl_dev, unsigned int pin, unsigned long *configs, unsigned int num_configs) { struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); 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_LEVEL: { bool input = param == PIN_CONFIG_INPUT_ENABLE; err = airoha_pinmux_set_direction(pctrl_dev, NULL, pin, input); if (err) return err; if (param == PIN_CONFIG_LEVEL) { err = airoha_pinconf_set_pin_value(pctrl_dev, pin, !!arg); if (err) return err; } break; } default: return -ENOTSUPP; } } return 0; } static int airoha_pinconf_group_get(struct pinctrl_dev *pctrl_dev, unsigned int group, unsigned long *config) { struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); unsigned long cur_config = 0; int i; for (i = 0; i < pinctrl->grps[group].npins; i++) { if (airoha_pinconf_get(pctrl_dev, pinctrl->grps[group].pins[i], config)) return -ENOTSUPP; if (i && cur_config != *config) return -ENOTSUPP; cur_config = *config; } return 0; } static int airoha_pinconf_group_set(struct pinctrl_dev *pctrl_dev, unsigned int group, unsigned long *configs, unsigned int num_configs) { struct airoha_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); int i; for (i = 0; i < pinctrl->grps[group].npins; i++) { int err; err = airoha_pinconf_set(pctrl_dev, pinctrl->grps[group].pins[i], configs, num_configs); if (err) return err; } return 0; } static const struct pinconf_ops airoha_confops = { .is_generic = true, .pin_config_get = airoha_pinconf_get, .pin_config_set = airoha_pinconf_set, .pin_config_group_get = airoha_pinconf_group_get, .pin_config_group_set = airoha_pinconf_group_set, .pin_config_config_dbg_show = pinconf_generic_dump_config, }; static const struct pinctrl_ops airoha_pctlops = { .get_groups_count = pinctrl_generic_get_group_count, .get_group_name = pinctrl_generic_get_group_name, .get_group_pins = pinctrl_generic_get_group_pins, .dt_node_to_map = pinconf_generic_dt_node_to_map_all, .dt_free_map = pinconf_generic_dt_free_map, }; int airoha_pinctrl_probe(struct platform_device *pdev) { const struct airoha_pinctrl_match_data *data; struct device *dev = &pdev->dev; struct airoha_pinctrl *pinctrl; struct regmap *map; int err, i; data = device_get_match_data(dev); if (!data) return -EINVAL; pinctrl = devm_kzalloc(dev, sizeof(*pinctrl), GFP_KERNEL); if (!pinctrl) return -ENOMEM; pinctrl->regmap = device_node_to_regmap(dev->parent->of_node); if (IS_ERR(pinctrl->regmap)) return PTR_ERR(pinctrl->regmap); map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "airoha,chip-scu"); if (IS_ERR_OR_NULL(map)) { map = syscon_regmap_lookup_by_compatible(data->chip_scu_compatible); if (IS_ERR(map)) return PTR_ERR(map); } pinctrl->chip_scu = map; /* Init pinctrl desc struct */ pinctrl->desc.name = data->pinctrl_name; pinctrl->desc.owner = data->pinctrl_owner; pinctrl->desc.pctlops = &airoha_pctlops; pinctrl->desc.pmxops = &airoha_pmxops; pinctrl->desc.confops = &airoha_confops; pinctrl->desc.pins = data->pins; pinctrl->desc.npins = data->num_pins; pinctrl->gpio_regs = &airoha_gpiochip_regs; err = devm_pinctrl_register_and_init(dev, &pinctrl->desc, pinctrl, &pinctrl->ctrl); if (err) return err; /* build pin groups */ for (i = 0; i < data->num_grps; i++) { const struct pingroup *grp = &data->grps[i]; err = pinctrl_generic_add_group(pinctrl->ctrl, grp->name, grp->pins, grp->npins, (void *)grp); if (err < 0) { dev_err(&pdev->dev, "Failed to register group %s\n", grp->name); return err; } } /* build functions */ for (i = 0; i < data->num_funcs; i++) { const struct airoha_pinctrl_func *func; func = &data->funcs[i]; err = pinmux_generic_add_pinfunction(pinctrl->ctrl, &func->desc, (void *)func); if (err < 0) { dev_err(dev, "Failed to register function %s\n", func->desc.name); return err; } } pinctrl->grps = data->grps; pinctrl->funcs = data->funcs; pinctrl->confs_info = data->confs_info; err = pinctrl_enable(pinctrl->ctrl); if (err) return err; /* build gpio-chip */ return airoha_pinctrl_add_gpiochip(pinctrl, pdev); } EXPORT_SYMBOL_GPL(airoha_pinctrl_probe); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Lorenzo Bianconi "); MODULE_AUTHOR("Benjamin Larsson "); MODULE_AUTHOR("Markus Gothe "); MODULE_DESCRIPTION("Pinctrl common driver for Airoha SoC");