1 /* SPDX-License-Identifier: GPL-2.0-only OR MIT */
2
3 #include <console/console.h>
4 #include <device/i2c_simple.h>
5 #include <soc/mt6360.h>
6 #include <stdbool.h>
7
8 static struct mt6360_i2c_data i2c_data[] = {
9 [MT6360_INDEX_LDO] = {
10 .addr = MT6360_LDO_I2C_ADDR,
11 },
12 [MT6360_INDEX_PMIC] = {
13 .addr = MT6360_PMIC_I2C_ADDR,
14 },
15 };
16
17 static const uint32_t mt6360_ldo1_vsel_table[0x10] = {
18 [0x4] = 1800000,
19 [0x5] = 2000000,
20 [0x6] = 2100000,
21 [0x7] = 2500000,
22 [0x8] = 2700000,
23 [0x9] = 2800000,
24 [0xA] = 2900000,
25 [0xB] = 3000000,
26 [0xC] = 3100000,
27 [0xD] = 3300000,
28 };
29
30 static const uint32_t mt6360_ldo3_vsel_table[0x10] = {
31 [0x4] = 1800000,
32 [0xA] = 2900000,
33 [0xB] = 3000000,
34 [0xD] = 3300000,
35 };
36
37 static const uint32_t mt6360_ldo5_vsel_table[0x10] = {
38 [0x2] = 2900000,
39 [0x3] = 3000000,
40 [0x5] = 3300000,
41 };
42
43 static const struct mt6360_data regulator_data[MT6360_REGULATOR_COUNT] = {
44 [MT6360_LDO3] = MT6360_DATA(0x05, 0x40, 0x09, 0xff, mt6360_ldo3_vsel_table),
45 [MT6360_LDO5] = MT6360_DATA(0x0b, 0x40, 0x0f, 0xff, mt6360_ldo5_vsel_table),
46 [MT6360_LDO6] = MT6360_DATA(0x37, 0x40, 0x3b, 0xff, mt6360_ldo3_vsel_table),
47 [MT6360_LDO7] = MT6360_DATA(0x31, 0x40, 0x35, 0xff, mt6360_ldo5_vsel_table),
48 [MT6360_BUCK1] = MT6360_DATA(0x17, 0x40, 0x10, 0xff, mt6360_ldo1_vsel_table),
49 [MT6360_BUCK2] = MT6360_DATA(0x27, 0x40, 0x20, 0xff, mt6360_ldo1_vsel_table),
50 [MT6360_LDO1] = MT6360_DATA(0x17, 0x40, 0x1b, 0xff, mt6360_ldo1_vsel_table),
51 [MT6360_LDO2] = MT6360_DATA(0x11, 0x40, 0x15, 0xff, mt6360_ldo1_vsel_table),
52 };
53
54 #define CRC8_TABLE_SIZE 256
55 static u8 crc8_table[MT6360_INDEX_COUNT][CRC8_TABLE_SIZE];
56
crc8(const u8 table[CRC8_TABLE_SIZE],u8 * pdata,size_t nbytes)57 static u8 crc8(const u8 table[CRC8_TABLE_SIZE], u8 *pdata, size_t nbytes)
58 {
59 u8 crc = 0;
60
61 while (nbytes-- > 0)
62 crc = table[(crc ^ *pdata++) & 0xff];
63
64 return crc;
65 }
66
crc8_populate_msb(u8 table[CRC8_TABLE_SIZE],u8 polynomial)67 static void crc8_populate_msb(u8 table[CRC8_TABLE_SIZE], u8 polynomial)
68 {
69 int i, j;
70 const u8 msbit = 0x80;
71 u8 t = msbit;
72
73 table[0] = 0;
74
75 for (i = 1; i < CRC8_TABLE_SIZE; i *= 2) {
76 t = (t << 1) ^ (t & msbit ? polynomial : 0);
77 for (j = 0; j < i; j++)
78 table[i + j] = table[j] ^ t;
79 }
80 }
81
mt6360_i2c_write_byte(u8 index,u8 reg,u8 data)82 static int mt6360_i2c_write_byte(u8 index, u8 reg, u8 data)
83 {
84 u8 chunk[5] = { 0 };
85 struct mt6360_i2c_data *i2c = &i2c_data[index];
86
87 if ((reg & 0xc0) != 0) {
88 printk(BIOS_ERR, "%s: not support reg [%#x]\n", __func__, reg);
89 return -1;
90 }
91
92 /*
93 * chunk[0], dev address
94 * chunk[1], reg address
95 * chunk[2], data to write
96 * chunk[3], crc of chunk[0 ~ 2]
97 * chunk[4], blank
98 */
99 chunk[0] = (i2c->addr & 0x7f) << 1;
100 chunk[1] = (reg & 0x3f);
101 chunk[2] = data;
102 chunk[3] = crc8(crc8_table[index], chunk, 3);
103
104 return i2c_write_raw(i2c->bus, i2c->addr, &chunk[1], 4);
105 }
106
mt6360_i2c_read_byte(u8 index,u8 reg,u8 * data)107 static int mt6360_i2c_read_byte(u8 index, u8 reg, u8 *data)
108 {
109 u8 chunk[4] = { 0 };
110 u8 buf[2];
111 int ret;
112 u8 crc;
113 struct mt6360_i2c_data *i2c = &i2c_data[index];
114
115 ret = i2c_read_bytes(i2c->bus, i2c->addr, reg & 0x3f, buf, 2);
116
117 if (ret)
118 return ret;
119 /*
120 * chunk[0], dev address
121 * chunk[1], reg address
122 * chunk[2], received data
123 * chunk[3], received crc of chunk[0 ~ 2]
124 */
125 chunk[0] = ((i2c->addr & 0x7f) << 1) + 1;
126 chunk[1] = (reg & 0x3f);
127 chunk[2] = buf[0];
128 chunk[3] = buf[1];
129 crc = crc8(crc8_table[index], chunk, 3);
130
131 if (chunk[3] != crc) {
132 printk(BIOS_ERR, "%s: incorrect CRC: expected %#x, got %#x",
133 __func__, crc, chunk[3]);
134 return -1;
135 }
136
137 *data = chunk[2];
138
139 return 0;
140 }
141
mt6360_read_interface(u8 index,u8 reg,u8 * data,u8 mask,u8 shift)142 static int mt6360_read_interface(u8 index, u8 reg, u8 *data, u8 mask, u8 shift)
143 {
144 int ret;
145 u8 val = 0;
146
147 ret = mt6360_i2c_read_byte(index, reg, &val);
148 if (ret < 0) {
149 printk(BIOS_ERR, "%s: fail, reg = %#x, ret = %d\n",
150 __func__, reg, ret);
151 return ret;
152 }
153 val &= (mask << shift);
154 *data = (val >> shift);
155 return 0;
156 }
157
mt6360_config_interface(u8 index,u8 reg,u8 data,u8 mask,u8 shift)158 static int mt6360_config_interface(u8 index, u8 reg, u8 data, u8 mask, u8 shift)
159 {
160 int ret;
161 u8 val = 0;
162
163 ret = mt6360_i2c_read_byte(index, reg, &val);
164 if (ret < 0) {
165 printk(BIOS_ERR, "%s: fail, reg = %#x, ret = %d\n",
166 __func__, reg, ret);
167 return ret;
168 }
169 val &= ~(mask << shift);
170 val |= (data << shift);
171
172 return mt6360_i2c_write_byte(index, reg, val);
173 }
174
is_valid_ldo(enum mt6360_regulator_id id)175 static bool is_valid_ldo(enum mt6360_regulator_id id)
176 {
177 if (id != MT6360_LDO1 && id != MT6360_LDO2 &&
178 id != MT6360_LDO3 && id != MT6360_LDO5) {
179 printk(BIOS_ERR, "%s: LDO %d is not supported\n", __func__, id);
180 return false;
181 }
182
183 return true;
184 }
185
is_valid_pmic(enum mt6360_regulator_id id)186 static bool is_valid_pmic(enum mt6360_regulator_id id)
187 {
188 if (id != MT6360_LDO6 && id != MT6360_LDO7 &&
189 id != MT6360_BUCK1 && id != MT6360_BUCK2) {
190 printk(BIOS_ERR, "%s: PMIC %d is not supported\n", __func__, id);
191 return false;
192 }
193
194 return true;
195 }
196
mt6360_ldo_enable(enum mt6360_regulator_id id,uint8_t enable)197 static void mt6360_ldo_enable(enum mt6360_regulator_id id, uint8_t enable)
198 {
199 u8 val;
200 const struct mt6360_data *data;
201
202 if (!is_valid_ldo(id))
203 return;
204
205 data = ®ulator_data[id];
206
207 if (mt6360_read_interface(MT6360_INDEX_LDO, data->enable_reg, &val, 0xff, 0) < 0)
208 return;
209
210 if (enable)
211 val |= data->enable_mask;
212 else
213 val &= ~(data->enable_mask);
214
215 mt6360_config_interface(MT6360_INDEX_LDO, data->enable_reg, val, 0xff, 0);
216 }
217
mt6360_ldo_is_enabled(enum mt6360_regulator_id id)218 static uint8_t mt6360_ldo_is_enabled(enum mt6360_regulator_id id)
219 {
220 u8 val;
221 const struct mt6360_data *data;
222
223 if (!is_valid_ldo(id))
224 return 0;
225
226 data = ®ulator_data[id];
227
228 if (mt6360_read_interface(MT6360_INDEX_LDO, data->enable_reg, &val, 0xff, 0) < 0)
229 return 0;
230
231 return (val & data->enable_mask) ? 1 : 0;
232 }
233
mt6360_ldo_set_voltage(enum mt6360_regulator_id id,u32 voltage_uv)234 static void mt6360_ldo_set_voltage(enum mt6360_regulator_id id, u32 voltage_uv)
235 {
236 u8 val = 0;
237 u32 voltage_uv_temp = 0;
238 int i;
239
240 const struct mt6360_data *data;
241
242 if (!is_valid_ldo(id))
243 return;
244
245 data = ®ulator_data[id];
246
247 for (i = 0; i < data->vsel_table_len; i++) {
248 u32 uv = data->vsel_table[i];
249
250 if (uv == 0)
251 continue;
252 if (uv > voltage_uv)
253 break;
254
255 val = i << 4;
256 voltage_uv_temp = voltage_uv - uv;
257 }
258
259 if (val == 0) {
260 printk(BIOS_ERR, "%s: LDO %d, set %d uV not supported\n",
261 __func__, id, voltage_uv);
262 return;
263 }
264
265 voltage_uv_temp /= 10000;
266 voltage_uv_temp = MIN(voltage_uv_temp, 0xa);
267 val |= (u8)voltage_uv_temp;
268
269 mt6360_config_interface(MT6360_INDEX_LDO, data->vsel_reg, val, 0xff, 0);
270 }
271
mt6360_ldo_get_voltage(enum mt6360_regulator_id id)272 static u32 mt6360_ldo_get_voltage(enum mt6360_regulator_id id)
273 {
274 u8 val;
275 u32 voltage_uv;
276
277 const struct mt6360_data *data;
278
279 if (!is_valid_ldo(id))
280 return 0;
281
282 data = ®ulator_data[id];
283
284 if (mt6360_read_interface(MT6360_INDEX_LDO, data->vsel_reg, &val, 0xff, 0) < 0)
285 return 0;
286
287 voltage_uv = data->vsel_table[(val & 0xf0) >> 4];
288 if (voltage_uv == 0) {
289 printk(BIOS_ERR, "%s: LDO %d read fail, reg = %#x\n", __func__, id, val);
290 return 0;
291 }
292
293 val = MIN(val & 0x0f, 0x0a);
294 voltage_uv += val * 10000;
295
296 return voltage_uv;
297 }
298
mt6360_pmic_enable(enum mt6360_regulator_id id,uint8_t enable)299 static void mt6360_pmic_enable(enum mt6360_regulator_id id, uint8_t enable)
300 {
301 u8 val;
302 const struct mt6360_data *data;
303
304 if (!is_valid_pmic(id))
305 return;
306
307 data = ®ulator_data[id];
308
309 if (mt6360_read_interface(MT6360_INDEX_PMIC, data->enable_reg, &val, 0xff, 0) < 0)
310 return;
311
312 if (enable)
313 val |= data->enable_mask;
314 else
315 val &= ~(data->enable_mask);
316
317 mt6360_config_interface(MT6360_INDEX_PMIC, data->enable_reg, val, 0xff, 0);
318 }
319
mt6360_pmic_is_enabled(enum mt6360_regulator_id id)320 static uint8_t mt6360_pmic_is_enabled(enum mt6360_regulator_id id)
321 {
322 u8 val;
323 const struct mt6360_data *data;
324
325 if (!is_valid_pmic(id))
326 return 0;
327
328 data = ®ulator_data[id];
329
330 if (mt6360_read_interface(MT6360_INDEX_PMIC, data->enable_reg, &val, 0xff, 0) < 0)
331 return 0;
332
333 return (val & data->enable_mask) ? 1 : 0;
334 }
335
mt6360_pmic_set_voltage(enum mt6360_regulator_id id,u32 voltage_uv)336 static void mt6360_pmic_set_voltage(enum mt6360_regulator_id id, u32 voltage_uv)
337 {
338 u8 val = 0;
339
340 const struct mt6360_data *data;
341
342 if (!is_valid_pmic(id))
343 return;
344
345 data = ®ulator_data[id];
346
347 if (id == MT6360_BUCK1 || id == MT6360_BUCK2) {
348 val = (voltage_uv - 300000) / 5000;
349 } else if (id == MT6360_LDO6 || id == MT6360_LDO7) {
350 val = (((voltage_uv - 500000) / 100000) << 4);
351 val += (((voltage_uv - 500000) % 100000) / 10000);
352 }
353
354 mt6360_config_interface(MT6360_INDEX_PMIC, data->vsel_reg, val, 0xff, 0);
355 }
356
mt6360_pmic_get_voltage(enum mt6360_regulator_id id)357 static u32 mt6360_pmic_get_voltage(enum mt6360_regulator_id id)
358 {
359 u8 val;
360 u32 voltage_uv = 0;
361
362 const struct mt6360_data *data;
363
364 if (!is_valid_pmic(id))
365 return 0;
366
367 data = ®ulator_data[id];
368
369 if (mt6360_read_interface(MT6360_INDEX_PMIC, data->vsel_reg, &val, 0xff, 0) < 0)
370 return 0;
371
372 if (id == MT6360_BUCK1 || id == MT6360_BUCK2) {
373 voltage_uv = 300000 + val * 5000;
374 } else if (id == MT6360_LDO6 || id == MT6360_LDO7) {
375 voltage_uv = 500000 + 100000 * (val >> 4);
376 voltage_uv += MIN(val & 0xf, 0xa) * 10000;
377 }
378
379 return voltage_uv;
380 }
381
mt6360_init(uint8_t bus)382 void mt6360_init(uint8_t bus)
383 {
384 u8 delay01, delay02, delay03, delay04;
385
386 crc8_populate_msb(crc8_table[MT6360_INDEX_LDO], 0x7);
387 crc8_populate_msb(crc8_table[MT6360_INDEX_PMIC], 0x7);
388
389 i2c_data[MT6360_INDEX_LDO].bus = bus;
390 i2c_data[MT6360_INDEX_PMIC].bus = bus;
391
392 mt6360_config_interface(MT6360_INDEX_PMIC, 0x07, 0x04, 0xff, 0);
393 mt6360_config_interface(MT6360_INDEX_PMIC, 0x08, 0x00, 0xff, 0);
394 mt6360_config_interface(MT6360_INDEX_PMIC, 0x09, 0x02, 0xff, 0);
395 mt6360_config_interface(MT6360_INDEX_PMIC, 0x0a, 0x00, 0xff, 0);
396
397 mt6360_read_interface(MT6360_INDEX_PMIC, 0x07, &delay01, 0xff, 0);
398 mt6360_read_interface(MT6360_INDEX_PMIC, 0x08, &delay02, 0xff, 0);
399 mt6360_read_interface(MT6360_INDEX_PMIC, 0x09, &delay03, 0xff, 0);
400 mt6360_read_interface(MT6360_INDEX_PMIC, 0x0a, &delay04, 0xff, 0);
401 printk(BIOS_DEBUG,
402 "%s: power off sequence delay: %#x, %#x, %#x, %#x\n",
403 __func__, delay01, delay02, delay03, delay04);
404 }
405
mt6360_enable(enum mt6360_regulator_id id,uint8_t enable)406 void mt6360_enable(enum mt6360_regulator_id id, uint8_t enable)
407 {
408 if (is_valid_ldo(id))
409 mt6360_ldo_enable(id, enable);
410 else if (is_valid_pmic(id))
411 mt6360_pmic_enable(id, enable);
412 }
413
mt6360_is_enabled(enum mt6360_regulator_id id)414 uint8_t mt6360_is_enabled(enum mt6360_regulator_id id)
415 {
416 if (is_valid_ldo(id))
417 return mt6360_ldo_is_enabled(id);
418 else if (is_valid_pmic(id))
419 return mt6360_pmic_is_enabled(id);
420 else
421 return 0;
422 }
423
mt6360_set_voltage(enum mt6360_regulator_id id,u32 voltage_uv)424 void mt6360_set_voltage(enum mt6360_regulator_id id, u32 voltage_uv)
425 {
426 if (is_valid_ldo(id))
427 mt6360_ldo_set_voltage(id, voltage_uv);
428 else if (is_valid_pmic(id))
429 mt6360_pmic_set_voltage(id, voltage_uv);
430 }
431
mt6360_get_voltage(enum mt6360_regulator_id id)432 u32 mt6360_get_voltage(enum mt6360_regulator_id id)
433 {
434 if (is_valid_ldo(id))
435 return mt6360_ldo_get_voltage(id);
436 else if (is_valid_pmic(id))
437 return mt6360_pmic_get_voltage(id);
438 else
439 return 0;
440 }
441