blob: b613acf34032316b83b47ed7fdb6fac9af042afe [file] [log] [blame]
Macpaul Lin5d16f8d2022-08-11 16:27:10 +08001/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
Andrew SH Cheng159d0972021-03-09 09:02:07 +08002
3#include <console/console.h>
4#include <device/i2c_simple.h>
5#include <soc/mt6360.h>
6#include <stdbool.h>
Andrew SH Cheng159d0972021-03-09 09:02:07 +08007
Andrew SH Cheng159d0972021-03-09 09:02:07 +08008static struct mt6360_i2c_data i2c_data[] = {
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +08009 [MT6360_INDEX_LDO] = {
Andrew SH Cheng159d0972021-03-09 09:02:07 +080010 .addr = MT6360_LDO_I2C_ADDR,
11 },
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +080012 [MT6360_INDEX_PMIC] = {
Andrew SH Cheng159d0972021-03-09 09:02:07 +080013 .addr = MT6360_PMIC_I2C_ADDR,
14 },
15};
16
17static 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
30static const uint32_t mt6360_ldo3_vsel_table[0x10] = {
31 [0x4] = 1800000,
32 [0xA] = 2900000,
33 [0xB] = 3000000,
34 [0xD] = 3300000,
35};
36
37static const uint32_t mt6360_ldo5_vsel_table[0x10] = {
38 [0x2] = 2900000,
39 [0x3] = 3000000,
40 [0x5] = 3300000,
41};
42
Rex-BC Chencd676572021-07-13 17:18:40 +080043static 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),
Andrew SH Cheng159d0972021-03-09 09:02:07 +080052};
53
54#define CRC8_TABLE_SIZE 256
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +080055static u8 crc8_table[MT6360_INDEX_COUNT][CRC8_TABLE_SIZE];
Andrew SH Cheng159d0972021-03-09 09:02:07 +080056
57static 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
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +080067static 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
82static int mt6360_i2c_write_byte(u8 index, u8 reg, u8 data)
Andrew SH Cheng159d0972021-03-09 09:02:07 +080083{
84 u8 chunk[5] = { 0 };
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +080085 struct mt6360_i2c_data *i2c = &i2c_data[index];
Andrew SH Cheng159d0972021-03-09 09:02:07 +080086
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;
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800102 chunk[3] = crc8(crc8_table[index], chunk, 3);
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800103
104 return i2c_write_raw(i2c->bus, i2c->addr, &chunk[1], 4);
105}
106
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800107static int mt6360_i2c_read_byte(u8 index, u8 reg, u8 *data)
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800108{
109 u8 chunk[4] = { 0 };
110 u8 buf[2];
111 int ret;
112 u8 crc;
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800113 struct mt6360_i2c_data *i2c = &i2c_data[index];
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800114
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];
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800129 crc = crc8(crc8_table[index], chunk, 3);
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800130
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
142static int mt6360_read_interface(u8 index, u8 reg, u8 *data, u8 mask, u8 shift)
143{
144 int ret;
145 u8 val = 0;
146
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800147 ret = mt6360_i2c_read_byte(index, reg, &val);
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800148 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
158static int mt6360_config_interface(u8 index, u8 reg, u8 data, u8 mask, u8 shift)
159{
160 int ret;
161 u8 val = 0;
162
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800163 ret = mt6360_i2c_read_byte(index, reg, &val);
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800164 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
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800172 return mt6360_i2c_write_byte(index, reg, val);
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800173}
174
Rex-BC Chencd676572021-07-13 17:18:40 +0800175static bool is_valid_ldo(enum mt6360_regulator_id id)
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800176{
Rex-BC Chencd676572021-07-13 17:18:40 +0800177 if (id != MT6360_LDO1 && id != MT6360_LDO2 &&
178 id != MT6360_LDO3 && id != MT6360_LDO5) {
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800179 printk(BIOS_ERR, "%s: LDO %d is not supported\n", __func__, id);
180 return false;
181 }
182
183 return true;
184}
185
Rex-BC Chencd676572021-07-13 17:18:40 +0800186static bool is_valid_pmic(enum mt6360_regulator_id id)
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800187{
Rex-BC Chencd676572021-07-13 17:18:40 +0800188 if (id != MT6360_LDO6 && id != MT6360_LDO7 &&
189 id != MT6360_BUCK1 && id != MT6360_BUCK2) {
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800190 printk(BIOS_ERR, "%s: PMIC %d is not supported\n", __func__, id);
191 return false;
192 }
193
194 return true;
195}
196
Rex-BC Chen86c50e12021-07-16 20:04:26 +0800197static void mt6360_ldo_enable(enum mt6360_regulator_id id, uint8_t enable)
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800198{
199 u8 val;
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800200 const struct mt6360_data *data;
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800201
202 if (!is_valid_ldo(id))
203 return;
204
Rex-BC Chencd676572021-07-13 17:18:40 +0800205 data = &regulator_data[id];
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800206
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800207 if (mt6360_read_interface(MT6360_INDEX_LDO, data->enable_reg, &val, 0xff, 0) < 0)
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800208 return;
209
210 if (enable)
211 val |= data->enable_mask;
212 else
213 val &= ~(data->enable_mask);
214
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800215 mt6360_config_interface(MT6360_INDEX_LDO, data->enable_reg, val, 0xff, 0);
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800216}
217
Rex-BC Chen86c50e12021-07-16 20:04:26 +0800218static uint8_t mt6360_ldo_is_enabled(enum mt6360_regulator_id id)
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800219{
220 u8 val;
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800221 const struct mt6360_data *data;
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800222
223 if (!is_valid_ldo(id))
224 return 0;
225
Rex-BC Chencd676572021-07-13 17:18:40 +0800226 data = &regulator_data[id];
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800227
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800228 if (mt6360_read_interface(MT6360_INDEX_LDO, data->enable_reg, &val, 0xff, 0) < 0)
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800229 return 0;
230
231 return (val & data->enable_mask) ? 1 : 0;
232}
233
Rex-BC Chen86c50e12021-07-16 20:04:26 +0800234static void mt6360_ldo_set_voltage(enum mt6360_regulator_id id, u32 voltage_uv)
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800235{
236 u8 val = 0;
237 u32 voltage_uv_temp = 0;
238 int i;
239
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800240 const struct mt6360_data *data;
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800241
242 if (!is_valid_ldo(id))
243 return;
244
Rex-BC Chencd676572021-07-13 17:18:40 +0800245 data = &regulator_data[id];
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800246
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;
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800266 voltage_uv_temp = MIN(voltage_uv_temp, 0xa);
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800267 val |= (u8)voltage_uv_temp;
268
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800269 mt6360_config_interface(MT6360_INDEX_LDO, data->vsel_reg, val, 0xff, 0);
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800270}
271
Rex-BC Chen86c50e12021-07-16 20:04:26 +0800272static u32 mt6360_ldo_get_voltage(enum mt6360_regulator_id id)
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800273{
274 u8 val;
275 u32 voltage_uv;
276
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800277 const struct mt6360_data *data;
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800278
279 if (!is_valid_ldo(id))
280 return 0;
281
Rex-BC Chencd676572021-07-13 17:18:40 +0800282 data = &regulator_data[id];
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800283
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800284 if (mt6360_read_interface(MT6360_INDEX_LDO, data->vsel_reg, &val, 0xff, 0) < 0)
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800285 return 0;
286
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800287 voltage_uv = data->vsel_table[(val & 0xf0) >> 4];
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800288 if (voltage_uv == 0) {
289 printk(BIOS_ERR, "%s: LDO %d read fail, reg = %#x\n", __func__, id, val);
290 return 0;
291 }
292
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800293 val = MIN(val & 0x0f, 0x0a);
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800294 voltage_uv += val * 10000;
295
296 return voltage_uv;
297}
298
Rex-BC Chen86c50e12021-07-16 20:04:26 +0800299static 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 = &regulator_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
320static 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 = &regulator_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
336static 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 = &regulator_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
357static 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 = &regulator_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
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800382void mt6360_init(uint8_t bus)
383{
384 u8 delay01, delay02, delay03, delay04;
385
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800386 crc8_populate_msb(crc8_table[MT6360_INDEX_LDO], 0x7);
387 crc8_populate_msb(crc8_table[MT6360_INDEX_PMIC], 0x7);
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800388
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800389 i2c_data[MT6360_INDEX_LDO].bus = bus;
390 i2c_data[MT6360_INDEX_PMIC].bus = bus;
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800391
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800392 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);
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800396
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800397 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);
Andrew SH Cheng159d0972021-03-09 09:02:07 +0800401 printk(BIOS_DEBUG,
402 "%s: power off sequence delay: %#x, %#x, %#x, %#x\n",
403 __func__, delay01, delay02, delay03, delay04);
404}
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800405
Rex-BC Chen86c50e12021-07-16 20:04:26 +0800406void mt6360_enable(enum mt6360_regulator_id id, uint8_t enable)
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800407{
Rex-BC Chen86c50e12021-07-16 20:04:26 +0800408 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}
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800413
Rex-BC Chen86c50e12021-07-16 20:04:26 +0800414uint8_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);
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800420 else
Rex-BC Chen86c50e12021-07-16 20:04:26 +0800421 return 0;
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800422}
423
Rex-BC Chen86c50e12021-07-16 20:04:26 +0800424void mt6360_set_voltage(enum mt6360_regulator_id id, u32 voltage_uv)
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800425{
Rex-BC Chen86c50e12021-07-16 20:04:26 +0800426 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);
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800430}
431
Rex-BC Chen86c50e12021-07-16 20:04:26 +0800432u32 mt6360_get_voltage(enum mt6360_regulator_id id)
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800433{
Rex-BC Chen86c50e12021-07-16 20:04:26 +0800434 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
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800439 return 0;
Andrew SH Cheng0865b4f2021-06-22 14:50:17 +0800440}