On Thu, Sep 12, 2019 at 03:59:10PM +0800, Rahul Tanwar wrote:
> Intel Lightning Mountain SoC has a pinmux controller & GPIO controller IP
> which controls pin multiplexing & configuration including GPIO functions
> selection & GPIO attributes configuration. Add GPIO & pin control framework
> based driver for this IP.

Can you put few more words to explain how new this IP, and why it requires a
complete separate driver?

> +#define PIN_NAME_FMT "io-%d"
> +#define PIN_NAME_LEN 10
> +#define PAD_REG_OFF  0x100
> +
> +static const struct pin_config pin_cfg_type[] = {
> +     {"intel,pullup",                PINCONF_TYPE_PULL_UP},
> +     {"intel,pulldown",              PINCONF_TYPE_PULL_DOWN},
> +     {"intel,drive-current",         PINCONF_TYPE_DRIVE_CURRENT},
> +     {"intel,slew-rate",             PINCONF_TYPE_SLEW_RATE},
> +     {"intel,open-drain",            PINCONF_TYPE_OPEN_DRAIN},
> +     {"intel,output",                PINCONF_TYPE_OUTPUT},
> +};

Doesn't DT provide a generic naming scheme for these?

> +     val = readl(addr) & ~(mask << offset);
> +     writel(val | ((set & mask) << offset), addr);

More traditional pattern is

        val = readl(...);
        val = (val & ~mask) | (newval & mask);
        writel(val, ...);

> +     virq = irq_find_mapping(desc->irq_domain, offset);
> +     if (virq)
> +             return virq;

> +     else

Redundant.

> +             return irq_create_mapping(desc->irq_domain, offset);

Don't we have more clever helper for this? AFAIR something like this is done in
IRQ framework when you get a mapping from certain domain.


> +     gc->base                = -1; /* desc->bank->pin_base; */

Useless comment.

> +     ret = devm_gpiochip_add_data(dev, gc, desc);
> +     if (ret)
> +             dev_err(dev, "failed to register gpiochip: %s, err: %d\n",
> +                     gc->label, ret);
> +
> +     return ret;

Simple
        return devm_gpiochip_add_data(...);
would be enough.

> +             writel(readl(addr) & (~BIT(offset)), addr);

Too many parentheses.

> +static int eqbr_gpio_irq_req_res(struct irq_data *d)
> +{
> +     struct intel_gpio_desc *desc = irq_data_get_irq_chip_data(d);
> +     unsigned int offset;
> +     int ret;
> +
> +     offset = irqd_to_hwirq(d);
> +
> +     /* gpio must be set as input */
> +     intel_eqbr_gpio_dir_input(&desc->chip, offset);
> +     ret = gpiochip_lock_as_irq(&desc->chip, offset);
> +     if (ret) {
> +             pr_err("%s: Failed to lock gpio %u as irq!\n",
> +                    desc->name, offset);
> +             return ret;
> +     }

Above is pretty much generic one...

> +     eqbr_gpio_enable_irq(d);

...and this may go to unmask() / enable() callback.

No?

> +     return 0;
> +}

> +static void eqbr_gpio_irq_rel_res(struct irq_data *d)
> +{
> +     struct intel_gpio_desc *desc = irq_data_get_irq_chip_data(d);
> +     unsigned int offset = irqd_to_hwirq(d);
> +
> +     eqbr_gpio_disable_irq(d);
> +     gpiochip_unlock_as_irq(&desc->chip, offset);
> +}

Ditto.

> +static void eqbr_irq_handler(struct irq_desc *desc)
> +{
> +     struct intel_gpio_desc *gc;
> +     struct irq_chip *ic;
> +     u32 pins, offset;
> +     unsigned int virq;
> +
> +     gc = irq_desc_get_handler_data(desc);
> +     ic = irq_desc_get_chip(desc);
> +
> +     chained_irq_enter(ic, desc);
> +     pins = readl(gc->membase + GPIO_IRNCR);
> +
> +     for_each_set_bit(offset, (unsigned long *)&pins, gc->bank->nr_pins) {
> +             virq = irq_linear_revmap(gc->irq_domain, offset);
> +             if (!virq)

> +                     pr_err("gc[%s]:pin:%d irq not registered!\n",
> +                            gc->name, offset);

dev_err() ? But Why is it needed? Shouldn't be registered as a spurious IRQ for
later debugging?

> +             else
> +                     generic_handle_irq(virq);
> +     }
> +     chained_irq_exit(ic, desc);
> +}

