2020-10-15 03:33:35

by Evan Green

[permalink] [raw]
Subject: [PATCH v2] i2c: i2c-mux-gpio: Enable this driver in ACPI land

Enable i2c-mux-gpio devices to be defined via ACPI. The idle-state
property translates directly to a fwnode_property_*() call. The child
reg property translates naturally into _ADR in ACPI.

The i2c-parent binding is a relic from the days when all direct children
of an i2c controller in Linux had to be i2c devices. These days that
implementation detail has been worked out, so the i2c-mux can sit
as a direct child of its parent controller, which is where it makes the
most sense from a hardware description perspective. For the ACPI
implementation we'll assume that's always how the i2c-mux-gpio is
instantiated.

Signed-off-by: Evan Green <[email protected]>
---

Changes in v2:
- Make it compile properly when !CONFIG_ACPI (Randy)
- Update commit message regarding i2c-parent (Peter)

drivers/i2c/muxes/i2c-mux-gpio.c | 103 ++++++++++++++++++++++---------
1 file changed, 75 insertions(+), 28 deletions(-)

diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c
index 4effe563e9e8d..8e4008f4a9b5d 100644
--- a/drivers/i2c/muxes/i2c-mux-gpio.c
+++ b/drivers/i2c/muxes/i2c-mux-gpio.c
@@ -49,34 +49,80 @@ static int i2c_mux_gpio_deselect(struct i2c_mux_core *muxc, u32 chan)
return 0;
}

