// 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");