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
|
// SPDX-License-Identifier: GPL-2.0-only
/*
* X-Powers AC200 Core driver
*
* Copyright (C) 2019 Jernej Skrabec <jernej.skrabec@gmail.com>
* Copyright (C) 2026 James Hilliard <james.hilliard1@gmail.com>
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/irq.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/regmap.h>
#define AC200_SYS_CONTROL_REG 0x0002
#define AC200_SYS_CONTROL_CHIP_RESET_DEASSERT BIT(0)
#define AC200_SYS_IRQ_ENABLE_REG 0x0004
#define AC200_SYS_IRQ_INTB_ENABLE BIT(15)
#define AC200_SYS_IRQ_INTB_ACTIVE_HIGH BIT(14)
#define AC200_SYS_IRQ_RTC BIT(12)
#define AC200_SYS_IRQ_EPHY BIT(8)
#define AC200_SYS_IRQ_TVE BIT(4)
/* Interface register accessible from every register page. */
#define AC200_TWI_REG_ADDR_H 0x00fe
#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,
.window_len = 256,
},
};
/*
* Each AC200 sub-block can reset independently, invalidating its register
* contents without regmap's knowledge. Cache only the common page selector;
* this avoids a selector read-modify-write for every access on the same page
* without ever returning stale functional-register values.
*/
static bool ac200_volatile_reg(struct device *dev, unsigned int reg)
{
return reg != AC200_TWI_REG_ADDR_H;
}
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,
.volatile_reg = ac200_volatile_reg,
.cache_type = REGCACHE_MAPLE,
};
static const char * const ac200_codec_supplies[] = {
"ac-ldoin",
};
static const char * const ac200_tve_supplies[] = {
"tv-vcc",
};
static const struct resource ac200_tve_resources[] = {
DEFINE_RES_IRQ_NAMED(0, "intb"),
};
#define AC200_CELL(_name, _supplies, _resources) \
{ \
.name = (_name), \
.parent_supplies = (_supplies), \
.num_parent_supplies = ARRAY_SIZE(_supplies), \
.resources = (_resources), \
.num_resources = MFD_RES_SIZE(_resources), \
}
static const struct mfd_cell ac200_cells[] = {
AC200_CELL("ac200-codec", ac200_codec_supplies, NULL),
AC200_CELL("ac200-tve", ac200_tve_supplies, ac200_tve_resources),
};
static const struct mfd_cell ac200_noirq_cells[] = {
AC200_CELL("ac200-codec", ac200_codec_supplies, NULL),
AC200_CELL("ac200-tve", ac200_tve_supplies, NULL),
};
static void ac200_disable_intb(void *data)
{
struct regmap *regmap = data;
int ret;
ret = regmap_clear_bits(regmap, AC200_SYS_IRQ_ENABLE_REG, AC200_SYS_IRQ_INTB_ENABLE);
if (ret)
dev_err(regmap_get_device(regmap), "failed to disable INTB: %d\n", ret);
}
static int ac200_init_irq(struct device *dev, struct regmap *regmap, int irq)
{
unsigned int trigger;
u16 value = 0;
int ret;
trigger = irq_get_trigger_type(irq);
switch (trigger) {
case IRQ_TYPE_LEVEL_HIGH:
value |= AC200_SYS_IRQ_INTB_ACTIVE_HIGH;
break;
case IRQ_TYPE_NONE:
case IRQ_TYPE_LEVEL_LOW:
break;
default:
return dev_err_probe(dev, -EINVAL,
"INTB is level triggered, not type %u\n", trigger);
}
/* Mask every source before enabling the shared output. */
ret = regmap_update_bits(regmap, AC200_SYS_IRQ_ENABLE_REG,
AC200_SYS_IRQ_INTB_ENABLE | AC200_SYS_IRQ_INTB_ACTIVE_HIGH |
AC200_SYS_IRQ_RTC | AC200_SYS_IRQ_EPHY | AC200_SYS_IRQ_TVE, value);
if (ret)
return ret;
/* Children are removed before INTB is disabled and the regmap released. */
ret = devm_add_action_or_reset(dev, ac200_disable_intb, regmap);
if (ret)
return ret;
if (trigger == IRQ_TYPE_NONE) {
ret = irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
if (ret)
return dev_err_probe(dev, ret, "failed to set INTB trigger\n");
}
/*
* Function drivers request INTB with IRQF_SHARED | IRQF_ONESHOT before
* enabling their own source. They check and clear their own status and
* mask their source before freeing the IRQ. No IRQ domain is needed.
*/
return regmap_set_bits(regmap, AC200_SYS_IRQ_ENABLE_REG, AC200_SYS_IRQ_INTB_ENABLE);
}
static int ac200_probe(struct i2c_client *client)
{
const struct mfd_cell *cells = ac200_noirq_cells;
unsigned int num_cells = ARRAY_SIZE(ac200_noirq_cells);
struct device *dev = &client->dev;
struct regmap *regmap;
struct clk *clk;
int ret;
clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(clk))
return dev_err_probe(dev, PTR_ERR(clk), "failed to enable input clock\n");
ret = devm_clk_rate_exclusive_get(dev, clk);
if (ret)
return dev_err_probe(dev, ret, "failed to lock clock rate\n");
/*
* No minimum delay is documented. Retain the vendor resume path's 40 ms
* wait after enabling the input clock and before register access.
*/
msleep(40);
regmap = devm_regmap_init_i2c(client, &ac200_regmap_config);
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap), "failed to initialize regmap\n");
ret = regmap_set_bits(regmap, AC200_SYS_CONTROL_REG, AC200_SYS_CONTROL_CHIP_RESET_DEASSERT);
if (ret)
return ret;
if (client->irq > 0) {
ret = ac200_init_irq(dev, regmap, client->irq);
if (ret)
return ret;
cells = ac200_cells;
num_cells = ARRAY_SIZE(ac200_cells);
}
/* Function drivers read their firmware properties from the parent. */
ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, cells, num_cells,
NULL, client->irq, NULL);
if (ret)
return dev_err_probe(dev, ret, "failed to add function devices\n");
return 0;
}
static const struct of_device_id ac200_of_match[] = {
{ .compatible = "x-powers,ac200" },
{ }
};
MODULE_DEVICE_TABLE(of, ac200_of_match);
static const struct i2c_device_id ac200_i2c_ids[] = {
{ .name = "ac200" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ac200_i2c_ids);
static struct i2c_driver ac200_driver = {
.driver = {
.name = "ac200",
.of_match_table = ac200_of_match,
},
.probe = ac200_probe,
.id_table = ac200_i2c_ids,
};
module_i2c_driver(ac200_driver);
MODULE_AUTHOR("Jernej Skrabec <jernej.skrabec@gmail.com>");
MODULE_AUTHOR("James Hilliard <james.hilliard1@gmail.com>");
MODULE_DESCRIPTION("X-Powers AC200 Core driver");
MODULE_LICENSE("GPL");
|