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

166 lines
3.5 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0-only
/*
* General Purpose I2C multiplexer
*
* Copyright (C) 2017 Axentia Technologies AB
*
* Author: Peter Rosin <peda@axentia.se>
*/
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
#include <linux/module.h>
#include <linux/mux/consumer.h>
#include <linux/of.h>
#include <linux/platform_device.h>
struct mux {
struct mux_control *control;
bool do_not_deselect;
};
static int i2c_mux_select(struct i2c_mux_core *muxc, u32 chan)
{
struct mux *mux = i2c_mux_priv(muxc);
int ret;
ret = mux_control_select(mux->control, chan);
mux->do_not_deselect = ret < 0;
return ret;
}
static int i2c_mux_deselect(struct i2c_mux_core *muxc, u32 chan)
{
struct mux *mux = i2c_mux_priv(muxc);
if (mux->do_not_deselect)
return 0;
return mux_control_deselect(mux->control);
}
static struct i2c_adapter *mux_parent_adapter(struct device *dev)
{
struct device_node *np = dev->of_node;
struct device_node *parent_np;
struct i2c_adapter *parent;
parent_np = of_parse_phandle(np, "i2c-parent", 0);
if (!parent_np) {
dev_err(dev, "Cannot parse i2c-parent\n");
return ERR_PTR(-ENODEV);
}
parent = of_get_i2c_adapter_by_node(parent_np);
of_node_put(parent_np);
if (!parent)
return ERR_PTR(-EPROBE_DEFER);
return parent;
}
static const struct of_device_id i2c_mux_of_match[] = {
{ .compatible = "i2c-mux", },
{},
};
MODULE_DEVICE_TABLE(of, i2c_mux_of_match);
static int i2c_mux_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct device_node *child;
struct i2c_mux_core *muxc;
struct mux *mux;
struct i2c_adapter *parent;
int children;
int ret;
if (!np)
return -ENODEV;
mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
if (!mux)
return -ENOMEM;
mux->control = devm_mux_control_get(dev, NULL);
if (IS_ERR(mux->control))
return dev_err_probe(dev, PTR_ERR(mux->control),
"failed to get control-mux\n");
parent = mux_parent_adapter(dev);
if (IS_ERR(parent))
return dev_err_probe(dev, PTR_ERR(parent),
"failed to get i2c-parent adapter\n");
children = of_get_child_count(np);
muxc = i2c_mux_alloc(parent, dev, children, 0, 0,
i2c_mux_select, i2c_mux_deselect);
if (!muxc) {
ret = -ENOMEM;
goto err_parent;
}
muxc->priv = mux;
platform_set_drvdata(pdev, muxc);
muxc->mux_locked = of_property_read_bool(np, "mux-locked");
for_each_child_of_node(np, child) {
u32 chan;
ret = of_property_read_u32(child, "reg", &chan);
if (ret < 0) {
dev_err(dev, "no reg property for node '%pOFn'\n",
child);
goto err_children;
}
if (chan >= mux_control_states(mux->control)) {
dev_err(dev, "invalid reg %u\n", chan);
ret = -EINVAL;
goto err_children;
}
ret = i2c_mux_add_adapter(muxc, 0, chan, 0);
if (ret)
goto err_children;
}
dev_info(dev, "%d-port mux on %s adapter\n", children, parent->name);
return 0;
err_children:
of_node_put(child);
i2c_mux_del_adapters(muxc);
err_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-08 23:51:38 +03:00
static void i2c_mux_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 struct platform_driver i2c_mux_driver = {
.probe = i2c_mux_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-08 23:51:38 +03:00
.remove_new = i2c_mux_remove,
.driver = {
.name = "i2c-mux-gpmux",
.of_match_table = i2c_mux_of_match,
},
};
module_platform_driver(i2c_mux_driver);
MODULE_DESCRIPTION("General Purpose I2C multiplexer driver");
MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
MODULE_LICENSE("GPL v2");