summarylogtreecommitdiffstats
path: root/drv-mfd-Add-support-for-AC200.patch
blob: 780e5ff1f8f2fadc9cbb60e87f65fd90df36864f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
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,169 @@
+// 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,
+	.ack_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)
+{
+	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 void ac200_i2c_remove(struct i2c_client *i2c)
+{
+	struct ac200_dev *ac200 = i2c_get_clientdata(i2c);
+
+	regmap_write(ac200->regmap, AC200_SYS_CONTROL, 0);
+
+	mfd_remove_devices(&i2c->dev);
+	regmap_del_irq_chip(i2c->irq, ac200->regmap_irqc);
+
+	return;
+}
+
+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