> +static int gpiolib_reg(struct intel_pinctrl_drv_data *drvdata)
> +{
> +     struct device_node *np;
> +     struct intel_gpio_desc *desc;
> +     struct device *dev;
> +     int i, ret;
> +     char name[32];
> +     struct resource res;
> +
> +     dev = drvdata->dev;
> +     for (i = 0; i < drvdata->nr_gpio_descs; i++) {
> +             desc = drvdata->gpio_desc + i;
> +             np = desc->node;

> +             sprintf(name, "gpiochip%d", i);
> +             desc->name = devm_kmemdup(dev, name,
> +                                       strlen(name) + 1, GFP_KERNEL);

Have you run coccinelle scripts against this code?
Above is copy'n'paste of devm_kasprintf().

> +             if (!desc->name)
> +                     return -ENOMEM;

> +             if (of_address_to_resource(np, 0, &res)) {
> +                     dev_err(dev, "Failed to get GPIO register addrss\n");
> +                     return -ENXIO;
> +             }
> +             desc->membase = devm_ioremap_resource(dev, &res);
> +             if (IS_ERR(desc->membase)) {
> +                     dev_err(dev, "ioremap fail\n");
> +                     return PTR_ERR(desc->membase);
> +             }

Can't you simple use devm_platform_ioremap_resource()?

> +             dev_dbg(dev, "gpio resource: %pr\n", &res);
> +             dev_dbg(dev, "gpiochip membase: %px\n", desc->membase);

Is it anyhow useful?

> +             desc->virq = irq_of_parse_and_map(np, 0);
> +             if (!desc->virq) {
> +                     dev_err(dev, "%s: failed to parse and map irq\n",
> +                             name);
> +                     return -ENXIO;
> +             }
> +             raw_spin_lock_init(&desc->lock);
> +
> +             ret = gpiochip_setup(dev, desc);
> +             if (ret)
> +                     return ret;
> +             ret = irqchip_setup(dev, desc);
> +             if (ret)
> +                     return ret;
> +     }
> +
> +     return 0;
> +}

> +static int add_config(struct intel_pinctrl_drv_data *drvdata,
> +                   unsigned long **confs, unsigned int *nr_conf,
> +                   unsigned long pinconf)
> +{
> +     unsigned long *configs;
> +     struct device *dev = drvdata->dev;
> +     unsigned int num_conf = *nr_conf + 1;
> +
> +     if (!(*nr_conf)) {
> +             configs = devm_kcalloc(dev, 1, sizeof(pinconf), GFP_KERNEL);
> +             if (!configs)
> +                     return -ENOMEM;
> +     } else {
> +             configs = devm_kmemdup(dev, *confs,
> +                                    num_conf * sizeof(pinconf), GFP_KERNEL);
> +             if (!configs)
> +                     return -ENOMEM;

> +             devm_kfree(dev, *confs);

This a red flag for using devm_*().
Either a sign of bad design or misplacement of devm_*().

> +     }
> +
> +     configs[num_conf - 1] = pinconf;
> +     *confs = configs;
> +     *nr_conf = num_conf;
> +
> +     return 0;
> +}

> +     /**

Are you sure about this?
Have you run kernel-doc script? Does it complain?

> +      * Create pinctrl_map for each groups, per group per entry.
> +      * Create pinctrl_map for pin config, per group per entry.
> +      */
> +     if (nr_config)
> +             map_cnt++;

> +static void eqbr_dt_free_map(struct pinctrl_dev *pctldev,
> +                          struct pinctrl_map *map, unsigned int num_maps)
> +{
> +     struct intel_pinctrl_drv_data *pctl = pinctrl_dev_get_drvdata(pctldev);
> +     int i;
> +
> +     for (i = 0; i < num_maps; i++)
> +             if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP)
> +                     devm_kfree(pctl->dev, map[i].data.configs.configs);
> +     devm_kfree(pctl->dev, map);

Yeah, no devm_*() here.

