diff mbox series

[09/13] i2c: add AD24xx I2C controller driver

Message ID 20240517-a2b-v1-9-b8647554c67b@bang-olufsen.dk (mailing list archive)
State Not Applicable, archived
Headers show
Series Analog Devices Inc. Automotive Audio Bus (A2B) support | expand

Commit Message

Alvin Šipraga May 17, 2024, 1:02 p.m. UTC
From: Alvin Šipraga <alsi@bang-olufsen.dk>

Signed-off-by: Alvin Šipraga <alsi@bang-olufsen.dk>
---
 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 mbox series

Patch

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@bang-olufsen.dk>
+ */
+
+#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@bang-olufsen.dk>");
+MODULE_DESCRIPTION("AD24xx I2C controller driver");
+MODULE_LICENSE("GPL");