From 892bf746389faa0d31469990eeabdadf29f9b544 Mon Sep 17 00:00:00 2001 From: EvilOlaf Date: Mon, 12 Jan 2026 13:57:32 +0000 Subject: [PATCH] ac200: split hunks to combine 3 into 2 logical patches removing cross patch dependency --- .../0201-drv-mfd-ac200-add-support.patch | 265 ----- .../0202-drv-net-phy-ac200-ephy-add.patch | 142 --- .../drv-mfd-ac200-add-support.patch | 917 ++++++++++++++++++ ... drv-net-stmmac-sun8i-add-h618-emac.patch} | 622 +----------- patch/kernel/archive/sunxi-6.18/series.conf | 5 +- 5 files changed, 942 insertions(+), 1009 deletions(-) delete mode 100644 patch/kernel/archive/sunxi-6.18/patches.armbian/0201-drv-mfd-ac200-add-support.patch delete mode 100644 patch/kernel/archive/sunxi-6.18/patches.armbian/0202-drv-net-phy-ac200-ephy-add.patch create mode 100644 patch/kernel/archive/sunxi-6.18/patches.armbian/drv-mfd-ac200-add-support.patch rename patch/kernel/archive/sunxi-6.18/patches.armbian/{0203-drv-net-stmmac-sun8i-add-h618-emac.patch => drv-net-stmmac-sun8i-add-h618-emac.patch} (85%) diff --git a/patch/kernel/archive/sunxi-6.18/patches.armbian/0201-drv-mfd-ac200-add-support.patch b/patch/kernel/archive/sunxi-6.18/patches.armbian/0201-drv-mfd-ac200-add-support.patch deleted file mode 100644 index 1422c0b9ec..0000000000 --- a/patch/kernel/archive/sunxi-6.18/patches.armbian/0201-drv-mfd-ac200-add-support.patch +++ /dev/null @@ -1,265 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Jernej Skrabec -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 -Signed-off-by: Andre Przywara ---- - 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 -+ * -+ * Based on AC100 driver with following copyrights: -+ * Copyright (2016) Chen-Yu Tsai -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+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 "); -+MODULE_LICENSE("GPL v2"); --- -Armbian - diff --git a/patch/kernel/archive/sunxi-6.18/patches.armbian/0202-drv-net-phy-ac200-ephy-add.patch b/patch/kernel/archive/sunxi-6.18/patches.armbian/0202-drv-net-phy-ac200-ephy-add.patch deleted file mode 100644 index a36b63410d..0000000000 --- a/patch/kernel/archive/sunxi-6.18/patches.armbian/0202-drv-net-phy-ac200-ephy-add.patch +++ /dev/null @@ -1,142 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Jernej Skrabec -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 -Signed-off-by: Andre Przywara ---- - 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 -+ */ -+ -+#include -+#include -+#include -+#include -+ -+#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 "); -+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 - diff --git a/patch/kernel/archive/sunxi-6.18/patches.armbian/drv-mfd-ac200-add-support.patch b/patch/kernel/archive/sunxi-6.18/patches.armbian/drv-mfd-ac200-add-support.patch new file mode 100644 index 0000000000..13c1477359 --- /dev/null +++ b/patch/kernel/archive/sunxi-6.18/patches.armbian/drv-mfd-ac200-add-support.patch @@ -0,0 +1,917 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jernej Skrabec +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 +Signed-off-by: Andre Przywara +--- + 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 ++ * ++ * Based on AC100 driver with following copyrights: ++ * Copyright (2016) Chen-Yu Tsai ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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 "); ++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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 "); ++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 ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#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 "); ++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 ++ */ ++ ++#ifndef __LINUX_MFD_AC200_H ++#define __LINUX_MFD_AC200_H ++ ++#include ++#include ++ ++/* 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 diff --git a/patch/kernel/archive/sunxi-6.18/patches.armbian/0203-drv-net-stmmac-sun8i-add-h618-emac.patch b/patch/kernel/archive/sunxi-6.18/patches.armbian/drv-net-stmmac-sun8i-add-h618-emac.patch similarity index 85% rename from patch/kernel/archive/sunxi-6.18/patches.armbian/0203-drv-net-stmmac-sun8i-add-h618-emac.patch rename to patch/kernel/archive/sunxi-6.18/patches.armbian/drv-net-stmmac-sun8i-add-h618-emac.patch index 1c69bae27b..858058ddd1 100644 --- a/patch/kernel/archive/sunxi-6.18/patches.armbian/0203-drv-net-stmmac-sun8i-add-h618-emac.patch +++ b/patch/kernel/archive/sunxi-6.18/patches.armbian/drv-net-stmmac-sun8i-add-h618-emac.patch @@ -1,27 +1,30 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: chraac 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 --- 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 -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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 "); -+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 "); +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 -+ */ -+ -+#ifndef __LINUX_MFD_AC200_H -+#define __LINUX_MFD_AC200_H -+ -+#include -+#include -+ -+/* 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 -- Armbian - diff --git a/patch/kernel/archive/sunxi-6.18/series.conf b/patch/kernel/archive/sunxi-6.18/series.conf index ccb757bf3e..8e5f363b8c 100644 --- a/patch/kernel/archive/sunxi-6.18/series.conf +++ b/patch/kernel/archive/sunxi-6.18/series.conf @@ -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