> +}

> +static const struct pinmux_ops eqbr_pinmux_ops = {
> +     .get_functions_count    = eqbr_pinmux_get_func_count,
> +     .get_function_name      = eqbr_pinmux_get_fname,
> +     .get_function_groups    = eqbr_pinmux_get_groups,
> +     .set_mux                = eqbr_pinmux_set_mux,
> +     .gpio_request_enable    = eqbr_pinmux_gpio_request,

> +     .strict                 = 1,

Wrong type.

> +};

> +
> +static void set_drv_cur(void __iomem *mem, unsigned int offset,
> +                     unsigned int set, raw_spinlock_t *lock)
> +{
> +     unsigned int idx = offset; /* 16 pin per register*/
> +     unsigned int reg;
> +

> +     idx = idx / DRV_CUR_PINS;

It can be done in the first place in the definition block.

> +     offset %= DRV_CUR_PINS;
> +     reg = REG_DRCC(idx);

> +     eqbr_set_val(mem + REG_DRCC(idx), offset * 2,
> +                  0x3, set, lock);

Quite enough space in previous line for arguments.

> +}

> +static int get_drv_cur(void __iomem *mem, unsigned int offset)
> +{
> +     unsigned int idx = offset; /* 0-15, 16-31 per register*/
> +     unsigned int val;
> +
> +     idx = idx / DRV_CUR_PINS;
> +     val = readl(mem + REG_DRCC(idx));
> +     offset %= DRV_CUR_PINS;
> +     val = PARSE_DRV_CURRENT(val, offset);
> +
> +     return val;
> +}

Ditto.

> +             case PINCONF_TYPE_PULL_UP:
> +                     eqbr_set_val(mem + REG_PUEN, offset,
> +                                  1, val, &pctl->lock);

Please, configure you editor better. The recommended limit is 80, not 60.

> +                     break;
> +             case PINCONF_TYPE_PULL_DOWN:
> +                     eqbr_set_val(mem + REG_PDEN, offset,
> +                                  1, val, &pctl->lock);
> +                     break;
> +             case PINCONF_TYPE_OPEN_DRAIN:
> +                     eqbr_set_val(mem + REG_OD, offset,
> +                                  1, val, &pctl->lock);
> +                     break;
> +             case PINCONF_TYPE_DRIVE_CURRENT:
> +                     set_drv_cur(mem, offset, val, &pctl->lock);
> +                     break;
> +             case PINCONF_TYPE_SLEW_RATE:
> +                     eqbr_set_val(mem + REG_SRC, offset,
> +                                  1, val, &pctl->lock);
> +                     break;

Ditto.

> +     val = !!(readl(mem + REG_PUEN) & BIT(offset));
> +     seq_printf(s, "PULL UP: %u\n", val);
> +     val = !!(readl(mem + REG_PDEN) & BIT(offset));
> +     seq_printf(s, "PULL DOWN: %u\n", val);
> +     val = !!(readl(mem + REG_OD) & BIT(offset));
> +     seq_printf(s, "OPEN DRAIN: %u\n", val);
> +     val = get_drv_cur(mem, offset);
> +     seq_printf(s, "DRIVE CURRENT: %u\n", val);
> +     val = !!(readl(mem + REG_SRC) & BIT(offset));
> +     seq_printf(s, "SLEW RATE: %u\n", val);
> +     gpio = get_gpio_desc_via_bank(pctl, bank);
> +     val = intel_eqbr_gpio_get_dir(&gpio->chip, offset);
> +     seq_printf(s, "OUTPUT: %u\n", !val);

I think GPIO library does it for you.

> +static int add_group_to_func(struct device *dev, struct intel_pmx_func 
> *funcs,
> +                          unsigned int nr_funcs, unsigned int idx,
> +                          const char *grp_name)
> +{
> +     unsigned int nr_grps = funcs[idx].nr_groups + 1;
> +     const char **grps;
> +
> +     grps = devm_kmemdup(dev, funcs[idx].groups,
> +                         nr_grps * sizeof(*grps), GFP_KERNEL);
> +     if (!grps)
> +             return -ENOMEM;

> +     devm_kfree(dev, funcs[idx].groups);

Possible misplacement of devm_*().

> +     grps[nr_grps - 1] = grp_name;
> +     funcs[idx].groups = grps;
> +     funcs[idx].nr_groups = nr_grps;
> +
> +     return 0;
> +}

