Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7ba95590b | ||
|
|
1ba5a724de | ||
|
|
3c46be399a | ||
|
|
9b8a9f3dd1 | ||
|
|
be2941c18c |
12
config/boards/aw-h6-box.tvb
Normal file
12
config/boards/aw-h6-box.tvb
Normal 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
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 = <®_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 = <®_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 = <®_vcc1v8>;
|
||||
};
|
||||
|
||||
+&pwm {
|
||||
+ status = "okay";
|
||||
+};
|
||||
+
|
||||
&r_ir {
|
||||
status = "okay";
|
||||
};
|
||||
--
|
||||
2.34.1
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -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 = <®_aldo2>;
|
||||
+ status = "okay";
|
||||
+};
|
||||
+
|
||||
+&ac200_pwm_clk {
|
||||
+ status = "okay";
|
||||
+};
|
||||
+
|
||||
+&analog {
|
||||
+ status = "okay";
|
||||
+};
|
||||
+
|
||||
&cpu0 {
|
||||
cpu-supply = <®_dcdca>;
|
||||
};
|
||||
@@ -128,6 +141,14 @@ hdmi_out_con: endpoint {
|
||||
};
|
||||
};
|
||||
|
||||
+&i2c3 {
|
||||
+ status = "okay";
|
||||
+};
|
||||
+
|
||||
+&i2s3 {
|
||||
+ status = "okay";
|
||||
+};
|
||||
+
|
||||
&mmc0 {
|
||||
vmmc-supply = <®_cldo1>;
|
||||
cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */
|
||||
@@ -175,6 +196,10 @@ &pio {
|
||||
vcc-pg-supply = <®_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 = <®_aldo2>;
|
||||
+ status = "okay";
|
||||
+};
|
||||
+
|
||||
+&ac200_pwm_clk {
|
||||
+ status = "okay";
|
||||
+};
|
||||
+
|
||||
+&analog {
|
||||
+ status = "okay";
|
||||
+};
|
||||
+
|
||||
&cpu0 {
|
||||
cpu-supply = <®_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 = <®_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 = <®_vcc3v3>;
|
||||
+ status = "okay";
|
||||
+};
|
||||
+
|
||||
+&analog {
|
||||
+ status = "okay";
|
||||
+};
|
||||
+
|
||||
+&i2s3 {
|
||||
+ status = "okay";
|
||||
+};
|
||||
+
|
||||
&r_ir {
|
||||
linux,rc-map-name = "rc-tanix-tx3mini";
|
||||
};
|
||||
--
|
||||
2.34.1
|
||||
|
||||
@ -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 = <®_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 = <®_dcdca>;
|
||||
+};
|
||||
+
|
||||
+&de {
|
||||
+ status = "okay";
|
||||
+};
|
||||
+
|
||||
+&dwc3 {
|
||||
+ status = "okay";
|
||||
+};
|
||||
+
|
||||
+&ehci0 {
|
||||
+ status = "okay";
|
||||
+};
|
||||
+
|
||||
+&ehci3 {
|
||||
+ status = "okay";
|
||||
+};
|
||||
+
|
||||
+&gpu {
|
||||
+ mali-supply = <®_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 = <®_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 = <®_cldo1>;
|
||||
+ cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */
|
||||
+ bus-width = <4>;
|
||||
+ status = "okay";
|
||||
+};
|
||||
+
|
||||
+&mmc1 {
|
||||
+ vmmc-supply = <®_vcc33_wifi>;
|
||||
+ vqmmc-supply = <®_vcc_wifi_io>;
|
||||
+ mmc-pwrseq = <&wifi_pwrseq>;
|
||||
+ bus-width = <4>;
|
||||
+ non-removable;
|
||||
+ status = "okay";
|
||||
+};
|
||||
+
|
||||
+&mmc2 {
|
||||
+ pinctrl-names = "default";
|
||||
+ pinctrl-0 = <&mmc2_pins>;
|
||||
+ vmmc-supply = <®_cldo1>;
|
||||
+ vqmmc-supply = <®_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 = <®_bldo2>;
|
||||
+ vcc-pd-supply = <®_cldo1>;
|
||||
+ vcc-pg-supply = <®_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 = <®_vcc5v>;
|
||||
+ vinb-supply = <®_vcc5v>;
|
||||
+ vinc-supply = <®_vcc5v>;
|
||||
+ vind-supply = <®_vcc5v>;
|
||||
+ vine-supply = <®_vcc5v>;
|
||||
+ aldoin-supply = <®_vcc5v>;
|
||||
+ bldoin-supply = <®_vcc5v>;
|
||||
+ cldoin-supply = <®_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 = <®_vcc5v>;
|
||||
+ usb3_vbus-supply = <®_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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 = <®_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 = <®_vcc1v8>;
|
||||
};
|
||||
|
||||
+&pwm {
|
||||
+ status = "okay";
|
||||
+};
|
||||
+
|
||||
&r_ir {
|
||||
linux,rc-map-name = "rc-tanix-tx5max";
|
||||
status = "okay";
|
||||
--
|
||||
2.31.1
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 = <®_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 = <®_vcc3v3>;
|
||||
vqmmc-supply = <®_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 = <®_vcc1v8>;
|
||||
};
|
||||
|
||||
+&pwm {
|
||||
+ status = "okay";
|
||||
+};
|
||||
+
|
||||
&r_ir {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
+&sound_hdmi {
|
||||
+ status = "okay";
|
||||
+};
|
||||
+
|
||||
&spdif {
|
||||
status = "okay";
|
||||
};
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 = <®_vcc3v3>;
|
||||
cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>;
|
||||
bus-width = <4>;
|
||||
+ status = "okay";
|
||||
+};
|
||||
+
|
||||
+&mmc2 {
|
||||
+ vmmc-supply = <®_vcc3v3>;
|
||||
+ vqmmc-supply = <®_vcc3v3>;
|
||||
+ non-removable;
|
||||
+ cap-mmc-hw-reset;
|
||||
+ bus-width = <8>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user