ac200: split hunks to combine 3 into 2 logical patches removing cross patch dependency

This commit is contained in:
EvilOlaf 2026-01-12 13:57:32 +00:00 committed by Igor
parent 2e63422c7f
commit 892bf74638
5 changed files with 942 additions and 1009 deletions

View File

@ -1,265 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@siol.net>
Date: Fri, 16 Aug 2019 16:38:21 +0200
Subject: mfd: Add support for X-Powers AC200
The X-Powers AC200 is a mixed signal multi-purpose chip, which provides
audio DAC/ADCs, a CVBS video encoder, a 100Mbit/s Ethernet PHY and a
real-time clock. Its control registers can be accessed via I2C or
Allwinner's RSB bus.
Beside this chip being used on some older boards (for instance the Remix
Mini PC), it is quite wide spread due to its die being co-packaged on the
Allwinner H6 and H616 SoCs, which use its audio, video and PHY
functionality.
Aside from the RTC, the other functions do not need constant
hand-holding via the I2C registers, but rather need to be configured and
enabled only once.
We model the control side of this chip using the MFD subsystem. This
driver here just provides the parent device for the various subfunctions,
and takes care of enabling clocks and reset, but also provides the regmap,
which the respective child drivers will use.
Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
drivers/mfd/Kconfig | 12 +
drivers/mfd/Makefile | 1 +
drivers/mfd/ac200.c | 190 ++++++++++
3 files changed, 203 insertions(+)
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 111111111111..222222222222 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -204,6 +204,18 @@ config MFD_AC100
This driver include only the core APIs. You have to select individual
components like codecs or RTC under the corresponding menus.
+config MFD_AC200
+ tristate "X-Powers AC200"
+ select MFD_CORE
+ select REGMAP_I2C
+ depends on COMMON_CLK
+ depends on I2C
+ depends on OF
+ help
+ If you say Y here you get support for the X-Powers AC200 IC.
+ This driver include only the core APIs. You have to select individual
+ components like Ethernet PHY or codec under the corresponding menus.
+
config MFD_AXP20X
tristate
select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 111111111111..222222222222 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -149,6 +149,7 @@ obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o
obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o
obj-$(CONFIG_MFD_AC100) += ac100.o
+obj-$(CONFIG_MFD_AC200) += ac200.o
obj-$(CONFIG_MFD_AXP20X) += axp20x.o
obj-$(CONFIG_MFD_AXP20X_I2C) += axp20x-i2c.o
obj-$(CONFIG_MFD_AXP20X_RSB) += axp20x-rsb.o
diff --git a/drivers/mfd/ac200.c b/drivers/mfd/ac200.c
new file mode 100644
index 000000000000..111111111111
--- /dev/null
+++ b/drivers/mfd/ac200.c
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MFD core driver for X-Powers' AC200 IC
+ *
+ * The AC200 is a chip which is co-packaged with Allwinner H6 SoC and
+ * includes analog audio codec, analog TV encoder, ethernet PHY, eFuse
+ * and RTC.
+ *
+ * Copyright (c) 2019 Jernej Skrabec <jernej.skrabec@gmail.com>
+ *
+ * Based on AC100 driver with following copyrights:
+ * Copyright (2016) Chen-Yu Tsai
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+struct ac200_dev {
+ struct clk *clk;
+ struct regmap *regmap;
+};
+
+#define AC200_SYS_CONTROL 0x0002
+#define AC200_SYS_BG_CTL 0x0050
+
+/* interface register (can be accessed from any page) */
+#define AC200_TWI_REG_ADDR_H 0xFE
+
+#define AC200_MAX_REG 0xA1F2
+
+static const struct regmap_range_cfg ac200_range_cfg[] = {
+ {
+ .range_max = AC200_MAX_REG,
+ .selector_reg = AC200_TWI_REG_ADDR_H,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 256,
+ }
+};
+
+static const struct regmap_config ac200_regmap_config = {
+ .name = "AC200",
+ .reg_bits = 8,
+ .reg_stride = 2,
+ .val_bits = 16,
+ .ranges = ac200_range_cfg,
+ .num_ranges = ARRAY_SIZE(ac200_range_cfg),
+ .max_register = AC200_MAX_REG,
+};
+
+static struct mfd_cell ac200_cells[] = {
+ {
+ .name = "ac200-codec",
+ .of_compatible = "x-powers,ac200-codec",
+ }, {
+ .name = "ac200-ephy-ctl",
+ .of_compatible = "x-powers,ac200-ephy-ctl",
+ },
+};
+
+static int ac200_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct nvmem_cell *bgcell;
+ struct ac200_dev *ac200;
+ u16 *bgdata, bgval;
+ size_t bglen;
+ int ret;
+
+ ac200 = devm_kzalloc(dev, sizeof(*ac200), GFP_KERNEL);
+ if (!ac200)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, ac200);
+
+ ac200->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(ac200->clk))
+ return dev_err_probe(dev, PTR_ERR(ac200->clk),
+ "Can't obtain the clock\n");
+
+ ac200->regmap = devm_regmap_init_i2c(i2c, &ac200_regmap_config);
+ if (IS_ERR(ac200->regmap)) {
+ ret = PTR_ERR(ac200->regmap);
+ dev_err(dev, "Regmap init failed: %d\n", ret);
+ return ret;
+ }
+
+ bgcell = devm_nvmem_cell_get(dev, "bandgap");
+ if (IS_ERR(bgcell))
+ return dev_err_probe(dev, PTR_ERR(bgcell),
+ "Unable to find bandgap data!\n");
+
+ bgdata = nvmem_cell_read(bgcell, &bglen);
+ if (IS_ERR(bgdata)) {
+ dev_err(dev, "Unable to read bandgap data!\n");
+ return PTR_ERR(bgdata);
+ }
+
+ if (bglen != 2) {
+ dev_err(dev, "Invalid nvmem bandgap length!\n");
+ kfree(bgdata);
+ return -EINVAL;
+ }
+
+ bgval = *bgdata;
+ kfree(bgdata);
+
+ ret = clk_prepare_enable(ac200->clk);
+ if (ret)
+ return ret;
+
+ /*
+ * There is no documentation on how long we have to wait before
+ * executing first operation. Vendor driver sleeps for 40 ms.
+ */
+ msleep(40);
+
+ ret = regmap_write(ac200->regmap, AC200_SYS_CONTROL, 0);
+ if (ret)
+ goto err;
+
+ ret = regmap_write(ac200->regmap, AC200_SYS_CONTROL, 1);
+ if (ret)
+ goto err;
+
+ if (bgval) {
+ /* bandgap register is not documented */
+ ret = regmap_write(ac200->regmap, AC200_SYS_BG_CTL,
+ 0x8280 | bgval);
+ if (ret)
+ goto err;
+ }
+
+ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, ac200_cells,
+ ARRAY_SIZE(ac200_cells), NULL, 0, NULL);
+ if (ret) {
+ dev_err(dev, "Failed to add MFD devices: %d\n", ret);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ clk_disable_unprepare(ac200->clk);
+ return ret;
+}
+
+static void ac200_i2c_remove(struct i2c_client *i2c)
+{
+ struct ac200_dev *ac200 = i2c_get_clientdata(i2c);
+
+ regmap_write(ac200->regmap, AC200_SYS_CONTROL, 0);
+
+ clk_disable_unprepare(ac200->clk);
+}
+
+static const struct i2c_device_id ac200_ids[] = {
+ { "ac200", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ac200_ids);
+
+static const struct of_device_id ac200_of_match[] = {
+ { .compatible = "x-powers,ac200" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ac200_of_match);
+
+static struct i2c_driver ac200_i2c_driver = {
+ .driver = {
+ .name = "ac200",
+ .of_match_table = of_match_ptr(ac200_of_match),
+ },
+ .probe = ac200_i2c_probe,
+ .remove = ac200_i2c_remove,
+ .id_table = ac200_ids,
+};
+module_i2c_driver(ac200_i2c_driver);
+
+MODULE_DESCRIPTION("MFD core driver for AC200");
+MODULE_AUTHOR("Jernej Skrabec <jernej.skrabec@gmail.com>");
+MODULE_LICENSE("GPL v2");
--
Armbian

View File

@ -1,142 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@siol.net>
Date: Fri, 16 Aug 2019 16:38:57 +0200
Subject: net: phy: Add support for AC200 EPHY
The X-Powers AC200 mixed signal chip contains a 100Mbit/s Ethernet PHY.
While its sporting a usable default setup, and can be controlled by the
generic IEEE802.3-C22 PHY driver, the BSP sets up some extra registers,
which this driver here covers.
Add a PHY driver matching the AC200 EPHY ID registers, and which
programs some PHY registers according to the BSP code.
Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
drivers/net/phy/Kconfig | 7 +
drivers/net/phy/Makefile | 1 +
drivers/net/phy/ac200-phy.c | 82 ++++++++++
3 files changed, 90 insertions(+)
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 111111111111..222222222222 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -101,6 +101,13 @@ config AIR_EN8811H_PHY
help
Currently supports the Airoha EN8811H PHY.
+config AC200_PHY
+ tristate "AC200 EPHY"
+ depends on NVMEM
+ depends on OF
+ help
+ Fast ethernet PHY as found in X-Powers AC200 multi-function device.
+
config AMD_PHY
tristate "AMD and Altima PHYs"
help
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 111111111111..222222222222 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_SFP) += sfp.o
sfp-obj-$(CONFIG_SFP) += sfp-bus.o
obj-y += $(sfp-obj-y) $(sfp-obj-m)
+obj-$(CONFIG_AC200_PHY) += ac200-phy.o
obj-$(CONFIG_ADIN_PHY) += adin.o
obj-$(CONFIG_ADIN1100_PHY) += adin1100.o
obj-$(CONFIG_AIR_EN8811H_PHY) += air_en8811h.o
diff --git a/drivers/net/phy/ac200-phy.c b/drivers/net/phy/ac200-phy.c
new file mode 100644
index 000000000000..111111111111
--- /dev/null
+++ b/drivers/net/phy/ac200-phy.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0+
+/**
+ * Driver for AC200 Ethernet PHY
+ *
+ * Copyright (c) 2019 Jernej Skrabec <jernej.skrabec@gmail.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/phy.h>
+
+#define AC200_EPHY_ID 0x00441400
+#define AC200_EPHY_ID_MASK 0x0ffffff0
+
+static int ac200_ephy_config_init(struct phy_device *phydev)
+{
+ phy_write(phydev, 0x1f, 0x0100); /* Switch to Page 1 */
+ phy_write(phydev, 0x12, 0x4824); /* Disable APS */
+
+ phy_write(phydev, 0x1f, 0x0200); /* Switch to Page 2 */
+ phy_write(phydev, 0x18, 0x0000); /* PHYAFE TRX optimization */
+
+ phy_write(phydev, 0x1f, 0x0600); /* Switch to Page 6 */
+ phy_write(phydev, 0x14, 0x708f); /* PHYAFE TX optimization */
+ phy_write(phydev, 0x13, 0xF000); /* PHYAFE RX optimization */
+ phy_write(phydev, 0x15, 0x1530);
+
+ phy_write(phydev, 0x1f, 0x0800); /* Switch to Page 8 */
+ phy_write(phydev, 0x18, 0x00bc); /* PHYAFE TRX optimization */
+
+ phy_write(phydev, 0x1f, 0x0100); /* switch to page 1 */
+ phy_clear_bits(phydev, 0x17, BIT(3)); /* disable intelligent EEE */
+
+ /* disable 802.3az EEE */
+ phy_write(phydev, 0x1f, 0x0200); /* switch to page 2 */
+ phy_write(phydev, 0x18, 0x0000);
+ phy_write(phydev, 0x1f, 0x0000); /* switch to page 0 */
+ phy_clear_bits_mmd(phydev, 0x7, 0x3c, BIT(1));
+
+ /* FIXME: This is probably H6 specific */
+ phy_set_bits(phydev, 0x13, BIT(12));
+
+ return 0;
+}
+
+static int ac200_ephy_probe(struct phy_device *phydev)
+{
+ struct device *dev = &phydev->mdio.dev;
+ struct clk *clk;
+
+ clk = devm_clk_get_optional_enabled(dev, NULL);
+ if (IS_ERR(clk))
+ return dev_err_probe(dev, PTR_ERR(clk),
+ "Failed to request clock\n");
+
+ return 0;
+}
+
+static struct phy_driver ac200_ephy_driver[] = {
+ {
+ .phy_id = AC200_EPHY_ID,
+ .phy_id_mask = AC200_EPHY_ID_MASK,
+ .name = "Allwinner AC200 EPHY",
+ .soft_reset = genphy_soft_reset,
+ .config_init = ac200_ephy_config_init,
+ .probe = ac200_ephy_probe,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ }
+};
+module_phy_driver(ac200_ephy_driver);
+
+MODULE_AUTHOR("Jernej Skrabec <jernej.skrabec@gmail.com>");
+MODULE_DESCRIPTION("AC200 Ethernet PHY driver");
+MODULE_LICENSE("GPL");
+
+static const struct mdio_device_id __maybe_unused ac200_ephy_phy_tbl[] = {
+ { AC200_EPHY_ID, AC200_EPHY_ID_MASK },
+ { }
+};
+MODULE_DEVICE_TABLE(mdio, ac200_ephy_phy_tbl);
--
Armbian

