forked from libretro/Lakka-LibreELEC
407 lines
12 KiB
Diff
407 lines
12 KiB
Diff
From 59adceb6f34521c0f1a229f20ee9961269daa539 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 38/44] 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 | 150 +++++++++++++++++++++++++++
|
|
include/linux/mfd/ac200.h | 209 ++++++++++++++++++++++++++++++++++++++
|
|
4 files changed, 369 insertions(+)
|
|
create mode 100644 drivers/mfd/ac200.c
|
|
create mode 100644 include/linux/mfd/ac200.h
|
|
|
|
--- 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
|
|
+ tristate "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
|
|
--- a/drivers/mfd/Makefile
|
|
+++ b/drivers/mfd/Makefile
|
|
@@ -142,6 +142,7 @@ obj-$(CONFIG_MFD_DA9052_SPI) += da9052-s
|
|
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
|
|
--- /dev/null
|
|
+++ b/drivers/mfd/ac200.c
|
|
@@ -0,0 +1,148 @@
|
|
+// 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@siol.net>
|
|
+ *
|
|
+ * Based on AC100 driver with following copyrights:
|
|
+ * Copyright (2016) Chen-Yu Tsai
|
|
+ */
|
|
+
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/mfd/core.h>
|
|
+#include <linux/mfd/ac200.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/of.h>
|
|
+
|
|
+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 struct mfd_cell ac200_cells[] = {
|
|
+ {
|
|
+ .name = "ac200-codec",
|
|
+ .of_compatible = "x-powers,ac200-codec",
|
|
+ }, {
|
|
+ .name = "ac200-efuse",
|
|
+ .of_compatible = "x-powers,ac200-efuse",
|
|
+ }, {
|
|
+ .name = "ac200-ephy",
|
|
+ .of_compatible = "x-powers,ac200-ephy",
|
|
+ }, {
|
|
+ .name = "ac200-rtc",
|
|
+ .of_compatible = "x-powers,ac200-rtc",
|
|
+ }, {
|
|
+ .name = "ac200-tve",
|
|
+ .of_compatible = "x-powers,ac200-tve",
|
|
+ },
|
|
+};
|
|
+
|
|
+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->clk = devm_clk_get(dev, NULL);
|
|
+ if (IS_ERR(ac200->clk)) {
|
|
+ ret = PTR_ERR(ac200->clk);
|
|
+ dev_err(dev, "Can't obtain the clock: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ 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;
|
|
+ }
|
|
+
|
|
+ ret = clk_prepare_enable(ac200->clk);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ 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;
|
|
+
|
|
+ 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;
|
|
+}
|
|
+
|
|
+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", },
|
|
+ { /* 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");
|
|
--- /dev/null
|
|
+++ b/include/linux/mfd/ac200.h
|
|
@@ -0,0 +1,209 @@
|
|
+/* 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/clk.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 clk *clk;
|
|
+ struct regmap *regmap;
|
|
+};
|
|
+
|
|
+#endif /* __LINUX_MFD_AC200_H */
|