linux/drivers/i2c/muxes/i2c-mux-reg.c

265 lines
6.2 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* I2C multiplexer using a single register
*
* Copyright 2015 Freescale Semiconductor
* York Sun <yorksun@freescale.com>
*/
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/platform_data/i2c-mux-reg.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
struct regmux {
struct i2c_mux_reg_platform_data data;
};
static int i2c_mux_reg_set(const struct regmux *mux, unsigned int chan_id)
{
if (!mux->data.reg)
return -EINVAL;
/*
* Write to the register, followed by a read to ensure the write is
* completed on a "posted" bus, for example PCI or write buffers.
* The endianness of reading doesn't matter and the return data
* is not used.
*/
switch (mux->data.reg_size) {
case 4:
if (mux->data.little_endian)
iowrite32(chan_id, mux->data.reg);
else
iowrite32be(chan_id, mux->data.reg);
if (!mux->data.write_only)
ioread32(mux->data.reg);
break;
case 2:
if (mux->data.little_endian)
iowrite16(chan_id, mux->data.reg);
else
iowrite16be(chan_id, mux->data.reg);
if (!mux->data.write_only)
ioread16(mux->data.reg);
break;
case 1:
iowrite8(chan_id, mux->data.reg);
if (!mux->data.write_only)
ioread8(mux->data.reg);
break;
}
return 0;
}
static int i2c_mux_reg_select(struct i2c_mux_core *muxc, u32 chan)
{
struct regmux *mux = i2c_mux_priv(muxc);
return i2c_mux_reg_set(mux, chan);
}
static int i2c_mux_reg_deselect(struct i2c_mux_core *muxc, u32 chan)
{
struct regmux *mux = i2c_mux_priv(muxc);
if (mux->data.idle_in_use)
return i2c_mux_reg_set(mux, mux->data.idle);
return 0;
}
#ifdef CONFIG_OF
static int i2c_mux_reg_probe_dt(struct regmux *mux,
struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device_node *adapter_np, *child;
struct i2c_adapter *adapter;
struct resource res;
unsigned *values;
int i = 0;
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 = 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);
if (of_property_read_bool(np, "little-endian")) {
mux->data.little_endian = true;
} else if (of_property_read_bool(np, "big-endian")) {
mux->data.little_endian = false;
} else {
#if defined(__BYTE_ORDER) ? __BYTE_ORDER == __LITTLE_ENDIAN : \
defined(__LITTLE_ENDIAN)
mux->data.little_endian = true;
#elif defined(__BYTE_ORDER) ? __BYTE_ORDER == __BIG_ENDIAN : \
defined(__BIG_ENDIAN)
mux->data.little_endian = false;
#else
#error Endianness not defined?
#endif
}
mux->data.write_only = of_property_read_bool(np, "write-only");
treewide: devm_kzalloc() -> devm_kcalloc() The devm_kzalloc() function has a 2-factor argument form, devm_kcalloc(). This patch replaces cases of: devm_kzalloc(handle, a * b, gfp) with: devm_kcalloc(handle, a * b, gfp) as well as handling cases of: devm_kzalloc(handle, a * b * c, gfp) with: devm_kzalloc(handle, array3_size(a, b, c), gfp) as it's slightly less ugly than: devm_kcalloc(handle, array_size(a, b), c, gfp) This does, however, attempt to ignore constant size factors like: devm_kzalloc(handle, 4 * 1024, gfp) though any constants defined via macros get caught up in the conversion. Any factors with a sizeof() of "unsigned char", "char", and "u8" were dropped, since they're redundant. Some manual whitespace fixes were needed in this patch, as Coccinelle really liked to write "=devm_kcalloc..." instead of "= devm_kcalloc...". The Coccinelle script used for this was: // Fix redundant parens around sizeof(). @@ expression HANDLE; type TYPE; expression THING, E; @@ ( devm_kzalloc(HANDLE, - (sizeof(TYPE)) * E + sizeof(TYPE) * E , ...) | devm_kzalloc(HANDLE, - (sizeof(THING)) * E + sizeof(THING) * E , ...) ) // Drop single-byte sizes and redundant parens. @@ expression HANDLE; expression COUNT; typedef u8; typedef __u8; @@ ( devm_kzalloc(HANDLE, - sizeof(u8) * (COUNT) + COUNT , ...) | devm_kzalloc(HANDLE, - sizeof(__u8) * (COUNT) + COUNT , ...) | devm_kzalloc(HANDLE, - sizeof(char) * (COUNT) + COUNT , ...) | devm_kzalloc(HANDLE, - sizeof(unsigned char) * (COUNT) + COUNT , ...) | devm_kzalloc(HANDLE, - sizeof(u8) * COUNT + COUNT , ...) | devm_kzalloc(HANDLE, - sizeof(__u8) * COUNT + COUNT , ...) | devm_kzalloc(HANDLE, - sizeof(char) * COUNT + COUNT , ...) | devm_kzalloc(HANDLE, - sizeof(unsigned char) * COUNT + COUNT , ...) ) // 2-factor product with sizeof(type/expression) and identifier or constant. @@ expression HANDLE; type TYPE; expression THING; identifier COUNT_ID; constant COUNT_CONST; @@ ( - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(TYPE) * (COUNT_ID) + COUNT_ID, sizeof(TYPE) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(TYPE) * COUNT_ID + COUNT_ID, sizeof(TYPE) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(TYPE) * (COUNT_CONST) + COUNT_CONST, sizeof(TYPE) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(TYPE) * COUNT_CONST + COUNT_CONST, sizeof(TYPE) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(THING) * (COUNT_ID) + COUNT_ID, sizeof(THING) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(THING) * COUNT_ID + COUNT_ID, sizeof(THING) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(THING) * (COUNT_CONST) + COUNT_CONST, sizeof(THING) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(THING) * COUNT_CONST + COUNT_CONST, sizeof(THING) , ...) ) // 2-factor product, only identifiers. @@ expression HANDLE; identifier SIZE, COUNT; @@ - devm_kzalloc + devm_kcalloc (HANDLE, - SIZE * COUNT + COUNT, SIZE , ...) // 3-factor product with 1 sizeof(type) or sizeof(expression), with // redundant parens removed. @@ expression HANDLE; expression THING; identifier STRIDE, COUNT; type TYPE; @@ ( devm_kzalloc(HANDLE, - sizeof(TYPE) * (COUNT) * (STRIDE) + array3_size(COUNT, STRIDE, sizeof(TYPE)) , ...) | devm_kzalloc(HANDLE, - sizeof(TYPE) * (COUNT) * STRIDE + array3_size(COUNT, STRIDE, sizeof(TYPE)) , ...) | devm_kzalloc(HANDLE, - sizeof(TYPE) * COUNT * (STRIDE) + array3_size(COUNT, STRIDE, sizeof(TYPE)) , ...) | devm_kzalloc(HANDLE, - sizeof(TYPE) * COUNT * STRIDE + array3_size(COUNT, STRIDE, sizeof(TYPE)) , ...) | devm_kzalloc(HANDLE, - sizeof(THING) * (COUNT) * (STRIDE) + array3_size(COUNT, STRIDE, sizeof(THING)) , ...) | devm_kzalloc(HANDLE, - sizeof(THING) * (COUNT) * STRIDE + array3_size(COUNT, STRIDE, sizeof(THING)) , ...) | devm_kzalloc(HANDLE, - sizeof(THING) * COUNT * (STRIDE) + array3_size(COUNT, STRIDE, sizeof(THING)) , ...) | devm_kzalloc(HANDLE, - sizeof(THING) * COUNT * STRIDE + array3_size(COUNT, STRIDE, sizeof(THING)) , ...) ) // 3-factor product with 2 sizeof(variable), with redundant parens removed. @@ expression HANDLE; expression THING1, THING2; identifier COUNT; type TYPE1, TYPE2; @@ ( devm_kzalloc(HANDLE, - sizeof(TYPE1) * sizeof(TYPE2) * COUNT + array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2)) , ...) | devm_kzalloc(HANDLE, - sizeof(TYPE1) * sizeof(THING2) * (COUNT) + array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2)) , ...) | devm_kzalloc(HANDLE, - sizeof(THING1) * sizeof(THING2) * COUNT + array3_size(COUNT, sizeof(THING1), sizeof(THING2)) , ...) | devm_kzalloc(HANDLE, - sizeof(THING1) * sizeof(THING2) * (COUNT) + array3_size(COUNT, sizeof(THING1), sizeof(THING2)) , ...) | devm_kzalloc(HANDLE, - sizeof(TYPE1) * sizeof(THING2) * COUNT + array3_size(COUNT, sizeof(TYPE1), sizeof(THING2)) , ...) | devm_kzalloc(HANDLE, - sizeof(TYPE1) * sizeof(THING2) * (COUNT) + array3_size(COUNT, sizeof(TYPE1), sizeof(THING2)) , ...) ) // 3-factor product, only identifiers, with redundant parens removed. @@ expression HANDLE; identifier STRIDE, SIZE, COUNT; @@ ( devm_kzalloc(HANDLE, - (COUNT) * STRIDE * SIZE + array3_size(COUNT, STRIDE, SIZE) , ...) | devm_kzalloc(HANDLE, - COUNT * (STRIDE) * SIZE + array3_size(COUNT, STRIDE, SIZE) , ...) | devm_kzalloc(HANDLE, - COUNT * STRIDE * (SIZE) + array3_size(COUNT, STRIDE, SIZE) , ...) | devm_kzalloc(HANDLE, - (COUNT) * (STRIDE) * SIZE + array3_size(COUNT, STRIDE, SIZE) , ...) | devm_kzalloc(HANDLE, - COUNT * (STRIDE) * (SIZE) + array3_size(COUNT, STRIDE, SIZE) , ...) | devm_kzalloc(HANDLE, - (COUNT) * STRIDE * (SIZE) + array3_size(COUNT, STRIDE, SIZE) , ...) | devm_kzalloc(HANDLE, - (COUNT) * (STRIDE) * (SIZE) + array3_size(COUNT, STRIDE, SIZE) , ...) | devm_kzalloc(HANDLE, - COUNT * STRIDE * SIZE + array3_size(COUNT, STRIDE, SIZE) , ...) ) // Any remaining multi-factor products, first at least 3-factor products, // when they're not all constants... @@ expression HANDLE; expression E1, E2, E3; constant C1, C2, C3; @@ ( devm_kzalloc(HANDLE, C1 * C2 * C3, ...) | devm_kzalloc(HANDLE, - (E1) * E2 * E3 + array3_size(E1, E2, E3) , ...) | devm_kzalloc(HANDLE, - (E1) * (E2) * E3 + array3_size(E1, E2, E3) , ...) | devm_kzalloc(HANDLE, - (E1) * (E2) * (E3) + array3_size(E1, E2, E3) , ...) | devm_kzalloc(HANDLE, - E1 * E2 * E3 + array3_size(E1, E2, E3) , ...) ) // And then all remaining 2 factors products when they're not all constants, // keeping sizeof() as the second factor argument. @@ expression HANDLE; expression THING, E1, E2; type TYPE; constant C1, C2, C3; @@ ( devm_kzalloc(HANDLE, sizeof(THING) * C2, ...) | devm_kzalloc(HANDLE, sizeof(TYPE) * C2, ...) | devm_kzalloc(HANDLE, C1 * C2 * C3, ...) | devm_kzalloc(HANDLE, C1 * C2, ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(TYPE) * (E2) + E2, sizeof(TYPE) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(TYPE) * E2 + E2, sizeof(TYPE) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(THING) * (E2) + E2, sizeof(THING) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - sizeof(THING) * E2 + E2, sizeof(THING) , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - (E1) * E2 + E1, E2 , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - (E1) * (E2) + E1, E2 , ...) | - devm_kzalloc + devm_kcalloc (HANDLE, - E1 * E2 + E1, E2 , ...) ) Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-13 05:07:58 +08:00
values = devm_kcalloc(&pdev->dev,
mux->data.n_values, sizeof(*mux->data.values),
GFP_KERNEL);
if (!values)
return -ENOMEM;
for_each_child_of_node(np, child) {
of_property_read_u32(child, "reg", values + i);
i++;
}
mux->data.values = values;
if (!of_property_read_u32(np, "idle-state", &mux->data.idle))
mux->data.idle_in_use = true;
/* map address from "reg" if exists */
if (of_address_to_resource(np, 0, &res) == 0) {
mux->data.reg_size = resource_size(&res);
mux->data.reg = devm_ioremap_resource(&pdev->dev, &res);
if (IS_ERR(mux->data.reg))
return PTR_ERR(mux->data.reg);
}
return 0;
}
#else
static int i2c_mux_reg_probe_dt(struct regmux *mux,
struct platform_device *pdev)
{
return 0;
}
#endif
static int i2c_mux_reg_probe(struct platform_device *pdev)
{
struct i2c_mux_core *muxc;
struct regmux *mux;
struct i2c_adapter *parent;
struct resource *res;
unsigned int class;
int i, ret, nr;
mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL);
if (!mux)
return -ENOMEM;
if (dev_get_platdata(&pdev->dev)) {
memcpy(&mux->data, dev_get_platdata(&pdev->dev),
sizeof(mux->data));
} else {
ret = i2c_mux_reg_probe_dt(mux, pdev);
if (ret < 0)
return dev_err_probe(&pdev->dev, ret,
"Error parsing device tree");
}
parent = i2c_get_adapter(mux->data.parent);
if (!parent)
return -EPROBE_DEFER;
if (!mux->data.reg) {
dev_info(&pdev->dev,
"Register not set, using platform resource\n");
mux->data.reg = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(mux->data.reg)) {
ret = PTR_ERR(mux->data.reg);
goto err_put_parent;
}
mux->data.reg_size = resource_size(res);
}
if (mux->data.reg_size != 4 && mux->data.reg_size != 2 &&
mux->data.reg_size != 1) {
dev_err(&pdev->dev, "Invalid register size\n");
ret = -EINVAL;
goto err_put_parent;
}
muxc = i2c_mux_alloc(parent, &pdev->dev, mux->data.n_values, 0, 0,
i2c_mux_reg_select, NULL);
if (!muxc) {
ret = -ENOMEM;
goto err_put_parent;
}
muxc->priv = mux;
platform_set_drvdata(pdev, muxc);
if (mux->data.idle_in_use)
muxc->deselect = i2c_mux_reg_deselect;
for (i = 0; i < mux->data.n_values; i++) {
nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0;
class = mux->data.classes ? mux->data.classes[i] : 0;
ret = i2c_mux_add_adapter(muxc, nr, mux->data.values[i], class);
if (ret)
goto err_del_mux_adapters;
}
dev_dbg(&pdev->dev, "%d port mux on %s adapter\n",
mux->data.n_values, muxc->parent->name);
return 0;
err_del_mux_adapters:
i2c_mux_del_adapters(muxc);
err_put_parent:
i2c_put_adapter(parent);
return ret;
}
i2c: Convert to platform remove callback returning void The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Acked-by: Alain Volmat <alain.volmat@foss.st.com> Acked-by: Ard Biesheuvel <ardb@kernel.org> Acked-by: Baruch Siach <baruch@tkos.co.il> Acked-by: Florian Fainelli <f.fainelli@gmail.com> Acked-by: Heiko Stuebner <heiko@sntech.de> Acked-by: Jarkko Nikula <jarkko.nikula@linux.intel.com> Acked-by: Jernej Skrabec <jernej.skrabec@gmail.com> Acked-by: Jochen Friedrich <jochen@scram.de> Acked-by: Peter Rosin <peda@axentia.se> Acked-by: Vadim Pasternak <vadimp@nvidia.com> Reviewed-by: Asmaa Mnebhi <asnaa@nvidia.com> Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org> Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz> Reviewed-by: Chris Pringle <chris.pringle@phabrix.com> Reviewed-by: Claudiu Beznea <claudiu.beznea@microchip.com> Reviewed-by: Conor Dooley <conor.dooley@microchip.com> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be> Reviewed-by: Hans de Goede <hdegoede@redhat.com> Reviewed-by: Jean Delvare <jdelvare@suse.de> Reviewed-by: Konrad Dybcio <konrad.dybcio@linaro.org> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Reviewed-by: Linus Walleij <linus.walleij@linaro.org> Reviewed-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Reviewed-by: Matthias Brugger <matthias.bgg@gmail.com> Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com> Reviewed-by: Tali Perry <tali.perry@nuvoton.com> Reviewed-by: Vignesh Raghavendra <vigneshr@ti.com> Signed-off-by: Wolfram Sang <wsa@kernel.org>
2023-05-09 04:51:38 +08:00
static void i2c_mux_reg_remove(struct platform_device *pdev)
{
struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
i2c_mux_del_adapters(muxc);
i2c_put_adapter(muxc->parent);
}
static const struct of_device_id i2c_mux_reg_of_match[] = {
{ .compatible = "i2c-mux-reg", },
{},
};
MODULE_DEVICE_TABLE(of, i2c_mux_reg_of_match);
static struct platform_driver i2c_mux_reg_driver = {
.probe = i2c_mux_reg_probe,
i2c: Convert to platform remove callback returning void The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Acked-by: Alain Volmat <alain.volmat@foss.st.com> Acked-by: Ard Biesheuvel <ardb@kernel.org> Acked-by: Baruch Siach <baruch@tkos.co.il> Acked-by: Florian Fainelli <f.fainelli@gmail.com> Acked-by: Heiko Stuebner <heiko@sntech.de> Acked-by: Jarkko Nikula <jarkko.nikula@linux.intel.com> Acked-by: Jernej Skrabec <jernej.skrabec@gmail.com> Acked-by: Jochen Friedrich <jochen@scram.de> Acked-by: Peter Rosin <peda@axentia.se> Acked-by: Vadim Pasternak <vadimp@nvidia.com> Reviewed-by: Asmaa Mnebhi <asnaa@nvidia.com> Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org> Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz> Reviewed-by: Chris Pringle <chris.pringle@phabrix.com> Reviewed-by: Claudiu Beznea <claudiu.beznea@microchip.com> Reviewed-by: Conor Dooley <conor.dooley@microchip.com> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be> Reviewed-by: Hans de Goede <hdegoede@redhat.com> Reviewed-by: Jean Delvare <jdelvare@suse.de> Reviewed-by: Konrad Dybcio <konrad.dybcio@linaro.org> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Reviewed-by: Linus Walleij <linus.walleij@linaro.org> Reviewed-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Reviewed-by: Matthias Brugger <matthias.bgg@gmail.com> Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com> Reviewed-by: Tali Perry <tali.perry@nuvoton.com> Reviewed-by: Vignesh Raghavendra <vigneshr@ti.com> Signed-off-by: Wolfram Sang <wsa@kernel.org>
2023-05-09 04:51:38 +08:00
.remove_new = i2c_mux_reg_remove,
.driver = {
.name = "i2c-mux-reg",
.of_match_table = of_match_ptr(i2c_mux_reg_of_match),
},
};
module_platform_driver(i2c_mux_reg_driver);
MODULE_DESCRIPTION("Register-based I2C multiplexer driver");
MODULE_AUTHOR("York Sun <yorksun@freescale.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:i2c-mux-reg");