Compare commits

...

5 Commits

Author SHA1 Message Date
Igor
c7ba95590b
Adding most recent patches
Need further rework as few general things are missing
2023-01-20 12:01:38 +01:00
Igor
1ba5a724de
Merge branch 'master' into AR-1487 2023-01-18 21:57:10 +01:00
Igor
3c46be399a
Rename board to tvb 2023-01-11 21:24:05 +01:00
Igor
9b8a9f3dd1
Rework patches and enable auto build 2023-01-10 21:11:35 +01:00
Igor
be2941c18c
Add Innovato TV box as CSC target 2023-01-10 20:20:54 +01:00
23 changed files with 2555 additions and 3330 deletions

View File

@ -0,0 +1,12 @@
# Allwinner TV box H6
BOARD_NAME="AW-H6-TV"
BOARDFAMILY="sun50iw6"
BOOTCONFIG="tanix_tx6_defconfig"
KERNEL_TARGET="current,edge"
FULL_DESKTOP="yes"
ATFBRANCH="tag:v2.2"
BOOT_LOGO="desktop"
BOOT_FDT_FILE="allwinner/sun50i-h6-tanix-tx6.dtb"
SRC_EXTLINUX="yes"
SRC_CMDLINE="console=ttyS0,115200 console=tty0 mem=2048M video=HDMI-A-1:e"
OFFSET=16

View File

@ -2,6 +2,11 @@
# board branch release desktop|cli|minimal stable|beta create images #
####################################################################################################################
# Innovato Quadra
aw-h6-tv current jammy minimal beta yes
aw-h6-tv edge jammy minimal beta yes
# JetHub J80
jethubj80 edge jammy minimal beta yes

View File