> +static int is_func_exist(struct intel_pmx_func *funcs, const char *name,
> +                      unsigned int nr_funcs, unsigned int *idx)
> +{
> +     int i;
> +
> +     if (!funcs || !nr_funcs)
> +             return 0;
> +
> +     for (i = 0; i < nr_funcs; i++) {
> +             if (strcmp(funcs[i].name, name) == 0) {
> +                     *idx = i;
> +                     return 1;
> +             }
> +     }
> +
> +     return 0;

NIH match_string(). But I think pin control core does it for drivers.

> +}

> +static void dump_pinctrl_group_func(struct intel_pinctrl_drv_data *drvdata)
> +{
> +     struct device *dev = drvdata->dev;
> +     const struct intel_pin_group *group;
> +     const struct intel_pmx_func *func;
> +     int i, j;
> +
> +     dev_info(dev, "Total %u groups, %u functions\n",
> +              drvdata->nr_grps, drvdata->nr_funcs);
> +
> +     for (i = 0; i < drvdata->nr_grps; i++) {
> +             group = &drvdata->pin_grps[i];
> +
> +             dev_dbg(dev, "group name: %s, pin num: %u, pmx: %u\n",
> +                     group->name, group->nr_pins, group->pmx);
> +             for (j = 0; j < group->nr_pins; j++)
> +                     dev_dbg(dev, "pin[%d]: %u\n", j, group->pins[j]);
> +     }
> +
> +     for (i = 0; i < drvdata->nr_funcs; i++) {
> +             func = &drvdata->pmx_funcs[i];
> +
> +             dev_dbg(dev, "function name: %s, group num: %u\n",
> +                     func->name, func->nr_groups);
> +             for (j = 0; j < func->nr_groups; j++)
> +                     dev_dbg(dev, "group[%d]: %s\n", j, func->groups[j]);
> +     }
> +}

Ditto.

> +static int pinctrl_setup_from_dt(struct device *dev,
> +                              struct intel_pinctrl_drv_data *drvdata)
> +{

It's a very big function with potential of refactoring.
Also check what core provides.

> +}

> +static int pinctrl_reg(struct intel_pinctrl_drv_data *drvdata)
> +{

> +     drvdata->pctl_dev = devm_pinctrl_register(dev, pctl_desc, drvdata);
> +     if (IS_ERR(drvdata->pctl_dev)) {

> +             dev_err(dev, "Register pinctrl failed!\n");

Useless.

> +             return PTR_ERR(drvdata->pctl_dev);
> +     }
> +
> +     return 0;

PTR_ERR_OR_ZERO()

> +}

> +/**
> + * reset all pins to DEFAULT state, including below registers
> + * PINMUX set to GPIO
> + * DIR set to INPUT
> + * Clear PULLUP/PULLDOWN/SLEW RATE/DRIVE CURRENT/OPEN DRAIN
> + */

Please, read kernel doc documentation.

> +static void pinctrl_pin_reset(struct intel_pinctrl_drv_data *drvdata)
> +{

> +     for (i = 0; i < drvdata->nr_gpio_descs; i++) {
> +             gdesc = &drvdata->gpio_desc[i];

> +             for (pin = 0; pin < gdesc->bank->nr_pins; pin++) {
> +                     if (!(BIT(pin) & gdesc->bank->aval_pinmap))
> +                             continue;

for_each_set_bit()

> +             }
> +     }

> +}

> +static int intel_pinctrl_probe(struct platform_device *pdev)
> +{

> +     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +     drvdata->membase = devm_ioremap_resource(dev, res);

devm_platform_ioremap_resource()

> +     if (IS_ERR(drvdata->membase))
> +             return PTR_ERR(drvdata->membase);

> +}

> +#define PINCONF_SET_MASK             (BIT(PINCONF_SET_SIZE) - 1)
> +#define PINCONF_TYPE_MASK            (BIT(PINCONF_TYPE_SIZE) - 1)

GENMASK()

-- 
With Best Regards,
Andy Shevchenko


Reply via email to