Re: [PATCH V10 5/9] mfd: pm8008: Use i2c_new_dummy_device() API

From: Stephen Boyd
Date: Thu Apr 14 2022 - 20:22:13 EST


Quoting Satya Priya (2022-04-14 05:30:14)
> Use i2c_new_dummy_device() to register clients along with
> the main mfd device.

Why?

>
> Signed-off-by: Satya Priya <quic_c_skakit@xxxxxxxxxxx>
> ---
> Changes in V10:
> - Implement i2c_new_dummy_device to register extra clients.
>
> drivers/mfd/qcom-pm8008.c | 54 +++++++++++++++++++++++++++++++++--------
> include/linux/mfd/qcom_pm8008.h | 13 ++++++++++
> 2 files changed, 57 insertions(+), 10 deletions(-)
> create mode 100644 include/linux/mfd/qcom_pm8008.h
>
> diff --git a/drivers/mfd/qcom-pm8008.c b/drivers/mfd/qcom-pm8008.c
> index 97a72da..ca5240d 100644
> --- a/drivers/mfd/qcom-pm8008.c
> +++ b/drivers/mfd/qcom-pm8008.c
> @@ -56,8 +57,10 @@ enum {
> #define PM8008_PERIPH_OFFSET(paddr) (paddr - PM8008_PERIPH_0_BASE)
>
> struct pm8008_data {
> + bool ready;
> struct device *dev;
> - struct regmap *regmap;
> + struct i2c_client *clients[PM8008_NUM_CLIENTS];
> + struct regmap *regmap[PM8008_NUM_CLIENTS];
> struct gpio_desc *reset_gpio;
> int irq;
> struct regmap_irq_chip_data *irq_data;
> @@ -152,9 +155,20 @@ static struct regmap_config qcom_mfd_regmap_cfg = {
> .max_register = 0xFFFF,
> };
>
> +struct regmap *pm8008_get_regmap(struct pm8008_data *chip, u8 sid)
> +{
> + if (!chip || !chip->ready) {

Is it even possible?

> + pr_err("pm8008 chip not initialized\n");
> + return NULL;
> + }
> +
> + return chip->regmap[sid];
> +}
> +
> static int pm8008_init(struct pm8008_data *chip)
> {
> int rc;
> + struct regmap *regmap = pm8008_get_regmap(chip, PM8008_INFRA_SID);
>
> /*
> * Set TEMP_ALARM peripheral's TYPE so that the regmap-irq framework
> @@ -162,19 +176,19 @@ static int pm8008_init(struct pm8008_data *chip)
> * This is required to enable the writing of TYPE registers in
> * regmap_irq_sync_unlock().
> */
> - rc = regmap_write(chip->regmap,
> + rc = regmap_write(regmap,
> (PM8008_TEMP_ALARM_ADDR | INT_SET_TYPE_OFFSET),
> BIT(0));
> if (rc)
> return rc;
>
> /* Do the same for GPIO1 and GPIO2 peripherals */
> - rc = regmap_write(chip->regmap,
> + rc = regmap_write(regmap,
> (PM8008_GPIO1_ADDR | INT_SET_TYPE_OFFSET), BIT(0));
> if (rc)
> return rc;
>
> - rc = regmap_write(chip->regmap,
> + rc = regmap_write(regmap,
> (PM8008_GPIO2_ADDR | INT_SET_TYPE_OFFSET), BIT(0));
>
> return rc;
> @@ -186,6 +200,7 @@ static int pm8008_probe_irq_peripherals(struct pm8008_data *chip,
> int rc, i;
> struct regmap_irq_type *type;
> struct regmap_irq_chip_data *irq_data;
> + struct regmap *regmap = pm8008_get_regmap(chip, PM8008_INFRA_SID);

Instead of calling pm8008_get_regmap() many times why not pass the
regmap through from pm8008_probe_irq_peripherals() called in probe? At
that point we could remove the regmap pointer from struct pm8008_data?

>
> rc = pm8008_init(chip);
> if (rc) {
> @@ -209,7 +224,7 @@ static int pm8008_probe_irq_peripherals(struct pm8008_data *chip,
> IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW);
> }
>
> - rc = devm_regmap_add_irq_chip(chip->dev, chip->regmap, client_irq,
> + rc = devm_regmap_add_irq_chip(chip->dev, regmap, client_irq,
> IRQF_SHARED, 0, &pm8008_irq_chip, &irq_data);
> if (rc) {
> dev_err(chip->dev, "Failed to add IRQ chip: %d\n", rc);
> @@ -221,19 +236,38 @@ static int pm8008_probe_irq_peripherals(struct pm8008_data *chip,
>
> static int pm8008_probe(struct i2c_client *client)
> {
> - int rc;
> + int rc, i;
> struct pm8008_data *chip;
> + struct device_node *node = client->dev.of_node;
>
> chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
> if (!chip)
> return -ENOMEM;
>
> chip->dev = &client->dev;
> - chip->regmap = devm_regmap_init_i2c(client, &qcom_mfd_regmap_cfg);
> - if (!chip->regmap)
> - return -ENODEV;
>
> - i2c_set_clientdata(client, chip);
> + for (i = 0; i < PM8008_NUM_CLIENTS; i++) {

This is 2. Why do we have a loop? Just register the i2c client for
pm8008_infra first and then make a dummy for the second address without
the loop and the indentation. Are there going to be more i2c clients?

> + if (i == 0) {
> + chip->clients[i] = client;
> + } else {
> + chip->clients[i] = i2c_new_dummy_device(client->adapter,
> + client->addr + i);
> + if (IS_ERR(chip->clients[i])) {
> + dev_err(&client->dev, "can't attach client %d\n", i);
> + return PTR_ERR(chip->clients[i]);
> + }
> + chip->clients[i]->dev.of_node = of_node_get(node);
> + }
> +
> + chip->regmap[i] = devm_regmap_init_i2c(chip->clients[i],
> + &qcom_mfd_regmap_cfg);
> + if (!chip->regmap[i])
> + return -ENODEV;
> +
> + i2c_set_clientdata(chip->clients[i], chip);
> + }
> +
> + chip->ready = true;
>
> if (of_property_read_bool(chip->dev->of_node, "interrupt-controller")) {
> rc = pm8008_probe_irq_peripherals(chip, client->irq);
> diff --git a/include/linux/mfd/qcom_pm8008.h b/include/linux/mfd/qcom_pm8008.h
> new file mode 100644
> index 0000000..bc64f01
> --- /dev/null
> +++ b/include/linux/mfd/qcom_pm8008.h
> @@ -0,0 +1,13 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef __QCOM_PM8008_H__
> +#define __QCOM_PM8008_H__
> +
> +#define PM8008_INFRA_SID 0
> +#define PM8008_REGULATORS_SID 1
> +
> +#define PM8008_NUM_CLIENTS 2
> +
> +struct pm8008_data;
> +struct regmap *pm8008_get_regmap(struct pm8008_data *chip, u8 sid);

Could this be avoided if the regulator driver used
dev_get_regmap(&pdev->dev.parent, "regulator") to find the regmap named
"regulator" of the parent device, i.e. pm8008-infra.