diff options
Diffstat (limited to 'drivers/pinctrl/airoha/pinctrl-airoha.c')
| -rw-r--r-- | drivers/pinctrl/airoha/pinctrl-airoha.c | 865 |
1 files changed, 865 insertions, 0 deletions
diff --git a/drivers/pinctrl/airoha/pinctrl-airoha.c b/drivers/pinctrl/airoha/pinctrl-airoha.c new file mode 100644 index 000000000..f505a3f69 --- /dev/null +++ b/drivers/pinctrl/airoha/pinctrl-airoha.c @@ -0,0 +1,865 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Author: Lorenzo Bianconi <lorenzo@kernel.org> + * Author: Benjamin Larsson <benjamin.larsson@genexis.eu> + * Author: Markus Gothe <markus.gothe@genexis.eu> + */ + +#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 <lorenzo@kernel.org>"); +MODULE_AUTHOR("Benjamin Larsson <benjamin.larsson@genexis.eu>"); +MODULE_AUTHOR("Markus Gothe <markus.gothe@genexis.eu>"); +MODULE_DESCRIPTION("Pinctrl common driver for Airoha SoC"); |
