From: Alvin Šipraga <alsi@xxxxxxxxxxxxxxx> Signed-off-by: Alvin Šipraga <alsi@xxxxxxxxxxxxxxx> --- drivers/a2b/Kconfig | 1 + drivers/clk/Kconfig | 2 +- drivers/i2c/busses/Kconfig | 7 +++ drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-ad24xx.c | 121 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 131 insertions(+), 1 deletion(-) diff --git a/drivers/a2b/Kconfig b/drivers/a2b/Kconfig index 08acf5728023..e3c38520a90a 100644 --- a/drivers/a2b/Kconfig +++ b/drivers/a2b/Kconfig @@ -36,6 +36,7 @@ config A2B_AD24XX_NODE imply GPIO_AD24XX imply SND_SOC_AD24XX imply COMMON_CLK_AD24XX + imply I2C_AD24XX help Say Y here to enable support for AD24xx A2B transceiver nodes. This applies to both main nodes and subordinate nodes. Supported models diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index a3d54b077e68..460762f44434 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -258,7 +258,7 @@ config COMMON_CLK_LAN966X within the SoC. config COMMON_CLK_AD24XX - bool "Clock driver for Analog Devices Inc. AD24xx" + tristate "Clock driver for Analog Devices Inc. AD24xx" depends on A2B_AD24XX_NODE help This driver supports the clock output functionality of AD24xx series diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index fe6e8a1bb607..d1f303bd7c90 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -1387,6 +1387,13 @@ config I2C_ACORN If you don't know, say Y. +config I2C_AD24XX + tristate "Analog Devices Inc. AD24xx I2C controller support" + depends on A2B_AD24XX_NODE + help + Say yes if you want to support the I2C controller function of AD24xx + A2B transceiver chips. + config I2C_ELEKTOR tristate "Elektor ISA card" depends on ISA && HAS_IOPORT_MAP && BROKEN_ON_SMP diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 3d65934f5eb4..892a32b02267 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -145,6 +145,7 @@ obj-$(CONFIG_I2C_VIPERBOARD) += i2c-viperboard.o # Other I2C/SMBus bus drivers obj-$(CONFIG_I2C_ACORN) += i2c-acorn.o +obj-$(CONFIG_I2C_AD24XX) += i2c-ad24xx.o obj-$(CONFIG_I2C_BCM_KONA) += i2c-bcm-kona.o obj-$(CONFIG_I2C_BRCMSTB) += i2c-brcmstb.o obj-$(CONFIG_I2C_CROS_EC_TUNNEL) += i2c-cros-ec-tunnel.o diff --git a/drivers/i2c/busses/i2c-ad24xx.c b/drivers/i2c/busses/i2c-ad24xx.c new file mode 100644 index 000000000000..ad9657df25fb --- /dev/null +++ b/drivers/i2c/busses/i2c-ad24xx.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AD24xx I2C controller (master) driver + * + * Copyright (c) 2023-2024 Alvin Šipraga <alsi@xxxxxxxxxxxxxxx> + */ + +#include <linux/a2b/a2b.h> +#include <linux/a2b/ad24xx.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of_irq.h> + +struct ad24xx_i2c_adapter { + struct device *dev; + struct a2b_func *func; + struct a2b_node *node; + struct i2c_adapter adap; +}; + +static int ad24xx_i2c_adapter_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + struct ad24xx_i2c_adapter *ada = i2c_get_adapdata(adap); + struct a2b_node *node = ada->node; + + return a2b_node_i2c_xfer(node, msgs, num); +} + +static u32 ad24xx_i2c_adapter_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_adapter_quirks ad24xx_i2c_adapter_quirks = { + .flags = I2C_AQ_COMB | I2C_AQ_COMB_SAME_ADDR, +}; + +static const struct i2c_algorithm ad24xx_i2c_adapter_algo = { + .master_xfer = ad24xx_i2c_adapter_xfer, + .functionality = ad24xx_i2c_adapter_functionality, +}; + +static int ad24xx_i2c_adapter_probe(struct device *dev) +{ + struct a2b_func *func = to_a2b_func(dev); + struct device_node *np = dev->of_node; + struct ad24xx_i2c_adapter *ada; + unsigned int val = 0; + u32 bus_speed; + int ret; + + ada = devm_kzalloc(dev, sizeof(*ada), GFP_KERNEL); + if (!ada) + return -ENOMEM; + + ada->dev = dev; + ada->func = func; + ada->node = func->node; + + ada->adap.owner = THIS_MODULE; + ada->adap.algo = &ad24xx_i2c_adapter_algo; + ada->adap.dev.parent = dev; + ada->adap.dev.of_node = dev->of_node; + ada->adap.quirks = &ad24xx_i2c_adapter_quirks; + strscpy(ada->adap.name, dev_name(dev), sizeof(ada->adap.name)); + i2c_set_adapdata(&ada->adap, ada); + + ret = of_property_read_u32(np, "clock-frequency", &bus_speed); + if (ret) + bus_speed = I2C_MAX_STANDARD_MODE_FREQ; + + if (bus_speed != I2C_MAX_STANDARD_MODE_FREQ && + bus_speed != I2C_MAX_FAST_MODE_FREQ) + return -EINVAL; + + val |= FIELD_PREP(A2B_I2CCFG_DATARATE_MASK, + bus_speed == I2C_MAX_FAST_MODE_FREQ ? 1 : 0); + val |= FIELD_PREP(A2B_I2CCFG_FRAMERATE_MASK, + func->node->bus->sff == A2B_SFF_44100 ? 1 : 0); + + ret = a2b_node_write(func->node, A2B_I2CCFG, val); + if (ret) + return ret; + + ret = devm_i2c_add_adapter(dev, &ada->adap); + if (ret) + return ret; + + return 0; +} + +static const struct of_device_id ad24xx_i2c_adapter_of_match_table[] = { + { .compatible = "adi,ad2401-i2c" }, + { .compatible = "adi,ad2402-i2c" }, + { .compatible = "adi,ad2403-i2c" }, + { .compatible = "adi,ad2410-i2c" }, + { .compatible = "adi,ad2420-i2c" }, + { .compatible = "adi,ad2421-i2c" }, + { .compatible = "adi,ad2422-i2c" }, + { .compatible = "adi,ad2425-i2c" }, + { .compatible = "adi,ad2426-i2c" }, + { .compatible = "adi,ad2427-i2c" }, + { .compatible = "adi,ad2428-i2c" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ad24xx_i2c_adapter_of_match_table); + +static struct a2b_driver ad24xx_i2c_adapter_driver = { + .driver = { + .name = "ad24xx-i2c-adapter", + .of_match_table = ad24xx_i2c_adapter_of_match_table, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, + .probe = ad24xx_i2c_adapter_probe, +}; +module_a2b_driver(ad24xx_i2c_adapter_driver); + +MODULE_AUTHOR("Alvin Šipraga <alsi@xxxxxxxxxxxxxxx>"); +MODULE_DESCRIPTION("AD24xx I2C controller driver"); +MODULE_LICENSE("GPL"); -- 2.44.0