Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Rahul Tanwar | 4730 | 96.81% | 3 | 23.08% |
Andy Shevchenko | 94 | 1.92% | 6 | 46.15% |
Linus Walleij | 52 | 1.06% | 1 | 7.69% |
Bixuan Cui | 7 | 0.14% | 1 | 7.69% |
Peng Fan | 2 | 0.04% | 1 | 7.69% |
Marc Zyngier | 1 | 0.02% | 1 | 7.69% |
Total | 4886 | 13 |
// SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2019 Intel Corporation */ #include <linux/gpio/driver.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinconf.h> #include <linux/pinctrl/pinconf-generic.h> #include <linux/pinctrl/pinmux.h> #include <linux/platform_device.h> #include <linux/property.h> #include "core.h" #include "pinconf.h" #include "pinmux.h" #include "pinctrl-equilibrium.h" #define PIN_NAME_FMT "io-%d" #define PIN_NAME_LEN 10 #define PAD_REG_OFF 0x100 static void eqbr_gpio_disable_irq(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct eqbr_gpio_ctrl *gctrl = gpiochip_get_data(gc); unsigned int offset = irqd_to_hwirq(d); unsigned long flags; raw_spin_lock_irqsave(&gctrl->lock, flags); writel(BIT(offset), gctrl->membase + GPIO_IRNENCLR); raw_spin_unlock_irqrestore(&gctrl->lock, flags); gpiochip_disable_irq(gc, offset); } static void eqbr_gpio_enable_irq(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct eqbr_gpio_ctrl *gctrl = gpiochip_get_data(gc); unsigned int offset = irqd_to_hwirq(d); unsigned long flags; gc->direction_input(gc, offset); gpiochip_enable_irq(gc, offset); raw_spin_lock_irqsave(&gctrl->lock, flags); writel(BIT(offset), gctrl->membase + GPIO_IRNRNSET); raw_spin_unlock_irqrestore(&gctrl->lock, flags); } static void eqbr_gpio_ack_irq(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct eqbr_gpio_ctrl *gctrl = gpiochip_get_data(gc); unsigned int offset = irqd_to_hwirq(d); unsigned long flags; raw_spin_lock_irqsave(&gctrl->lock, flags); writel(BIT(offset), gctrl->membase + GPIO_IRNCR); raw_spin_unlock_irqrestore(&gctrl->lock, flags); } static void eqbr_gpio_mask_ack_irq(struct irq_data *d) { eqbr_gpio_disable_irq(d); eqbr_gpio_ack_irq(d); } static inline void eqbr_cfg_bit(void __iomem *addr, unsigned int offset, unsigned int set) { if (set) writel(readl(addr) | BIT(offset), addr); else writel(readl(addr) & ~BIT(offset), addr); } static int eqbr_irq_type_cfg(struct gpio_irq_type *type, struct eqbr_gpio_ctrl *gctrl, unsigned int offset) { unsigned long flags; raw_spin_lock_irqsave(&gctrl->lock, flags); eqbr_cfg_bit(gctrl->membase + GPIO_IRNCFG, offset, type->trig_type); eqbr_cfg_bit(gctrl->membase + GPIO_EXINTCR1, offset, type->trig_type); eqbr_cfg_bit(gctrl->membase + GPIO_EXINTCR0, offset, type->logic_type); raw_spin_unlock_irqrestore(&gctrl->lock, flags); return 0; } static int eqbr_gpio_set_irq_type(struct irq_data *d, unsigned int type) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct eqbr_gpio_ctrl *gctrl = gpiochip_get_data(gc); unsigned int offset = irqd_to_hwirq(d); struct gpio_irq_type it; memset(&it, 0, sizeof(it)); if ((type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_NONE) return 0; switch (type) { case IRQ_TYPE_EDGE_RISING: it.trig_type = GPIO_EDGE_TRIG; it.edge_type = GPIO_SINGLE_EDGE; it.logic_type = GPIO_POSITIVE_TRIG; break; case IRQ_TYPE_EDGE_FALLING: it.trig_type = GPIO_EDGE_TRIG; it.edge_type = GPIO_SINGLE_EDGE; it.logic_type = GPIO_NEGATIVE_TRIG; break; case IRQ_TYPE_EDGE_BOTH: it.trig_type = GPIO_EDGE_TRIG; it.edge_type = GPIO_BOTH_EDGE; it.logic_type = GPIO_POSITIVE_TRIG; break; case IRQ_TYPE_LEVEL_HIGH: it.trig_type = GPIO_LEVEL_TRIG; it.edge_type = GPIO_SINGLE_EDGE; it.logic_type = GPIO_POSITIVE_TRIG; break; case IRQ_TYPE_LEVEL_LOW: it.trig_type = GPIO_LEVEL_TRIG; it.edge_type = GPIO_SINGLE_EDGE; it.logic_type = GPIO_NEGATIVE_TRIG; break; default: return -EINVAL; } eqbr_irq_type_cfg(&it, gctrl, offset); if (it.trig_type == GPIO_EDGE_TRIG) irq_set_handler_locked(d, handle_edge_irq); else irq_set_handler_locked(d, handle_level_irq); return 0; } static void eqbr_irq_handler(struct irq_desc *desc) { struct gpio_chip *gc = irq_desc_get_handler_data(desc); struct eqbr_gpio_ctrl *gctrl = gpiochip_get_data(gc); struct irq_chip *ic = irq_desc_get_chip(desc); unsigned long pins, offset; chained_irq_enter(ic, desc); pins = readl(gctrl->membase + GPIO_IRNCR); for_each_set_bit(offset, &pins, gc->ngpio) generic_handle_domain_irq(gc->irq.domain, offset); chained_irq_exit(ic, desc); } static const struct irq_chip eqbr_irq_chip = { .name = "gpio_irq", .irq_mask = eqbr_gpio_disable_irq, .irq_unmask = eqbr_gpio_enable_irq, .irq_ack = eqbr_gpio_ack_irq, .irq_mask_ack = eqbr_gpio_mask_ack_irq, .irq_set_type = eqbr_gpio_set_irq_type, .flags = IRQCHIP_IMMUTABLE, GPIOCHIP_IRQ_RESOURCE_HELPERS, }; static int gpiochip_setup(struct device *dev, struct eqbr_gpio_ctrl *gctrl) { struct gpio_irq_chip *girq; struct gpio_chip *gc; gc = &gctrl->chip; gc->label = gctrl->name; gc->fwnode = gctrl->fwnode; if (!fwnode_property_read_bool(gctrl->fwnode, "interrupt-controller")) { dev_dbg(dev, "gc %s: doesn't act as interrupt controller!\n", gctrl->name); return 0; } girq = &gctrl->chip.irq; gpio_irq_chip_set_chip(girq, &eqbr_irq_chip); girq->parent_handler = eqbr_irq_handler; girq->num_parents = 1; girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents), GFP_KERNEL); if (!girq->parents) return -ENOMEM; girq->default_type = IRQ_TYPE_NONE; girq->handler = handle_bad_irq; girq->parents[0] = gctrl->virq; return 0; } static int gpiolib_reg(struct eqbr_pinctrl_drv_data *drvdata) { struct device *dev = drvdata->dev; struct eqbr_gpio_ctrl *gctrl; struct device_node *np; struct resource res; int i, ret; for (i = 0; i < drvdata->nr_gpio_ctrls; i++) { gctrl = drvdata->gpio_ctrls + i; np = to_of_node(gctrl->fwnode); gctrl->name = devm_kasprintf(dev, GFP_KERNEL, "gpiochip%d", i); if (!gctrl->name) return -ENOMEM; if (of_address_to_resource(np, 0, &res)) { dev_err(dev, "Failed to get GPIO register address\n"); return -ENXIO; } gctrl->membase = devm_ioremap_resource(dev, &res); if (IS_ERR(gctrl->membase)) return PTR_ERR(gctrl->membase); gctrl->virq = irq_of_parse_and_map(np, 0); if (!gctrl->virq) { dev_err(dev, "%s: failed to parse and map irq\n", gctrl->name); return -ENXIO; } raw_spin_lock_init(&gctrl->lock); ret = bgpio_init(&gctrl->chip, dev, gctrl->bank->nr_pins / 8, gctrl->membase + GPIO_IN, gctrl->membase + GPIO_OUTSET, gctrl->membase + GPIO_OUTCLR, gctrl->membase + GPIO_DIR, NULL, 0); if (ret) { dev_err(dev, "unable to init generic GPIO\n"); return ret; } ret = gpiochip_setup(dev, gctrl); if (ret) return ret; ret = devm_gpiochip_add_data(dev, &gctrl->chip, gctrl); if (ret) return ret; } return 0; } static inline struct eqbr_pin_bank *find_pinbank_via_pin(struct eqbr_pinctrl_drv_data *pctl, unsigned int pin) { struct eqbr_pin_bank *bank; int i; for (i = 0; i < pctl->nr_banks; i++) { bank = &pctl->pin_banks[i]; if (pin >= bank->pin_base && (pin - bank->pin_base) < bank->nr_pins) return bank; } return NULL; } static const struct pinctrl_ops eqbr_pctl_ops = { .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, }; static int eqbr_set_pin_mux(struct eqbr_pinctrl_drv_data *pctl, unsigned int pmx, unsigned int pin) { struct eqbr_pin_bank *bank; unsigned long flags; unsigned int offset; void __iomem *mem; bank = find_pinbank_via_pin(pctl, pin); if (!bank) { dev_err(pctl->dev, "Couldn't find pin bank for pin %u\n", pin); return -ENODEV; } mem = bank->membase; offset = pin - bank->pin_base; if (!(bank->aval_pinmap & BIT(offset))) { dev_err(pctl->dev, "PIN: %u is not valid, pinbase: %u, bitmap: %u\n", pin, bank->pin_base, bank->aval_pinmap); return -ENODEV; } raw_spin_lock_irqsave(&pctl->lock, flags); writel(pmx, mem + (offset * 4)); raw_spin_unlock_irqrestore(&pctl->lock, flags); return 0; } static int eqbr_pinmux_set_mux(struct pinctrl_dev *pctldev, unsigned int selector, unsigned int group) { struct eqbr_pinctrl_drv_data *pctl = pinctrl_dev_get_drvdata(pctldev); struct function_desc *func; struct group_desc *grp; unsigned int *pinmux; int i; func = pinmux_generic_get_function(pctldev, selector); if (!func) return -EINVAL; grp = pinctrl_generic_get_group(pctldev, group); if (!grp) return -EINVAL; pinmux = grp->data; for (i = 0; i < grp->grp.npins; i++) eqbr_set_pin_mux(pctl, pinmux[i], grp->grp.pins[i]); return 0; } static int eqbr_pinmux_gpio_request(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned int pin) { struct eqbr_pinctrl_drv_data *pctl = pinctrl_dev_get_drvdata(pctldev); return eqbr_set_pin_mux(pctl, EQBR_GPIO_MODE, pin); } static const struct pinmux_ops eqbr_pinmux_ops = { .get_functions_count = pinmux_generic_get_function_count, .get_function_name = pinmux_generic_get_function_name, .get_function_groups = pinmux_generic_get_function_groups, .set_mux = eqbr_pinmux_set_mux, .gpio_request_enable = eqbr_pinmux_gpio_request, .strict = true, }; static int get_drv_cur(void __iomem *mem, unsigned int offset) { unsigned int idx = offset / DRV_CUR_PINS; /* 0-15, 16-31 per register*/ unsigned int pin_offset = offset % DRV_CUR_PINS; return PARSE_DRV_CURRENT(readl(mem + REG_DRCC(idx)), pin_offset); } static struct eqbr_gpio_ctrl *get_gpio_ctrls_via_bank(struct eqbr_pinctrl_drv_data *pctl, struct eqbr_pin_bank *bank) { int i; for (i = 0; i < pctl->nr_gpio_ctrls; i++) { if (pctl->gpio_ctrls[i].bank == bank) return &pctl->gpio_ctrls[i]; } return NULL; } static int eqbr_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, unsigned long *config) { struct eqbr_pinctrl_drv_data *pctl = pinctrl_dev_get_drvdata(pctldev); enum pin_config_param param = pinconf_to_config_param(*config); struct eqbr_gpio_ctrl *gctrl; struct eqbr_pin_bank *bank; unsigned long flags; unsigned int offset; void __iomem *mem; u32 val; bank = find_pinbank_via_pin(pctl, pin); if (!bank) { dev_err(pctl->dev, "Couldn't find pin bank for pin %u\n", pin); return -ENODEV; } mem = bank->membase; offset = pin - bank->pin_base; if (!(bank->aval_pinmap & BIT(offset))) { dev_err(pctl->dev, "PIN: %u is not valid, pinbase: %u, bitmap: %u\n", pin, bank->pin_base, bank->aval_pinmap); return -ENODEV; } raw_spin_lock_irqsave(&pctl->lock, flags); switch (param) { case PIN_CONFIG_BIAS_PULL_UP: val = !!(readl(mem + REG_PUEN) & BIT(offset)); break; case PIN_CONFIG_BIAS_PULL_DOWN: val = !!(readl(mem + REG_PDEN) & BIT(offset)); break; case PIN_CONFIG_DRIVE_OPEN_DRAIN: val = !!(readl(mem + REG_OD) & BIT(offset)); break; case PIN_CONFIG_DRIVE_STRENGTH: val = get_drv_cur(mem, offset); break; case PIN_CONFIG_SLEW_RATE: val = !!(readl(mem + REG_SRC) & BIT(offset)); break; case PIN_CONFIG_OUTPUT_ENABLE: gctrl = get_gpio_ctrls_via_bank(pctl, bank); if (!gctrl) { dev_err(pctl->dev, "Failed to find gpio via bank pinbase: %u, pin: %u\n", bank->pin_base, pin); raw_spin_unlock_irqrestore(&pctl->lock, flags); return -ENODEV; } val = !!(readl(gctrl->membase + GPIO_DIR) & BIT(offset)); break; default: raw_spin_unlock_irqrestore(&pctl->lock, flags); return -ENOTSUPP; } raw_spin_unlock_irqrestore(&pctl->lock, flags); *config = pinconf_to_config_packed(param, val); ; return 0; } static int eqbr_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, unsigned long *configs, unsigned int num_configs) { struct eqbr_pinctrl_drv_data *pctl = pinctrl_dev_get_drvdata(pctldev); struct eqbr_gpio_ctrl *gctrl; enum pin_config_param param; struct eqbr_pin_bank *bank; unsigned int val, offset; struct gpio_chip *gc; unsigned long flags; void __iomem *mem; u32 regval, mask; int i; for (i = 0; i < num_configs; i++) { param = pinconf_to_config_param(configs[i]); val = pinconf_to_config_argument(configs[i]); bank = find_pinbank_via_pin(pctl, pin); if (!bank) { dev_err(pctl->dev, "Couldn't find pin bank for pin %u\n", pin); return -ENODEV; } mem = bank->membase; offset = pin - bank->pin_base; switch (param) { case PIN_CONFIG_BIAS_PULL_UP: mem += REG_PUEN; mask = BIT(offset); break; case PIN_CONFIG_BIAS_PULL_DOWN: mem += REG_PDEN; mask = BIT(offset); break; case PIN_CONFIG_DRIVE_OPEN_DRAIN: mem += REG_OD; mask = BIT(offset); break; case PIN_CONFIG_DRIVE_STRENGTH: mem += REG_DRCC(offset / DRV_CUR_PINS); offset = (offset % DRV_CUR_PINS) * 2; mask = GENMASK(1, 0) << offset; break; case PIN_CONFIG_SLEW_RATE: mem += REG_SRC; mask = BIT(offset); break; case PIN_CONFIG_OUTPUT_ENABLE: gctrl = get_gpio_ctrls_via_bank(pctl, bank); if (!gctrl) { dev_err(pctl->dev, "Failed to find gpio via bank pinbase: %u, pin: %u\n", bank->pin_base, pin); return -ENODEV; } gc = &gctrl->chip; gc->direction_output(gc, offset, 0); continue; default: return -ENOTSUPP; } raw_spin_lock_irqsave(&pctl->lock, flags); regval = readl(mem); regval = (regval & ~mask) | ((val << offset) & mask); writel(regval, mem); raw_spin_unlock_irqrestore(&pctl->lock, flags); } return 0; } static int eqbr_pinconf_group_get(struct pinctrl_dev *pctldev, unsigned int group, unsigned long *config) { unsigned int i, npins, old = 0; const unsigned int *pins; int ret; ret = pinctrl_generic_get_group_pins(pctldev, group, &pins, &npins); if (ret) return ret; for (i = 0; i < npins; i++) { if (eqbr_pinconf_get(pctldev, pins[i], config)) return -ENOTSUPP; if (i && old != *config) return -ENOTSUPP; old = *config; } return 0; } static int eqbr_pinconf_group_set(struct pinctrl_dev *pctldev, unsigned int group, unsigned long *configs, unsigned int num_configs) { const unsigned int *pins; unsigned int i, npins; int ret; ret = pinctrl_generic_get_group_pins(pctldev, group, &pins, &npins); if (ret) return ret; for (i = 0; i < npins; i++) { ret = eqbr_pinconf_set(pctldev, pins[i], configs, num_configs); if (ret) return ret; } return 0; } static const struct pinconf_ops eqbr_pinconf_ops = { .is_generic = true, .pin_config_get = eqbr_pinconf_get, .pin_config_set = eqbr_pinconf_set, .pin_config_group_get = eqbr_pinconf_group_get, .pin_config_group_set = eqbr_pinconf_group_set, .pin_config_config_dbg_show = pinconf_generic_dump_config, }; static bool is_func_exist(struct pinfunction *funcs, const char *name, unsigned int nr_funcs, unsigned int *idx) { int i; if (!funcs) return false; for (i = 0; i < nr_funcs; i++) { if (funcs[i].name && !strcmp(funcs[i].name, name)) { *idx = i; return true; } } return false; } static int funcs_utils(struct device *dev, struct pinfunction *funcs, unsigned int *nr_funcs, funcs_util_ops op) { struct device_node *node = dev->of_node; struct property *prop; const char *fn_name; const char **groups; unsigned int fid; int i, j; i = 0; for_each_child_of_node_scoped(node, np) { prop = of_find_property(np, "groups", NULL); if (!prop) continue; if (of_property_read_string(np, "function", &fn_name)) { /* some groups may not have function, it's OK */ dev_dbg(dev, "Group %s: not function binded!\n", (char *)prop->value); continue; } switch (op) { case OP_COUNT_NR_FUNCS: if (!is_func_exist(funcs, fn_name, *nr_funcs, &fid)) *nr_funcs = *nr_funcs + 1; break; case OP_ADD_FUNCS: if (!is_func_exist(funcs, fn_name, *nr_funcs, &fid)) funcs[i].name = fn_name; break; case OP_COUNT_NR_FUNC_GRPS: if (is_func_exist(funcs, fn_name, *nr_funcs, &fid)) funcs[fid].ngroups++; break; case OP_ADD_FUNC_GRPS: if (is_func_exist(funcs, fn_name, *nr_funcs, &fid)) { groups = (const char **)funcs[fid].groups; for (j = 0; j < funcs[fid].ngroups; j++) if (!groups[j]) break; groups[j] = prop->value; } break; default: return -EINVAL; } i++; } return 0; } static int eqbr_build_functions(struct eqbr_pinctrl_drv_data *drvdata) { struct device *dev = drvdata->dev; struct pinfunction *funcs = NULL; unsigned int nr_funcs = 0; int i, ret; ret = funcs_utils(dev, funcs, &nr_funcs, OP_COUNT_NR_FUNCS); if (ret) return ret; funcs = devm_kcalloc(dev, nr_funcs, sizeof(*funcs), GFP_KERNEL); if (!funcs) return -ENOMEM; ret = funcs_utils(dev, funcs, &nr_funcs, OP_ADD_FUNCS); if (ret) return ret; ret = funcs_utils(dev, funcs, &nr_funcs, OP_COUNT_NR_FUNC_GRPS); if (ret) return ret; for (i = 0; i < nr_funcs; i++) { if (!funcs[i].ngroups) continue; funcs[i].groups = devm_kcalloc(dev, funcs[i].ngroups, sizeof(*(funcs[i].groups)), GFP_KERNEL); if (!funcs[i].groups) return -ENOMEM; } ret = funcs_utils(dev, funcs, &nr_funcs, OP_ADD_FUNC_GRPS); if (ret) return ret; for (i = 0; i < nr_funcs; i++) { /* Ignore the same function with multiple groups */ if (funcs[i].name == NULL) continue; ret = pinmux_generic_add_function(drvdata->pctl_dev, funcs[i].name, funcs[i].groups, funcs[i].ngroups, drvdata); if (ret < 0) { dev_err(dev, "Failed to register function %s\n", funcs[i].name); return ret; } } return 0; } static int eqbr_build_groups(struct eqbr_pinctrl_drv_data *drvdata) { struct device *dev = drvdata->dev; struct device_node *node = dev->of_node; unsigned int *pins, *pinmux, pin_id, pinmux_id; struct pingroup group, *grp = &group; struct property *prop; int j, err; for_each_child_of_node_scoped(node, np) { prop = of_find_property(np, "groups", NULL); if (!prop) continue; err = of_property_count_u32_elems(np, "pins"); if (err < 0) { dev_err(dev, "No pins in the group: %s\n", prop->name); return err; } grp->npins = err; grp->name = prop->value; pins = devm_kcalloc(dev, grp->npins, sizeof(*pins), GFP_KERNEL); if (!pins) return -ENOMEM; grp->pins = pins; pinmux = devm_kcalloc(dev, grp->npins, sizeof(*pinmux), GFP_KERNEL); if (!pinmux) return -ENOMEM; for (j = 0; j < grp->npins; j++) { if (of_property_read_u32_index(np, "pins", j, &pin_id)) { dev_err(dev, "Group %s: Read intel pins id failed\n", grp->name); return -EINVAL; } if (pin_id >= drvdata->pctl_desc.npins) { dev_err(dev, "Group %s: Invalid pin ID, idx: %d, pin %u\n", grp->name, j, pin_id); return -EINVAL; } pins[j] = pin_id; if (of_property_read_u32_index(np, "pinmux", j, &pinmux_id)) { dev_err(dev, "Group %s: Read intel pinmux id failed\n", grp->name); return -EINVAL; } pinmux[j] = pinmux_id; } err = pinctrl_generic_add_group(drvdata->pctl_dev, grp->name, grp->pins, grp->npins, pinmux); if (err < 0) { dev_err(dev, "Failed to register group %s\n", grp->name); return err; } memset(&group, 0, sizeof(group)); pinmux = NULL; } return 0; } static int pinctrl_reg(struct eqbr_pinctrl_drv_data *drvdata) { struct pinctrl_desc *pctl_desc; struct pinctrl_pin_desc *pdesc; struct device *dev; unsigned int nr_pins; char *pin_names; int i, ret; dev = drvdata->dev; pctl_desc = &drvdata->pctl_desc; pctl_desc->name = "eqbr-pinctrl"; pctl_desc->owner = THIS_MODULE; pctl_desc->pctlops = &eqbr_pctl_ops; pctl_desc->pmxops = &eqbr_pinmux_ops; pctl_desc->confops = &eqbr_pinconf_ops; raw_spin_lock_init(&drvdata->lock); for (i = 0, nr_pins = 0; i < drvdata->nr_banks; i++) nr_pins += drvdata->pin_banks[i].nr_pins; pdesc = devm_kcalloc(dev, nr_pins, sizeof(*pdesc), GFP_KERNEL); if (!pdesc) return -ENOMEM; pin_names = devm_kcalloc(dev, nr_pins, PIN_NAME_LEN, GFP_KERNEL); if (!pin_names) return -ENOMEM; for (i = 0; i < nr_pins; i++) { sprintf(pin_names, PIN_NAME_FMT, i); pdesc[i].number = i; pdesc[i].name = pin_names; pin_names += PIN_NAME_LEN; } pctl_desc->pins = pdesc; pctl_desc->npins = nr_pins; dev_dbg(dev, "pinctrl total pin number: %u\n", nr_pins); ret = devm_pinctrl_register_and_init(dev, pctl_desc, drvdata, &drvdata->pctl_dev); if (ret) return ret; ret = eqbr_build_groups(drvdata); if (ret) { dev_err(dev, "Failed to build groups\n"); return ret; } ret = eqbr_build_functions(drvdata); if (ret) { dev_err(dev, "Failed to build functions\n"); return ret; } return pinctrl_enable(drvdata->pctl_dev); } static int pinbank_init(struct device_node *np, struct eqbr_pinctrl_drv_data *drvdata, struct eqbr_pin_bank *bank, unsigned int id) { struct device *dev = drvdata->dev; struct of_phandle_args spec; int ret; bank->membase = drvdata->membase + id * PAD_REG_OFF; ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 0, &spec); if (ret) { dev_err(dev, "gpio-range not available!\n"); return ret; } bank->pin_base = spec.args[1]; bank->nr_pins = spec.args[2]; bank->aval_pinmap = readl(bank->membase + REG_AVAIL); bank->id = id; dev_dbg(dev, "pinbank id: %d, reg: %px, pinbase: %u, pin number: %u, pinmap: 0x%x\n", id, bank->membase, bank->pin_base, bank->nr_pins, bank->aval_pinmap); return ret; } static int pinbank_probe(struct eqbr_pinctrl_drv_data *drvdata) { struct device *dev = drvdata->dev; struct device_node *np_gpio; struct eqbr_gpio_ctrl *gctrls; struct eqbr_pin_bank *banks; int i, nr_gpio; /* Count gpio bank number */ nr_gpio = 0; for_each_node_by_name(np_gpio, "gpio") { if (of_device_is_available(np_gpio)) nr_gpio++; } if (!nr_gpio) { dev_err(dev, "NO pin bank available!\n"); return -ENODEV; } /* Count pin bank number and gpio controller number */ banks = devm_kcalloc(dev, nr_gpio, sizeof(*banks), GFP_KERNEL); if (!banks) return -ENOMEM; gctrls = devm_kcalloc(dev, nr_gpio, sizeof(*gctrls), GFP_KERNEL); if (!gctrls) return -ENOMEM; dev_dbg(dev, "found %d gpio controller!\n", nr_gpio); /* Initialize Pin bank */ i = 0; for_each_node_by_name(np_gpio, "gpio") { if (!of_device_is_available(np_gpio)) continue; pinbank_init(np_gpio, drvdata, banks + i, i); gctrls[i].fwnode = of_fwnode_handle(np_gpio); gctrls[i].bank = banks + i; i++; } drvdata->pin_banks = banks; drvdata->nr_banks = nr_gpio; drvdata->gpio_ctrls = gctrls; drvdata->nr_gpio_ctrls = nr_gpio; return 0; } static int eqbr_pinctrl_probe(struct platform_device *pdev) { struct eqbr_pinctrl_drv_data *drvdata; struct device *dev = &pdev->dev; int ret; drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; drvdata->dev = dev; drvdata->membase = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(drvdata->membase)) return PTR_ERR(drvdata->membase); ret = pinbank_probe(drvdata); if (ret) return ret; ret = pinctrl_reg(drvdata); if (ret) return ret; ret = gpiolib_reg(drvdata); if (ret) return ret; platform_set_drvdata(pdev, drvdata); return 0; } static const struct of_device_id eqbr_pinctrl_dt_match[] = { { .compatible = "intel,lgm-io" }, {} }; MODULE_DEVICE_TABLE(of, eqbr_pinctrl_dt_match); static struct platform_driver eqbr_pinctrl_driver = { .probe = eqbr_pinctrl_probe, .driver = { .name = "eqbr-pinctrl", .of_match_table = eqbr_pinctrl_dt_match, }, }; module_platform_driver(eqbr_pinctrl_driver); MODULE_AUTHOR("Zhu Yixin <yixin.zhu@intel.com>, Rahul Tanwar <rahul.tanwar@intel.com>"); MODULE_DESCRIPTION("Pinctrl Driver for LGM SoC (Equilibrium)"); MODULE_LICENSE("GPL v2");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1