1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Allegro A8293 SEC driver |
4 | * |
5 | * Copyright (C) 2011 Antti Palosaari <crope@iki.fi> |
6 | */ |
7 | |
8 | #include "a8293.h" |
9 | |
10 | #define A8293_FLAG_ODT 0x10 |
11 | |
12 | struct a8293_dev { |
13 | struct i2c_client *client; |
14 | u8 reg[2]; |
15 | int volt_slew_nanos_per_mv; |
16 | }; |
17 | |
18 | /* |
19 | * When increasing voltage, do so in minimal steps over time, minimizing |
20 | * risk of vIN undervoltage. |
21 | */ |
22 | |
23 | static int a8293_set_voltage_slew(struct a8293_dev *dev, |
24 | struct i2c_client *client, |
25 | enum fe_sec_voltage fe_sec_voltage, |
26 | int min_nanos_per_mv) |
27 | { |
28 | int ret; |
29 | u8 reg0, reg1; |
30 | int new_volt_idx; |
31 | const int idx_to_mv[] = { |
32 | 0, 12709, 13042, 13375, 14042, 15042, 18042, 18709, 19042 |
33 | }; |
34 | const u8 idx_to_reg[] = { |
35 | 0x00, 0x20, 0x21, 0x22, 0x24, 0x27, 0x28, 0x2A, 0x2B |
36 | }; |
37 | int this_volt_idx; |
38 | u8 status; |
39 | int prev_volt_idx; |
40 | |
41 | dev_dbg(&client->dev, "set_voltage_slew fe_sec_voltage=%d\n" , |
42 | fe_sec_voltage); |
43 | |
44 | /* Read status register to clear any stale faults. */ |
45 | ret = i2c_master_recv(client, buf: &status, count: 1); |
46 | if (ret < 0) |
47 | goto err; |
48 | |
49 | /* Determine previous voltage */ |
50 | switch (dev->reg[0] & 0x2F) { |
51 | case 0x00: |
52 | prev_volt_idx = 0; |
53 | break; |
54 | case 0x20: |
55 | prev_volt_idx = 1; |
56 | break; |
57 | case 0x21: |
58 | prev_volt_idx = 2; |
59 | break; |
60 | case 0x22: |
61 | prev_volt_idx = 3; |
62 | break; |
63 | case 0x24: |
64 | prev_volt_idx = 4; |
65 | break; |
66 | case 0x27: |
67 | prev_volt_idx = 5; |
68 | break; |
69 | case 0x28: |
70 | prev_volt_idx = 6; |
71 | break; |
72 | case 0x2A: |
73 | prev_volt_idx = 7; |
74 | break; |
75 | case 0x2B: |
76 | prev_volt_idx = 8; |
77 | break; |
78 | default: |
79 | prev_volt_idx = 0; |
80 | } |
81 | |
82 | /* Determine new voltage */ |
83 | switch (fe_sec_voltage) { |
84 | case SEC_VOLTAGE_OFF: |
85 | new_volt_idx = 0; |
86 | break; |
87 | case SEC_VOLTAGE_13: |
88 | new_volt_idx = 2; |
89 | break; |
90 | case SEC_VOLTAGE_18: |
91 | new_volt_idx = 6; |
92 | break; |
93 | default: |
94 | ret = -EINVAL; |
95 | goto err; |
96 | } |
97 | |
98 | /* Slew to new voltage if new voltage is greater than current voltage */ |
99 | this_volt_idx = prev_volt_idx; |
100 | if (this_volt_idx < new_volt_idx) { |
101 | while (this_volt_idx < new_volt_idx) { |
102 | int delta_mv = idx_to_mv[this_volt_idx+1] - idx_to_mv[this_volt_idx]; |
103 | int min_wait_time = delta_mv * min_nanos_per_mv; |
104 | |
105 | reg0 = idx_to_reg[this_volt_idx+1]; |
106 | reg0 |= A8293_FLAG_ODT; |
107 | |
108 | ret = i2c_master_send(client, buf: ®0, count: 1); |
109 | if (ret < 0) |
110 | goto err; |
111 | dev->reg[0] = reg0; |
112 | this_volt_idx++; |
113 | usleep_range(min: min_wait_time, max: min_wait_time * 2); |
114 | } |
115 | } else { /* Else just set the voltage */ |
116 | reg0 = idx_to_reg[new_volt_idx]; |
117 | reg0 |= A8293_FLAG_ODT; |
118 | ret = i2c_master_send(client, buf: ®0, count: 1); |
119 | if (ret < 0) |
120 | goto err; |
121 | dev->reg[0] = reg0; |
122 | } |
123 | |
124 | /* TMODE=0, TGATE=1 */ |
125 | reg1 = 0x82; |
126 | if (reg1 != dev->reg[1]) { |
127 | ret = i2c_master_send(client, buf: ®1, count: 1); |
128 | if (ret < 0) |
129 | goto err; |
130 | dev->reg[1] = reg1; |
131 | } |
132 | |
133 | usleep_range(min: 1500, max: 5000); |
134 | |
135 | return 0; |
136 | err: |
137 | dev_dbg(&client->dev, "failed=%d\n" , ret); |
138 | return ret; |
139 | } |
140 | |
141 | |
142 | static int a8293_set_voltage_noslew(struct dvb_frontend *fe, |
143 | enum fe_sec_voltage fe_sec_voltage) |
144 | { |
145 | struct a8293_dev *dev = fe->sec_priv; |
146 | struct i2c_client *client = dev->client; |
147 | int ret; |
148 | u8 reg0, reg1; |
149 | |
150 | dev_dbg(&client->dev, "set_voltage_noslew fe_sec_voltage=%d\n" , |
151 | fe_sec_voltage); |
152 | |
153 | switch (fe_sec_voltage) { |
154 | case SEC_VOLTAGE_OFF: |
155 | /* ENB=0 */ |
156 | reg0 = 0x10; |
157 | break; |
158 | case SEC_VOLTAGE_13: |
159 | /* VSEL0=1, VSEL1=0, VSEL2=0, VSEL3=0, ENB=1*/ |
160 | reg0 = 0x31; |
161 | break; |
162 | case SEC_VOLTAGE_18: |
163 | /* VSEL0=0, VSEL1=0, VSEL2=0, VSEL3=1, ENB=1*/ |
164 | reg0 = 0x38; |
165 | break; |
166 | default: |
167 | ret = -EINVAL; |
168 | goto err; |
169 | } |
170 | if (reg0 != dev->reg[0]) { |
171 | ret = i2c_master_send(client, buf: ®0, count: 1); |
172 | if (ret < 0) |
173 | goto err; |
174 | dev->reg[0] = reg0; |
175 | } |
176 | |
177 | /* TMODE=0, TGATE=1 */ |
178 | reg1 = 0x82; |
179 | if (reg1 != dev->reg[1]) { |
180 | ret = i2c_master_send(client, buf: ®1, count: 1); |
181 | if (ret < 0) |
182 | goto err; |
183 | dev->reg[1] = reg1; |
184 | } |
185 | |
186 | usleep_range(min: 1500, max: 50000); |
187 | return 0; |
188 | err: |
189 | dev_dbg(&client->dev, "failed=%d\n" , ret); |
190 | return ret; |
191 | } |
192 | |
193 | static int a8293_set_voltage(struct dvb_frontend *fe, |
194 | enum fe_sec_voltage fe_sec_voltage) |
195 | { |
196 | struct a8293_dev *dev = fe->sec_priv; |
197 | struct i2c_client *client = dev->client; |
198 | int volt_slew_nanos_per_mv = dev->volt_slew_nanos_per_mv; |
199 | |
200 | dev_dbg(&client->dev, "set_voltage volt_slew_nanos_per_mv=%d\n" , |
201 | volt_slew_nanos_per_mv); |
202 | |
203 | /* Use slew version if slew rate is set to a sane value */ |
204 | if (volt_slew_nanos_per_mv > 0 && volt_slew_nanos_per_mv < 1600) |
205 | a8293_set_voltage_slew(dev, client, fe_sec_voltage, |
206 | min_nanos_per_mv: volt_slew_nanos_per_mv); |
207 | else |
208 | a8293_set_voltage_noslew(fe, fe_sec_voltage); |
209 | |
210 | return 0; |
211 | } |
212 | |
213 | static int a8293_probe(struct i2c_client *client) |
214 | { |
215 | struct a8293_dev *dev; |
216 | struct a8293_platform_data *pdata = client->dev.platform_data; |
217 | struct dvb_frontend *fe = pdata->dvb_frontend; |
218 | int ret; |
219 | u8 buf[2]; |
220 | |
221 | dev = kzalloc(size: sizeof(*dev), GFP_KERNEL); |
222 | if (!dev) { |
223 | ret = -ENOMEM; |
224 | goto err; |
225 | } |
226 | |
227 | dev->client = client; |
228 | dev->volt_slew_nanos_per_mv = pdata->volt_slew_nanos_per_mv; |
229 | |
230 | /* check if the SEC is there */ |
231 | ret = i2c_master_recv(client, buf, count: 2); |
232 | if (ret < 0) |
233 | goto err_kfree; |
234 | |
235 | /* override frontend ops */ |
236 | fe->ops.set_voltage = a8293_set_voltage; |
237 | fe->sec_priv = dev; |
238 | i2c_set_clientdata(client, data: dev); |
239 | |
240 | dev_info(&client->dev, "Allegro A8293 SEC successfully attached\n" ); |
241 | return 0; |
242 | err_kfree: |
243 | kfree(objp: dev); |
244 | err: |
245 | dev_dbg(&client->dev, "failed=%d\n" , ret); |
246 | return ret; |
247 | } |
248 | |
249 | static void a8293_remove(struct i2c_client *client) |
250 | { |
251 | struct a8293_dev *dev = i2c_get_clientdata(client); |
252 | |
253 | dev_dbg(&client->dev, "\n" ); |
254 | |
255 | kfree(objp: dev); |
256 | } |
257 | |
258 | static const struct i2c_device_id a8293_id_table[] = { |
259 | {"a8293" , 0}, |
260 | {} |
261 | }; |
262 | MODULE_DEVICE_TABLE(i2c, a8293_id_table); |
263 | |
264 | static struct i2c_driver a8293_driver = { |
265 | .driver = { |
266 | .name = "a8293" , |
267 | .suppress_bind_attrs = true, |
268 | }, |
269 | .probe = a8293_probe, |
270 | .remove = a8293_remove, |
271 | .id_table = a8293_id_table, |
272 | }; |
273 | |
274 | module_i2c_driver(a8293_driver); |
275 | |
276 | MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>" ); |
277 | MODULE_DESCRIPTION("Allegro A8293 SEC driver" ); |
278 | MODULE_LICENSE("GPL" ); |
279 | |