coreboot
coreboot is an Open Source project aimed at replacing the proprietary BIOS found in most computers.
mt6360.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-only */
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] = {
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
56 
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 
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 
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 
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 
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 
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 
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 
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 
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 = &regulator_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 
216 }
217 
219 {
220  u8 val;
221  const struct mt6360_data *data;
222 
223  if (!is_valid_ldo(id))
224  return 0;
225 
226  data = &regulator_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 
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 = &regulator_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 
270 }
271 
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 = &regulator_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 
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 = &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 
318 }
319 
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 
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 = &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 
355 }
356 
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 
383 {
384  u8 delay01, delay02, delay03, delay04;
385 
388 
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);
402  "%s: power off sequence delay: %#x, %#x, %#x, %#x\n",
403  __func__, delay01, delay02, delay03, delay04);
404 }
405 
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 
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 
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 
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 }
#define MIN(a, b)
Definition: helpers.h:37
#define printk(level,...)
Definition: stdlib.h:16
static int i2c_read_bytes(unsigned int bus, uint8_t slave, uint8_t reg, uint8_t *data, int len)
Read multi-bytes with two segments in one frame.
Definition: i2c_simple.h:87
static int i2c_write_raw(unsigned int bus, uint8_t slave, uint8_t *data, int len)
Definition: i2c_simple.h:72
#define BIOS_DEBUG
BIOS_DEBUG - Verbose output.
Definition: loglevel.h:128
#define BIOS_ERR
BIOS_ERR - System in incomplete state.
Definition: loglevel.h:72
static uint8_t * buf
Definition: uart.c:7
static void mt6360_pmic_enable(enum mt6360_regulator_id id, uint8_t enable)
Definition: mt6360.c:299
static const uint32_t mt6360_ldo1_vsel_table[0x10]
Definition: mt6360.c:17
static bool is_valid_ldo(enum mt6360_regulator_id id)
Definition: mt6360.c:175
static u8 crc8(const u8 table[CRC8_TABLE_SIZE], u8 *pdata, size_t nbytes)
Definition: mt6360.c:57
static void mt6360_ldo_set_voltage(enum mt6360_regulator_id id, u32 voltage_uv)
Definition: mt6360.c:234
void mt6360_init(uint8_t bus)
Definition: mt6360.c:382
static void mt6360_ldo_enable(enum mt6360_regulator_id id, uint8_t enable)
Definition: mt6360.c:197
u32 mt6360_get_voltage(enum mt6360_regulator_id id)
Definition: mt6360.c:432
uint8_t mt6360_is_enabled(enum mt6360_regulator_id id)
Definition: mt6360.c:414
static int mt6360_i2c_read_byte(u8 index, u8 reg, u8 *data)
Definition: mt6360.c:107
static const uint32_t mt6360_ldo3_vsel_table[0x10]
Definition: mt6360.c:30
static u8 crc8_table[MT6360_INDEX_COUNT][CRC8_TABLE_SIZE]
Definition: mt6360.c:55
static int mt6360_read_interface(u8 index, u8 reg, u8 *data, u8 mask, u8 shift)
Definition: mt6360.c:142
static uint8_t mt6360_ldo_is_enabled(enum mt6360_regulator_id id)
Definition: mt6360.c:218
static u32 mt6360_ldo_get_voltage(enum mt6360_regulator_id id)
Definition: mt6360.c:272
static void mt6360_pmic_set_voltage(enum mt6360_regulator_id id, u32 voltage_uv)
Definition: mt6360.c:336
#define CRC8_TABLE_SIZE
Definition: mt6360.c:54
static u32 mt6360_pmic_get_voltage(enum mt6360_regulator_id id)
Definition: mt6360.c:357
static bool is_valid_pmic(enum mt6360_regulator_id id)
Definition: mt6360.c:186
static struct mt6360_i2c_data i2c_data[]
Definition: mt6360.c:8
static int mt6360_config_interface(u8 index, u8 reg, u8 data, u8 mask, u8 shift)
Definition: mt6360.c:158
void mt6360_set_voltage(enum mt6360_regulator_id id, u32 voltage_uv)
Definition: mt6360.c:424
static int mt6360_i2c_write_byte(u8 index, u8 reg, u8 data)
Definition: mt6360.c:82
static const uint32_t mt6360_ldo5_vsel_table[0x10]
Definition: mt6360.c:37
static const struct mt6360_data regulator_data[MT6360_REGULATOR_COUNT]
Definition: mt6360.c:43
static void crc8_populate_msb(u8 table[CRC8_TABLE_SIZE], u8 polynomial)
Definition: mt6360.c:67
void mt6360_enable(enum mt6360_regulator_id id, uint8_t enable)
Definition: mt6360.c:406
static uint8_t mt6360_pmic_is_enabled(enum mt6360_regulator_id id)
Definition: mt6360.c:320
mt6360_regulator_id
Definition: mt6360.h:6
@ MT6360_BUCK1
Definition: mt6360.h:11
@ MT6360_LDO5
Definition: mt6360.h:8
@ MT6360_LDO3
Definition: mt6360.h:7
@ MT6360_BUCK2
Definition: mt6360.h:12
@ MT6360_LDO6
Definition: mt6360.h:9
@ MT6360_REGULATOR_COUNT
Definition: mt6360.h:13
@ MT6360_LDO7
Definition: mt6360.h:10
@ MT6360_LDO1
Definition: mt6360.h:38
@ MT6360_LDO2
Definition: mt6360.h:39
@ MT6360_INDEX_PMIC
Definition: mt6360.h:23
@ MT6360_INDEX_COUNT
Definition: mt6360.h:24
@ MT6360_INDEX_LDO
Definition: mt6360.h:22
#define MT6360_DATA(_enreg, _enmask, _vreg, _vmask, _table)
Definition: mt6360.h:11
#define MT6360_LDO_I2C_ADDR
Definition: mt6360.h:8
#define MT6360_PMIC_I2C_ADDR
Definition: mt6360.h:9
static const int mask[4]
Definition: gpio.c:308
unsigned int uint32_t
Definition: stdint.h:14
uint32_t u32
Definition: stdint.h:51
uint8_t u8
Definition: stdint.h:45
unsigned char uint8_t
Definition: stdint.h:8
Definition: device.h:76
uint8_t enable_mask
Definition: mt6360.h:50
uint8_t vsel_reg
Definition: mt6360.h:51
uint32_t vsel_table_len
Definition: mt6360.h:54
uint8_t enable_reg
Definition: mt6360.h:49
const uint32_t * vsel_table
Definition: mt6360.h:53
u8 val
Definition: sys.c:300