View File

@ -0,0 +1,917 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@gmail.com>
Date: Fri, 16 Aug 2019 16:38:21 +0200
Subject: mfd: Add support for X-Powers AC200
The X-Powers AC200 is a mixed signal multi-purpose chip, which provides
audio DAC/ADCs, a CVBS video encoder, a 100Mbit/s Ethernet PHY and a
real-time clock. Its control registers can be accessed via I2C or
Allwinner's RSB bus.
This chip is co-packaged with Allwinner H6 and H616 SoCs, which use its
audio, video and PHY functionality.
This includes:
- MFD_AC200 core driver (ac200.c)
- MFD_AC200_SUNXI driver (sunxi-ac200.c) with EPHY and RTC support
- AC200 EPHY driver (ac200-phy.c)
- AC200 header definitions (ac200.h)
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
drivers/mfd/Kconfig | 22 ++++
drivers/mfd/Makefile | 2 +
drivers/mfd/ac200.c | 190 +++++++++++++++
drivers/mfd/sunxi-ac200.c | 289 +++++++++++++++++++++
drivers/net/phy/Kconfig | 15 +++
drivers/net/phy/Makefile | 2 +
drivers/net/phy/ac200-phy.c | 82 +++++++
include/linux/mfd/ac200.h | 213 ++++++++++++++++
8 files changed, 815 insertions(+)
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 6cec1858947b..dcfd6e5f04be 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -204,6 +204,28 @@ config MFD_AC100
This driver include only the core APIs. You have to select individual
components like codecs or RTC under the corresponding menus.
+config MFD_AC200
+ tristate "X-Powers AC200"
+ select MFD_CORE
+ select REGMAP_I2C
+ depends on COMMON_CLK
+ depends on I2C
+ depends on OF
+ help
+ If you say Y here you get support for the X-Powers AC200 IC.
+ This driver include only the core APIs. You have to select individual
+ components like Ethernet PHY or codec under the corresponding menus.
+
+config MFD_AC200_SUNXI
+ tristate "X-Powers AC200 (Sunxi)"
+ select MFD_CORE
+ depends on I2C
+ depends on PWM_SUNXI_ENHANCE
+ help
+ If you say Y here you get support for the X-Powers AC200 IC.
+ This driver include only the core APIs. You have to select individual
+ components like Ethernet PHY or RTC under the corresponding menus.
+
config MFD_AXP20X
tristate
select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 865e9f12faff..b02ff8c5691d 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -149,6 +149,8 @@ obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o
obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o
obj-$(CONFIG_MFD_AC100) += ac100.o
+obj-$(CONFIG_MFD_AC200) += ac200.o
+obj-$(CONFIG_MFD_AC200_SUNXI) += sunxi-ac200.o
obj-$(CONFIG_MFD_AXP20X) += axp20x.o
obj-$(CONFIG_MFD_AXP20X_I2C) += axp20x-i2c.o
obj-$(CONFIG_MFD_AXP20X_RSB) += axp20x-rsb.o
diff --git a/drivers/mfd/ac200.c b/drivers/mfd/ac200.c
new file mode 100644
index 000000000000..ad28c380c880
--- /dev/null
+++ b/drivers/mfd/ac200.c
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MFD core driver for X-Powers' AC200 IC
+ *
+ * The AC200 is a chip which is co-packaged with Allwinner H6 SoC and
+ * includes analog audio codec, analog TV encoder, ethernet PHY, eFuse
+ * and RTC.
+ *
+ * Copyright (c) 2019 Jernej Skrabec <jernej.skrabec@gmail.com>
+ *
+ * Based on AC100 driver with following copyrights:
+ * Copyright (2016) Chen-Yu Tsai
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+struct ac200_dev {
+ struct clk *clk;
+ struct regmap *regmap;
+};
+
+#define AC200_SYS_CONTROL 0x0002
+#define AC200_SYS_BG_CTL 0x0050
+
+/* interface register (can be accessed from any page) */
+#define AC200_TWI_REG_ADDR_H 0xFE
+
+#define AC200_MAX_REG 0xA1F2
+
+static const struct regmap_range_cfg ac200_range_cfg[] = {
+ {
+ .range_max = AC200_MAX_REG,
+ .selector_reg = AC200_TWI_REG_ADDR_H,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 256,
+ }
+};
+
+static const struct regmap_config ac200_regmap_config = {
+ .name = "AC200",
+ .reg_bits = 8,
+ .reg_stride = 2,
+ .val_bits = 16,
+ .ranges = ac200_range_cfg,
+ .num_ranges = ARRAY_SIZE(ac200_range_cfg),
+ .max_register = AC200_MAX_REG,
+};
+
+static struct mfd_cell ac200_cells[] = {
+ {
+ .name = "ac200-codec",
+ .of_compatible = "x-powers,ac200-codec",
+ }, {
+ .name = "ac200-ephy-ctl",
+ .of_compatible = "x-powers,ac200-ephy-ctl",
+ },
+};
+
+static int ac200_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct nvmem_cell *bgcell;
+ struct ac200_dev *ac200;
+ u16 *bgdata, bgval;
+ size_t bglen;
+ int ret;
+
+ ac200 = devm_kzalloc(dev, sizeof(*ac200), GFP_KERNEL);
+ if (!ac200)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, ac200);
+
+ ac200->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(ac200->clk))
+ return dev_err_probe(dev, PTR_ERR(ac200->clk),
+ "Can't obtain the clock\n");
+
+ ac200->regmap = devm_regmap_init_i2c(i2c, &ac200_regmap_config);
+ if (IS_ERR(ac200->regmap)) {
+ ret = PTR_ERR(ac200->regmap);
+ dev_err(dev, "Regmap init failed: %d\n", ret);
+ return ret;
+ }
+
+ bgcell = devm_nvmem_cell_get(dev, "bandgap");
+ if (IS_ERR(bgcell))
+ return dev_err_probe(dev, PTR_ERR(bgcell),
+ "Unable to find bandgap data!\n");
+
+ bgdata = nvmem_cell_read(bgcell, &bglen);
+ if (IS_ERR(bgdata)) {
+ dev_err(dev, "Unable to read bandgap data!\n");
+ return PTR_ERR(bgdata);
+ }
+
+ if (bglen != 2) {
+ dev_err(dev, "Invalid nvmem bandgap length!\n");
+ kfree(bgdata);
+ return -EINVAL;
+ }
+
+ bgval = *bgdata;
+ kfree(bgdata);
+
+ ret = clk_prepare_enable(ac200->clk);
+ if (ret)
+ return ret;
+
+ /*
+ * There is no documentation on how long we have to wait before
+ * executing first operation. Vendor driver sleeps for 40 ms.
+ */
+ msleep(40);
+
+ ret = regmap_write(ac200->regmap, AC200_SYS_CONTROL, 0);
+ if (ret)
+ goto err;
+
+ ret = regmap_write(ac200->regmap, AC200_SYS_CONTROL, 1);
+ if (ret)
+ goto err;
+
+ if (bgval) {
+ /* bandgap register is not documented */
+ ret = regmap_write(ac200->regmap, AC200_SYS_BG_CTL,
+ 0x8280 | bgval);
+ if (ret)
+ goto err;
+ }
+
+ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, ac200_cells,
+ ARRAY_SIZE(ac200_cells), NULL, 0, NULL);
+ if (ret) {
+ dev_err(dev, "Failed to add MFD devices: %d\n", ret);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ clk_disable_unprepare(ac200->clk);
+ return ret;
+}
+
+static void ac200_i2c_remove(struct i2c_client *i2c)
+{
+ struct ac200_dev *ac200 = i2c_get_clientdata(i2c);
+
+ regmap_write(ac200->regmap, AC200_SYS_CONTROL, 0);
+
+ clk_disable_unprepare(ac200->clk);
+}
+
+static const struct i2c_device_id ac200_ids[] = {
+ { "ac200", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ac200_ids);
+
+static const struct of_device_id ac200_of_match[] = {
+ { .compatible = "x-powers,ac200" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ac200_of_match);
+
+static struct i2c_driver ac200_i2c_driver = {
+ .driver = {
+ .name = "ac200",
+ .of_match_table = of_match_ptr(ac200_of_match),
+ },
+ .probe = ac200_i2c_probe,
+ .remove = ac200_i2c_remove,
+ .id_table = ac200_ids,
+};
+module_i2c_driver(ac200_i2c_driver);
+
+MODULE_DESCRIPTION("MFD core driver for AC200");
+MODULE_AUTHOR("Jernej Skrabec <jernej.skrabec@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/sunxi-ac200.c b/drivers/mfd/sunxi-ac200.c
new file mode 100644
index 000000000000..75c032ce147f
--- /dev/null
+++ b/drivers/mfd/sunxi-ac200.c
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MFD core driver for X-Powers' AC200 IC
+ *
+ * The AC200 is a chip which is co-packaged with Allwinner H6 SoC and
+ * includes analog audio codec, analog TV encoder, ethernet PHY, eFuse
+ * and RTC.
+ *
+ * Copyright (c) 2020 Jernej Skrabec <jernej.skrabec@siol.net>
+ */
+
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/ac200.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/nvmem-consumer.h>
+
+#define SUNXI_AC300_KEY (0x1 << 8)
+
+/* Interrupts */
+#define AC200_IRQ_RTC 0
+#define AC200_IRQ_EPHY 1
+#define AC200_IRQ_TVE 2
+
+/* IRQ enable register */
+#define AC200_SYS_IRQ_ENABLE_OUT_EN BIT(15)
+#define AC200_SYS_IRQ_ENABLE_RTC BIT(12)
+#define AC200_SYS_IRQ_ENABLE_EPHY BIT(8)
+#define AC200_SYS_IRQ_ENABLE_TVE BIT(4)
+
+static const struct regmap_range_cfg ac200_range_cfg[] = {
+ {
+ .range_min = AC200_SYS_VERSION,
+ .range_max = AC200_IC_CHARA1,
+ .selector_reg = AC200_TWI_REG_ADDR_H,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 256,
+ }
+};
+
+static const struct regmap_config ac200_regmap_config = {
+ .name = "ac200",
+ .reg_bits = 8,
+ .val_bits = 16,
+ .ranges = ac200_range_cfg,
+ .num_ranges = ARRAY_SIZE(ac200_range_cfg),
+ .max_register = AC200_IC_CHARA1,
+};
+
+static const struct regmap_irq ac200_regmap_irqs[] = {
+ REGMAP_IRQ_REG(AC200_IRQ_RTC, 0, AC200_SYS_IRQ_ENABLE_RTC),
+ REGMAP_IRQ_REG(AC200_IRQ_EPHY, 0, AC200_SYS_IRQ_ENABLE_EPHY),
+ REGMAP_IRQ_REG(AC200_IRQ_TVE, 0, AC200_SYS_IRQ_ENABLE_TVE),
+};
+
+static const struct regmap_irq_chip ac200_regmap_irq_chip = {
+ .name = "ac200_irq_chip",
+ .status_base = AC200_SYS_IRQ_STATUS,
+ .mask_base = AC200_SYS_IRQ_ENABLE,
+ .irqs = ac200_regmap_irqs,
+ .num_irqs = ARRAY_SIZE(ac200_regmap_irqs),
+ .num_regs = 1,
+};
+
+static const struct resource ephy_resource[] = {
+ DEFINE_RES_IRQ(AC200_IRQ_EPHY),
+};
+
+static const struct mfd_cell ac200_cells[] = {
+ {
+ .name = "ac200-ephy-sunxi",
+ .num_resources = ARRAY_SIZE(ephy_resource),
+ .resources = ephy_resource,
+ .of_compatible = "x-powers,ac200-ephy-sunxi",
+ },
+ {
+ .name = "acx00-codec",
+ .of_compatible = "x-powers,ac200-codec-sunxi",
+ },
+};
+
+atomic_t ac200_en;
+
+int ac200_enable(void)
+{
+ return atomic_read(&ac200_en);
+}
+
+EXPORT_SYMBOL(ac200_enable);
+
+static uint16_t ephy_caldata = 0;
+
+static int sun50i_ephy_get_calibrate(struct device *dev)
+{
+ struct nvmem_cell *calcell;
+ uint16_t *caldata;
+ size_t callen;
+ int ret = 0;
+
+ calcell = devm_nvmem_cell_get(dev, "calibration");
+ if (IS_ERR(calcell)) {
+ dev_err_probe(dev, PTR_ERR(calcell),
+ "Failed to get calibration nvmem cell (%pe)\n",
+ calcell);
+
+ if (PTR_ERR(calcell) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ goto out;
+ }
+
+ caldata = nvmem_cell_read(calcell, &callen);
+ if (IS_ERR(caldata)) {
+ ret = PTR_ERR(caldata);
+ dev_err(dev, "Failed to read calibration data (%pe)\n",
+ caldata);
+ goto out;
+ }
+
+ ephy_caldata = *caldata;
+ kfree(caldata);
+out:
+ return ret;
+}
+
+uint16_t sun50i_ephy_calibrate_value(void)
+{
+ return ephy_caldata;
+}
+
+EXPORT_SYMBOL(sun50i_ephy_calibrate_value);
+
+static int ac200_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct ac200_dev *ac200;
+ uint32_t ephy_cal;
+ int ret;
+
+ // 24Mhz clock for both ac200 and ac300 devices
+ ac200 = devm_kzalloc(dev, sizeof(*ac200), GFP_KERNEL);
+ if (!ac200)
+ return -ENOMEM;
+
+ ac200->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(ac200->clk)) {
+ dev_err(dev, "Can't obtain the clock!\n");
+ return PTR_ERR(ac200->clk);
+ }
+
+ ret = clk_prepare_enable(ac200->clk);
+ if (ret) {
+ dev_err(dev, "rclk_prepare_enable failed! \n");
+ return ret;
+ }
+
+ ret = sun50i_ephy_get_calibrate(dev);
+ if (ret) {
+ dev_err(dev, "sun50i get ephy id failed\n");
+ return ret;
+ }
+ ephy_cal = sun50i_ephy_calibrate_value();
+
+ if (ephy_cal & SUNXI_AC300_KEY) {
+ pr_warn("it's ac300, skip the ac200 init!\n");
+ return -EINVAL;
+ } else {
+ pr_warn("it's ac200, ac200 init start!\n");
+ }
+
+ i2c_set_clientdata(i2c, ac200);
+
+ ac200->regmap = devm_regmap_init_i2c(i2c, &ac200_regmap_config);
+ if (IS_ERR(ac200->regmap))
+ {
+ ret = PTR_ERR(ac200->regmap);
+ dev_err(dev, "regmap init failed: %d\n", ret);
+ return ret;
+ }
+
+ /* do a reset to put chip in a known state */
+ ret = regmap_write(ac200->regmap, AC200_SYS_CONTROL, 0);
+ if (ret)
+ {
+ dev_err(dev, "AC200_SYS_CONTROL 0 failed! \n");
+ return ret;
+ }
+ atomic_set(&ac200_en, 0);
+
+ ret = regmap_write(ac200->regmap, AC200_SYS_CONTROL, 1);
+ if (ret)
+ {
+ dev_err(dev, "AC200_SYS_CONTROL 1 failed! \n");
+ return ret;
+ }
+ atomic_set(&ac200_en, 1);
+
+ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, ac200_cells,
+ ARRAY_SIZE(ac200_cells), NULL, 0, NULL);
+ if (ret)
+ {
+ dev_err(dev, "failed to add MFD devices: %d\n", ret);
+ return ret;
+ }
+ else
+ {
+ dev_err(dev, "add MFD devices success! \n");
+ }
+
+ return 0;
+}
+
+static void ac200_i2c_remove(struct i2c_client *i2c)
+{
+ struct ac200_dev *ac200 = i2c_get_clientdata(i2c);
+
+ regmap_write(ac200->regmap, AC200_SYS_CONTROL, 0);
+ mfd_remove_devices(&i2c->dev);
+}
+
+static void ac200_i2c_shutdown(struct i2c_client *i2c)
+{
+ struct ac200_dev *ac200 = i2c_get_clientdata(i2c);
+
+ regmap_write(ac200->regmap, AC200_SYS_CONTROL, 0);
+}
+
+static int ac200_i2c_suspend(struct device *dev)
+{
+ struct ac200_dev *ac200 = dev_get_drvdata(dev);
+
+ if (!IS_ERR_OR_NULL(ac200->clk))
+ clk_disable_unprepare(ac200->clk);
+
+ atomic_set(&ac200_en, 0);
+ return 0;
+}
+
+static int ac200_i2c_resume(struct device *dev)
+{
+ struct ac200_dev *ac200 = dev_get_drvdata(dev);
+
+ if (!IS_ERR_OR_NULL(ac200->clk))
+ clk_prepare_enable(ac200->clk);
+
+ atomic_set(&ac200_en, 0);
+ msleep(40);
+ regmap_write(ac200->regmap, AC200_SYS_CONTROL, 1);
+ atomic_set(&ac200_en, 1);
+ return 0;
+}
+
+static const struct dev_pm_ops ac200_core_pm_ops = {
+ .suspend_late = ac200_i2c_suspend,
+ .resume_early = ac200_i2c_resume,
+};
+
+static const struct i2c_device_id ac200_ids[] = {
+ { "ac200", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, ac200_ids);
+
+static const struct of_device_id ac200_of_match[] = {
+ { .compatible = "x-powers,ac200-sunxi" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ac200_of_match);
+
+static struct i2c_driver ac200_i2c_driver = {
+ .driver = {
+ .name = "ac200-sunxi",
+ .of_match_table = of_match_ptr(ac200_of_match),
+ .pm = &ac200_core_pm_ops,
+ },
+ .probe = ac200_i2c_probe,
+ .remove = ac200_i2c_remove,
+ .shutdown = ac200_i2c_shutdown,
+ .id_table = ac200_ids,
+};
+module_i2c_driver(ac200_i2c_driver);
+
+MODULE_DESCRIPTION("MFD core driver for AC200");
+MODULE_AUTHOR("Jernej Skrabec <jernej.skrabec@siol.net>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 98700d069191..ad85ec8822ed 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -101,6 +101,21 @@ config AIR_EN8811H_PHY
help
Currently supports the Airoha EN8811H PHY.
+config AC200_PHY
+ tristate "AC200 EPHY"
+ depends on NVMEM
+ depends on OF
+ help
+ Fast ethernet PHY as found in X-Powers AC200 multi-function device.
+
+config AC200_PHY_SUNXI
+ tristate "AC200 EPHY(Sunxi)"
+ depends on NVMEM
+ depends on OF
+ depends on MFD_AC200_SUNXI
+ help
+ Fast ethernet PHY as found in X-Powers AC200(Sunxi) multi-function device.
+
config AMD_PHY
tristate "AMD and Altima PHYs"
help
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 76e0db40f879..707651e20d50 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -27,6 +27,8 @@ obj-$(CONFIG_SFP) += sfp.o
sfp-obj-$(CONFIG_SFP) += sfp-bus.o
obj-y += $(sfp-obj-y) $(sfp-obj-m)
+obj-$(CONFIG_AC200_PHY) += ac200-phy.o
+obj-$(CONFIG_AC200_PHY_SUNXI) += sunxi-ephy.o
obj-$(CONFIG_ADIN_PHY) += adin.o
obj-$(CONFIG_ADIN1100_PHY) += adin1100.o
obj-$(CONFIG_AIR_EN8811H_PHY) += air_en8811h.o
diff --git a/drivers/net/phy/ac200-phy.c b/drivers/net/phy/ac200-phy.c
new file mode 100644
index 000000000000..8499914f49b8
--- /dev/null
+++ b/drivers/net/phy/ac200-phy.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0+
+/**
+ * Driver for AC200 Ethernet PHY
+ *
+ * Copyright (c) 2019 Jernej Skrabec <jernej.skrabec@gmail.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/phy.h>
+
+#define AC200_EPHY_ID 0x00441400
+#define AC200_EPHY_ID_MASK 0x0ffffff0
+
+static int ac200_ephy_config_init(struct phy_device *phydev)
+{
+ phy_write(phydev, 0x1f, 0x0100); /* Switch to Page 1 */
+ phy_write(phydev, 0x12, 0x4824); /* Disable APS */
+
+ phy_write(phydev, 0x1f, 0x0200); /* Switch to Page 2 */
+ phy_write(phydev, 0x18, 0x0000); /* PHYAFE TRX optimization */
+
+ phy_write(phydev, 0x1f, 0x0600); /* Switch to Page 6 */
+ phy_write(phydev, 0x14, 0x708f); /* PHYAFE TX optimization */
+ phy_write(phydev, 0x13, 0xF000); /* PHYAFE RX optimization */
+ phy_write(phydev, 0x15, 0x1530);
+
+ phy_write(phydev, 0x1f, 0x0800); /* Switch to Page 8 */
+ phy_write(phydev, 0x18, 0x00bc); /* PHYAFE TRX optimization */
+
+ phy_write(phydev, 0x1f, 0x0100); /* switch to page 1 */
+ phy_clear_bits(phydev, 0x17, BIT(3)); /* disable intelligent EEE */
+
+ /* disable 802.3az EEE */
+ phy_write(phydev, 0x1f, 0x0200); /* switch to page 2 */
+ phy_write(phydev, 0x18, 0x0000);
+ phy_write(phydev, 0x1f, 0x0000); /* switch to page 0 */
+ phy_clear_bits_mmd(phydev, 0x7, 0x3c, BIT(1));
+
+ /* FIXME: This is probably H6 specific */
+ phy_set_bits(phydev, 0x13, BIT(12));
+
+ return 0;
+}
+
+static int ac200_ephy_probe(struct phy_device *phydev)
+{
+ struct device *dev = &phydev->mdio.dev;
+ struct clk *clk;
+
+ clk = devm_clk_get_optional_enabled(dev, NULL);
+ if (IS_ERR(clk))
+ return dev_err_probe(dev, PTR_ERR(clk),
+ "Failed to request clock\n");
+
+ return 0;
+}
+
+static struct phy_driver ac200_ephy_driver[] = {
+ {
+ .phy_id = AC200_EPHY_ID,
+ .phy_id_mask = AC200_EPHY_ID_MASK,
+ .name = "Allwinner AC200 EPHY",
+ .soft_reset = genphy_soft_reset,
+ .config_init = ac200_ephy_config_init,
+ .probe = ac200_ephy_probe,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ }
+};
+module_phy_driver(ac200_ephy_driver);
+
+MODULE_AUTHOR("Jernej Skrabec <jernej.skrabec@gmail.com>");
+MODULE_DESCRIPTION("AC200 Ethernet PHY driver");
+MODULE_LICENSE("GPL");
+
+static const struct mdio_device_id __maybe_unused ac200_ephy_phy_tbl[] = {
+ { AC200_EPHY_ID, AC200_EPHY_ID_MASK },
+ { }
+};
+MODULE_DEVICE_TABLE(mdio, ac200_ephy_phy_tbl);
diff --git a/include/linux/mfd/ac200.h b/include/linux/mfd/ac200.h
new file mode 100644
index 000000000000..84d89cbfbd3d
--- /dev/null
+++ b/include/linux/mfd/ac200.h
@@ -0,0 +1,213 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * AC200 register list
+ *
+ * Copyright (C) 2019 Jernej Skrabec <jernej.skrabec@siol.net>
+ */
+
+#ifndef __LINUX_MFD_AC200_H
+#define __LINUX_MFD_AC200_H
+
+#include <linux/regmap.h>
+#include <linux/clk.h>
+
+/* interface registers (can be accessed from any page) */
+#define AC200_TWI_CHANGE_TO_RSB 0x3E
+#define AC200_TWI_PAD_DELAY 0xC4
+#define AC200_TWI_REG_ADDR_H 0xFE
+
+/* General registers */
+#define AC200_SYS_VERSION 0x0000
+#define AC200_SYS_CONTROL 0x0002
+#define AC200_SYS_IRQ_ENABLE 0x0004
+#define AC200_SYS_IRQ_STATUS 0x0006
+#define AC200_SYS_CLK_CTL 0x0008
+#define AC200_SYS_DLDO_OSC_CTL 0x000A
+#define AC200_SYS_PLL_CTL0 0x000C
+#define AC200_SYS_PLL_CTL1 0x000E
+#define AC200_SYS_AUDIO_CTL0 0x0010
+#define AC200_SYS_AUDIO_CTL1 0x0012
+#define AC200_SYS_EPHY_CTL0 0x0014
+#define AC200_SYS_EPHY_CTL1 0x0016
+#define AC200_SYS_TVE_CTL0 0x0018
+#define AC200_SYS_TVE_CTL1 0x001A
+
+/* Audio Codec registers */
+#define AC200_AC_SYS_CLK_CTL 0x2000
+#define AC200_SYS_MOD_RST 0x2002
+#define AC200_SYS_SAMP_CTL 0x2004
+#define AC200_I2S_CTL 0x2100
+#define AC200_I2S_CLK 0x2102
+#define AC200_I2S_FMT0 0x2104
+#define AC200_I2S_FMT1 0x2108
+#define AC200_I2S_MIX_SRC 0x2114
+#define AC200_I2S_MIX_GAIN 0x2116
+#define AC200_I2S_DACDAT_DVC 0x2118
+#define AC200_I2S_ADCDAT_DVC 0x211A
+#define AC200_AC_DAC_DPC 0x2200
+#define AC200_AC_DAC_MIX_SRC 0x2202
+#define AC200_AC_DAC_MIX_GAIN 0x2204
+#define AC200_DACA_OMIXER_CTRL 0x2220
+#define AC200_OMIXER_SR 0x2222
+#define AC200_LINEOUT_CTRL 0x2224
+#define AC200_AC_ADC_DPC 0x2300
+#define AC200_MBIAS_CTRL 0x2310
+#define AC200_ADC_MIC_CTRL 0x2320
+#define AC200_ADCMIXER_SR 0x2322
+#define AC200_ANALOG_TUNING0 0x232A
+#define AC200_ANALOG_TUNING1 0x232C
+#define AC200_AC_AGC_SEL 0x2480
+#define AC200_ADC_DAPLCTRL 0x2500
+#define AC200_ADC_DAPRCTRL 0x2502
+#define AC200_ADC_DAPLSTA 0x2504
+#define AC200_ADC_DAPRSTA 0x2506
+#define AC200_ADC_DAPLTL 0x2508
+#define AC200_ADC_DAPRTL 0x250A
+#define AC200_ADC_DAPLHAC 0x250C
+#define AC200_ADC_DAPLLAC 0x250E
+#define AC200_ADC_DAPRHAC 0x2510
+#define AC200_ADC_DAPRLAC 0x2512
+#define AC200_ADC_DAPLDT 0x2514
+#define AC200_ADC_DAPLAT 0x2516
+#define AC200_ADC_DAPRDT 0x2518
+#define AC200_ADC_DAPRAT 0x251A
+#define AC200_ADC_DAPNTH 0x251C
+#define AC200_ADC_DAPLHNAC 0x251E
+#define AC200_ADC_DAPLLNAC 0x2520
+#define AC200_ADC_DAPRHNAC 0x2522
+#define AC200_ADC_DAPRLNAC 0x2524
+#define AC200_AC_DAPHHPFC 0x2526
+#define AC200_AC_DAPLHPFC 0x2528
+#define AC200_AC_DAPOPT 0x252A
+#define AC200_AC_DAC_DAPCTRL 0x3000
+#define AC200_AC_DRC_HHPFC 0x3002
+#define AC200_AC_DRC_LHPFC 0x3004
+#define AC200_AC_DRC_CTRL 0x3006
+#define AC200_AC_DRC_LPFHAT 0x3008
+#define AC200_AC_DRC_LPFLAT 0x300A
+#define AC200_AC_DRC_RPFHAT 0x300C
+#define AC200_AC_DRC_RPFLAT 0x300E
+#define AC200_AC_DRC_LPFHRT 0x3010
+#define AC200_AC_DRC_LPFLRT 0x3012
+#define AC200_AC_DRC_RPFHRT 0x3014
+#define AC200_AC_DRC_RPFLRT 0x3016
+#define AC200_AC_DRC_LRMSHAT 0x3018
+#define AC200_AC_DRC_LRMSLAT 0x301A
+#define AC200_AC_DRC_RRMSHAT 0x301C
+#define AC200_AC_DRC_RRMSLAT 0x301E
+#define AC200_AC_DRC_HCT 0x3020
+#define AC200_AC_DRC_LCT 0x3022
+#define AC200_AC_DRC_HKC 0x3024
+#define AC200_AC_DRC_LKC 0x3026
+#define AC200_AC_DRC_HOPC 0x3028
+#define AC200_AC_DRC_LOPC 0x302A
+#define AC200_AC_DRC_HLT 0x302C
+#define AC200_AC_DRC_LLT 0x302E
+#define AC200_AC_DRC_HKI 0x3030
+#define AC200_AC_DRC_LKI 0x3032
+#define AC200_AC_DRC_HOPL 0x3034
+#define AC200_AC_DRC_LOPL 0x3036
+#define AC200_AC_DRC_HET 0x3038
+#define AC200_AC_DRC_LET 0x303A
+#define AC200_AC_DRC_HKE 0x303C
+#define AC200_AC_DRC_LKE 0x303E
+#define AC200_AC_DRC_HOPE 0x3040
+#define AC200_AC_DRC_LOPE 0x3042
+#define AC200_AC_DRC_HKN 0x3044
+#define AC200_AC_DRC_LKN 0x3046
+#define AC200_AC_DRC_SFHAT 0x3048
+#define AC200_AC_DRC_SFLAT 0x304A
+#define AC200_AC_DRC_SFHRT 0x304C
+#define AC200_AC_DRC_SFLRT 0x304E
+#define AC200_AC_DRC_MXGHS 0x3050
+#define AC200_AC_DRC_MXGLS 0x3052
+#define AC200_AC_DRC_MNGHS 0x3054
+#define AC200_AC_DRC_MNGLS 0x3056
+#define AC200_AC_DRC_EPSHC 0x3058
+#define AC200_AC_DRC_EPSLC 0x305A
+#define AC200_AC_DRC_HPFHGAIN 0x305E
+#define AC200_AC_DRC_HPFLGAIN 0x3060
+#define AC200_AC_DRC_BISTCR 0x3100
+#define AC200_AC_DRC_BISTST 0x3102
+
+/* TVE registers */
+#define AC200_TVE_CTL0 0x4000
+#define AC200_TVE_CTL1 0x4002
+#define AC200_TVE_MOD0 0x4004
+#define AC200_TVE_MOD1 0x4006
+#define AC200_TVE_DAC_CFG0 0x4008
+#define AC200_TVE_DAC_CFG1 0x400A
+#define AC200_TVE_YC_DELAY 0x400C
+#define AC200_TVE_YC_FILTER 0x400E
+#define AC200_TVE_BURST_FRQ0 0x4010
+#define AC200_TVE_BURST_FRQ1 0x4012
+#define AC200_TVE_FRONT_PORCH 0x4014
+#define AC200_TVE_BACK_PORCH 0x4016
+#define AC200_TVE_TOTAL_LINE 0x401C
+#define AC200_TVE_FIRST_ACTIVE 0x401E
+#define AC200_TVE_BLACK_LEVEL 0x4020
+#define AC200_TVE_BLANK_LEVEL 0x4022
+#define AC200_TVE_PLUG_EN 0x4030
+#define AC200_TVE_PLUG_IRQ_EN 0x4032
+#define AC200_TVE_PLUG_IRQ_STA 0x4034
+#define AC200_TVE_PLUG_STA 0x4038
+#define AC200_TVE_PLUG_DEBOUNCE 0x4040
+#define AC200_TVE_DAC_TEST 0x4042
+#define AC200_TVE_PLUG_PULSE_LEVEL 0x40F4
+#define AC200_TVE_PLUG_PULSE_START 0x40F8
+#define AC200_TVE_PLUG_PULSE_PERIOD 0x40FA
+#define AC200_TVE_IF_CTL 0x5000
+#define AC200_TVE_IF_TIM0 0x5008
+#define AC200_TVE_IF_TIM1 0x500A
+#define AC200_TVE_IF_TIM2 0x500C
+#define AC200_TVE_IF_TIM3 0x500E
+#define AC200_TVE_IF_SYNC0 0x5010
+#define AC200_TVE_IF_SYNC1 0x5012
+#define AC200_TVE_IF_SYNC2 0x5014
+#define AC200_TVE_IF_TIM4 0x5016
+#define AC200_TVE_IF_STATUS 0x5018
+
+/* EPHY registers */
+#define AC200_EPHY_CTL 0x6000
+#define AC200_EPHY_BIST 0x6002
+
+/* eFuse registers (0x8000 - 0x9FFF, layout unknown) */
+
+/* RTC registers */
+#define AC200_LOSC_CTRL0 0xA000
+#define AC200_LOSC_CTRL1 0xA002
+#define AC200_LOSC_AUTO_SWT_STA 0xA004
+#define AC200_INTOSC_CLK_PRESCAL 0xA008
+#define AC200_RTC_YY_MM_DD0 0xA010
+#define AC200_RTC_YY_MM_DD1 0xA012
+#define AC200_RTC_HH_MM_SS0 0xA014
+#define AC200_RTC_HH_MM_SS1 0xA016
+#define AC200_ALARM0_CUR_VLU0 0xA024
+#define AC200_ALARM0_CUR_VLU1 0xA026
+#define AC200_ALARM0_ENABLE 0xA028
+#define AC200_ALARM0_IRQ_EN 0xA02C
+#define AC200_ALARM0_IRQ_STA 0xA030
+#define AC200_ALARM1_WK_HH_MM_SS0 0xA040
+#define AC200_ALARM1_WK_HH_MM_SS1 0xA042
+#define AC200_ALARM1_ENABLE 0xA044
+#define AC200_ALARM1_IRQ_EN 0xA048
+#define AC200_ALARM1_IRQ_STA 0xA04C
+#define AC200_ALARM_CONFIG 0xA050
+#define AC200_LOSC_OUT_GATING 0xA060
+#define AC200_GP_DATA(x) (0xA100 + (x) * 2)
+#define AC200_RTC_DEB 0xA170
+#define AC200_GPL_HOLD_OUTPUT 0xA180
+#define AC200_VDD_RTC 0xA190
+#define AC200_IC_CHARA0 0xA1F0
+#define AC200_IC_CHARA1 0xA1F2
+
+struct ac200_dev {
+ struct clk *clk;
+ struct regmap *regmap;
+ struct regmap_irq_chip_data *regmap_irqc;
+};
+
+extern int ac200_enable(void);
+extern uint16_t sun50i_ephy_calibrate_value(void);
+
+#endif /* __LINUX_MFD_AC200_H */
--
Armbian

View File

@ -1,27 +1,30 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: chraac <chraac@gmail.com>
Date: Fri, 16 Aug 2024 16:44:41 +0800
Subject: driver: allwinner h618 emac
Subject: drv:net:stmmac:sun8i: Add H618 EMAC support
Add GMAC (sunxi-gmac) driver for Allwinner H618 and related SoCs,
along with the internal EPHY driver (sunxi-ephy) and GPIO enhancements
for PHY reset control.
The GMAC driver supports the Allwinner Ethernet MAC controller with
RMII/RGMII interfaces and works with both internal EPHY and external
PHYs including the AC200.
Signed-off-by: chraac <chraac@gmail.com>
---
drivers/gpio/gpiolib-of.c | 29 +-
drivers/mfd/Kconfig | 10 +
drivers/mfd/Makefile | 1 +
drivers/mfd/sunxi-ac200.c | 289 ++
drivers/net/ethernet/allwinner/Kconfig | 8 +
drivers/net/ethernet/allwinner/Makefile | 2 +
drivers/net/ethernet/allwinner/sunxi-gmac.c | 2217 ++++++++++
drivers/net/ethernet/allwinner/sunxi-gmac.h | 270 ++
drivers/net/ethernet/allwinner/sunxi_gmac_ops.c | 768 ++++
drivers/net/phy/Kconfig | 8 +
drivers/net/phy/Makefile | 1 +
drivers/net/phy/sunxi-ephy.c | 516 +++
include/linux/mfd/ac200.h | 213 +
drivers/net/ethernet/allwinner/sunxi-gmac.c | 2217 ++++++++++++++++++
drivers/net/ethernet/allwinner/sunxi-gmac.h | 270 +++
drivers/net/ethernet/allwinner/sunxi_gmac_ops.c | 768 ++++++
drivers/net/phy/sunxi-ephy.c | 516 ++++++
include/linux/of_gpio.h | 18 +
14 files changed, 4335 insertions(+), 15 deletions(-)
8 files changed, 3813 insertions(+), 15 deletions(-)
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index 111111111111..222222222222 100644
index fad4edf9cc5c..9b68a7d440c2 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -25,21 +25,6 @@
@ -67,336 +70,8 @@ index 111111111111..222222222222 100644
/**
* of_get_named_gpio() - Get a GPIO number to use with GPIO API
* @np: device node to get GPIO from
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 111111111111..222222222222 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -216,6 +216,16 @@ config MFD_AC200
This driver include only the core APIs. You have to select individual
components like Ethernet PHY or codec under the corresponding menus.
+config MFD_AC200_SUNXI
+ tristate "X-Powers AC200 (Sunxi)"
+ select MFD_CORE
+ depends on I2C
+ depends on PWM_SUNXI_ENHANCE
+ help
+ If you say Y here you get support for the X-Powers AC200 IC.
+ This driver include only the core APIs. You have to select individual
+ components like Ethernet PHY or RTC under the corresponding menus.
+
config MFD_AXP20X
tristate
select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 111111111111..222222222222 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -150,6 +150,7 @@ obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o
obj-$(CONFIG_MFD_AC100) += ac100.o
obj-$(CONFIG_MFD_AC200) += ac200.o
+obj-$(CONFIG_MFD_AC200_SUNXI) += sunxi-ac200.o
obj-$(CONFIG_MFD_AXP20X) += axp20x.o
obj-$(CONFIG_MFD_AXP20X_I2C) += axp20x-i2c.o
obj-$(CONFIG_MFD_AXP20X_RSB) += axp20x-rsb.o
diff --git a/drivers/mfd/sunxi-ac200.c b/drivers/mfd/sunxi-ac200.c
new file mode 100644
index 000000000000..111111111111
--- /dev/null
+++ b/drivers/mfd/sunxi-ac200.c
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MFD core driver for X-Powers' AC200 IC
+ *
+ * The AC200 is a chip which is co-packaged with Allwinner H6 SoC and
+ * includes analog audio codec, analog TV encoder, ethernet PHY, eFuse
+ * and RTC.
+ *
+ * Copyright (c) 2020 Jernej Skrabec <jernej.skrabec@siol.net>
+ */
+
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/ac200.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/nvmem-consumer.h>
+
+#define SUNXI_AC300_KEY (0x1 << 8)
+
+/* Interrupts */
+#define AC200_IRQ_RTC 0
+#define AC200_IRQ_EPHY 1
+#define AC200_IRQ_TVE 2
+
+/* IRQ enable register */
+#define AC200_SYS_IRQ_ENABLE_OUT_EN BIT(15)
+#define AC200_SYS_IRQ_ENABLE_RTC BIT(12)
+#define AC200_SYS_IRQ_ENABLE_EPHY BIT(8)
+#define AC200_SYS_IRQ_ENABLE_TVE BIT(4)
+
+static const struct regmap_range_cfg ac200_range_cfg[] = {
+ {
+ .range_min = AC200_SYS_VERSION,
+ .range_max = AC200_IC_CHARA1,
+ .selector_reg = AC200_TWI_REG_ADDR_H,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 256,
+ }
+};
+
+static const struct regmap_config ac200_regmap_config = {
+ .name = "ac200",
+ .reg_bits = 8,
+ .val_bits = 16,
+ .ranges = ac200_range_cfg,
+ .num_ranges = ARRAY_SIZE(ac200_range_cfg),
+ .max_register = AC200_IC_CHARA1,
+};
+
+static const struct regmap_irq ac200_regmap_irqs[] = {
+ REGMAP_IRQ_REG(AC200_IRQ_RTC, 0, AC200_SYS_IRQ_ENABLE_RTC),
+ REGMAP_IRQ_REG(AC200_IRQ_EPHY, 0, AC200_SYS_IRQ_ENABLE_EPHY),
+ REGMAP_IRQ_REG(AC200_IRQ_TVE, 0, AC200_SYS_IRQ_ENABLE_TVE),
+};
+
+static const struct regmap_irq_chip ac200_regmap_irq_chip = {
+ .name = "ac200_irq_chip",
+ .status_base = AC200_SYS_IRQ_STATUS,
+ .mask_base = AC200_SYS_IRQ_ENABLE,
+ .irqs = ac200_regmap_irqs,
+ .num_irqs = ARRAY_SIZE(ac200_regmap_irqs),
+ .num_regs = 1,
+};
+
+static const struct resource ephy_resource[] = {
+ DEFINE_RES_IRQ(AC200_IRQ_EPHY),
+};
+
+static const struct mfd_cell ac200_cells[] = {
+ {
+ .name = "ac200-ephy-sunxi",
+ .num_resources = ARRAY_SIZE(ephy_resource),
+ .resources = ephy_resource,
+ .of_compatible = "x-powers,ac200-ephy-sunxi",
+ },
+ {
+ .name = "acx00-codec",
+ .of_compatible = "x-powers,ac200-codec-sunxi",
+ },
+};
+
+atomic_t ac200_en;
+
+int ac200_enable(void)
+{
+ return atomic_read(&ac200_en);
+}
+
+EXPORT_SYMBOL(ac200_enable);
+
+static uint16_t ephy_caldata = 0;
+
+static int sun50i_ephy_get_calibrate(struct device *dev)
+{
+ struct nvmem_cell *calcell;
+ uint16_t *caldata;
+ size_t callen;
+ int ret = 0;
+
+ calcell = devm_nvmem_cell_get(dev, "calibration");
+ if (IS_ERR(calcell)) {
+ dev_err_probe(dev, PTR_ERR(calcell),
+ "Failed to get calibration nvmem cell (%pe)\n",
+ calcell);
+
+ if (PTR_ERR(calcell) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ goto out;
+ }
+
+ caldata = nvmem_cell_read(calcell, &callen);
+ if (IS_ERR(caldata)) {
+ ret = PTR_ERR(caldata);
+ dev_err(dev, "Failed to read calibration data (%pe)\n",
+ caldata);
+ goto out;
+ }
+
+ ephy_caldata = *caldata;
+ kfree(caldata);
+out:
+ return ret;
+}
+
+uint16_t sun50i_ephy_calibrate_value(void)
+{
+ return ephy_caldata;
+}
+
+EXPORT_SYMBOL(sun50i_ephy_calibrate_value);
+
+static int ac200_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct ac200_dev *ac200;
+ uint32_t ephy_cal;
+ int ret;
+
+ // 24Mhz clock for both ac200 and ac300 devices
+ ac200 = devm_kzalloc(dev, sizeof(*ac200), GFP_KERNEL);
+ if (!ac200)
+ return -ENOMEM;
+
+ ac200->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(ac200->clk)) {
+ dev_err(dev, "Can't obtain the clock!\n");
+ return PTR_ERR(ac200->clk);
+ }
+
+ ret = clk_prepare_enable(ac200->clk);
+ if (ret) {
+ dev_err(dev, "rclk_prepare_enable failed! \n");
+ return ret;
+ }
+
+ ret = sun50i_ephy_get_calibrate(dev);
+ if (ret) {
+ dev_err(dev, "sun50i get ephy id failed\n");
+ return ret;
+ }
+ ephy_cal = sun50i_ephy_calibrate_value();
+
+ if (ephy_cal & SUNXI_AC300_KEY) {
+ pr_warn("it's ac300, skip the ac200 init!\n");
+ return -EINVAL;
+ } else {
+ pr_warn("it's ac200, ac200 init start!\n");
+ }
+
+ i2c_set_clientdata(i2c, ac200);
+
+ ac200->regmap = devm_regmap_init_i2c(i2c, &ac200_regmap_config);
+ if (IS_ERR(ac200->regmap))
+ {
+ ret = PTR_ERR(ac200->regmap);
+ dev_err(dev, "regmap init failed: %d\n", ret);
+ return ret;
+ }
+
+ /* do a reset to put chip in a known state */
+ ret = regmap_write(ac200->regmap, AC200_SYS_CONTROL, 0);
+ if (ret)
+ {
+ dev_err(dev, "AC200_SYS_CONTROL 0 failed! \n");
+ return ret;
+ }
+ atomic_set(&ac200_en, 0);
+
+ ret = regmap_write(ac200->regmap, AC200_SYS_CONTROL, 1);
+ if (ret)
+ {
+ dev_err(dev, "AC200_SYS_CONTROL 1 failed! \n");
+ return ret;
+ }
+ atomic_set(&ac200_en, 1);
+
+ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, ac200_cells,
+ ARRAY_SIZE(ac200_cells), NULL, 0, NULL);
+ if (ret)
+ {
+ dev_err(dev, "failed to add MFD devices: %d\n", ret);
+ return ret;
+ }
+ else
+ {
+ dev_err(dev, "add MFD devices success! \n");
+ }
+
+ return 0;
+}
+
+static void ac200_i2c_remove(struct i2c_client *i2c)
+{
+ struct ac200_dev *ac200 = i2c_get_clientdata(i2c);
+
+ regmap_write(ac200->regmap, AC200_SYS_CONTROL, 0);
+ mfd_remove_devices(&i2c->dev);
+}
+
+static void ac200_i2c_shutdown(struct i2c_client *i2c)
+{
+ struct ac200_dev *ac200 = i2c_get_clientdata(i2c);
+
+ regmap_write(ac200->regmap, AC200_SYS_CONTROL, 0);
+}
+
+static int ac200_i2c_suspend(struct device *dev)
+{
+ struct ac200_dev *ac200 = dev_get_drvdata(dev);
+
+ if (!IS_ERR_OR_NULL(ac200->clk))
+ clk_disable_unprepare(ac200->clk);
+
+ atomic_set(&ac200_en, 0);
+ return 0;
+}
+
+static int ac200_i2c_resume(struct device *dev)
+{
+ struct ac200_dev *ac200 = dev_get_drvdata(dev);
+
+ if (!IS_ERR_OR_NULL(ac200->clk))
+ clk_prepare_enable(ac200->clk);
+
+ atomic_set(&ac200_en, 0);
+ msleep(40);
+ regmap_write(ac200->regmap, AC200_SYS_CONTROL, 1);
+ atomic_set(&ac200_en, 1);
+ return 0;
+}
+
+static const struct dev_pm_ops ac200_core_pm_ops = {
+ .suspend_late = ac200_i2c_suspend,
+ .resume_early = ac200_i2c_resume,
+};
+
+static const struct i2c_device_id ac200_ids[] = {
+ { "ac200", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, ac200_ids);
+
+static const struct of_device_id ac200_of_match[] = {
+ { .compatible = "x-powers,ac200-sunxi" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ac200_of_match);
+
+static struct i2c_driver ac200_i2c_driver = {
+ .driver = {
+ .name = "ac200-sunxi",
+ .of_match_table = of_match_ptr(ac200_of_match),
+ .pm = &ac200_core_pm_ops,
+ },
+ .probe = ac200_i2c_probe,
+ .remove = ac200_i2c_remove,
+ .shutdown = ac200_i2c_shutdown,
+ .id_table = ac200_ids,
+};
+module_i2c_driver(ac200_i2c_driver);
+
+MODULE_DESCRIPTION("MFD core driver for AC200");
+MODULE_AUTHOR("Jernej Skrabec <jernej.skrabec@siol.net>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/ethernet/allwinner/Kconfig b/drivers/net/ethernet/allwinner/Kconfig
index 111111111111..222222222222 100644
index 3e81059f8693..36d808810ca4 100644
--- a/drivers/net/ethernet/allwinner/Kconfig
+++ b/drivers/net/ethernet/allwinner/Kconfig
@@ -34,4 +34,12 @@ config SUN4I_EMAC
@ -413,7 +88,7 @@ index 111111111111..222222222222 100644
+
endif # NET_VENDOR_ALLWINNER
diff --git a/drivers/net/ethernet/allwinner/Makefile b/drivers/net/ethernet/allwinner/Makefile
index 111111111111..222222222222 100644
index ddd5a5079e8a..56b9c434a5b8 100644
--- a/drivers/net/ethernet/allwinner/Makefile
+++ b/drivers/net/ethernet/allwinner/Makefile
@@ -4,3 +4,5 @@
@ -424,7 +99,7 @@ index 111111111111..222222222222 100644
+obj-$(CONFIG_SUNXI_GMAC) += sunxi_gmac.o
diff --git a/drivers/net/ethernet/allwinner/sunxi-gmac.c b/drivers/net/ethernet/allwinner/sunxi-gmac.c
new file mode 100644
index 000000000000..111111111111
index 000000000000..249cee0607e3
--- /dev/null
+++ b/drivers/net/ethernet/allwinner/sunxi-gmac.c
@@ -0,0 +1,2217 @@
@ -2647,7 +2322,7 @@ index 000000000000..111111111111
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/ethernet/allwinner/sunxi-gmac.h b/drivers/net/ethernet/allwinner/sunxi-gmac.h
new file mode 100644
index 000000000000..111111111111
index 000000000000..0ba8977d28f4
--- /dev/null
+++ b/drivers/net/ethernet/allwinner/sunxi-gmac.h
@@ -0,0 +1,270 @@
@ -2923,7 +2598,7 @@ index 000000000000..111111111111
+#endif
diff --git a/drivers/net/ethernet/allwinner/sunxi_gmac_ops.c b/drivers/net/ethernet/allwinner/sunxi_gmac_ops.c
new file mode 100644
index 000000000000..111111111111
index 000000000000..926516835023
--- /dev/null
+++ b/drivers/net/ethernet/allwinner/sunxi_gmac_ops.c
@@ -0,0 +1,768 @@
@ -3695,40 +3370,9 @@ index 000000000000..111111111111
+ return ret;
+}
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 111111111111..222222222222 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -108,6 +108,14 @@ config AC200_PHY
help
Fast ethernet PHY as found in X-Powers AC200 multi-function device.
+config AC200_PHY_SUNXI
+ tristate "AC200 EPHY(Sunxi)"
+ depends on NVMEM
+ depends on OF
+ depends on MFD_AC200_SUNXI
+ help
+ Fast ethernet PHY as found in X-Powers AC200(Sunxi) multi-function device.
+
config AMD_PHY
tristate "AMD and Altima PHYs"
help
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 111111111111..222222222222 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -28,6 +28,7 @@ sfp-obj-$(CONFIG_SFP) += sfp-bus.o
obj-y += $(sfp-obj-y) $(sfp-obj-m)
obj-$(CONFIG_AC200_PHY) += ac200-phy.o
+obj-$(CONFIG_AC200_PHY_SUNXI) += sunxi-ephy.o
obj-$(CONFIG_ADIN_PHY) += adin.o
obj-$(CONFIG_ADIN1100_PHY) += adin1100.o
obj-$(CONFIG_AIR_EN8811H_PHY) += air_en8811h.o
diff --git a/drivers/net/phy/sunxi-ephy.c b/drivers/net/phy/sunxi-ephy.c
new file mode 100644
index 000000000000..111111111111
index 000000000000..44eb2790ea62
--- /dev/null
+++ b/drivers/net/phy/sunxi-ephy.c
@@ -0,0 +1,516 @@
@ -4248,227 +3892,8 @@ index 000000000000..111111111111
+MODULE_DESCRIPTION("Allwinner EPHY drivers");
+MODULE_AUTHOR("Sugar <shugeLinux@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/ac200.h b/include/linux/mfd/ac200.h
new file mode 100644
index 000000000000..111111111111
--- /dev/null
+++ b/include/linux/mfd/ac200.h
@@ -0,0 +1,213 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * AC200 register list
+ *
+ * Copyright (C) 2019 Jernej Skrabec <jernej.skrabec@siol.net>
+ */
+
+#ifndef __LINUX_MFD_AC200_H
+#define __LINUX_MFD_AC200_H
+
+#include <linux/regmap.h>
+#include <linux/clk.h>
+
+/* interface registers (can be accessed from any page) */
+#define AC200_TWI_CHANGE_TO_RSB 0x3E
+#define AC200_TWI_PAD_DELAY 0xC4
+#define AC200_TWI_REG_ADDR_H 0xFE
+
+/* General registers */
+#define AC200_SYS_VERSION 0x0000
+#define AC200_SYS_CONTROL 0x0002
+#define AC200_SYS_IRQ_ENABLE 0x0004
+#define AC200_SYS_IRQ_STATUS 0x0006
+#define AC200_SYS_CLK_CTL 0x0008
+#define AC200_SYS_DLDO_OSC_CTL 0x000A
+#define AC200_SYS_PLL_CTL0 0x000C
+#define AC200_SYS_PLL_CTL1 0x000E
+#define AC200_SYS_AUDIO_CTL0 0x0010
+#define AC200_SYS_AUDIO_CTL1 0x0012
+#define AC200_SYS_EPHY_CTL0 0x0014
+#define AC200_SYS_EPHY_CTL1 0x0016
+#define AC200_SYS_TVE_CTL0 0x0018
+#define AC200_SYS_TVE_CTL1 0x001A
+
+/* Audio Codec registers */
+#define AC200_AC_SYS_CLK_CTL 0x2000
+#define AC200_SYS_MOD_RST 0x2002
+#define AC200_SYS_SAMP_CTL 0x2004
+#define AC200_I2S_CTL 0x2100
+#define AC200_I2S_CLK 0x2102
+#define AC200_I2S_FMT0 0x2104
+#define AC200_I2S_FMT1 0x2108
+#define AC200_I2S_MIX_SRC 0x2114
+#define AC200_I2S_MIX_GAIN 0x2116
+#define AC200_I2S_DACDAT_DVC 0x2118
+#define AC200_I2S_ADCDAT_DVC 0x211A
+#define AC200_AC_DAC_DPC 0x2200
+#define AC200_AC_DAC_MIX_SRC 0x2202
+#define AC200_AC_DAC_MIX_GAIN 0x2204
+#define AC200_DACA_OMIXER_CTRL 0x2220
+#define AC200_OMIXER_SR 0x2222
+#define AC200_LINEOUT_CTRL 0x2224
+#define AC200_AC_ADC_DPC 0x2300
+#define AC200_MBIAS_CTRL 0x2310
+#define AC200_ADC_MIC_CTRL 0x2320
+#define AC200_ADCMIXER_SR 0x2322
+#define AC200_ANALOG_TUNING0 0x232A
+#define AC200_ANALOG_TUNING1 0x232C
+#define AC200_AC_AGC_SEL 0x2480
+#define AC200_ADC_DAPLCTRL 0x2500
+#define AC200_ADC_DAPRCTRL 0x2502
+#define AC200_ADC_DAPLSTA 0x2504
+#define AC200_ADC_DAPRSTA 0x2506
+#define AC200_ADC_DAPLTL 0x2508
+#define AC200_ADC_DAPRTL 0x250A
+#define AC200_ADC_DAPLHAC 0x250C
+#define AC200_ADC_DAPLLAC 0x250E
+#define AC200_ADC_DAPRHAC 0x2510
+#define AC200_ADC_DAPRLAC 0x2512
+#define AC200_ADC_DAPLDT 0x2514
+#define AC200_ADC_DAPLAT 0x2516
+#define AC200_ADC_DAPRDT 0x2518
+#define AC200_ADC_DAPRAT 0x251A
+#define AC200_ADC_DAPNTH 0x251C
+#define AC200_ADC_DAPLHNAC 0x251E
+#define AC200_ADC_DAPLLNAC 0x2520
+#define AC200_ADC_DAPRHNAC 0x2522
+#define AC200_ADC_DAPRLNAC 0x2524
+#define AC200_AC_DAPHHPFC 0x2526
+#define AC200_AC_DAPLHPFC 0x2528
+#define AC200_AC_DAPOPT 0x252A
+#define AC200_AC_DAC_DAPCTRL 0x3000
+#define AC200_AC_DRC_HHPFC 0x3002
+#define AC200_AC_DRC_LHPFC 0x3004
+#define AC200_AC_DRC_CTRL 0x3006
+#define AC200_AC_DRC_LPFHAT 0x3008
+#define AC200_AC_DRC_LPFLAT 0x300A
+#define AC200_AC_DRC_RPFHAT 0x300C
+#define AC200_AC_DRC_RPFLAT 0x300E
+#define AC200_AC_DRC_LPFHRT 0x3010
+#define AC200_AC_DRC_LPFLRT 0x3012
+#define AC200_AC_DRC_RPFHRT 0x3014
+#define AC200_AC_DRC_RPFLRT 0x3016
+#define AC200_AC_DRC_LRMSHAT 0x3018
+#define AC200_AC_DRC_LRMSLAT 0x301A
+#define AC200_AC_DRC_RRMSHAT 0x301C
+#define AC200_AC_DRC_RRMSLAT 0x301E
+#define AC200_AC_DRC_HCT 0x3020
+#define AC200_AC_DRC_LCT 0x3022
+#define AC200_AC_DRC_HKC 0x3024
+#define AC200_AC_DRC_LKC 0x3026
+#define AC200_AC_DRC_HOPC 0x3028
+#define AC200_AC_DRC_LOPC 0x302A
+#define AC200_AC_DRC_HLT 0x302C
+#define AC200_AC_DRC_LLT 0x302E
+#define AC200_AC_DRC_HKI 0x3030
+#define AC200_AC_DRC_LKI 0x3032
+#define AC200_AC_DRC_HOPL 0x3034
+#define AC200_AC_DRC_LOPL 0x3036
+#define AC200_AC_DRC_HET 0x3038
+#define AC200_AC_DRC_LET 0x303A
+#define AC200_AC_DRC_HKE 0x303C
+#define AC200_AC_DRC_LKE 0x303E
+#define AC200_AC_DRC_HOPE 0x3040
+#define AC200_AC_DRC_LOPE 0x3042
+#define AC200_AC_DRC_HKN 0x3044
+#define AC200_AC_DRC_LKN 0x3046
+#define AC200_AC_DRC_SFHAT 0x3048
+#define AC200_AC_DRC_SFLAT 0x304A
+#define AC200_AC_DRC_SFHRT 0x304C
+#define AC200_AC_DRC_SFLRT 0x304E
+#define AC200_AC_DRC_MXGHS 0x3050
+#define AC200_AC_DRC_MXGLS 0x3052
+#define AC200_AC_DRC_MNGHS 0x3054
+#define AC200_AC_DRC_MNGLS 0x3056
+#define AC200_AC_DRC_EPSHC 0x3058
+#define AC200_AC_DRC_EPSLC 0x305A
+#define AC200_AC_DRC_HPFHGAIN 0x305E
+#define AC200_AC_DRC_HPFLGAIN 0x3060
+#define AC200_AC_DRC_BISTCR 0x3100
+#define AC200_AC_DRC_BISTST 0x3102
+
+/* TVE registers */
+#define AC200_TVE_CTL0 0x4000
+#define AC200_TVE_CTL1 0x4002
+#define AC200_TVE_MOD0 0x4004
+#define AC200_TVE_MOD1 0x4006
+#define AC200_TVE_DAC_CFG0 0x4008
+#define AC200_TVE_DAC_CFG1 0x400A
+#define AC200_TVE_YC_DELAY 0x400C
+#define AC200_TVE_YC_FILTER 0x400E
+#define AC200_TVE_BURST_FRQ0 0x4010
+#define AC200_TVE_BURST_FRQ1 0x4012
+#define AC200_TVE_FRONT_PORCH 0x4014
+#define AC200_TVE_BACK_PORCH 0x4016
+#define AC200_TVE_TOTAL_LINE 0x401C
+#define AC200_TVE_FIRST_ACTIVE 0x401E
+#define AC200_TVE_BLACK_LEVEL 0x4020
+#define AC200_TVE_BLANK_LEVEL 0x4022
+#define AC200_TVE_PLUG_EN 0x4030
+#define AC200_TVE_PLUG_IRQ_EN 0x4032
+#define AC200_TVE_PLUG_IRQ_STA 0x4034
+#define AC200_TVE_PLUG_STA 0x4038
+#define AC200_TVE_PLUG_DEBOUNCE 0x4040
+#define AC200_TVE_DAC_TEST 0x4042
+#define AC200_TVE_PLUG_PULSE_LEVEL 0x40F4
+#define AC200_TVE_PLUG_PULSE_START 0x40F8
+#define AC200_TVE_PLUG_PULSE_PERIOD 0x40FA
+#define AC200_TVE_IF_CTL 0x5000
+#define AC200_TVE_IF_TIM0 0x5008
+#define AC200_TVE_IF_TIM1 0x500A
+#define AC200_TVE_IF_TIM2 0x500C
+#define AC200_TVE_IF_TIM3 0x500E
+#define AC200_TVE_IF_SYNC0 0x5010
+#define AC200_TVE_IF_SYNC1 0x5012
+#define AC200_TVE_IF_SYNC2 0x5014
+#define AC200_TVE_IF_TIM4 0x5016
+#define AC200_TVE_IF_STATUS 0x5018
+
+/* EPHY registers */
+#define AC200_EPHY_CTL 0x6000
+#define AC200_EPHY_BIST 0x6002
+
+/* eFuse registers (0x8000 - 0x9FFF, layout unknown) */
+
+/* RTC registers */
+#define AC200_LOSC_CTRL0 0xA000
+#define AC200_LOSC_CTRL1 0xA002
+#define AC200_LOSC_AUTO_SWT_STA 0xA004
+#define AC200_INTOSC_CLK_PRESCAL 0xA008
+#define AC200_RTC_YY_MM_DD0 0xA010
+#define AC200_RTC_YY_MM_DD1 0xA012
+#define AC200_RTC_HH_MM_SS0 0xA014
+#define AC200_RTC_HH_MM_SS1 0xA016
+#define AC200_ALARM0_CUR_VLU0 0xA024
+#define AC200_ALARM0_CUR_VLU1 0xA026
+#define AC200_ALARM0_ENABLE 0xA028
+#define AC200_ALARM0_IRQ_EN 0xA02C
+#define AC200_ALARM0_IRQ_STA 0xA030
+#define AC200_ALARM1_WK_HH_MM_SS0 0xA040
+#define AC200_ALARM1_WK_HH_MM_SS1 0xA042
+#define AC200_ALARM1_ENABLE 0xA044
+#define AC200_ALARM1_IRQ_EN 0xA048
+#define AC200_ALARM1_IRQ_STA 0xA04C
+#define AC200_ALARM_CONFIG 0xA050
+#define AC200_LOSC_OUT_GATING 0xA060
+#define AC200_GP_DATA(x) (0xA100 + (x) * 2)
+#define AC200_RTC_DEB 0xA170
+#define AC200_GPL_HOLD_OUTPUT 0xA180
+#define AC200_VDD_RTC 0xA190
+#define AC200_IC_CHARA0 0xA1F0
+#define AC200_IC_CHARA1 0xA1F2
+
+struct ac200_dev {
+ struct clk *clk;
+ struct regmap *regmap;
+ struct regmap_irq_chip_data *regmap_irqc;
+};
+
+extern int ac200_enable(void);
+extern uint16_t sun50i_ephy_calibrate_value(void);
+
+#endif /* __LINUX_MFD_AC200_H */
diff --git a/include/linux/of_gpio.h b/include/linux/of_gpio.h
index 111111111111..222222222222 100644
index d0f66a5e1b2a..71349cac1a67 100644
--- a/include/linux/of_gpio.h
+++ b/include/linux/of_gpio.h
@@ -15,6 +15,21 @@
@ -4505,4 +3930,3 @@ index 111111111111..222222222222 100644
#include <linux/errno.h>
--
Armbian

View File

@ -415,9 +415,6 @@
#
################################################################################
patches.armbian/0001-drv-input-tsc2007-add-polling-method.patch
patches.armbian/0201-drv-mfd-ac200-add-support.patch
patches.armbian/0202-drv-net-phy-ac200-ephy-add.patch
patches.armbian/0203-drv-net-stmmac-sun8i-add-h618-emac.patch
patches.armbian/0301-arm64-dts-sun50i-h616-add-emac1-rmii-pins.patch
patches.armbian/0302-arm64-dts-sun50i-h618-orangepi-zero2w-add-emac-sound.patch
patches.armbian/0401-arm64-dts-sun50i-h6-add-ac200-ephy.patch
@ -544,10 +541,12 @@
patches.armbian/drv-leds-ws2812-add-h616-driver.patch
patches.armbian/drv-media-dvb-frontends-si2168-fix-cmd-timeout.patch
patches.armbian/drv-mfd-ac200-add-ephy-syscon.patch
patches.armbian/drv-mfd-ac200-add-support.patch
patches.armbian/drv-mfd-axp20x-add-sysfs.patch
patches.armbian/drv-misc-sunxi-add-addr-mgt-driver-uwe5622.patch
patches.armbian/drv-mtd-nand-add-h27ubg8t2btr-nand.patch
patches.armbian/drv-net-stmmac-dwmac-sun8i-add-second-emac-clock.patch
patches.armbian/drv-net-stmmac-sun8i-add-h618-emac.patch
patches.armbian/drv-net-usb-r8152-add-led-configuration-from-of.patch
patches.armbian/drv-nvmem-sunxi-add-chipid-serial-helpers.patch
patches.armbian/drv-nvmem-sunxi-add-h616-support.patch