@ -0,0 +1,251 @@
From 74381d1e7fc471232edfbefa7f7b3cdfc032aad4 Mon Sep 17 00:00:00 2001
From: Andre Przywara <andre.przywara@arm.com>
Date: Fri, 10 Jun 2022 18:20:29 +0100
Subject: [PATCH 1/9] clk: gate: add support for regmap based gates
While we have nice wrappers for simple bit-flip MMIO based clock gates,
a single bit to toggle in a regmap still requires to write a lot of clock
framework boilerplate.
Support generic wrappers for regmap based clock gates, by adding them to
the existing clock-gates.c file. Since a read-modify-write operation in a
regmap can be much more complex than a readl/writel pair, we cannot use
the .enable/.disable ops members, but do the actual flipping already in
.prepare/.unprepare, where we can sleep. Also we cannot provide an
.is_enabled function, since this must not sleep as well.
On the upside all the locking for the r/m/w operation is provided by
regmap already, so we can skip that.
The rest of the CCF boilerplate code can be shared.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
drivers/clk/clk-gate.c | 60 ++++++++++++++++++++++++++++++++++--
include/linux/clk-provider.h | 36 +++++++++++++++++++---
2 files changed, 89 insertions(+), 7 deletions(-)
diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c
index 64283807600b..f83aef5e6e79 100644
--- a/drivers/clk/clk-gate.c
+++ b/drivers/clk/clk-gate.c
@@ -9,6 +9,7 @@
#include <linux/clk-provider.h>
#include <linux/device.h>
#include <linux/module.h>
+#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/err.h>
@@ -124,11 +125,42 @@ const struct clk_ops clk_gate_ops = {
};
EXPORT_SYMBOL_GPL(clk_gate_ops);
+static int clk_gate_regmap_setclrbit(struct clk_hw *hw, bool enable)
+{
+ struct clk_gate *gate = to_clk_gate(hw);
+ bool set = gate->flags & CLK_GATE_SET_TO_DISABLE;
+
+ set ^= enable;
+
+ if (set)
+ return regmap_set_bits(gate->regmap, gate->regmap_offs,
+ BIT(gate->bit_idx));
+ else
+ return regmap_clear_bits(gate->regmap, gate->regmap_offs,
+ BIT(gate->bit_idx));
+}
+
+static int clk_gate_regmap_prepare(struct clk_hw *hw)
+{
+ return clk_gate_regmap_setclrbit(hw, true);
+}
+
+static void clk_gate_regmap_unprepare(struct clk_hw *hw)
+{
+ clk_gate_regmap_setclrbit(hw, false);
+}
+
+const struct clk_ops clk_gate_regmap_ops = {
+ .prepare = clk_gate_regmap_prepare,
+ .unprepare = clk_gate_regmap_unprepare,
+};
+
struct clk_hw *__clk_hw_register_gate(struct device *dev,
struct device_node *np, const char *name,
const char *parent_name, const struct clk_hw *parent_hw,
const struct clk_parent_data *parent_data,
unsigned long flags,
+ struct regmap *regmap, unsigned int regmap_offs,
void __iomem *reg, u8 bit_idx,
u8 clk_gate_flags, spinlock_t *lock)
{
@@ -150,7 +182,10 @@ struct clk_hw *__clk_hw_register_gate(struct device *dev,
return ERR_PTR(-ENOMEM);
init.name = name;
- init.ops = &clk_gate_ops;
+ if (regmap)
+ init.ops = &clk_gate_regmap_ops;
+ else
+ init.ops = &clk_gate_ops;
init.flags = flags;
init.parent_names = parent_name ? &parent_name : NULL;
init.parent_hws = parent_hw ? &parent_hw : NULL;
@@ -162,6 +197,8 @@ struct clk_hw *__clk_hw_register_gate(struct device *dev,
/* struct clk_gate assignments */
gate->reg = reg;
+ gate->regmap = regmap;
+ gate->regmap_offs = regmap_offs;
gate->bit_idx = bit_idx;
gate->flags = clk_gate_flags;
gate->lock = lock;
@@ -197,6 +234,22 @@ struct clk *clk_register_gate(struct device *dev, const char *name,
}
EXPORT_SYMBOL_GPL(clk_register_gate);
+struct clk *clk_register_regmap_gate(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ struct regmap *regmap, unsigned int regmap_offs,
+ u8 bit_idx, u8 clk_gate_flags)
+{
+ struct clk_hw *hw;
+
+ hw = clk_hw_register_regmap_gate(dev, name, parent_name, flags, regmap,
+ regmap_offs, bit_idx, clk_gate_flags);
+
+ if (IS_ERR(hw))
+ return ERR_CAST(hw);
+ return hw->clk;
+}
+EXPORT_SYMBOL_GPL(clk_register_regmap_gate);
+
void clk_unregister_gate(struct clk *clk)
{
struct clk_gate *gate;
@@ -234,6 +287,7 @@ struct clk_hw *__devm_clk_hw_register_gate(struct device *dev,
const char *parent_name, const struct clk_hw *parent_hw,
const struct clk_parent_data *parent_data,
unsigned long flags,
+ struct regmap *regmap, unsigned int regmap_offs,
void __iomem *reg, u8 bit_idx,
u8 clk_gate_flags, spinlock_t *lock)
{
@@ -244,8 +298,8 @@ struct clk_hw *__devm_clk_hw_register_gate(struct device *dev,
return ERR_PTR(-ENOMEM);
hw = __clk_hw_register_gate(dev, np, name, parent_name, parent_hw,
- parent_data, flags, reg, bit_idx,
- clk_gate_flags, lock);
+ parent_data, flags, regmap, regmap_offs,
+ reg, bit_idx, clk_gate_flags, lock);
if (!IS_ERR(hw)) {
*ptr = hw;
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 267cd06b54a0..5de2c071fcf8 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -8,6 +8,7 @@
#include <linux/of.h>
#include <linux/of_clk.h>
+#include <linux/regmap.h>
/*
* flags used across common struct clk. these flags should only affect the
@@ -510,6 +511,8 @@ void of_fixed_clk_setup(struct device_node *np);
struct clk_gate {
struct clk_hw hw;
void __iomem *reg;
+ struct regmap *regmap;
+ unsigned int regmap_offs;
u8 bit_idx;
u8 flags;
spinlock_t *lock;
@@ -527,6 +530,7 @@ struct clk_hw *__clk_hw_register_gate(struct device *dev,
const char *parent_name, const struct clk_hw *parent_hw,
const struct clk_parent_data *parent_data,
unsigned long flags,
+ struct regmap *regmap, unsigned int regmap_offs,
void __iomem *reg, u8 bit_idx,
u8 clk_gate_flags, spinlock_t *lock);
struct clk_hw *__devm_clk_hw_register_gate(struct device *dev,
@@ -534,12 +538,17 @@ struct clk_hw *__devm_clk_hw_register_gate(struct device *dev,
const char *parent_name, const struct clk_hw *parent_hw,
const struct clk_parent_data *parent_data,
unsigned long flags,
+ struct regmap *regmap, unsigned int regmap_offs,
void __iomem *reg, u8 bit_idx,
u8 clk_gate_flags, spinlock_t *lock);
struct clk *clk_register_gate(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 bit_idx,
u8 clk_gate_flags, spinlock_t *lock);
+struct clk *clk_register_regmap_gate(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ struct regmap *regmap, unsigned int regmap_offs,
+ u8 bit_idx, u8 clk_gate_flags);
/**
* clk_hw_register_gate - register a gate clock with the clock framework
* @dev: device that is registering this clock
@@ -554,8 +563,14 @@ struct clk *clk_register_gate(struct device *dev, const char *name,
#define clk_hw_register_gate(dev, name, parent_name, flags, reg, bit_idx, \
clk_gate_flags, lock) \
__clk_hw_register_gate((dev), NULL, (name), (parent_name), NULL, \
- NULL, (flags), (reg), (bit_idx), \
+ NULL, (flags), NULL, 0, (reg), (bit_idx), \
(clk_gate_flags), (lock))
+
+#define clk_hw_register_regmap_gate(dev, name, parent_name, flags, regmap, \
+ regmap_offs, bit_idx, clk_gate_flags) \
+ __clk_hw_register_gate((dev), NULL, (name), (parent_name), NULL, \
+ NULL, (flags), regmap, regmap_offs, NULL, \
+ (bit_idx), (clk_gate_flags), NULL)
/**
* clk_hw_register_gate_parent_hw - register a gate clock with the clock
* framework
@@ -571,8 +586,15 @@ struct clk *clk_register_gate(struct device *dev, const char *name,
#define clk_hw_register_gate_parent_hw(dev, name, parent_hw, flags, reg, \
bit_idx, clk_gate_flags, lock) \
__clk_hw_register_gate((dev), NULL, (name), NULL, (parent_hw), \
- NULL, (flags), (reg), (bit_idx), \
+ NULL, (flags), NULL, 0, (reg), (bit_idx), \
(clk_gate_flags), (lock))
+
+#define clk_hw_register_regmap_gate_parent_hw(dev, name, parent_hw, flags, \
+ regmap, regmap_offs, bit_idx, \
+ clk_gate_flags) \
+ __clk_hw_register_gate((dev), NULL, (name), NULL, (parent_hw), \
+ NULL, (flags), regmap, regmap_offs, NULL, \
+ (bit_idx), (clk_gate_flags), NULL)
/**
* clk_hw_register_gate_parent_data - register a gate clock with the clock
* framework
@@ -588,7 +610,7 @@ struct clk *clk_register_gate(struct device *dev, const char *name,
#define clk_hw_register_gate_parent_data(dev, name, parent_data, flags, reg, \
bit_idx, clk_gate_flags, lock) \
__clk_hw_register_gate((dev), NULL, (name), NULL, NULL, (parent_data), \
- (flags), (reg), (bit_idx), \
+ (flags), NULL, 0, (reg), (bit_idx), \
(clk_gate_flags), (lock))
/**
* devm_clk_hw_register_gate - register a gate clock with the clock framework
@@ -604,8 +626,14 @@ struct clk *clk_register_gate(struct device *dev, const char *name,
#define devm_clk_hw_register_gate(dev, name, parent_name, flags, reg, bit_idx,\
clk_gate_flags, lock) \
__devm_clk_hw_register_gate((dev), NULL, (name), (parent_name), NULL, \
- NULL, (flags), (reg), (bit_idx), \
+ NULL, (flags), NULL, 0, (reg), (bit_idx), \
(clk_gate_flags), (lock))
+#define devm_clk_hw_register_regmap_gate(dev, name, parent_name, flags, \
+ regmap, regmap_offs, bit_idx, \
+ clk_gate_flags) \
+ __devm_clk_hw_register_gate((dev), NULL, (name), (parent_name), NULL, \
+ NULL, (flags), (regmap), (regmap_offs), NULL, \
+ (bit_idx), (clk_gate_flags), NULL)
void clk_unregister_gate(struct clk *clk);
void clk_hw_unregister_gate(struct clk_hw *hw);
int clk_gate_is_enabled(struct clk_hw *hw);
--
2.34.1

View File

@ -0,0 +1,267 @@
From aed54654df6512e89ff977591fb9ae56af3a91af Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@siol.net>
Date: Fri, 16 Aug 2019 16:38:21 +0200
Subject: [PATCH 2/9] 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 | 191 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 204 insertions(+)
create mode 100644 drivers/mfd/ac200.c
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 8b93856de432..b72ef08aea2b 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -178,6 +178,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 7ed3ef4a698c..91dc530d0bde 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -144,6 +144,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..625b119f53cf
--- /dev/null
+++ b/drivers/mfd/ac200.c
@@ -0,0 +1,191 @@
+// 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,
+ const struct i2c_device_id *id)
+{
+ 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");
--
2.34.1

View File

@ -0,0 +1,375 @@
From b1c6a18e8c6dcb05b24c033f1c6f5274c1f874b8 Mon Sep 17 00:00:00 2001
From: Andre Przywara <andre.przywara@arm.com>
Date: Mon, 13 Jun 2022 17:37:19 +0100
Subject: [PATCH 3/9] mfd: Add support for X-Powers AC200 EPHY syscon
The X-Powers AC200 mixed signal chip contains a 100Mbit/s Ethernet PHY.
While the PHY is using the standard MDIO and MII/RMII interfaces to
the MAC, there are some registers in the AC200 that need to be setup to
make the PHY functional:
- The MDIO PHY address needs to set.
- The LED polarity needs to be configured.
- The MII interface mode needs to be set (MII or RMII).
- There is a reset line that controls the PHY operation.
- There is a clock gate that blocks or forwards the AC200's internal clock
to the PHY.
This driver here takes care of those setup needs, but does not cover the
actual PHY operation. Once the PHY is set up and enabled, it behaves like
a standard MDIO/MII controlled PHY, and can be driven by the IEEE802.3-C22
driver.
To some degree those parameters mimic the typical wired setup of a
physical PHY chip: the LED polarity, MII interface mode and PHY address
are typically configued via connecting certain pins. We use the
devicetree to learn those parameters, which depend on the board setup.
This driver is a child of the AC200 MFD parent device, and uses its
regmap and input clock. It also provides a reset and clock controller,
which the actual PHY device (node) needs to refer to. This ensures that
this PHY control device is initialised before the PHY is expected to work.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
drivers/phy/allwinner/Kconfig | 9 +
drivers/phy/allwinner/Makefile | 1 +
drivers/phy/allwinner/ac200-ephy-ctl.c | 301 +++++++++++++++++++++++++
3 files changed, 311 insertions(+)
create mode 100644 drivers/phy/allwinner/ac200-ephy-ctl.c
diff --git a/drivers/phy/allwinner/Kconfig b/drivers/phy/allwinner/Kconfig
index fb584518b2d0..2e8ceca4a6a9 100644
--- a/drivers/phy/allwinner/Kconfig
+++ b/drivers/phy/allwinner/Kconfig
@@ -57,3 +57,12 @@ config PHY_SUN50I_USB3
part of Allwinner H6 SoC.
This driver controls each individual USB 2+3 host PHY combo.
+
+config AC200_PHY_CTL
+ tristate "X-Power AC200 PHY control driver"
+ depends on MFD_AC200
+ depends on RESET_CONTROLLER
+ help
+ Enable this to support the Ethernet PHY operation of the AC200
+ mixed signal chip. This driver just enables and configures the
+ PHY, the PHY itself is supported by a standard driver.
diff --git a/drivers/phy/allwinner/Makefile b/drivers/phy/allwinner/Makefile
index bd74901a1255..0eecec7a908a 100644
--- a/drivers/phy/allwinner/Makefile
+++ b/drivers/phy/allwinner/Makefile
@@ -3,3 +3,4 @@ obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o
obj-$(CONFIG_PHY_SUN6I_MIPI_DPHY) += phy-sun6i-mipi-dphy.o
obj-$(CONFIG_PHY_SUN9I_USB) += phy-sun9i-usb.o
obj-$(CONFIG_PHY_SUN50I_USB3) += phy-sun50i-usb3.o
+obj-$(CONFIG_AC200_PHY_CTL) += ac200-ephy-ctl.o
diff --git a/drivers/phy/allwinner/ac200-ephy-ctl.c b/drivers/phy/allwinner/ac200-ephy-ctl.c
new file mode 100644
index 000000000000..8efeaf18e42c
--- /dev/null
+++ b/drivers/phy/allwinner/ac200-ephy-ctl.c
@@ -0,0 +1,301 @@
+// SPDX-License-Identifier: GPL-2.0+
+/**
+ * syscon driver to control and configure AC200 Ethernet PHY
+ * Copyright (c) 2022 Arm Ltd.
+ *
+ * TODO's and questions:
+ * =========================
+ * - This driver is something like a syscon driver, as it controls various
+ * bits and registers that effect other devices (the actual PHY). It's
+ * unclear where it should live, though:
+ * - it could be integrated into the MFD driver, but this looks messy
+ * - it could live at the current location (drivers/phy/allwinner), but that
+ * sounds wrong
+ * - it could be a separate file, but in drivers/mfd
+ * - anything else
+ *
+ */
+
+#include <dt-bindings/gpio/gpio.h>
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of.h>
+#include <linux/of_net.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+
+/* macros for system ephy control 0 register */
+#define AC200_SYS_EPHY_CTL0 0x0014
+#define AC200_EPHY_RESET_INVALID BIT(0)
+#define AC200_EPHY_SYSCLK_GATING 1
+
+/* macros for system ephy control 1 register */
+#define AC200_SYS_EPHY_CTL1 0x0016
+#define AC200_EPHY_E_EPHY_MII_IO_EN BIT(0)
+#define AC200_EPHY_E_LNK_LED_IO_EN BIT(1)
+#define AC200_EPHY_E_SPD_LED_IO_EN BIT(2)
+#define AC200_EPHY_E_DPX_LED_IO_EN BIT(3)
+
+/* macros for ephy control register */
+#define AC200_EPHY_CTL 0x6000
+#define AC200_EPHY_SHUTDOWN BIT(0)
+#define AC200_EPHY_LED_POL BIT(1)
+#define AC200_EPHY_CLK_SEL BIT(2)
+#define AC200_EPHY_ADDR(x) (((x) & 0x1F) << 4)
+#define AC200_EPHY_XMII_SEL BIT(11)
+#define AC200_EPHY_CALIB(x) (((x) & 0xF) << 12)
+
+struct ac200_ephy_ctl_dev {
+ struct reset_controller_dev rcdev;
+ struct clk_hw *gate_clk;
+ struct regmap *regmap;
+};
+
+static struct ac200_ephy_ctl_dev *to_phy_dev(struct reset_controller_dev *rcdev)
+{
+ return container_of(rcdev, struct ac200_ephy_ctl_dev, rcdev);
+}
+
+static int ephy_ctl_reset(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ struct ac200_ephy_ctl_dev *ac200 = to_phy_dev(rcdev);
+ int ret;
+
+ ret = regmap_clear_bits(ac200->regmap, AC200_SYS_EPHY_CTL0,
+ AC200_EPHY_RESET_INVALID);
+ if (ret)
+ return ret;
+
+ /* This is going via I2C, so there is plenty of built-in delay. */
+ return regmap_set_bits(ac200->regmap, AC200_SYS_EPHY_CTL0,
+ AC200_EPHY_RESET_INVALID);
+}
+
+static int ephy_ctl_assert(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ struct ac200_ephy_ctl_dev *ac200 = to_phy_dev(rcdev);
+
+ return regmap_clear_bits(ac200->regmap, AC200_SYS_EPHY_CTL0,
+ AC200_EPHY_RESET_INVALID);
+}
+
+static int ephy_ctl_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct ac200_ephy_ctl_dev *ac200 = to_phy_dev(rcdev);
+
+ return regmap_set_bits(ac200->regmap, AC200_SYS_EPHY_CTL0,
+ AC200_EPHY_RESET_INVALID);
+}
+
+static int ephy_ctl_status(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ struct ac200_ephy_ctl_dev *ac200 = to_phy_dev(rcdev);
+
+ return regmap_test_bits(ac200->regmap, AC200_SYS_EPHY_CTL0,
+ AC200_EPHY_RESET_INVALID);
+}
+
+static int ephy_ctl_reset_of_xlate(struct reset_controller_dev *rcdev,
+ const struct of_phandle_args *reset_spec)
+{
+ if (WARN_ON(reset_spec->args_count != 0))
+ return -EINVAL;
+
+ return 0;
+}
+
+const struct reset_control_ops ephy_ctl_reset_ops = {
+ .assert = ephy_ctl_assert,
+ .deassert = ephy_ctl_deassert,
+ .reset = ephy_ctl_reset,
+ .status = ephy_ctl_status,
+};
+
+static void ac200_ephy_ctl_disable(struct ac200_ephy_ctl_dev *priv)
+{
+ regmap_write(priv->regmap, AC200_EPHY_CTL, AC200_EPHY_SHUTDOWN);
+ regmap_write(priv->regmap, AC200_SYS_EPHY_CTL1, 0);
+ regmap_write(priv->regmap, AC200_SYS_EPHY_CTL0, 0);
+}
+
+static int ac200_ephy_ctl_probe(struct platform_device *pdev)
+{
+ struct reset_controller_dev *rcdev;
+ struct device *dev = &pdev->dev;
+ struct ac200_ephy_ctl_dev *priv;
+ struct nvmem_cell *calcell;
+ const char *parent_name;
+ phy_interface_t phy_if;
+ u16 *caldata, ephy_ctl;
+ struct clk *clk;
+ size_t callen;
+ u32 value;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, priv);
+
+ priv->regmap = dev_get_regmap(dev->parent, NULL);
+ if (!priv->regmap)
+ return -EPROBE_DEFER;
+
+ calcell = devm_nvmem_cell_get(dev, "calibration");
+ if (IS_ERR(calcell))
+ return dev_err_probe(dev, PTR_ERR(calcell),
+ "Unable to find calibration data!\n");
+
+ caldata = nvmem_cell_read(calcell, &callen);
+ if (IS_ERR(caldata)) {
+ dev_err(dev, "Unable to read calibration data!\n");
+ return PTR_ERR(caldata);
+ }
+
+ if (callen != 2) {
+ dev_err(dev, "Calibration data length must be 2 bytes!\n");
+ kfree(caldata);
+ return -EINVAL;
+ }
+
+ ephy_ctl = AC200_EPHY_CALIB(*caldata + 3);
+ kfree(caldata);
+
+ ret = of_get_phy_mode(dev->of_node, &phy_if);
+ if (ret) {
+ dev_err(dev, "Unable to read PHY connection mode\n");
+ return ret;
+ }
+
+ switch (phy_if) {
+ case PHY_INTERFACE_MODE_MII:
+ break;
+ case PHY_INTERFACE_MODE_RMII:
+ ephy_ctl |= AC200_EPHY_XMII_SEL;
+ break;
+ default:
+ dev_err(dev, "Illegal PHY connection mode (%d), only RMII or MII supported\n",
+ phy_if);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32(dev->of_node, "x-powers,led-polarity",
+ &value);
+ if (ret) {
+ dev_err(dev, "Unable to read LED polarity setting\n");
+ return ret;
+ }
+
+ if (value == GPIO_ACTIVE_LOW)
+ ephy_ctl |= AC200_EPHY_LED_POL;
+
+ ret = of_property_read_u32(dev->of_node, "phy-address", &value);
+ if (ret) {
+ dev_err(dev, "Unable to read PHY address value\n");
+ return ret;
+ }
+
+ ephy_ctl |= AC200_EPHY_ADDR(value);
+
+ clk = clk_get(dev->parent, NULL);
+ if (IS_ERR(clk))
+ return dev_err_probe(dev, PTR_ERR(clk),
+ "Unable to obtain the clock\n");
+
+ if (clk_get_rate(clk) == 24000000)
+ ephy_ctl |= AC200_EPHY_CLK_SEL;
+
+ clk_put(clk);
+
+ /* Assert reset and gate clock, to disable PHY for now */
+ ret = regmap_write(priv->regmap, AC200_SYS_EPHY_CTL0, 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(priv->regmap, AC200_SYS_EPHY_CTL1,
+ AC200_EPHY_E_EPHY_MII_IO_EN |
+ AC200_EPHY_E_LNK_LED_IO_EN |
+ AC200_EPHY_E_SPD_LED_IO_EN |
+ AC200_EPHY_E_DPX_LED_IO_EN);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(priv->regmap, AC200_EPHY_CTL, ephy_ctl);
+ if (ret)
+ return ret;
+
+ rcdev = &priv->rcdev;
+ rcdev->owner = dev->driver->owner;
+ rcdev->nr_resets = 1;
+ rcdev->ops = &ephy_ctl_reset_ops;
+ rcdev->of_node = dev->of_node;
+ rcdev->of_reset_n_cells = 0;
+ rcdev->of_xlate = ephy_ctl_reset_of_xlate;
+
+ ret = devm_reset_controller_register(dev, rcdev);
+ if (ret) {
+ dev_err(dev, "Unable to register reset controller: %d\n", ret);
+ goto err_disable_ephy;
+ }
+
+ parent_name = of_clk_get_parent_name(dev->parent->of_node, 0);
+ priv->gate_clk = devm_clk_hw_register_regmap_gate(dev,
+ "ac200-ephy-ctl-gate", parent_name, 0,
+ priv->regmap, AC200_SYS_EPHY_CTL0,
+ AC200_EPHY_SYSCLK_GATING, 0);
+ if (IS_ERR(priv->gate_clk)) {
+ ret = PTR_ERR(priv->gate_clk);
+ dev_err(dev, "Unable to register gate clock: %d\n", ret);
+ goto err_disable_ephy;
+ }
+
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
+ priv->gate_clk);
+ if (ret) {
+ dev_err(dev, "Unable to register clock provider: %d\n", ret);
+ goto err_disable_ephy;
+ }
+
+ return 0;
+
+err_disable_ephy:
+ ac200_ephy_ctl_disable(priv);
+
+ return ret;
+}
+
+static int ac200_ephy_ctl_remove(struct platform_device *pdev)
+{
+ struct ac200_ephy_ctl_dev *priv = platform_get_drvdata(pdev);
+
+ ac200_ephy_ctl_disable(priv);
+
+ return 0;
+}
+
+static const struct of_device_id ac200_ephy_ctl_match[] = {
+ { .compatible = "x-powers,ac200-ephy-ctl" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ac200_ephy_ctl_match);
+
+static struct platform_driver ac200_ephy_ctl_driver = {
+ .probe = ac200_ephy_ctl_probe,
+ .remove = ac200_ephy_ctl_remove,
+ .driver = {
+ .name = "ac200-ephy-ctl",
+ .of_match_table = ac200_ephy_ctl_match,
+ },
+};
+module_platform_driver(ac200_ephy_ctl_driver);
+
+MODULE_AUTHOR("Andre Przywara <andre.przywara@arm.com>");
+MODULE_DESCRIPTION("AC200 Ethernet PHY control driver");
+MODULE_LICENSE("GPL");
--
2.34.1

View File

@ -0,0 +1,143 @@
From c8fcdf466f5dd8efa86926aa342fe51852d4153f Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@siol.net>
Date: Fri, 16 Aug 2019 16:38:57 +0200
Subject: [PATCH 4/9] 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.c | 82 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 90 insertions(+)
create mode 100644 drivers/net/phy/ac200.c
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index c57a0262fb64..a1f8fdfc5762 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -63,6 +63,13 @@ config SFP
comment "MII PHY device drivers"
+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 PHYs"
help
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index f7138d3c896b..9ad2b8cc01b1 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -30,6 +30,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.o
obj-$(CONFIG_ADIN_PHY) += adin.o
obj-$(CONFIG_ADIN1100_PHY) += adin1100.o
obj-$(CONFIG_AMD_PHY) += amd.o
diff --git a/drivers/net/phy/ac200.c b/drivers/net/phy/ac200.c
new file mode 100644
index 000000000000..8499914f49b8
--- /dev/null
+++ b/drivers/net/phy/ac200.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);
--
2.34.1

View File

@ -1,20 +1,32 @@
From c843cbfb9d8c5b68ed2fedfd2d6905ec852f2873 Mon Sep 17 00:00:00 2001
From 2ecb6ebd2e0e81809227e8e700b667b1ca5f8d96 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@siol.net>
Date: Sun, 12 Jan 2020 12:09:12 +0100
Subject: [PATCH 011/101] arm64:dts: sun50i-h6: Add AC200 EPHY related nodes
Date: Fri, 16 Aug 2019 16:40:20 +0200
Subject: [PATCH 5/9] arm64: dts: allwinner: h6: Add AC200 EPHY nodes
All Allwinner H6 SoCs feature a co-packaged AC200 die, which replaces
the integrated PHY and audio circuitry of its H3/H5 predecessors. It is
using an internal I2C connection, but otherwise pretty much behaves as
it would be externally connected.
Since every H6 SoC contains this chip, add the required DT nodes to the
SoC .dtsi, but keep them disabled. This is for now just covering the
AC200 MFD parent and its EPHY child.
Any board making use of one of the integrated PHY needs to enable it and
connect the MAC and PHY accordingly.
Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 63 ++++++++++++++++++++
1 file changed, 63 insertions(+)
arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 73 ++++++++++++++++++++
1 file changed, 73 insertions(+)
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
index 6cdebbbff..7c17076d9 100644
index 53f6660656ac..91352c2c84b5 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
@@ -22,6 +22,16 @@ aliases {
mmc2 = &mmc2;
};
@@ -17,6 +17,16 @@ / {
#address-cells = <1>;
#size-cells = <1>;
+ ac200_pwm_clk: ac200_clk {
+ compatible = "pwm-clock";
@ -29,22 +41,25 @@ index 6cdebbbff..7c17076d9 100644
cpus {
#address-cells = <1>;
#size-cells = <0>;
@@ -307,6 +317,10 @@ ths_calibration: thermal-sensor-calibration@14 {
reg = <0x14 0x8>;
};
+ ephy_calibration: ephy-calibration@2c {
+ reg = <0x2c 0x2>;
+ };
+
@@ -280,6 +290,14 @@ ths_calibration: thermal-sensor-calibration@14 {
cpu_speed_grade: cpu-speed-grade@1c {
reg = <0x1c 0x4>;
};
@@ -364,6 +378,14 @@ ext_rgmii_pins: rgmii-pins {
+
+ ephy_calib: ephy_calib@2c {
+ reg = <0x2c 0x2>;
+ };
+
+ ac200_bg: ac200_bg@30 {
+ reg = <0x30 0x2>;
+ };
};
timer@3009000 {
@@ -334,6 +352,13 @@ ext_rgmii_pins: rgmii-pins {
drive-strength = <40>;
};
+ /omit-if-no-ref/
+ ext_rmii_pins: rmii_pins {
+ pins = "PA0", "PA1", "PA2", "PA3", "PA4",
+ "PA5", "PA6", "PA7", "PA8", "PA9";
@ -55,19 +70,20 @@ index 6cdebbbff..7c17076d9 100644
hdmi_pins: hdmi-pins {
pins = "PH8", "PH9", "PH10";
function = "hdmi";
@@ -384,6 +406,11 @@ i2c2_pins: i2c2-pins {
@@ -354,6 +379,12 @@ i2c2_pins: i2c2-pins {
function = "i2c2";
};
+ i2c3_pins: i2c3-pins {
+ pins = "PB17", "PB18";
+ function = "i2c3";
+ bias-pull-up;
+ };
+
mmc0_pins: mmc0-pins {
pins = "PF0", "PF1", "PF2", "PF3",
"PF4", "PF5";
@@ -401,6 +428,11 @@ mmc1_pins: mmc1-pins {
@@ -380,6 +411,11 @@ mmc2_pins: mmc2-pins {
bias-pull-up;
};
@ -76,10 +92,10 @@ index 6cdebbbff..7c17076d9 100644
+ function = "pwm1";
+ };
+
mmc2_pins: mmc2-pins {
pins = "PC1", "PC4", "PC5", "PC6",
"PC7", "PC8", "PC9", "PC10",
@@ -643,6 +675,37 @@ spi1: spi@5011000 {
/omit-if-no-ref/
spi0_pins: spi0-pins {
pins = "PC0", "PC2", "PC3";
@@ -583,6 +619,43 @@ i2c2: i2c@5002800 {
#size-cells = <0>;
};
@ -92,6 +108,7 @@ index 6cdebbbff..7c17076d9 100644
+ resets = <&ccu RST_BUS_I2C3>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c3_pins>;
+ clock-frequency = <100000>;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
@ -99,24 +116,29 @@ index 6cdebbbff..7c17076d9 100644
+ ac200: mfd@10 {
+ compatible = "x-powers,ac200";
+ reg = <0x10>;
+ clocks = <&ac200_pwm_clk>;
+ interrupt-parent = <&pio>;
+ interrupts = <1 20 IRQ_TYPE_LEVEL_LOW>;
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ nvmem-cells = <&ac200_bg>;
+ nvmem-cell-names = "bandgap";
+
+ ac200_ephy: phy {
+ compatible = "x-powers,ac200-ephy";
+ clocks = <&ac200_pwm_clk>;
+ nvmem-cells = <&ephy_calibration>;
+ ac200_ephy_ctl: syscon {
+ compatible = "x-powers,ac200-ephy-ctl";
+ nvmem-cells = <&ephy_calib>;
+ nvmem-cell-names = "calibration";
+ #clock-cells = <0>;
+ #reset-cells = <0>;
+ phy-mode = "rmii";
+ status = "disabled";
+ };
+ };
+ };
+
emac: ethernet@5020000 {
compatible = "allwinner,sun50i-h6-emac",
"allwinner,sun50i-a64-emac";
spi0: spi@5010000 {
compatible = "allwinner,sun50i-h6-spi",
"allwinner,sun8i-h3-spi";
--
2.31.1
2.34.1

View File

@ -0,0 +1,97 @@
From 2a64799f7e6c833f0be3678937c10d1afd48c440 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@siol.net>
Date: Sat, 24 Aug 2019 01:03:05 +0200
Subject: [PATCH 6/9] arm64: dts: allwinner: h6: tanix: enable Ethernet
Tanix-TX6 and TX6 mini boards provide an 100MBit/s Ethernet port, which
uses the EPHY integrated into the Allwinner H6 SoC.
Enable the MAC node, plus the required AC200 nodes to allow configuring
and enabling the integrated PHY.
Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
.../boot/dts/allwinner/sun50i-h6-tanix.dtsi | 38 +++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix.dtsi
index 4903d6358112..51a75debab44 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix.dtsi
@@ -10,6 +10,7 @@
/ {
aliases {
+ ethernet0 = &emac;
serial0 = &uart0;
};
@@ -84,6 +85,16 @@ wifi_pwrseq: wifi-pwrseq {
};
};
+&ac200_ephy_ctl {
+ x-powers,led-polarity = <GPIO_ACTIVE_HIGH>;
+ phy-address = <1>;
+ status = "okay";
+};
+
+&ac200_pwm_clk {
+ status = "okay";
+};
+
&cpu0 {
cpu-supply = <&reg_vdd_cpu_gpu>;
};
@@ -104,6 +115,14 @@ &ehci3 {
status = "okay";
};
+&emac {
+ pinctrl-names = "default";
+ pinctrl-0 = <&ext_rmii_pins>;
+ phy-mode = "rmii";
+ phy-handle = <&ext_rmii_phy>;
+ status = "okay";
+};
+
&gpu {
mali-supply = <&reg_vdd_cpu_gpu>;
status = "okay";
@@ -119,6 +138,21 @@ hdmi_out_con: endpoint {
};
};
+&i2c3 {
+ status = "okay";
+};
+
+&mdio {
+ ext_rmii_phy: ethernet-phy@1 {
+ compatible = "ethernet-phy-id0044.1400",
+ "ethernet-phy-ieee802.3-c22";
+ reg = <1>;
+ resets = <&ac200_ephy_ctl>;
+ reset-names = "phy";
+ clocks = <&ac200_ephy_ctl>;
+ };
+};
+
&mmc0 {
pinctrl-names = "default";
pinctrl-0 = <&mmc0_pins>;
@@ -161,6 +195,10 @@ &pio {
vcc-pg-supply = <&reg_vcc1v8>;
};
+&pwm {
+ status = "okay";
+};
+
&r_ir {
status = "okay";
};
--
2.34.1

View File

@ -0,0 +1,844 @@
From 95ed80f8fda4be2cd6f3ece0f79e6106d56c9858 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@gmail.com>
Date: Thu, 1 Sep 2022 17:36:53 +0200
Subject: [PATCH 7/9] ASoC: AC200: Initial driver
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
sound/soc/codecs/Kconfig | 10 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/ac200.c | 774 ++++++++++++++++++++++++++++++++++++++
3 files changed, 786 insertions(+)
create mode 100644 sound/soc/codecs/ac200.c
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 7022e6286e6c..49b90c83b485 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -17,6 +17,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_88PM860X
imply SND_SOC_L3
imply SND_SOC_AB8500_CODEC
+ imply SND_SOC_AC200_CODEC
imply SND_SOC_AC97_CODEC
imply SND_SOC_AD1836
imply SND_SOC_AD193X_SPI
@@ -369,6 +370,15 @@ config SND_SOC_AB8500_CODEC
tristate
depends on ABX500_CORE
+config SND_SOC_AC200_CODEC
+ tristate "AC200 Codec"
+ depends on MFD_AC200
+ help
+ Enable support for X-Powers AC200 analog audio codec.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-soc-ac200.
+
config SND_SOC_AC97_CODEC
tristate "Build generic ASoC AC97 CODEC driver"
select SND_AC97_CODEC
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 9170ee1447dd..e6d44c2d2fd6 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
snd-soc-88pm860x-objs := 88pm860x-codec.o
snd-soc-ab8500-codec-objs := ab8500-codec.o
+snd-soc-ac200-objs := ac200.o
snd-soc-ac97-objs := ac97.o
snd-soc-ad1836-objs := ad1836.o
snd-soc-ad193x-objs := ad193x.o
@@ -359,6 +360,7 @@ snd-soc-simple-mux-objs := simple-mux.o
obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o
obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o
+obj-$(CONFIG_SND_SOC_AC200_CODEC) += snd-soc-ac200.o
obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o
obj-$(CONFIG_SND_SOC_AD193X) += snd-soc-ad193x.o
diff --git a/sound/soc/codecs/ac200.c b/sound/soc/codecs/ac200.c
new file mode 100644
index 000000000000..113a45408116
--- /dev/null
+++ b/sound/soc/codecs/ac200.c
@@ -0,0 +1,774 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * X-Powers AC200 Codec Driver
+ *
+ * Copyright (C) 2022 Jernej Skrabec <jernej.skrabec@gmail.com>
+ */
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define AC200_CODEC_RATES (SNDRV_PCM_RATE_8000 | \
+ SNDRV_PCM_RATE_11025 | \
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_192000 | \
+ SNDRV_PCM_RATE_KNOT)
+
+#define AC200_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define AC200_SYS_AUDIO_CTL0 0x0010
+#define AC200_SYS_AUDIO_CTL0_MCLK_GATING BIT(1)
+#define AC200_SYS_AUDIO_CTL0_RST_INVALID BIT(0)
+#define AC200_SYS_AUDIO_CTL1 0x0012
+#define AC200_SYS_AUDIO_CTL1_I2S_IO_EN BIT(0)
+
+#define AC200_SYS_CLK_CTL 0x2000
+#define AC200_SYS_CLK_CTL_I2S 15
+#define AC200_SYS_CLK_CTL_ADC 3
+#define AC200_SYS_CLK_CTL_DAC 2
+#define AC200_SYS_MOD_RST 0x2002
+#define AC200_SYS_MOD_RST_I2S 15
+#define AC200_SYS_MOD_RST_ADC 3
+#define AC200_SYS_MOD_RST_DAC 2
+#define AC200_SYS_SR_CTL 0x2004
+#define AC200_SYS_SR_CTL_SR_MASK GENMASK(3, 0)
+#define AC200_SYS_SR_CTL_SR(x) (x)
+#define AC200_I2S_CTL 0x2100
+#define AC200_I2S_CTL_SDO_EN 3
+#define AC200_I2S_CTL_TX_EN 2
+#define AC200_I2S_CTL_RX_EN 1
+#define AC200_I2S_CTL_GEN 0
+#define AC200_I2S_CLK 0x2102
+#define AC200_I2S_CLK_BCLK_OUT BIT(15)
+#define AC200_I2S_CLK_LRCK_OUT BIT(14)
+#define AC200_I2S_CLK_BCLKDIV_MASK GENMASK(13, 10)
+#define AC200_I2S_CLK_BCLKDIV(x) ((x) << 10)
+#define AC200_I2S_CLK_LRCK_MASK GENMASK(9, 0)
+#define AC200_I2S_CLK_LRCK(x) ((x) - 1)
+#define AC200_I2S_FMT0 0x2104
+#define AC200_I2S_FMT0_MODE_MASK GENMASK(15, 14)
+#define AC200_I2S_FMT0_MODE(x) ((x) << 14)
+#define AC200_I2S_FMT0_MODE_PCM 0
+#define AC200_I2S_FMT0_MODE_LEFT 1
+#define AC200_I2S_FMT0_MODE_RIGHT 2
+#define AC200_I2S_FMT0_TX_OFFSET_MASK GENMASK(11, 10)
+#define AC200_I2S_FMT0_TX_OFFSET(x) ((x) << 10)
+#define AC200_I2S_FMT0_RX_OFFSET_MASK GENMASK(9, 8)
+#define AC200_I2S_FMT0_RX_OFFSET(x) ((x) << 8)
+#define AC200_I2S_FMT0_SR_MASK GENMASK(6, 4)
+#define AC200_I2S_FMT0_SR(x) ((x) << 4)
+#define AC200_I2S_FMT0_SW_MASK GENMASK(3, 1)
+#define AC200_I2S_FMT0_SW(x) ((x) << 1)
+#define AC200_I2S_FMT1 0x2108
+#define AC200_I2S_FMT1_BCLK_POL_INVERT BIT(15)
+#define AC200_I2S_FMT1_LRCK_POL_INVERT BIT(14)
+#define AC200_I2S_MIX_SRC 0x2114
+#define AC200_I2S_MIX_SRC_LMIX_DAC 13
+#define AC200_I2S_MIX_SRC_LMIX_ADC 12
+#define AC200_I2S_MIX_SRC_RMIX_DAC 9
+#define AC200_I2S_MIX_SRC_RMIX_ADC 8
+#define AC200_I2S_MIX_GAIN 0x2116
+#define AC200_I2S_MIX_GAIN_LMIX_DAC 13
+#define AC200_I2S_MIX_GAIN_LMIX_ADC 12
+#define AC200_I2S_MIX_GAIN_RMIX_DAC 9
+#define AC200_I2S_MIX_GAIN_RMIX_ADC 8
+#define AC200_I2S_DAC_VOL 0x2118
+#define AC200_I2S_DAC_VOL_LEFT 8
+#define AC200_I2S_DAC_VOL_RIGHT 0
+#define AC200_I2S_ADC_VOL 0x211A
+#define AC200_I2S_ADC_VOL_LEFT 8
+#define AC200_I2S_ADC_VOL_RIGHT 0
+#define AC200_DAC_CTL 0x2200
+#define AC200_DAC_CTL_DAC_EN 15
+#define AC200_DAC_MIX_SRC 0x2202
+#define AC200_DAC_MIX_SRC_LMIX_DAC 13
+#define AC200_DAC_MIX_SRC_LMIX_ADC 12
+#define AC200_DAC_MIX_SRC_RMIX_DAC 9
+#define AC200_DAC_MIX_SRC_RMIX_ADC 8
+#define AC200_DAC_MIX_GAIN 0x2204
+#define AC200_DAC_MIX_GAIN_LMIX_DAC 13
+#define AC200_DAC_MIX_GAIN_LMIX_ADC 12
+#define AC200_DAC_MIX_GAIN_RMIX_DAC 9
+#define AC200_DAC_MIX_GAIN_RMIX_ADC 8
+#define AC200_OUT_MIX_CTL 0x2220
+#define AC200_OUT_MIX_CTL_RDAC_EN 15
+#define AC200_OUT_MIX_CTL_LDAC_EN 14
+#define AC200_OUT_MIX_CTL_RMIX_EN 13
+#define AC200_OUT_MIX_CTL_LMIX_EN 12
+#define AC200_OUT_MIX_CTL_MIC1_VOL 4
+#define AC200_OUT_MIX_CTL_MIC2_VOL 0
+#define AC200_OUT_MIX_SRC 0x2222
+#define AC200_OUT_MIX_SRC_RMIX_MIC1 14
+#define AC200_OUT_MIX_SRC_RMIX_MIC2 13
+#define AC200_OUT_MIX_SRC_RMIX_RDAC 9
+#define AC200_OUT_MIX_SRC_RMIX_LDAC 8
+#define AC200_OUT_MIX_SRC_LMIX_MIC1 6
+#define AC200_OUT_MIX_SRC_LMIX_MIC2 5
+#define AC200_OUT_MIX_SRC_LMIX_RDAC 1
+#define AC200_OUT_MIX_SRC_LMIX_LDAC 0
+#define AC200_LINEOUT_CTL 0x2224
+#define AC200_LINEOUT_CTL_EN 15
+#define AC200_LINEOUT_CTL_LEN 14
+#define AC200_LINEOUT_CTL_REN 13
+#define AC200_LINEOUT_CTL_LMONO 12
+#define AC200_LINEOUT_CTL_RMONO 11
+#define AC200_LINEOUT_CTL_VOL 0
+#define AC200_ADC_CTL 0x2300
+#define AC200_ADC_CTL_ADC_EN 15
+#define AC200_MBIAS_CTL 0x2310
+#define AC200_MBIAS_CTL_MBIAS_EN 15
+#define AC200_MBIAS_CTL_ADDA_BIAS_EN 3
+#define AC200_ADC_MIC_CTL 0x2320
+#define AC200_ADC_MIC_CTL_RADC_EN 15
+#define AC200_ADC_MIC_CTL_LADC_EN 14
+#define AC200_ADC_MIC_CTL_ADC_VOL 8
+#define AC200_ADC_MIC_CTL_MIC1_GAIN_EN 7
+#define AC200_ADC_MIC_CTL_MIC1_BOOST 4
+#define AC200_ADC_MIC_CTL_MIC2_GAIN_EN 3
+#define AC200_ADC_MIC_CTL_MIC2_BOOST 0
+#define AC200_ADC_MIX_SRC 0x2322
+#define AC200_ADC_MIX_SRC_RMIX_MIC1 14
+#define AC200_ADC_MIX_SRC_RMIX_MIC2 13
+#define AC200_ADC_MIX_SRC_RMIX_RMIX 9
+#define AC200_ADC_MIX_SRC_RMIX_LMIX 8
+#define AC200_ADC_MIX_SRC_LMIX_MIC1 6
+#define AC200_ADC_MIX_SRC_LMIX_MIC2 5
+#define AC200_ADC_MIX_SRC_LMIX_LMIX 1
+#define AC200_ADC_MIX_SRC_LMIX_RMIX 0
+
+struct ac200_codec {
+ struct regmap *regmap;
+ unsigned int format;
+};
+
+struct ac200_map {
+ int match;
+ int value;
+};
+
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(mixer_scale, -600, 600, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(gain_scale, -450, 150, 0);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(lineout_scale, -4650, 150, 1);
+static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(codec_scale, -12000, 75, 1);
+static const unsigned int mic_scale[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0),
+};
+
+static const struct snd_kcontrol_new ac200_codec_controls[] = {
+ SOC_DOUBLE_TLV("Master Playback Volume", AC200_I2S_DAC_VOL,
+ AC200_I2S_DAC_VOL_LEFT, AC200_I2S_DAC_VOL_RIGHT,
+ 0xff, 0, codec_scale),
+ SOC_DOUBLE_TLV("Master Capture Volume", AC200_I2S_ADC_VOL,
+ AC200_I2S_ADC_VOL_LEFT, AC200_I2S_ADC_VOL_RIGHT,
+ 0xff, 0, codec_scale),
+ SOC_DOUBLE_TLV("I2S ADC Capture Volume", AC200_I2S_MIX_GAIN,
+ AC200_I2S_MIX_GAIN_LMIX_ADC, AC200_I2S_MIX_GAIN_RMIX_ADC,
+ 0x1, 1, mixer_scale),
+ SOC_DOUBLE_TLV("I2S DAC Capture Volume", AC200_I2S_MIX_GAIN,
+ AC200_I2S_MIX_GAIN_LMIX_DAC, AC200_I2S_MIX_GAIN_RMIX_DAC,
+ 0x1, 1, mixer_scale),
+ SOC_DOUBLE_TLV("DAC I2S Playback Volume", AC200_DAC_MIX_GAIN,
+ AC200_DAC_MIX_GAIN_LMIX_DAC, AC200_DAC_MIX_GAIN_RMIX_DAC,
+ 0x1, 1, mixer_scale),
+ SOC_DOUBLE_TLV("ADC Playback Volume", AC200_DAC_MIX_GAIN,
+ AC200_DAC_MIX_GAIN_LMIX_ADC, AC200_DAC_MIX_GAIN_RMIX_ADC,
+ 0x1, 1, mixer_scale),
+ SOC_SINGLE_TLV("MIC1 Playback Volume", AC200_OUT_MIX_CTL,
+ AC200_OUT_MIX_CTL_MIC1_VOL, 0x7, 0, gain_scale),
+ SOC_SINGLE_TLV("MIC2 Playback Volume", AC200_OUT_MIX_CTL,
+ AC200_OUT_MIX_CTL_MIC2_VOL, 0x7, 0, gain_scale),
+ SOC_SINGLE_TLV("ADC Volume", AC200_ADC_MIC_CTL,
+ AC200_ADC_MIC_CTL_ADC_VOL, 0x07, 0, gain_scale),
+ SOC_SINGLE_TLV("Line Out Playback Volume", AC200_LINEOUT_CTL,
+ AC200_LINEOUT_CTL_VOL, 0x1f, 0, lineout_scale),
+ SOC_SINGLE_TLV("MIC1 Boost Volume", AC200_ADC_MIC_CTL,
+ AC200_ADC_MIC_CTL_MIC1_BOOST, 0x07, 0, mic_scale),
+ SOC_SINGLE_TLV("MIC2 Boost Volume", AC200_ADC_MIC_CTL,
+ AC200_ADC_MIC_CTL_MIC2_BOOST, 0x07, 0, mic_scale),
+ SOC_DOUBLE("Line Out Playback Switch", AC200_LINEOUT_CTL,
+ AC200_LINEOUT_CTL_LEN, AC200_LINEOUT_CTL_REN, 1, 0),
+};
+
+static const struct snd_kcontrol_new i2s_mixer[] = {
+ SOC_DAPM_DOUBLE("I2S DAC Capture Switch", AC200_I2S_MIX_SRC,
+ AC200_I2S_MIX_SRC_LMIX_DAC,
+ AC200_I2S_MIX_SRC_RMIX_DAC, 1, 0),
+ SOC_DAPM_DOUBLE("I2S ADC Capture Switch", AC200_I2S_MIX_SRC,
+ AC200_I2S_MIX_SRC_LMIX_ADC,
+ AC200_I2S_MIX_SRC_RMIX_ADC, 1, 0),
+};
+
+static const struct snd_kcontrol_new dac_mixer[] = {
+ SOC_DAPM_DOUBLE("DAC I2S Playback Switch", AC200_DAC_MIX_SRC,
+ AC200_DAC_MIX_SRC_LMIX_DAC,
+ AC200_DAC_MIX_SRC_RMIX_DAC, 1, 0),
+ SOC_DAPM_DOUBLE("ADC Playback Switch", AC200_DAC_MIX_SRC,
+ AC200_DAC_MIX_SRC_LMIX_ADC,
+ AC200_DAC_MIX_SRC_RMIX_ADC, 1, 0),
+};
+
+static const struct snd_kcontrol_new output_mixer[] = {
+ SOC_DAPM_DOUBLE("MIC1 Playback Switch", AC200_OUT_MIX_SRC,
+ AC200_OUT_MIX_SRC_LMIX_MIC1,
+ AC200_OUT_MIX_SRC_RMIX_MIC1, 1, 0),
+ SOC_DAPM_DOUBLE("MIC2 Playback Switch", AC200_OUT_MIX_SRC,
+ AC200_OUT_MIX_SRC_LMIX_MIC2,
+ AC200_OUT_MIX_SRC_RMIX_MIC2, 1, 0),
+ SOC_DAPM_DOUBLE("DAC Playback Switch", AC200_OUT_MIX_SRC,
+ AC200_OUT_MIX_SRC_LMIX_LDAC,
+ AC200_OUT_MIX_SRC_RMIX_RDAC, 1, 0),
+ SOC_DAPM_DOUBLE("DAC Reversed Playback Switch", AC200_OUT_MIX_SRC,
+ AC200_OUT_MIX_SRC_LMIX_RDAC,
+ AC200_OUT_MIX_SRC_RMIX_LDAC, 1, 0),
+};
+
+static const struct snd_kcontrol_new input_mixer[] = {
+ SOC_DAPM_DOUBLE("MIC1 Capture Switch", AC200_ADC_MIX_SRC,
+ AC200_ADC_MIX_SRC_LMIX_MIC1,
+ AC200_ADC_MIX_SRC_RMIX_MIC1, 1, 0),
+ SOC_DAPM_DOUBLE("MIC2 Capture Switch", AC200_ADC_MIX_SRC,
+ AC200_ADC_MIX_SRC_LMIX_MIC2,
+ AC200_ADC_MIX_SRC_RMIX_MIC2, 1, 0),
+ SOC_DAPM_DOUBLE("Output Mixer Capture Switch", AC200_ADC_MIX_SRC,
+ AC200_ADC_MIX_SRC_LMIX_LMIX,
+ AC200_ADC_MIX_SRC_RMIX_RMIX, 1, 0),
+ SOC_DAPM_DOUBLE("Output Mixer Reverse Capture Switch",
+ AC200_ADC_MIX_SRC,
+ AC200_ADC_MIX_SRC_LMIX_RMIX,
+ AC200_ADC_MIX_SRC_RMIX_LMIX, 1, 0),
+};
+
+const char * const lineout_mux_enum_text[] = {
+ "Stereo", "Mono",
+};
+
+static SOC_ENUM_DOUBLE_DECL(lineout_mux_enum, AC200_LINEOUT_CTL,
+ AC200_LINEOUT_CTL_LMONO, AC200_LINEOUT_CTL_RMONO,
+ lineout_mux_enum_text);
+
+static const struct snd_kcontrol_new lineout_mux =
+ SOC_DAPM_ENUM("Line Out Source Playback Route", lineout_mux_enum);
+
+static const struct snd_soc_dapm_widget ac200_codec_dapm_widgets[] = {
+ /* Regulator */
+ SND_SOC_DAPM_REGULATOR_SUPPLY("avcc", 0, 0),
+
+ /* System clocks */
+ SND_SOC_DAPM_SUPPLY("CLK SYS I2S", AC200_SYS_CLK_CTL,
+ AC200_SYS_CLK_CTL_I2S, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLK SYS DAC", AC200_SYS_CLK_CTL,
+ AC200_SYS_CLK_CTL_DAC, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLK SYS ADC", AC200_SYS_CLK_CTL,
+ AC200_SYS_CLK_CTL_ADC, 0, NULL, 0),
+
+ /* Module resets */
+ SND_SOC_DAPM_SUPPLY("RST SYS I2S", AC200_SYS_MOD_RST,
+ AC200_SYS_MOD_RST_I2S, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RST SYS DAC", AC200_SYS_MOD_RST,
+ AC200_SYS_MOD_RST_DAC, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("RST SYS ADC", AC200_SYS_MOD_RST,
+ AC200_SYS_MOD_RST_DAC, 0, NULL, 0),
+
+ /* I2S gates */
+ SND_SOC_DAPM_SUPPLY("CLK I2S GEN", AC200_I2S_CTL,
+ AC200_I2S_CTL_GEN, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLK I2S SDO", AC200_I2S_CTL,
+ AC200_I2S_CTL_SDO_EN, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLK I2S TX", AC200_I2S_CTL,
+ AC200_I2S_CTL_TX_EN, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CLK I2S RX", AC200_I2S_CTL,
+ AC200_I2S_CTL_RX_EN, 0, NULL, 0),
+
+ /* Module supplies */
+ SND_SOC_DAPM_SUPPLY("ADC Enable", AC200_ADC_CTL,
+ AC200_ADC_CTL_ADC_EN, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Enable", AC200_DAC_CTL,
+ AC200_DAC_CTL_DAC_EN, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Line Out Enable", AC200_LINEOUT_CTL,
+ AC200_LINEOUT_CTL_EN, 0, NULL, 0),
+
+ /* Bias */
+ SND_SOC_DAPM_SUPPLY("MIC Bias", AC200_MBIAS_CTL,
+ AC200_MBIAS_CTL_MBIAS_EN, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADDA Bias", AC200_MBIAS_CTL,
+ AC200_MBIAS_CTL_ADDA_BIAS_EN, 0, NULL, 0),
+
+ /* DAC */
+ SND_SOC_DAPM_DAC("Left DAC", "Playback", AC200_OUT_MIX_CTL,
+ AC200_OUT_MIX_CTL_LDAC_EN, 0),
+ SND_SOC_DAPM_DAC("Right DAC", "Playback", AC200_OUT_MIX_CTL,
+ AC200_OUT_MIX_CTL_RDAC_EN, 0),
+
+ /* ADC */
+ SND_SOC_DAPM_ADC("Left ADC", "Capture", AC200_ADC_MIC_CTL,
+ AC200_ADC_MIC_CTL_LADC_EN, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "Capture", AC200_ADC_MIC_CTL,
+ AC200_ADC_MIC_CTL_RADC_EN, 0),
+
+ /* Mixers */
+ SND_SOC_DAPM_MIXER("Left Output Mixer", AC200_OUT_MIX_CTL,
+ AC200_OUT_MIX_CTL_LMIX_EN, 0,
+ output_mixer, ARRAY_SIZE(output_mixer)),
+ SND_SOC_DAPM_MIXER("Right Output Mixer", AC200_OUT_MIX_CTL,
+ AC200_OUT_MIX_CTL_RMIX_EN, 0,
+ output_mixer, ARRAY_SIZE(output_mixer)),
+
+ SND_SOC_DAPM_MIXER("Left Input Mixer", SND_SOC_NOPM, 0, 0,
+ input_mixer, ARRAY_SIZE(input_mixer)),
+ SND_SOC_DAPM_MIXER("Right Input Mixer", SND_SOC_NOPM, 0, 0,
+ input_mixer, ARRAY_SIZE(input_mixer)),
+
+ SND_SOC_DAPM_MIXER("Left DAC Mixer", SND_SOC_NOPM, 0, 0,
+ dac_mixer, ARRAY_SIZE(dac_mixer)),
+ SND_SOC_DAPM_MIXER("Right DAC Mixer", SND_SOC_NOPM, 0, 0,
+ dac_mixer, ARRAY_SIZE(dac_mixer)),
+
+ SND_SOC_DAPM_MIXER("Left I2S Mixer", SND_SOC_NOPM, 0, 0,
+ i2s_mixer, ARRAY_SIZE(i2s_mixer)),
+ SND_SOC_DAPM_MIXER("Right I2S Mixer", SND_SOC_NOPM, 0, 0,
+ i2s_mixer, ARRAY_SIZE(i2s_mixer)),
+
+ /* Muxes */
+ SND_SOC_DAPM_MUX("Line Out Source Playback Route",
+ SND_SOC_NOPM, 0, 0, &lineout_mux),
+
+ /* Gain/attenuation */
+ SND_SOC_DAPM_PGA("MIC1 Amplifier", AC200_ADC_MIC_CTL,
+ AC200_ADC_MIC_CTL_MIC1_GAIN_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("MIC2 Amplifier", AC200_ADC_MIC_CTL,
+ AC200_ADC_MIC_CTL_MIC2_GAIN_EN, 0, NULL, 0),
+
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("LINEOUT"),
+};
+
+static const struct snd_soc_dapm_route ac200_codec_dapm_routes[] = {
+ { "RST SYS I2S", NULL, "CLK SYS I2S" },
+ { "RST SYS ADC", NULL, "CLK SYS ADC" },
+ { "RST SYS DAC", NULL, "CLK SYS DAC" },
+
+ { "CLK I2S GEN", NULL, "RST SYS I2S" },
+ { "CLK I2S SDO", NULL, "CLK I2S GEN" },
+ { "CLK I2S TX", NULL, "CLK I2S SDO" },
+ { "CLK I2S RX", NULL, "CLK I2S SDO" },
+
+ { "ADC Enable", NULL, "RST SYS ADC" },
+ { "ADC Enable", NULL, "ADDA Bias" },
+ { "ADC Enable", NULL, "avcc" },
+ { "DAC Enable", NULL, "RST SYS DAC" },
+ { "DAC Enable", NULL, "ADDA Bias" },
+ { "DAC Enable", NULL, "avcc" },
+
+ { "Left DAC", NULL, "DAC Enable" },
+ { "Left DAC", NULL, "CLK I2S RX" },
+ { "Right DAC", NULL, "DAC Enable" },
+ { "Right DAC", NULL, "CLK I2S RX" },
+
+ { "Left ADC", NULL, "ADC Enable" },
+ { "Left ADC", NULL, "CLK I2S TX" },
+ { "Right ADC", NULL, "ADC Enable" },
+ { "Right ADC", NULL, "CLK I2S TX" },
+
+ { "Left Output Mixer", "MIC1 Playback Switch", "MIC1 Amplifier" },
+ { "Left Output Mixer", "MIC2 Playback Switch", "MIC2 Amplifier" },
+ { "Left Output Mixer", "DAC Playback Switch", "Left DAC Mixer" },
+ { "Left Output Mixer", "DAC Reversed Playback Switch", "Right DAC Mixer" },
+
+ { "Right Output Mixer", "MIC1 Playback Switch", "MIC1 Amplifier" },
+ { "Right Output Mixer", "MIC2 Playback Switch", "MIC2 Amplifier" },
+ { "Right Output Mixer", "DAC Playback Switch", "Right DAC Mixer" },
+ { "Right Output Mixer", "DAC Reversed Playback Switch", "Left DAC Mixer" },
+
+ { "Left Input Mixer", "MIC1 Capture Switch", "MIC1 Amplifier" },
+ { "Left Input Mixer", "MIC2 Capture Switch", "MIC2 Amplifier" },
+ { "Left Input Mixer", "Output Mixer Capture Switch", "Left Output Mixer" },
+ { "Left Input Mixer", "Output Mixer Reverse Capture Switch", "Right Output Mixer" },
+
+ { "Right Input Mixer", "MIC1 Capture Switch", "MIC1 Amplifier" },
+ { "Right Input Mixer", "MIC2 Capture Switch", "MIC2 Amplifier" },
+ { "Right Input Mixer", "Output Mixer Capture Switch", "Right Output Mixer" },
+ { "Right Input Mixer", "Output Mixer Reverse Capture Switch", "Left Output Mixer" },
+
+ { "Left I2S Mixer", "I2S DAC Capture Switch", "Left DAC" },
+ { "Left I2S Mixer", "I2S ADC Capture Switch", "Left Input Mixer" },
+ { "Right I2S Mixer", "I2S DAC Capture Switch", "Right DAC" },
+ { "Right I2S Mixer", "I2S ADC Capture Switch", "Right Input Mixer" },
+
+ { "Left DAC Mixer", "DAC I2S Playback Switch", "Left DAC" },
+ { "Left DAC Mixer", "ADC Playback Switch", "Left Input Mixer" },
+ { "Right DAC Mixer", "DAC I2S Playback Switch", "Right DAC" },
+ { "Right DAC Mixer", "ADC Playback Switch", "Right Input Mixer" },
+
+ { "Line Out Source Playback Route", "Stereo", "Left Output Mixer" },
+ { "Line Out Source Playback Route", "Stereo", "Right Output Mixer" },
+ { "Line Out Source Playback Route", "Mono", "Right Output Mixer" },
+ { "Line Out Source Playback Route", "Mono", "Left Output Mixer" },
+
+ { "Left ADC", NULL, "Left I2S Mixer" },
+ { "Right ADC", NULL, "Right I2S Mixer" },
+
+ { "LINEOUT", NULL, "Line Out Enable", },
+ { "LINEOUT", NULL, "Line Out Source Playback Route" },
+
+ { "MIC1", NULL, "MIC Bias" },
+ { "MIC2", NULL, "MIC Bias" },
+ { "MIC1 Amplifier", NULL, "MIC1" },
+ { "MIC2 Amplifier", NULL, "MIC2" },
+};
+
+static int ac200_get_sr_sw(unsigned int width)
+{
+ switch (width) {
+ case 8:
+ return 1;
+ case 12:
+ return 2;
+ case 16:
+ return 3;
+ case 20:
+ return 4;
+ case 24:
+ return 5;
+ case 28:
+ return 6;
+ case 32:
+ return 7;
+ }
+
+ return -EINVAL;
+}
+
+static const struct ac200_map ac200_bclk_div_map[] = {
+ { .match = 1, .value = 1 },
+ { .match = 2, .value = 2 },
+ { .match = 4, .value = 3 },
+ { .match = 6, .value = 4 },
+ { .match = 8, .value = 5 },
+ { .match = 12, .value = 6 },
+ { .match = 16, .value = 7 },
+ { .match = 24, .value = 8 },
+ { .match = 32, .value = 9 },
+ { .match = 48, .value = 10 },
+ { .match = 64, .value = 11 },
+ { .match = 96, .value = 12 },
+ { .match = 128, .value = 13 },
+ { .match = 176, .value = 14 },
+ { .match = 192, .value = 15 },
+};
+
+static int ac200_get_bclk_div(unsigned int sample_rate, unsigned int period)
+{
+ unsigned int sysclk_rate = (sample_rate % 4000) ? 22579200 : 24576000;
+ unsigned int div = sysclk_rate / sample_rate / period;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ac200_bclk_div_map); i++) {
+ const struct ac200_map *bdiv = &ac200_bclk_div_map[i];
+
+ if (bdiv->match == div)
+ return bdiv->value;
+ }
+
+ return -EINVAL;
+}
+
+static const struct ac200_map ac200_ssr_map[] = {
+ { .match = 8000, .value = 0 },
+ { .match = 11025, .value = 1 },
+ { .match = 12000, .value = 2 },
+ { .match = 16000, .value = 3 },
+ { .match = 22050, .value = 4 },
+ { .match = 24000, .value = 5 },
+ { .match = 32000, .value = 6 },
+ { .match = 44100, .value = 7 },
+ { .match = 48000, .value = 8 },
+ { .match = 96000, .value = 9 },
+ { .match = 192000, .value = 10 },
+};
+
+static int ac200_get_ssr(unsigned int sample_rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ac200_ssr_map); i++) {
+ const struct ac200_map *ssr = &ac200_ssr_map[i];
+
+ if (ssr->match == sample_rate)
+ return ssr->value;
+ }
+
+ return -EINVAL;
+}
+
+static int ac200_codec_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct ac200_codec *priv = snd_soc_dai_get_drvdata(dai);
+ unsigned int slot_width = params_physical_width(params);
+ unsigned int sample_rate = params_rate(params);
+ int sr, period, sw, bclkdiv, ssr;
+
+ sr = ac200_get_sr_sw(params_width(params));
+ if (sr < 0)
+ return sr;
+
+ sw = ac200_get_sr_sw(slot_width);
+ if (sw < 0)
+ return sw;
+
+ regmap_update_bits(priv->regmap, AC200_I2S_FMT0,
+ AC200_I2S_FMT0_SR_MASK |
+ AC200_I2S_FMT0_SW_MASK,
+ AC200_I2S_FMT0_SR(sr) |
+ AC200_I2S_FMT0_SW(sw));
+
+ switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ period = slot_width;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ period = slot_width * 2;
+ break;
+ }
+
+ bclkdiv = ac200_get_bclk_div(sample_rate, period);
+ if (bclkdiv < 0)
+ return bclkdiv;
+
+ regmap_update_bits(priv->regmap, AC200_I2S_CLK,
+ AC200_I2S_CLK_LRCK_MASK |
+ AC200_I2S_CLK_BCLKDIV_MASK,
+ AC200_I2S_CLK_LRCK(period) |
+ AC200_I2S_CLK_BCLKDIV(bclkdiv));
+
+ ssr = ac200_get_ssr(sample_rate);
+ if (ssr < 0)
+ return ssr;
+
+ regmap_update_bits(priv->regmap, AC200_SYS_SR_CTL,
+ AC200_SYS_SR_CTL_SR_MASK,
+ AC200_SYS_SR_CTL_SR(ssr));
+
+ return 0;
+}
+
+static int ac200_codec_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct ac200_codec *priv = snd_soc_dai_get_drvdata(dai);
+ unsigned long offset, mode, value;
+
+ priv->format = fmt;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ value = AC200_I2S_CLK_BCLK_OUT | AC200_I2S_CLK_LRCK_OUT;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFP:
+ value = AC200_I2S_CLK_LRCK_OUT;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFC:
+ value = AC200_I2S_CLK_BCLK_OUT;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ value = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(priv->regmap, AC200_I2S_CLK,
+ AC200_I2S_CLK_BCLK_OUT |
+ AC200_I2S_CLK_LRCK_OUT, value);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ mode = AC200_I2S_FMT0_MODE_LEFT;
+ offset = 1;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ mode = AC200_I2S_FMT0_MODE_RIGHT;
+ offset = 0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ mode = AC200_I2S_FMT0_MODE_LEFT;
+ offset = 0;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ mode = AC200_I2S_FMT0_MODE_PCM;
+ offset = 1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ mode = AC200_I2S_FMT0_MODE_PCM;
+ offset = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(priv->regmap, AC200_I2S_FMT0,
+ AC200_I2S_FMT0_MODE_MASK |
+ AC200_I2S_FMT0_TX_OFFSET_MASK |
+ AC200_I2S_FMT0_RX_OFFSET_MASK,
+ AC200_I2S_FMT0_MODE(mode) |
+ AC200_I2S_FMT0_TX_OFFSET(offset) |
+ AC200_I2S_FMT0_RX_OFFSET(offset));
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ value = 0;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ value = AC200_I2S_FMT1_LRCK_POL_INVERT;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ value = AC200_I2S_FMT1_BCLK_POL_INVERT;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ value = AC200_I2S_FMT1_BCLK_POL_INVERT |
+ AC200_I2S_FMT1_LRCK_POL_INVERT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(priv->regmap, AC200_I2S_FMT1,
+ AC200_I2S_FMT1_BCLK_POL_INVERT |
+ AC200_I2S_FMT1_LRCK_POL_INVERT, value);
+
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops ac200_codec_dai_ops = {
+ .hw_params = ac200_codec_hw_params,
+ .set_fmt = ac200_codec_set_fmt,
+};
+
+static struct snd_soc_dai_driver ac200_codec_dai = {
+ .name = "ac200-dai",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AC200_CODEC_RATES,
+ .formats = AC200_CODEC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AC200_CODEC_RATES,
+ .formats = AC200_CODEC_FORMATS,
+ },
+ .ops = &ac200_codec_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+};
+
+static int ac200_codec_component_probe(struct snd_soc_component *component)
+{
+ struct ac200_codec *priv = snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_init_regmap(component, priv->regmap);
+
+ return 0;
+}
+
+static struct snd_soc_component_driver ac200_soc_component = {
+ .controls = ac200_codec_controls,
+ .num_controls = ARRAY_SIZE(ac200_codec_controls),
+ .dapm_widgets = ac200_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ac200_codec_dapm_widgets),
+ .dapm_routes = ac200_codec_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ac200_codec_dapm_routes),
+ .probe = ac200_codec_component_probe,
+};
+
+static int ac200_codec_probe(struct platform_device *pdev)
+{
+ struct ac200_codec *priv;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct ac200_codec), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!priv->regmap)
+ return -EPROBE_DEFER;
+
+ platform_set_drvdata(pdev, priv);
+
+ ret = regmap_write(priv->regmap, AC200_SYS_AUDIO_CTL0,
+ AC200_SYS_AUDIO_CTL0_RST_INVALID |
+ AC200_SYS_AUDIO_CTL0_MCLK_GATING);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(priv->regmap, AC200_SYS_AUDIO_CTL1,
+ AC200_SYS_AUDIO_CTL1_I2S_IO_EN);
+ if (ret)
+ return ret;
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &ac200_soc_component,
+ &ac200_codec_dai, 1);
+
+ if (ret)
+ dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
+
+ return ret;
+}
+
+static int ac200_codec_remove(struct platform_device *pdev)
+{
+ struct ac200_codec *priv = dev_get_drvdata(&pdev->dev);
+
+ regmap_write(priv->regmap, AC200_SYS_AUDIO_CTL0, 0);
+ regmap_write(priv->regmap, AC200_SYS_AUDIO_CTL1, 0);
+
+ return 0;
+}
+
+static const struct of_device_id ac200_codec_match[] = {
+ { .compatible = "x-powers,ac200-codec" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ac200_codec_match);
+
+static struct platform_driver ac200_codec_driver = {
+ .driver = {
+ .name = "ac200-codec",
+ .of_match_table = ac200_codec_match,
+ },
+ .probe = ac200_codec_probe,
+ .remove = ac200_codec_remove,
+};
+module_platform_driver(ac200_codec_driver);
+
+MODULE_DESCRIPTION("X-Powers AC200 Codec Driver");
+MODULE_AUTHOR("Jernej Skrabec <jernej.skrabec@gmail.com>");
+MODULE_LICENSE("GPL");
--
2.34.1

View File

@ -0,0 +1,87 @@
From 3d753e7ff3fac741de81406116787629db2f9512 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@gmail.com>
Date: Thu, 1 Sep 2022 17:45:03 +0200
Subject: [PATCH 8/9] arm64: dts: allwinner: h6: add AC200 codec nodes
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 42 ++++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
index 91352c2c84b5..2f94c13e3d73 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
@@ -17,6 +17,22 @@ / {
#address-cells = <1>;
#size-cells = <1>;
+ analog: analog-codec {
+ compatible = "simple-audio-card";
+ simple-audio-card,format = "i2s";
+ simple-audio-card,name = "ac200-audio";
+ simple-audio-card,mclk-fs = <512>;
+ status = "disabled";
+
+ simple-audio-card,cpu {
+ sound-dai = <&i2s3>;
+ };
+
+ simple-audio-card,codec {
+ sound-dai = <&ac200_codec>;
+ };
+ };
+
ac200_pwm_clk: ac200_clk {
compatible = "pwm-clock";
#clock-cells = <0>;
@@ -385,6 +401,11 @@ i2c3_pins: i2c3-pins {
bias-pull-up;
};
+ i2s3_pins: i2s3-pins {
+ pins = "PB12", "PB13", "PB14", "PB15", "PB16";
+ function = "i2s3";
+ };
+
mmc0_pins: mmc0-pins {
pins = "PF0", "PF1", "PF2", "PF3",
"PF4", "PF5";
@@ -653,6 +674,12 @@ ac200_ephy_ctl: syscon {
phy-mode = "rmii";
status = "disabled";
};
+
+ ac200_codec: codec {
+ #sound-dai-cells = <0>;
+ compatible = "x-powers,ac200-codec";
+ status = "disabled";
+ };
};
};
@@ -706,6 +733,21 @@ mdio: mdio {
};
};
+ i2s3: i2s@508f000 {
+ #sound-dai-cells = <0>;
+ compatible = "allwinner,sun50i-h6-i2s";
+ reg = <0x0508f000 0x1000>;
+ interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_BUS_I2S3>, <&ccu CLK_I2S3>;
+ clock-names = "apb", "mod";
+ dmas = <&dma 6>, <&dma 6>;
+ resets = <&ccu RST_BUS_I2S3>;
+ dma-names = "rx", "tx";
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2s3_pins>;
+ status = "disabled";
+ };
+
i2s1: i2s@5091000 {
#sound-dai-cells = <0>;
compatible = "allwinner,sun50i-h6-i2s";
--
2.34.1

View File

@ -0,0 +1,142 @@
From fdf29f6de51f19679fb557e98624e003b0e09264 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@gmail.com>
Date: Thu, 1 Sep 2022 17:49:28 +0200
Subject: [PATCH 9/9] arm64: dts: allwinner: h6: enable AC200 codec
Enable AC200 analog codec on H6 based boards where present.
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
.../dts/allwinner/sun50i-h6-orangepi-3.dts | 25 +++++++++++++++++++
.../boot/dts/allwinner/sun50i-h6-pine-h64.dts | 25 +++++++++++++++++++
.../allwinner/sun50i-h6-tanix-tx6-mini.dts | 14 +++++++++++
3 files changed, 64 insertions(+)
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts
index 6fc65e8db220..cf25305f3f8d 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3.dts
@@ -93,6 +93,19 @@ wifi_pwrseq: wifi-pwrseq {
};
};
+&ac200_codec {
+ avcc-supply = <&reg_aldo2>;
+ status = "okay";
+};
+
+&ac200_pwm_clk {
+ status = "okay";
+};
+
+&analog {
+ status = "okay";
+};
+
&cpu0 {
cpu-supply = <&reg_dcdca>;
};
@@ -128,6 +141,14 @@ hdmi_out_con: endpoint {
};
};
+&i2c3 {
+ status = "okay";
+};
+
+&i2s3 {
+ status = "okay";
+};
+
&mmc0 {
vmmc-supply = <&reg_cldo1>;
cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */
@@ -175,6 +196,10 @@ &pio {
vcc-pg-supply = <&reg_vcc_wifi_io>;
};
+&pwm {
+ status = "okay";
+};
+
&r_ir {
status = "okay";
};
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dts b/arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dts
index 1ffd68f43f87..aab11a49d2cb 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dts
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dts
@@ -81,6 +81,19 @@ reg_usb_vbus: vbus {
};
};
+&ac200_codec {
+ avcc-supply = <&reg_aldo2>;
+ status = "okay";
+};
+
+&ac200_pwm_clk {
+ status = "okay";
+};
+
+&analog {
+ status = "okay";
+};
+
&cpu0 {
cpu-supply = <&reg_dcdca>;
};
@@ -123,6 +136,14 @@ hdmi_out_con: endpoint {
};
};
+&i2c3 {
+ status = "okay";
+};
+
+&i2s3 {
+ status = "okay";
+};
+
&mdio {
ext_rgmii_phy: ethernet-phy@1 {
compatible = "ethernet-phy-ieee802.3-c22";
@@ -161,6 +182,10 @@ &pio {
vcc-pg-supply = <&reg_aldo1>;
};
+&pwm {
+ status = "okay";
+};
+
&r_i2c {
status = "okay";
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix-tx6-mini.dts b/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix-tx6-mini.dts
index 08d84160d88f..931e8b99fd93 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix-tx6-mini.dts
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix-tx6-mini.dts
@@ -10,6 +10,20 @@ / {
compatible = "oranth,tanix-tx6-mini", "allwinner,sun50i-h6";
};
+
+&ac200_codec {
+ avcc-supply = <&reg_vcc3v3>;
+ status = "okay";
+};
+
+&analog {
+ status = "okay";
+};
+
+&i2s3 {
+ status = "okay";
+};
+
&r_ir {
linux,rc-map-name = "rc-tanix-tx3mini";
};
--
2.34.1

View File

@ -1,610 +0,0 @@
From be2dfa31058ab3191133ad1da5e8ec03ae217835 Mon Sep 17 00:00:00 2001
From: The-going <48602507+The-going@users.noreply.github.com>
Date: Fri, 6 May 2022 12:29:05 +0300
Subject: [PATCH] Add initial support for orangepi3-lts
---
arch/arm64/boot/dts/allwinner/Makefile | 1 +
.../allwinner/sun50i-h6-orangepi-3-lts.dts | 398 ++++++++++++++++++
arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi | 101 ++++-
3 files changed, 487 insertions(+), 13 deletions(-)
create mode 100644 arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3-lts.dts
diff --git a/arch/arm64/boot/dts/allwinner/Makefile b/arch/arm64/boot/dts/allwinner/Makefile
index a14ad689b..7dcf90b32 100644
--- a/arch/arm64/boot/dts/allwinner/Makefile
+++ b/arch/arm64/boot/dts/allwinner/Makefile
@@ -41,6 +41,7 @@ dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h5-orangepi-zero-plus.dtb
dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h5-orangepi-zero-plus2.dtb
dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h6-beelink-gs1.dtb
dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h6-orangepi-3.dtb
+dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h6-orangepi-3-lts.dtb
dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h6-orangepi-lite2.dtb
dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h6-orangepi-one-plus.dtb
dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h6-pine-h64.dtb
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3-lts.dts b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3-lts.dts
new file mode 100644
index 000000000..cc5a73026
--- /dev/null
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3-lts.dts
@@ -0,0 +1,397 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+// Copyright (C) 2019 Ondřej Jirman <megous@megous.com>
+
+/dts-v1/;
+
+#include "sun50i-h6.dtsi"
+#include "sun50i-h6-cpu-opp.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+
+/ {
+ model = "OrangePi 3 LTS";
+ compatible = "xunlong,orangepi-3-lts", "allwinner,sun50i-h6";
+
+ aliases {
+ serial0 = &uart0;
+ serial1 = &uart1;
+ serial9 = &r_uart;
+ ethernet0 = &emac;
+ };
+
+ chosen {
+ stdout-path = "serial0:115200n8";
+ };
+
+ connector {
+ compatible = "hdmi-connector";
+ ddc-en-gpios = <&pio 7 2 GPIO_ACTIVE_HIGH>; /* PH2 */
+ type = "a";
+
+ port {
+ hdmi_con_in: endpoint {
+ remote-endpoint = <&hdmi_out_con>;
+ };
+ };
+ };
+
+ ext_osc32k: ext_osc32k_clk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <32768>;
+ clock-output-names = "ext_osc32k";
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ status {
+ label = "green-led";
+ gpios = <&r_pio 0 7 GPIO_ACTIVE_HIGH>; /* PL7 */
+ default-state = "on";
+ };
+
+ power {
+ label = "red-led";
+ gpios = <&r_pio 0 4 GPIO_ACTIVE_HIGH>; /* PL4 */
+ };
+ };
+
+ reg_vcc5v: vcc5v {
+ /* board wide 5V supply directly from the DC jack */
+ compatible = "regulator-fixed";
+ regulator-name = "vcc-5v";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ regulator-always-on;
+ };
+
+ reg_gmac_3v3: gmac-3v3 {
+ compatible = "regulator-fixed";
+ regulator-name = "vcc-gmac-3v3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ startup-delay-us = <100000>;
+ gpio = <&pio 3 6 GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+ };
+
+ reg_vcc33_wifi: vcc33-wifi {
+ /* Always on 3.3V regulator for WiFi and BT */
+ compatible = "regulator-fixed";
+ regulator-name = "vcc33-wifi";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ enable-active-high;
+ gpio = <&pio 7 7 GPIO_ACTIVE_HIGH>; /* PH7 */
+ };
+
+ reg_vcc_wifi_io: vcc-wifi-io {
+ /* Always on 1.8V/300mA regulator for WiFi and BT IO */
+ compatible = "regulator-fixed";
+ regulator-name = "vcc-wifi-io";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ vin-supply = <&reg_vcc33_wifi>;
+ };
+
+ wifi_pwrseq: wifi-pwrseq {
+ compatible = "mmc-pwrseq-simple";
+ clocks = <&rtc 1>;
+ clock-names = "ext_clock";
+ reset-gpios = <&r_pio 1 3 GPIO_ACTIVE_LOW>; /* PM3 */
+ post-power-on-delay-ms = <200>;
+ };
+};
+
+&cpu0 {
+ cpu-supply = <&reg_dcdca>;
+};
+
+&de {
+ status = "okay";
+};
+
+&dwc3 {
+ status = "okay";
+};
+
+&ehci0 {
+ status = "okay";
+};
+
+&ehci3 {
+ status = "okay";
+};
+
+&gpu {
+ mali-supply = <&reg_dcdcc>;
+ status = "okay";
+};
+
+&hdmi {
+ status = "okay";
+};
+
+&hdmi_out {
+ hdmi_out_con: endpoint {
+ remote-endpoint = <&hdmi_con_in>;
+ };
+};
+
+&emac {
+ pinctrl-names = "default";
+ pinctrl-0 = <&ext_rgmii_pins>;
+ phy-mode = "rgmii-id";
+ phy-handle = <&ext_rgmii_phy>;
+ phy-supply = <&reg_gmac_3v3>;
+ allwinner,rx-delay-ps = <200>;
+ allwinner,tx-delay-ps = <300>;
+ snps,reset-gpio = <&pio 3 14 GPIO_ACTIVE_LOW>; /* PD14 */
+ snps,reset-delays-us = <0 10000 1000000>;
+ snps,reset-active-low;
+ status = "okay";
+};
+
+&mdio {
+ ext_rgmii_phy: ethernet-phy@1 {
+ compatible = "ethernet-phy-ieee802.3-c22";
+ reg = <1>;
+ };
+};
+
+&i2s1 {
+ status = "okay";
+};
+
+&mmc0 {
+ vmmc-supply = <&reg_cldo1>;
+ cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */
+ bus-width = <4>;
+ status = "okay";
+};
+
+&mmc1 {
+ vmmc-supply = <&reg_vcc33_wifi>;
+ vqmmc-supply = <&reg_vcc_wifi_io>;
+ mmc-pwrseq = <&wifi_pwrseq>;
+ bus-width = <4>;
+ non-removable;
+ status = "okay";
+};
+
+&mmc2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc2_pins>;
+ vmmc-supply = <&reg_cldo1>;
+ vqmmc-supply = <&reg_bldo2>;
+ bus-width = <8>;
+ non-removable;
+ cap-mmc-hw-reset;
+ mmc-hs200-1_8v;
+ status = "okay";
+};
+
+&ohci0 {
+ status = "okay";
+};
+
+&ohci3 {
+ status = "okay";
+};
+
+&pio {
+ vcc-pc-supply = <&reg_bldo2>;
+ vcc-pd-supply = <&reg_cldo1>;
+ vcc-pg-supply = <&reg_vcc_wifi_io>;
+};
+
+&r_rsb {
+ status = "okay";
+
+ axp805: pmic@745 {
+ compatible = "x-powers,axp805", "x-powers,axp806";
+ reg = <0x745>;
+ interrupt-parent = <&r_intc>;
+ interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_LOW>;
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ x-powers,self-working-mode;
+ vina-supply = <&reg_vcc5v>;
+ vinb-supply = <&reg_vcc5v>;
+ vinc-supply = <&reg_vcc5v>;
+ vind-supply = <&reg_vcc5v>;
+ vine-supply = <&reg_vcc5v>;
+ aldoin-supply = <&reg_vcc5v>;
+ bldoin-supply = <&reg_vcc5v>;
+ cldoin-supply = <&reg_vcc5v>;
+
+ regulators {
+ reg_aldo1: aldo1 {
+ regulator-always-on;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-name = "vcc-pl-led-ir";
+ };
+
+ reg_aldo2: aldo2 {
+ regulator-always-on;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-name = "vcc33-audio-tv-ephy-mac";
+ regulator-enable-ramp-delay = <100000>;
+ };
+
+ /* ALDO3 is shorted to CLDO1 */
+ reg_aldo3: aldo3 {
+ regulator-always-on;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-name = "vcc33-io-pd-emmc-sd-usb-uart-1";
+ };
+
+ reg_bldo1: bldo1 {
+ regulator-always-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcc18-dram-bias-pll";
+ };
+
+ reg_bldo2: bldo2 {
+ regulator-always-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcc-efuse-pcie-hdmi-pc";
+ };
+
+ reg_blod3: bldo3 {
+ regulator-always-on;
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vcc-wifi-io-pm-pg";
+ };
+
+ bldo4 {
+ /* unused */
+ };
+
+ reg_cldo1: cldo1 {
+ regulator-always-on;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-name = "vcc33-io-pd-emmc-sd-usb-uart-2";
+ };
+
+ cldo2 {
+ /* unused */
+ };
+
+ cldo3 {
+ /* unused */
+ };
+
+ reg_dcdca: dcdca {
+ regulator-always-on;
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1160000>;
+ regulator-ramp-delay = <2500>;
+ regulator-name = "vdd-cpu";
+ };
+
+ reg_dcdcc: dcdcc {
+ regulator-enable-ramp-delay = <32000>;
+ regulator-min-microvolt = <810000>;
+ regulator-max-microvolt = <1080000>;
+ regulator-ramp-delay = <2500>;
+ regulator-name = "vdd-gpu";
+ };
+
+ reg_dcdcd: dcdcd {
+ regulator-always-on;
+ regulator-min-microvolt = <960000>;
+ regulator-max-microvolt = <960000>;
+ regulator-name = "vdd-sys";
+ };
+
+ reg_dcdce: dcdce {
+ regulator-always-on;
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ regulator-name = "vcc-dram";
+ };
+
+ sw {
+ /* unused */
+ };
+ };
+ };
+};
+
+&pwm {
+ status = "okay";
+};
+
+&ac200_pwm_clk {
+ status = "okay";
+};
+
+&i2s3 {
+ status = "okay";
+};
+
+&i2c3 {
+ status = "okay";
+};
+
+&r_ir {
+ status = "okay";
+};
+
+&rtc {
+ clocks = <&ext_osc32k>;
+};
+
+&sound_hdmi {
+ status = "okay";
+};
+
+&sound_ac200 {
+ status = "okay";
+};
+
+/delete-node/ &spi0;
+
+&uart0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart0_ph_pins>;
+ status = "okay";
+};
+
+&uart1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&uart1_pins>, <&uart1_rts_cts_pins>;
+ uart-has-rtscts;
+ status = "disabled";
+};
+
+&usb2otg {
+ /*
+ * This board doesn't have a controllable VBUS even though it
+ * does have an ID pin. Using it as anything but a USB host is
+ * unsafe.
+ */
+ dr_mode = "host";
+ status = "okay";
+};
+
+&usb2phy {
+ usb0_id_det-gpios = <&pio 2 15 GPIO_ACTIVE_HIGH>; /* PC15 */
+ usb0_vbus-supply = <&reg_vcc5v>;
+ usb3_vbus-supply = <&reg_vcc5v>;
+ status = "okay";
+};
+
+&usb3phy {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
index 40be5ad6d..d5a3e046f 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6.dtsi
@@ -91,6 +91,13 @@ osc24M: osc24M_clk {
clock-output-names = "osc24M";
};
+ ext_osc32k: ext_osc32k_clk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <32768>;
+ clock-output-names = "ext_osc32k";
+ };
+
pmu {
compatible = "arm,cortex-a53-pmu";
interrupts = <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>,
@@ -126,6 +133,28 @@ cpu {
};
};
+ sound_ac200: sound_ac200 {
+ status = "disabled";
+ compatible = "simple-audio-card";
+ simple-audio-card,format = "i2s";
+ simple-audio-card,frame-master = <&i2s3_master>;
+ simple-audio-card,bitclock-master = <&i2s3_master>;
+ simple-audio-card,name = "allwinner,ac200-codec";
+ simple-audio-card,mclk-fs = <512>;
+ i2s3_master: simple-audio-card,cpu {
+ sound-dai = <&i2s3>;
+ system-clock-frequency = <22579200>;
+ dai-tdm-slot-num = <2>;
+ dai-tdm-slot-width = <32>;
+ };
+ simple-audio-card,codec {
+ sound-dai = <&ac200_codec>;
+ system-clock-frequency = <22579200>;
+ dai-tdm-slot-num = <2>;
+ dai-tdm-slot-width = <32>;
+ };
+ };
+
timer {
compatible = "arm,armv8-timer";
arm,no-tick-in-suspend;
@@ -370,7 +399,6 @@ pwm: pwm@300a000 {
pio: pinctrl@300b000 {
compatible = "allwinner,sun50i-h6-pinctrl";
reg = <0x0300b000 0x400>;
- interrupt-parent = <&r_intc>;
interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>,
@@ -520,6 +548,11 @@ uart3_rts_cts_pins: uart3-rts-cts-pins {
pins = "PD25", "PD26";
function = "uart3";
};
+
+ i2s3_pins: i2s3-pins {
+ pins = "PB12", "PB13", "PB14", "PB15", "PB16";
+ function = "i2s3";
+ };
};
iommu: iommu@30f0000 {
@@ -718,6 +751,7 @@ i2c3: i2c@5002c00 {
ac200: mfd@10 {
compatible = "x-powers,ac200";
reg = <0x10>;
+ clocks = <&ac200_pwm_clk>;
interrupt-parent = <&pio>;
interrupts = <1 20 IRQ_TYPE_LEVEL_LOW>;
interrupt-controller;
@@ -725,11 +759,16 @@ ac200: mfd@10 {
ac200_ephy: phy {
compatible = "x-powers,ac200-ephy";
- clocks = <&ac200_pwm_clk>;
nvmem-cells = <&ephy_calibration>;
nvmem-cell-names = "calibration";
status = "disabled";
};
+
+ ac200_codec: codec {
+ #sound-dai-cells = <0>;
+ compatible = "x-powers,ac200-codec";
+ status = "okay";
+ };
};
};
@@ -766,6 +805,21 @@ i2s1: i2s@5091000 {
status = "disabled";
};
+ i2s3: i2s@508f000 {
+ #sound-dai-cells = <0>;
+ compatible = "allwinner,sun50i-h6-i2s";
+ reg = <0x0508f000 0x1000>;
+ interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_BUS_I2S3>, <&ccu CLK_I2S3>;
+ clock-names = "apb", "mod";
+ dmas = <&dma 6>, <&dma 6>;
+ resets = <&ccu RST_BUS_I2S3>;
+ dma-names = "rx", "tx";
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2s3_pins>;
+ status = "disabled";
+ };
+
spdif: spdif@5093000 {
#sound-dai-cells = <0>;
compatible = "allwinner,sun50i-h6-spdif";
@@ -1061,6 +1115,7 @@ rtc: rtc@7000000 {
interrupts = <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>;
clock-output-names = "osc32k", "osc32k-out", "iosc";
+ clocks = <&ext_osc32k>;
#clock-cells = <1>;
};
@@ -1126,17 +1181,18 @@ r_uart_pins: r-uart-pins {
};
r_ir: ir@7040000 {
- compatible = "allwinner,sun50i-h6-ir",
- "allwinner,sun6i-a31-ir";
- reg = <0x07040000 0x400>;
- interrupts = <GIC_SPI 109 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&r_ccu CLK_R_APB1_IR>,
- <&r_ccu CLK_IR>;
- clock-names = "apb", "ir";
- resets = <&r_ccu RST_R_APB1_IR>;
- pinctrl-names = "default";
- pinctrl-0 = <&r_ir_rx_pin>;
- status = "disabled";
+ compatible = "allwinner,sun50i-h6-ir",
+ "allwinner,sun6i-a31-ir";
+ reg = <0x07040000 0x400>;
+ interrupt-parent = <&r_intc>;
+ interrupts = <GIC_SPI 109 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&r_ccu CLK_R_APB1_IR>,
+ <&r_ccu CLK_IR>;
+ clock-names = "apb", "ir";
+ resets = <&r_ccu RST_R_APB1_IR>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&r_ir_rx_pin>;
+ status = "disabled";
};
r_i2c: i2c@7081400 {
@@ -1178,6 +1234,25 @@ ths: thermal-sensor@5070400 {
nvmem-cell-names = "calibration";
#thermal-sensor-cells = <1>;
};
+
+ sunxi-info {
+ compatible = "allwinner,sun50i-h6-sys-info";
+ status = "okay";
+ };
+
+ addr_mgt: addr-mgt {
+ compatible = "allwinner,sunxi-addr_mgt";
+ type_addr_wifi = <0x2>;
+ type_addr_bt = <0x2>;
+ type_addr_eth = <0x2>;
+ status = "okay";
+ };
+
+ dump_reg: dump_reg@20000 {
+ compatible = "allwinner,sunxi-dump-reg";
+ reg = <0x0 0x03001000 0x0 0x0f20>;
+ status = "okay";
+ };
};
thermal-zones {
--
2.35.3

View File

@ -0,0 +1,59 @@
From 6f4abbea26de4ef963e9edd8eb051f5e7f2e0c6c Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@gmail.com>
Date: Mon, 2 Jan 2023 15:49:59 +0100
Subject: [PATCH] OrangePi 3 LTS support
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
---
arch/arm64/boot/dts/allwinner/Makefile | 1 +
.../allwinner/sun50i-h6-orangepi-3-lts.dts | 26 +++++++++++++++++++
2 files changed, 27 insertions(+)
create mode 100644 arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3-lts.dts
diff --git a/arch/arm64/boot/dts/allwinner/Makefile b/arch/arm64/boot/dts/allwinner/Makefile
index 6a96494a2e0a..ace8159a6324 100644
--- a/arch/arm64/boot/dts/allwinner/Makefile
+++ b/arch/arm64/boot/dts/allwinner/Makefile
@@ -32,6 +32,7 @@ dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h5-orangepi-zero-plus.dtb
dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h5-orangepi-zero-plus2.dtb
dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h6-beelink-gs1.dtb
dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h6-orangepi-3.dtb
+dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h6-orangepi-3-lts.dtb
dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h6-orangepi-lite2.dtb
dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h6-orangepi-one-plus.dtb
dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h6-pine-h64.dtb
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3-lts.dts b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3-lts.dts
new file mode 100644
index 000000000000..0e490936b50c
--- /dev/null
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-orangepi-3-lts.dts
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+// Copyright (C) 2023 Jernej Skrabec <jernej.skrabec@gmail.com>
+
+#include "sun50i-h6-orangepi-3.dts"
+
+/ {
+ model = "OrangePi 3 LTS";
+ compatible = "xunlong,orangepi-3-lts", "allwinner,sun50i-h6";
+};
+
+&emac {
+ allwinner,rx-delay-ps = <200>;
+ allwinner,tx-delay-ps = <300>;
+};
+
+&mmc1 {
+ status = "disabled";
+};
+
+&r_rsb {
+ clock-frequency = <100000>;
+};
+
+&uart1 {
+ status = "disabled";
+};
--
2.39.0

View File

@ -1,84 +0,0 @@
From 30ac1964293d718fe9fe16f8beec7c1cc80e18cc Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@siol.net>
Date: Sun, 12 Jan 2020 12:19:51 +0100
Subject: [PATCH 013/101] arm64:dts: sun50i-h6-tanix-tx6 Enable ethernet
Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---
.../dts/allwinner/sun50i-h6-tanix-tx6.dts | 32 +++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix-tx6.dts b/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix-tx6.dts
index e91860a9e..d21dfe721 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix-tx6.dts
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix-tx6.dts
@@ -13,6 +13,7 @@ / {
compatible = "oranth,tanix-tx6", "allwinner,sun50i-h6";
aliases {
+ ethernet0 = &emac;
serial0 = &uart0;
};
@@ -58,6 +59,14 @@ &cpu0 {
cpu-supply = <&reg_vdd_cpu_gpu>;
};
+&ac200_ephy {
+ status = "okay";
+};
+
+&ac200_pwm_clk {
+ status = "okay";
+};
+
&de {
status = "okay";
};
@@ -66,6 +75,14 @@ &dwc3 {
status = "okay";
};
+&emac {
+ pinctrl-names = "default";
+ pinctrl-0 = <&ext_rmii_pins>;
+ phy-mode = "rmii";
+ phy-handle = <&ext_rmii_phy>;
+ status = "okay";
+};
+
&ehci0 {
status = "okay";
};
@@ -93,6 +110,17 @@ &i2s1 {
status = "okay";
};
+&i2c3 {
+ status = "okay";
+};
+
+&mdio {
+ ext_rmii_phy: ethernet-phy@1 {
+ compatible = "ethernet-phy-ieee802.3-c22";
+ reg = <1>;
+ };
+};
+
&mmc0 {
pinctrl-names = "default";
pinctrl-0 = <&mmc0_pins>;
@@ -126,6 +154,10 @@ &pio {
vcc-pg-supply = <&reg_vcc1v8>;
};
+&pwm {
+ status = "okay";
+};
+
&r_ir {
linux,rc-map-name = "rc-tanix-tx5max";
status = "okay";
--
2.31.1

View File

@ -1,440 +0,0 @@
From 3e6411db1aa4dfe1d1a6d63c2cbed2c588d35aef Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@siol.net>
Date: Fri, 16 Aug 2019 16:38:21 +0200
Subject: [PATCH 006/101] drv:mfd: Add support for AC200
Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---
drivers/mfd/Kconfig | 9 ++
drivers/mfd/Makefile | 1 +
drivers/mfd/ac200.c | 170 +++++++++++++++++++++++++++++++
include/linux/mfd/ac200.h | 208 ++++++++++++++++++++++++++++++++++++++
4 files changed, 388 insertions(+)
create mode 100644 drivers/mfd/ac200.c
create mode 100644 include/linux/mfd/ac200.h
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index d2f345245..5b756e81b 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -178,6 +178,15 @@ 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
+ bool "X-Powers AC200"
+ select MFD_CORE
+ depends on I2C
+ 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 2ba6646e8..7edc825f9 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -142,6 +142,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 000000000..570573790
--- /dev/null
+++ b/drivers/mfd/ac200.c
@@ -0,0 +1,170 @@
+// 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>
+
+/* 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 = {
+ .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,
+ .mask_invert = true,
+ .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",
+ .num_resources = ARRAY_SIZE(ephy_resource),
+ .resources = ephy_resource,
+ .of_compatible = "x-powers,ac200-ephy",
+ },
+};
+
+static int ac200_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &i2c->dev;
+ struct ac200_dev *ac200;
+ int ret;
+
+ ac200 = devm_kzalloc(dev, sizeof(*ac200), GFP_KERNEL);
+ if (!ac200)
+ return -ENOMEM;
+
+ 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)
+ return ret;
+
+ ret = regmap_write(ac200->regmap, AC200_SYS_CONTROL, 1);
+ if (ret)
+ return ret;
+
+ /* enable interrupt pin */
+
+ ret = regmap_write(ac200->regmap, AC200_SYS_IRQ_ENABLE,
+ AC200_SYS_IRQ_ENABLE_OUT_EN);
+ if (ret)
+ return ret;
+
+ ret = regmap_add_irq_chip(ac200->regmap, i2c->irq, IRQF_ONESHOT, 0,
+ &ac200_regmap_irq_chip, &ac200->regmap_irqc);
+ if (ret)
+ return ret;
+
+ 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);
+ regmap_del_irq_chip(i2c->irq, ac200->regmap_irqc);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int 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);
+ regmap_del_irq_chip(i2c->irq, ac200->regmap_irqc);
+
+ return 0;
+}
+
+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" },
+ { /* sentinel */ }
+};
+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@siol.net>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/ac200.h b/include/linux/mfd/ac200.h
new file mode 100644
index 000000000..0c677094a
--- /dev/null
+++ b/include/linux/mfd/ac200.h
@@ -0,0 +1,208 @@
+/* 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>
+
+/* 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 regmap *regmap;
+ struct regmap_irq_chip_data *regmap_irqc;
+};
+
+#endif /* __LINUX_MFD_AC200_H */
--
2.31.1

View File

@ -1,272 +0,0 @@
From e0b1f9c0e3ca7fb234215eef9f643a8fb98a4d06 Mon Sep 17 00:00:00 2001
From: Jernej Skrabec <jernej.skrabec@siol.net>
Date: Fri, 16 Aug 2019 16:38:57 +0200
Subject: [PATCH 009/101] drv:net:phy: Add support for AC200 EPHY
Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
---
drivers/net/phy/Kconfig | 7 ++
drivers/net/phy/Makefile | 1 +
drivers/net/phy/ac200.c | 220 +++++++++++++++++++++++++++++++++++++++
3 files changed, 228 insertions(+)
create mode 100644 drivers/net/phy/ac200.c
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 902495afc..056b7965d 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -63,6 +63,13 @@ config SFP
comment "MII PHY device drivers"
+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 PHYs"
help
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index b2728d00f..dbaa8f29c 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -30,6 +30,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.o
obj-$(CONFIG_ADIN_PHY) += adin.o
obj-$(CONFIG_AMD_PHY) += amd.o
aquantia-objs += aquantia_main.o
diff --git a/drivers/net/phy/ac200.c b/drivers/net/phy/ac200.c
new file mode 100644
index 000000000..cb713188f
--- /dev/null
+++ b/drivers/net/phy/ac200.c
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-2.0+
+/**
+ * Driver for AC200 Ethernet PHY
+ *
+ * Copyright (c) 2020 Jernej Skrabec <jernej.skrabec@siol.net>
+ */
+
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mfd/ac200.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+
+#define AC200_EPHY_ID 0x00441400
+#define AC200_EPHY_ID_MASK 0x0ffffff0
+
+/* macros for system ephy control 0 register */
+#define AC200_EPHY_RESET_INVALID BIT(0)
+#define AC200_EPHY_SYSCLK_GATING BIT(1)
+
+/* macros for system ephy control 1 register */
+#define AC200_EPHY_E_EPHY_MII_IO_EN BIT(0)
+#define AC200_EPHY_E_LNK_LED_IO_EN BIT(1)
+#define AC200_EPHY_E_SPD_LED_IO_EN BIT(2)
+#define AC200_EPHY_E_DPX_LED_IO_EN BIT(3)
+
+/* macros for ephy control register */
+#define AC200_EPHY_SHUTDOWN BIT(0)
+#define AC200_EPHY_LED_POL BIT(1)
+#define AC200_EPHY_CLK_SEL BIT(2)
+#define AC200_EPHY_ADDR(x) (((x) & 0x1F) << 4)
+#define AC200_EPHY_XMII_SEL BIT(11)
+#define AC200_EPHY_CALIB(x) (((x) & 0xF) << 12)
+
+struct ac200_ephy_dev {
+ struct clk *clk;
+ struct phy_driver *ephy;
+ struct regmap *regmap;
+};
+
+static char *ac200_phy_name = "AC200 EPHY";
+
+static int ac200_ephy_config_init(struct phy_device *phydev)
+{
+ const struct ac200_ephy_dev *priv = phydev->drv->driver_data;
+ unsigned int value;
+ int ret;
+
+ 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 6 */
+ 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 IEEE */
+
+ /* next two blocks disable 802.3az IEEE */
+ 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));
+
+ if (phydev->interface == PHY_INTERFACE_MODE_RMII)
+ value = AC200_EPHY_XMII_SEL;
+ else
+ value = 0;
+
+ ret = regmap_update_bits(priv->regmap, AC200_EPHY_CTL,
+ AC200_EPHY_XMII_SEL, value);
+ if (ret)
+ return ret;
+
+ /* FIXME: This is H6 specific */
+ phy_set_bits(phydev, 0x13, BIT(12));
+
+ return 0;
+}
+
+static int ac200_ephy_probe(struct platform_device *pdev)
+{
+ struct ac200_dev *ac200 = dev_get_drvdata(pdev->dev.parent);
+ struct device *dev = &pdev->dev;
+ struct ac200_ephy_dev *priv;
+ struct nvmem_cell *calcell;
+ struct phy_driver *ephy;
+ u16 *caldata, calib;
+ size_t callen;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ ephy = devm_kzalloc(dev, sizeof(*ephy), GFP_KERNEL);
+ if (!ephy)
+ return -ENOMEM;
+
+ priv->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(priv->clk)) {
+ dev_err(dev, "Can't obtain the clock!\n");
+ return PTR_ERR(priv->clk);
+ }
+
+ calcell = devm_nvmem_cell_get(dev, "calibration");
+ if (IS_ERR(calcell)) {
+ dev_err(dev, "Unable to find calibration data!\n");
+ return PTR_ERR(calcell);
+ }
+
+ caldata = nvmem_cell_read(calcell, &callen);
+ if (IS_ERR(caldata)) {
+ dev_err(dev, "Unable to read calibration data!\n");
+ return PTR_ERR(caldata);
+ }
+
+ if (callen != 2) {
+ dev_err(dev, "Calibration data has wrong length: 2 != %zu\n",
+ callen);
+ kfree(caldata);
+ return -EINVAL;
+ }
+
+ calib = *caldata + 3;
+ kfree(caldata);
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret)
+ return ret;
+
+ ephy->phy_id = AC200_EPHY_ID;
+ ephy->phy_id_mask = AC200_EPHY_ID_MASK;
+ ephy->name = ac200_phy_name;
+ ephy->driver_data = priv;
+ ephy->soft_reset = genphy_soft_reset;
+ ephy->config_init = ac200_ephy_config_init;
+ ephy->suspend = genphy_suspend;
+ ephy->resume = genphy_resume;
+
+ priv->ephy = ephy;
+ priv->regmap = ac200->regmap;
+ platform_set_drvdata(pdev, priv);
+
+ ret = regmap_write(ac200->regmap, AC200_SYS_EPHY_CTL0,
+ AC200_EPHY_RESET_INVALID |
+ AC200_EPHY_SYSCLK_GATING);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(ac200->regmap, AC200_SYS_EPHY_CTL1,
+ AC200_EPHY_E_EPHY_MII_IO_EN |
+ AC200_EPHY_E_LNK_LED_IO_EN |
+ AC200_EPHY_E_SPD_LED_IO_EN |
+ AC200_EPHY_E_DPX_LED_IO_EN);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(ac200->regmap, AC200_EPHY_CTL,
+ AC200_EPHY_LED_POL |
+ AC200_EPHY_CLK_SEL |
+ AC200_EPHY_ADDR(1) |
+ AC200_EPHY_CALIB(calib));
+ if (ret)
+ return ret;
+
+ ret = phy_driver_register(priv->ephy, THIS_MODULE);
+ if (ret) {
+ dev_err(dev, "Unable to register phy\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ac200_ephy_remove(struct platform_device *pdev)
+{
+ struct ac200_ephy_dev *priv = platform_get_drvdata(pdev);
+
+ phy_driver_unregister(priv->ephy);
+
+ regmap_write(priv->regmap, AC200_EPHY_CTL, AC200_EPHY_SHUTDOWN);
+ regmap_write(priv->regmap, AC200_SYS_EPHY_CTL1, 0);
+ regmap_write(priv->regmap, AC200_SYS_EPHY_CTL0, 0);
+
+ clk_disable_unprepare(priv->clk);
+
+ return 0;
+}
+
+static const struct of_device_id ac200_ephy_match[] = {
+ { .compatible = "x-powers,ac200-ephy" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ac200_ephy_match);
+
+static struct platform_driver ac200_ephy_driver = {
+ .probe = ac200_ephy_probe,
+ .remove = ac200_ephy_remove,
+ .driver = {
+ .name = "ac200-ephy",
+ .of_match_table = ac200_ephy_match,
+ },
+};
+module_platform_driver(ac200_ephy_driver);
+
+MODULE_AUTHOR("Jernej Skrabec <jernej.skrabec@siol.net>");
+MODULE_DESCRIPTION("AC200 Ethernet PHY driver");
+MODULE_LICENSE("GPL");
--
2.31.1

View File

@ -3,6 +3,20 @@
# Armbian patches
#
############################################################################
# ac200
patches.armbian/0001-clk-gate-add-support-for-regmap-based-gates.patch
patches.armbian/0002-mfd-Add-support-for-X-Powers-AC200.patch
patches.armbian/0003-mfd-Add-support-for-X-Powers-AC200-EPHY-syscon.patch
patches.armbian/0004-net-phy-Add-support-for-AC200-EPHY.patch
patches.armbian/0005-arm64-dts-allwinner-h6-Add-AC200-EPHY-nodes.patch
patches.armbian/0006-arm64-dts-allwinner-h6-tanix-enable-Ethernet.patch
patches.armbian/0007-ASoC-AC200-Initial-driver.patch
patches.armbian/0008-arm64-dts-allwinner-h6-add-AC200-codec-nodes.patch
patches.armbian/0009-arm64-dts-allwinner-h6-enable-AC200-codec.patch
patches.armbian/0077-OrangePi-3-LTS-support.patch
patches.armbian/drv-bluetooth-btrtl-Add-rtl8822cs-hci-ver-0008.patch
patches.armbian/drv-bluetooth-hci_h5-power-reset-via-gpio-in-h5_btrt.patch
patches.armbian/Revert-net-Remove-net-ipx.h-and-uapi-linux-ipx.h-hea.patch
@ -13,9 +27,7 @@
patches.armbian/Doc-dt-bindings-gpio-gpio-axp209-add-AXP803-GPIO-bin.patch
patches.armbian/Doc-dt-bindings-usb-add-binding-for-DWC3-controller-.patch
patches.armbian/Doc-dt-bindings-arm-sunxi-Add-two-H616-board-compatible-strings.patch
patches.armbian/drv-mfd-Add-support-for-AC200.patch
patches.armbian/drv-pinctrl-pinctrl-sun50i-a64-disable_strict_mode.patch
patches.armbian/drv-net-phy-Add-support-for-AC200-EPHY.patch
patches.armbian/drv-rtc-sun6i-Fix-time-overflow-handling.patch
patches.armbian/drv-rtc-sun6i-Add-support-linear-day-storage.patch
patches.armbian/drv-rtc-sun6i-Add-support-for-broken-down-alarm-regi.patch
@ -99,8 +111,6 @@
patches.armbian/arm-dts-sun7i-a20-bananapro-add-hdmi-connector-de.patch
patches.armbian/arm-dts-sunxi-h3-h5.dtsi-force-mmc0-bus-width.patch
patches.armbian/arm64-dts-sun50i-a64-pine64-enable-wifi-mmc1.patch
patches.armbian/arm64-dts-sun50i-h6-Add-AC200-EPHY-related-nodes.patch
patches.armbian/arm64-dts-sun50i-h6-tanix-tx6-Enable-ethernet.patch
patches.armbian/arm64-dts-sun50i-a64-sopine-baseboard-Add-i2s2-mmc1.patch
patches.armbian/arm64-dts-sun50i-h6-Add-r_uart-uart2-3-pins.patch
patches.armbian/arm64-dts-allwinner-Add-sun50i-h616.dtsi-file.patch
@ -192,9 +202,7 @@
patches.armbian/0005-net-phy-support-yt8531c.patch
patches.armbian/0007-wireless-add-uwe5622-driver.patch
patches.armbian/0008-uwe5622-bluetooth-fix-firmware-init-fail.patch
patches.armbian/0009-allwinner-h6-support-ac200-audio-codec.patch
patches.armbian/0010-allwinner-add-sunxi_get_soc_chipid-and-sunxi_get_ser.patch
patches.armbian/0011-add-initial-support-for-orangepi3-lts.patch
patches.armbian/0010-allwinner-add-sunxi_get_soc_chipid-and-sunxi_get_ser.patch
patches.armbian/0012-fix-h6-emmc.patch
patches.armbian/0013-x-fix-h6-emmc-dts.patch
patches.armbian/0014-add-uwe-bsp-to-orangepi3-lts-dts-file.patch

View File

@ -468,6 +468,7 @@
# Armbian patches
#
############################################################################
patches.armbian/0077-OrangePi-3-LTS-support.patch
patches.armbian/drv-bluetooth-btrtl-Add-rtl8822cs-hci-ver-0008.patch
patches.armbian/drv-bluetooth-hci_h5-power-reset-via-gpio-in-h5_btrt.patch
patches.armbian/Revert-net-Remove-net-ipx.h-and-uapi-linux-ipx.h-hea.patch
@ -478,9 +479,7 @@
patches.armbian/Doc-dt-bindings-gpio-gpio-axp209-add-AXP803-GPIO-bin.patch
patches.armbian/Doc-dt-bindings-usb-add-binding-for-DWC3-controller-.patch
patches.armbian/Doc-dt-bindings-arm-sunxi-Add-two-H616-board-compatible-strings.patch
patches.armbian/drv-mfd-Add-support-for-AC200.patch
patches.armbian/drv-pinctrl-pinctrl-sun50i-a64-disable_strict_mode.patch
patches.armbian/drv-net-phy-Add-support-for-AC200-EPHY.patch
- patches.armbian/drv-rtc-sun6i-Fix-time-overflow-handling.patch
patches.armbian/drv-rtc-sun6i-Add-support-linear-day-storage.patch
patches.armbian/drv-rtc-sun6i-Add-support-for-broken-down-alarm-regi.patch
@ -565,8 +564,6 @@
patches.armbian/Bananapro-add-AXP209-regulators.patch
patches.armbian/arm-dts-sunxi-h3-h5.dtsi-force-mmc0-bus-width.patch
patches.armbian/arm64-dts-sun50i-a64-pine64-enable-wifi-mmc1.patch
patches.armbian/arm64-dts-sun50i-h6-Add-AC200-EPHY-related-nodes.patch
patches.armbian/arm64-dts-sun50i-h6-tanix-tx6-Enable-ethernet.patch
patches.armbian/arm64-dts-sun50i-a64-sopine-baseboard-Add-i2s2-mmc1.patch
patches.armbian/arm64-dts-sun50i-h6-Add-r_uart-uart2-3-pins.patch
patches.armbian/arm64-dts-allwinner-Add-sun50i-h616.dtsi-file.patch
@ -664,9 +661,7 @@
patches.armbian/0005-net-phy-support-yt8531c.patch
patches.armbian/0007-wireless-add-uwe5622-driver.patch
patches.armbian/0008-uwe5622-bluetooth-fix-firmware-init-fail.patch
patches.armbian/0009-allwinner-h6-support-ac200-audio-codec.patch
patches.armbian/0010-allwinner-add-sunxi_get_soc_chipid-and-sunxi_get_ser.patch
patches.armbian/0011-add-initial-support-for-orangepi3-lts.patch
patches.armbian/0012-fix-h6-emmc.patch
patches.armbian/0013-x-fix-h6-emmc-dts.patch
patches.armbian/0014-add-uwe-bsp-to-orangepi3-lts-dts-file.patch
@ -679,3 +674,13 @@
patches.armbian/arm-dts-sun8i-r40-add-nodes-for-audio-codec.patch
patches.armbian/arm-dts-sun8i-r40-bananapi-m2-ultra-enable-audio-codec.patch
patches.armbian/arm-dts-sun8i-v40-bananapi-m2-berry-enable-audio-codec.patch
patches.armbian/0001-clk-gate-add-support-for-regmap-based-gates.patch
patches.armbian/0002-mfd-Add-support-for-X-Powers-AC200.patch
patches.armbian/0003-mfd-Add-support-for-X-Powers-AC200-EPHY-syscon.patch
patches.armbian/0004-net-phy-Add-support-for-AC200-EPHY.patch
patches.armbian/0005-arm64-dts-allwinner-h6-Add-AC200-EPHY-nodes.patch
patches.armbian/0006-arm64-dts-allwinner-h6-tanix-enable-Ethernet.patch
patches.armbian/0007-ASoC-AC200-Initial-driver.patch
patches.armbian/0008-arm64-dts-allwinner-h6-add-AC200-codec-nodes.patch
patches.armbian/0009-arm64-dts-allwinner-h6-enable-AC200-codec.patch

View File

@ -0,0 +1,121 @@
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix.dtsi
index edb71e4a0304..f8614e2b297c 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h6-tanix.dtsi
@@ -10,6 +10,8 @@
/ {
aliases {
+ ethernet0 = &emac;
+ ethernet1 = &xr819;
serial0 = &uart0;
};
@@ -81,6 +83,7 @@ wifi_pwrseq: wifi-pwrseq {
clocks = <&rtc 1>;
clock-names = "ext_clock";
reset-gpios = <&r_pio 1 3 GPIO_ACTIVE_LOW>; /* PM3 */
+ post-power-on-delay-ms = <200>;
};
};
@@ -88,6 +91,14 @@ &cpu0 {
cpu-supply = <&reg_vdd_cpu_gpu>;
};
+&ac200_ephy {
+ status = "okay";
+};
+
+&ac200_pwm_clk {
+ status = "okay";
+};
+
&de {
status = "okay";
};
@@ -96,6 +107,15 @@ &dwc3 {
status = "okay";
};
+&emac {
+ pinctrl-names = "default";
+ pinctrl-0 = <&ext_rmii_pins>;
+ phy-mode = "rmii";
+ phy-handle = <&ext_rmii_phy>;
+ status = "okay";
+};
+
+
&ehci0 {
status = "okay";
};
@@ -119,6 +139,21 @@ hdmi_out_con: endpoint {
};
};
+&i2s1 {
+ status = "okay";
+};
+
+&i2c3 {
+ status = "okay";
+};
+
+&mdio {
+ ext_rmii_phy: ethernet-phy@1 {
+ compatible = "ethernet-phy-ieee802.3-c22";
+ reg = <1>;
+ };
+};
+
&mmc0 {
pinctrl-names = "default";
pinctrl-0 = <&mmc0_pins>;
@@ -129,12 +164,27 @@ &mmc0 {
};
&mmc1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc1_pins>;
vmmc-supply = <&reg_vcc3v3>;
vqmmc-supply = <&reg_vcc1v8>;
mmc-pwrseq = <&wifi_pwrseq>;
bus-width = <4>;
non-removable;
status = "okay";
+
+ /*
+ * Explicitly define the sdio device, so that we can add an ethernet
+ * alias for it (which e.g. makes u-boot set a mac-address).
+ */
+ xr819: sdio_wifi@1 {
+ reg = <1>;
+ compatible = "xradio,xr819";
+ interrupt-parent = <&r_pio>;
+ local-mac-address = [dc 44 6d c0 ff ee];
+ interrupts = <1 0 IRQ_TYPE_EDGE_RISING>; /* PM0 */
+ interrupt-names = "host-wake";
+ };
};
&mmc2 {
@@ -161,10 +211,18 @@ &pio {
vcc-pg-supply = <&reg_vcc1v8>;
};
+&pwm {
+ status = "okay";
+};
+
&r_ir {
status = "okay";
};
+&sound_hdmi {
+ status = "okay";
+};
+
&spdif {
status = "okay";
};

View File

@ -155,6 +155,7 @@
patches.armbian/arm64-dts-sun50i-h5-add-termal-zones.patch
patches.armbian/arm64-dts-sun50i-h6-orangepi-add-cpu-opp-refs.patch
patches.armbian/arm64-dts-sun50i-h6-orangepi-enable-higher-clock-regulator-max-.patch
patches.armbian/arm64-dts-sun50i-h6-tanix-add-wifi.patch
patches.armbian/Fix-compile-error-node-not-found.patch
patches.armbian/drv-staging-rtl8723bs-AP-bugfix.patch
patches.armbian/arm-dts-sun8i-h3-orangepi-pc-plus-add-wifi_pwrseq.patch

View File

@ -556,6 +556,7 @@
patches.armbian/arm64-dts-sun50i-h5-add-termal-zones.patch
patches.armbian/arm64-dts-sun50i-h6-orangepi-add-cpu-opp-refs.patch
patches.armbian/arm64-dts-sun50i-h6-orangepi-enable-higher-clock-regulator-max-.patch
patches.armbian/arm64-dts-sun50i-h6-tanix-add-wifi.patch
patches.armbian/Fix-compile-error-node-not-found.patch
patches.armbian/drv-staging-rtl8723bs-AP-bugfix.patch
patches.armbian/arm-dts-sun8i-h3-orangepi-pc-plus-add-wifi_pwrseq.patch

View File

@ -0,0 +1,69 @@
--- a/arch/arm/dts/sun50i-h6-tanix-tx6.dts
+++ b/arch/arm/dts/sun50i-h6-tanix-tx6.dts
@@ -13,6 +13,7 @@
compatible = "oranth,tanix-tx6", "allwinner,sun50i-h6";
aliases {
+ ethernet0 = &emac;
serial0 = &uart0;
};
@@ -45,6 +46,15 @@
regulator-min-microvolt = <1135000>;
regulator-max-microvolt = <1135000>;
};
+
+ reg_vcc5v: vcc5v {
+ /* board wide 5V supply directly from the DC jack */
+ compatible = "regulator-fixed";
+ regulator-name = "vcc-5v";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ regulator-always-on;
+ };
};
&cpu0 {
@@ -56,6 +66,12 @@
};
&dwc3 {
+ status = "okay";
+};
+
+&emac {
+ phy-mode = "rmii";
+ phy-handle = <&ext_rmii_phy>;
status = "okay";
};
@@ -82,12 +98,28 @@
};
};
+&mdio {
+ ext_rmii_phy: ethernet-phy@1 {
+ compatible = "ethernet-phy-ieee802.3-c22";
+ reg = <1>;
+ };
+};
+
&mmc0 {
pinctrl-names = "default";
pinctrl-0 = <&mmc0_pins>;
vmmc-supply = <&reg_vcc3v3>;
cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>;
bus-width = <4>;
+ status = "okay";
+};
+
+&mmc2 {
+ vmmc-supply = <&reg_vcc3v3>;
+ vqmmc-supply = <&reg_vcc3v3>;
+ non-removable;
+ cap-mmc-hw-reset;
+ bus-width = <8>;
status = "okay";
};