-#ifdef CONFIG_OF
-static int i2c_mux_gpio_probe_dt(struct gpiomux *mux,
- struct platform_device *pdev)
+#ifdef CONFIG_ACPI
+
+static int i2c_mux_gpio_get_acpi_adr(struct device *dev,
+ struct fwnode_handle *fwdev,
+ unsigned int *adr)
+
+{
+ unsigned long long adr64;
+ acpi_status status;
+
+ status = acpi_evaluate_integer(ACPI_HANDLE_FWNODE(fwdev),
+ METHOD_NAME__ADR,
+ NULL, &adr64);
+
+ if (!ACPI_SUCCESS(status)) {
+ dev_err(dev, "Cannot get address");
+ return -EINVAL;
+ }
+
+ *adr = adr64;
+ return 0;
+}
+
+#else
+
+static int i2c_mux_gpio_get_acpi_adr(struct device *dev,
+ struct fwnode_handle *fwdev,
+ unsigned int *adr)
+{
+ return -EINVAL;
+}
+
+#endif
+
+static int i2c_mux_gpio_probe_fw(struct gpiomux *mux,
+ struct platform_device *pdev)
{
- struct device_node *np = pdev->dev.of_node;
- struct device_node *adapter_np, *child;
- struct i2c_adapter *adapter;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ acpi_handle dev_handle;
+ struct device_node *adapter_np;
+ struct i2c_adapter *adapter = NULL;
+ struct fwnode_handle *child = NULL;
unsigned *values;
- int i = 0;
+ int rc, i = 0;

- if (!np)
- return -ENODEV;
+ if (is_of_node(dev->fwnode)) {
+ if (!np)
+ return -ENODEV;

- adapter_np = of_parse_phandle(np, "i2c-parent", 0);
- if (!adapter_np) {
- dev_err(&pdev->dev, "Cannot parse i2c-parent\n");
- return -ENODEV;
+ adapter_np = of_parse_phandle(np, "i2c-parent", 0);
+ if (!adapter_np) {
+ dev_err(&pdev->dev, "Cannot parse i2c-parent\n");
+ return -ENODEV;
+ }
+ adapter = of_find_i2c_adapter_by_node(adapter_np);
+ of_node_put(adapter_np);
+
+ } else if (is_acpi_node(dev->fwnode)) {
+ /*
+ * In ACPI land the mux should be a direct child of the i2c
+ * bus it muxes.
+ */
+ dev_handle = ACPI_HANDLE(dev->parent);
+ adapter = i2c_acpi_find_adapter_by_handle(dev_handle);
}
- adapter = of_find_i2c_adapter_by_node(adapter_np);
- of_node_put(adapter_np);
+
if (!adapter)
return -EPROBE_DEFER;

mux->data.parent = i2c_adapter_id(adapter);
put_device(&adapter->dev);

- mux->data.n_values = of_get_child_count(np);
-
+ mux->data.n_values = device_get_child_node_count(dev);
values = devm_kcalloc(&pdev->dev,
mux->data.n_values, sizeof(*mux->data.values),
GFP_KERNEL);
@@ -85,24 +131,25 @@ static int i2c_mux_gpio_probe_dt(struct gpiomux *mux,
return -ENOMEM;
}

- for_each_child_of_node(np, child) {
- of_property_read_u32(child, "reg", values + i);
+ device_for_each_child_node(dev, child) {
+ if (is_of_node(child)) {
+ fwnode_property_read_u32(child, "reg", values + i);
+
+ } else if (is_acpi_node(child)) {
+ rc = i2c_mux_gpio_get_acpi_adr(dev, child, values + i);
+ if (rc)
+ return rc;
+ }
+
i++;
}
mux->data.values = values;

- if (of_property_read_u32(np, "idle-state", &mux->data.idle))
+ if (fwnode_property_read_u32(dev->fwnode, "idle-state", &mux->data.idle))
mux->data.idle = I2C_MUX_GPIO_NO_IDLE;

return 0;
}
-#else
-static int i2c_mux_gpio_probe_dt(struct gpiomux *mux,
- struct platform_device *pdev)
-{
- return 0;
-}
-#endif

static int i2c_mux_gpio_probe(struct platform_device *pdev)
{
@@ -118,7 +165,7 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
return -ENOMEM;

if (!dev_get_platdata(&pdev->dev)) {
- ret = i2c_mux_gpio_probe_dt(mux, pdev);
+ ret = i2c_mux_gpio_probe_fw(mux, pdev);
if (ret < 0)
return ret;
} else {
--
2.26.2


2020-10-15 16:35:23

by Evan Green

[permalink] [raw]
Subject: Re: [PATCH v2] i2c: i2c-mux-gpio: Enable this driver in ACPI land

On Thu, Oct 15, 2020 at 3:23 AM Peter Rosin <[email protected]> wrote:
>
> Hi!
>
> On 2020-10-15 03:02, Evan Green wrote:
> > Enable i2c-mux-gpio devices to be defined via ACPI. The idle-state
> > property translates directly to a fwnode_property_*() call. The child
> > reg property translates naturally into _ADR in ACPI.
> >
> > The i2c-parent binding is a relic from the days when all direct children
> > of an i2c controller in Linux had to be i2c devices. These days that
>
> I2C controller. I2C devices.
>
> I fail to see why this "relic" has to be explicitly blamed on Linux? In the
> beginning, the bindings for all I2C controllers (sometimes implicitely,
> sometimes explicitely) specified that all child nodes had to be I2C devices.
> The *bindings* were simply not as flexible before the i2c-bus subnode was
> invented only a few years ago. So, there are arguments that the "problem"
> was in DT-land and that Linux just followed suit.

Gotcha, that makes sense. I was probably reading between the lines
incorrectly in your previous reply. I'll blame it on the bindings :)

>
> > implementation detail has been worked out, so the i2c-mux can sit
> > as a direct child of its parent controller, which is where it makes the
> > most sense from a hardware description perspective. For the ACPI
> > implementation we'll assume that's always how the i2c-mux-gpio is
> > instantiated.
>
> There is potential to match this and make i2c-parent optional for the
> DT case and require it to be a child of its parent in such cases, if
> someone has the time/energy...

I won't plan to since I don't have a device like this, but yeah I
agree this would be a fine convention for DT to start following as
well.

>
> >
> > Signed-off-by: Evan Green <[email protected]>
> > ---
> >
> > Changes in v2:
> > - Make it compile properly when !CONFIG_ACPI (Randy)
> > - Update commit message regarding i2c-parent (Peter)
> >
> > drivers/i2c/muxes/i2c-mux-gpio.c | 103 ++++++++++++++++++++++---------
> > 1 file changed, 75 insertions(+), 28 deletions(-)
> >
> > diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c
> > index 4effe563e9e8d..8e4008f4a9b5d 100644
> > --- a/drivers/i2c/muxes/i2c-mux-gpio.c
> > +++ b/drivers/i2c/muxes/i2c-mux-gpio.c
> > @@ -49,34 +49,80 @@ static int i2c_mux_gpio_deselect(struct i2c_mux_core *muxc, u32 chan)
> > return 0;
> > }
> >
> > -#ifdef CONFIG_OF
> > -static int i2c_mux_gpio_probe_dt(struct gpiomux *mux,
> > - struct platform_device *pdev)
> > +#ifdef CONFIG_ACPI
> > +
> > +static int i2c_mux_gpio_get_acpi_adr(struct device *dev,
> > + struct fwnode_handle *fwdev,
> > + unsigned int *adr)
> > +
> > +{
> > + unsigned long long adr64;
> > + acpi_status status;
> > +
> > + status = acpi_evaluate_integer(ACPI_HANDLE_FWNODE(fwdev),
> > + METHOD_NAME__ADR,
> > + NULL, &adr64);
> > +
> > + if (!ACPI_SUCCESS(status)) {
> > + dev_err(dev, "Cannot get address");
>
> Missing trailing \n

Whoops.

>
> > + return -EINVAL;
> > + }
> > +
> > + *adr = adr64;
>
> Maybe this is too pedantic? Optional, ignore if I'm just being insane...
>
> if (*adr != adr64) {
> dev_err(dev, "Address out of range\n");
> return -EINVAL;
> }
>

Sure, will add.

> > + return 0;
> > +}
> > +
> > +#else
> > +
> > +static int i2c_mux_gpio_get_acpi_adr(struct device *dev,
> > + struct fwnode_handle *fwdev,
> > + unsigned int *adr)
> > +{
> > + return -EINVAL;
> > +}
> > +
> > +#endif
> > +
> > +static int i2c_mux_gpio_probe_fw(struct gpiomux *mux,
> > + struct platform_device *pdev)
> > {
> > - struct device_node *np = pdev->dev.of_node;
> > - struct device_node *adapter_np, *child;
> > - struct i2c_adapter *adapter;
> > + struct device *dev = &pdev->dev;
> > + struct device_node *np = dev->of_node;
> > + acpi_handle dev_handle;
>
> Remove the dev_handle declaration here...(push)...
>
> > + struct device_node *adapter_np;
> > + struct i2c_adapter *adapter = NULL;
> > + struct fwnode_handle *child = NULL;
>
> Why do you need these two " = NULL" here? I can't believe compilers are
> that stupid? If they are, fine. But otherwise, why single out these two
> pointers and add NULL only there and not everywhere? But NULL everywhere
> would be ugly...

The adapter NULL is there because in theory you could have
!is_acpi_node && !is_of_node (maybe in some weird compile test?), and
then you would be checking an uninitialized value.

child I could safely not initialize, so I'll change that.

>
> > unsigned *values;
> > - int i = 0;
> > + int rc, i = 0;
> >
> > - if (!np)
> > - return -ENODEV;
> > + if (is_of_node(dev->fwnode)) {
> > + if (!np)
> > + return -ENODEV;
> >
> > - adapter_np = of_parse_phandle(np, "i2c-parent", 0);
> > - if (!adapter_np) {
> > - dev_err(&pdev->dev, "Cannot parse i2c-parent\n");
> > - return -ENODEV;
> > + adapter_np = of_parse_phandle(np, "i2c-parent", 0);
> > + if (!adapter_np) {
> > + dev_err(&pdev->dev, "Cannot parse i2c-parent\n");
>
> You should do "&pdev->dev" -> "dev" here, because I hate having
> the dev variable and then not use it. But that should perhaps be
> a preparatory patch, because I see more instances and this is an
> orthogonal change.

I see 3 other instances of it in this function. I'll create an initial
patch that introduces the dev local and uses it throughout this
function, then another patch with my main change on top.

>
> > + return -ENODEV;
> > + }
> > + adapter = of_find_i2c_adapter_by_node(adapter_np);
> > + of_node_put(adapter_np);
> > +
> > + } else if (is_acpi_node(dev->fwnode)) {
> > + /*
> > + * In ACPI land the mux should be a direct child of the i2c
> > + * bus it muxes.
> > + */
> > + dev_handle = ACPI_HANDLE(dev->parent);
>
> ...(pop)...and perhaps say
>
> acpi_handle dev_handle = ACPI_HANDLE(dev->parent);
>
> here?

Will do.

Thanks for the prompt review.
-Evan

2020-10-15 18:26:55

by Peter Rosin

[permalink] [raw]
Subject: Re: [PATCH v2] i2c: i2c-mux-gpio: Enable this driver in ACPI land

Hi!

On 2020-10-15 03:02, Evan Green wrote:
> Enable i2c-mux-gpio devices to be defined via ACPI. The idle-state
> property translates directly to a fwnode_property_*() call. The child
> reg property translates naturally into _ADR in ACPI.
>
> The i2c-parent binding is a relic from the days when all direct children
> of an i2c controller in Linux had to be i2c devices. These days that

I2C controller. I2C devices.

I fail to see why this "relic" has to be explicitly blamed on Linux? In the
beginning, the bindings for all I2C controllers (sometimes implicitely,
sometimes explicitely) specified that all child nodes had to be I2C devices.
The *bindings* were simply not as flexible before the i2c-bus subnode was
invented only a few years ago. So, there are arguments that the "problem"
was in DT-land and that Linux just followed suit.

> implementation detail has been worked out, so the i2c-mux can sit
> as a direct child of its parent controller, which is where it makes the
> most sense from a hardware description perspective. For the ACPI
> implementation we'll assume that's always how the i2c-mux-gpio is
> instantiated.

There is potential to match this and make i2c-parent optional for the
DT case and require it to be a child of its parent in such cases, if
someone has the time/energy...

>
> Signed-off-by: Evan Green <[email protected]>
> ---
>
> Changes in v2:
> - Make it compile properly when !CONFIG_ACPI (Randy)
> - Update commit message regarding i2c-parent (Peter)
>
> drivers/i2c/muxes/i2c-mux-gpio.c | 103 ++++++++++++++++++++++---------
> 1 file changed, 75 insertions(+), 28 deletions(-)
>
> diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c
> index 4effe563e9e8d..8e4008f4a9b5d 100644
> --- a/drivers/i2c/muxes/i2c-mux-gpio.c
> +++ b/drivers/i2c/muxes/i2c-mux-gpio.c
> @@ -49,34 +49,80 @@ static int i2c_mux_gpio_deselect(struct i2c_mux_core *muxc, u32 chan)
> return 0;
> }
>
> -#ifdef CONFIG_OF
> -static int i2c_mux_gpio_probe_dt(struct gpiomux *mux,
> - struct platform_device *pdev)
> +#ifdef CONFIG_ACPI
> +
> +static int i2c_mux_gpio_get_acpi_adr(struct device *dev,
> + struct fwnode_handle *fwdev,
> + unsigned int *adr)
> +
> +{
> + unsigned long long adr64;
> + acpi_status status;
> +
> + status = acpi_evaluate_integer(ACPI_HANDLE_FWNODE(fwdev),
> + METHOD_NAME__ADR,
> + NULL, &adr64);
> +
> + if (!ACPI_SUCCESS(status)) {
> + dev_err(dev, "Cannot get address");

Missing trailing \n

> + return -EINVAL;
> + }
> +
> + *adr = adr64;

Maybe this is too pedantic? Optional, ignore if I'm just being insane...

if (*adr != adr64) {
dev_err(dev, "Address out of range\n");
return -EINVAL;
}

> + return 0;
> +}
> +
> +#else
> +
> +static int i2c_mux_gpio_get_acpi_adr(struct device *dev,
> + struct fwnode_handle *fwdev,
> + unsigned int *adr)
> +{
> + return -EINVAL;
> +}
> +
> +#endif
> +
> +static int i2c_mux_gpio_probe_fw(struct gpiomux *mux,
> + struct platform_device *pdev)
> {
> - struct device_node *np = pdev->dev.of_node;
> - struct device_node *adapter_np, *child;
> - struct i2c_adapter *adapter;
> + struct device *dev = &pdev->dev;
> + struct device_node *np = dev->of_node;
> + acpi_handle dev_handle;

Remove the dev_handle declaration here...(push)...

> + struct device_node *adapter_np;
> + struct i2c_adapter *adapter = NULL;
> + struct fwnode_handle *child = NULL;

Why do you need these two " = NULL" here? I can't believe compilers are
that stupid? If they are, fine. But otherwise, why single out these two
pointers and add NULL only there and not everywhere? But NULL everywhere
would be ugly...

> unsigned *values;
> - int i = 0;
> + int rc, i = 0;
>
> - if (!np)
> - return -ENODEV;
> + if (is_of_node(dev->fwnode)) {
> + if (!np)
> + return -ENODEV;
>
> - adapter_np = of_parse_phandle(np, "i2c-parent", 0);
> - if (!adapter_np) {
> - dev_err(&pdev->dev, "Cannot parse i2c-parent\n");
> - return -ENODEV;
> + adapter_np = of_parse_phandle(np, "i2c-parent", 0);
> + if (!adapter_np) {
> + dev_err(&pdev->dev, "Cannot parse i2c-parent\n");

You should do "&pdev->dev" -> "dev" here, because I hate having
the dev variable and then not use it. But that should perhaps be
a preparatory patch, because I see more instances and this is an
orthogonal change.

> + return -ENODEV;
> + }
> + adapter = of_find_i2c_adapter_by_node(adapter_np);
> + of_node_put(adapter_np);
> +
> + } else if (is_acpi_node(dev->fwnode)) {
> + /*
> + * In ACPI land the mux should be a direct child of the i2c
> + * bus it muxes.
> + */
> + dev_handle = ACPI_HANDLE(dev->parent);

...(pop)...and perhaps say

acpi_handle dev_handle = ACPI_HANDLE(dev->parent);

here?

Cheers,
Peter

> + adapter = i2c_acpi_find_adapter_by_handle(dev_handle);
> }
> - adapter = of_find_i2c_adapter_by_node(adapter_np);
> - of_node_put(adapter_np);
> +
> if (!adapter)
> return -EPROBE_DEFER;
>
> mux->data.parent = i2c_adapter_id(adapter);
> put_device(&adapter->dev);
>
> - mux->data.n_values = of_get_child_count(np);
> -
> + mux->data.n_values = device_get_child_node_count(dev);
> values = devm_kcalloc(&pdev->dev,
> mux->data.n_values, sizeof(*mux->data.values),
> GFP_KERNEL);
> @@ -85,24 +131,25 @@ static int i2c_mux_gpio_probe_dt(struct gpiomux *mux,
> return -ENOMEM;
> }
>
> - for_each_child_of_node(np, child) {
> - of_property_read_u32(child, "reg", values + i);
> + device_for_each_child_node(dev, child) {
> + if (is_of_node(child)) {
> + fwnode_property_read_u32(child, "reg", values + i);
> +
> + } else if (is_acpi_node(child)) {
> + rc = i2c_mux_gpio_get_acpi_adr(dev, child, values + i);
> + if (rc)
> + return rc;
> + }
> +
> i++;
> }
> mux->data.values = values;
>
> - if (of_property_read_u32(np, "idle-state", &mux->data.idle))
> + if (fwnode_property_read_u32(dev->fwnode, "idle-state", &mux->data.idle))
> mux->data.idle = I2C_MUX_GPIO_NO_IDLE;
>
> return 0;
> }
> -#else
> -static int i2c_mux_gpio_probe_dt(struct gpiomux *mux,
> - struct platform_device *pdev)
> -{
> - return 0;
> -}
> -#endif
>
> static int i2c_mux_gpio_probe(struct platform_device *pdev)
> {
> @@ -118,7 +165,7 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
> return -ENOMEM;
>
> if (!dev_get_platdata(&pdev->dev)) {
> - ret = i2c_mux_gpio_probe_dt(mux, pdev);
> + ret = i2c_mux_gpio_probe_fw(mux, pdev);
> if (ret < 0)
> return ret;
> } else {
>