1/*
2 * Copyright 2017, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13#include <utils/util.h>
14#include <platsupport/mach/pmic.h>
15#include <string.h>
16
17#define mV
18
19#define REG_CHIPID        0x00
20#define REG_RESET_DELAY   0x0A
21
22#define MAX77686_CHIPID   0x02
23#define MAX77802_CHIPID   0x06
24#define MAXXXXXX_CHIPID   0x91
25
26#define NLDO              (26 + 1 /* + reserved LDO0 */)
27#define LDO_VMIN          800 mV
28#define LDO_VSTEP          50 mV
29#define LDO_VMASK         0x3F
30#define LDO_MV(mv)        (( ((mv) + LDO_VSTEP / 2) - LDO_VMIN) / LDO_VSTEP)
31#define LDO_GET_MV(reg)   (((reg) & LDO_VMASK) * LDO_VSTEP + LDO_VMIN)
32#define LDOMODE_OFF       (0x0 << 6)
33#define LDOMODE_STANDBY   (BIT(6))
34#define LDOMODE_LOWPWR    (0x2 << 6)
35#define LDOMODE_ON        (0x3 << 6)
36#define LDOMODE_MASK      (0x3 << 6)
37
38struct max77_config {
39    int ctrl1_start;
40    int ctrl2_start;
41    int nldo;
42};
43
44static const struct max77_config max77802_cfg = {
45    .ctrl1_start = 0x60,
46    .ctrl2_start = 0x90,
47    .nldo        = 35
48};
49
50static const struct max77_config max77686_cfg = {
51    .ctrl1_start = 0x40,
52    .ctrl2_start = 0x60,
53    .nldo        = 26
54};
55
56static inline const struct max77_config*
57pmic_get_priv(pmic_t* pmic) {
58    return (const struct max77_config*)pmic->priv;
59}
60
61static int
62pmic_reg_read(pmic_t* pmic, uint8_t reg, void* data, int count)
63{
64    return !(i2c_kvslave_read(&pmic->kvslave, reg, data, count) == count);
65}
66
67static int
68pmic_reg_write(pmic_t* pmic, uint8_t reg, const void* data, int count)
69{
70    return !(i2c_kvslave_write(&pmic->kvslave, reg, data, count) == count);
71}
72
73static int
74ldo_valid(pmic_t* pmic, int ldo)
75{
76    int nldo = pmic_nldo(pmic);
77    return !(nldo < 0 || ldo <= 0 || ldo > nldo);
78}
79
80int
81pmic_init(i2c_bus_t* i2c, int addr, pmic_t* pmic)
82{
83    uint16_t chip_id;
84    int ret;
85    ret = i2c_slave_init(i2c, addr,
86                         I2C_SLAVE_ADDR_7BIT, I2C_SLAVE_SPEED_FAST,
87                         0, &pmic->i2c_slave);
88    if (ret) {
89        ZF_LOGE("Failed to register I2C slave");
90        return -1;
91    }
92
93    ret = i2c_kvslave_init(&pmic->i2c_slave, LITTLE8, LITTLE8, &pmic->kvslave);
94    if (ret) {
95        ZF_LOGE("Failed to initialize I2C KV-slave lib instance.");
96        return -1;
97    }
98
99    /* Read the chip ID */
100    if (pmic_reg_read(pmic, REG_CHIPID, &chip_id, 2)) {
101        ZF_LOGE("Bus error");
102        return -1;
103    }
104    /* Check the chip ID */
105    switch (chip_id) {
106    case MAX77686_CHIPID:
107        pmic->priv = (void*)&max77686_cfg;
108        break;
109    case MAXXXXXX_CHIPID:
110    case MAX77802_CHIPID:
111        pmic->priv = (void*)&max77802_cfg;
112        break;
113    default:
114        ZF_LOGE("Unidentified chip 0x%02x", chip_id);
115        return -1;
116    }
117
118    ZF_LOGE("found chip ID 0x%x", chip_id);
119    return 0;
120}
121
122int
123pmic_nldo(pmic_t* pmic)
124{
125    const struct max77_config* cfg = pmic_get_priv(pmic);
126    assert(cfg);
127    return cfg->nldo;
128}
129
130int
131pmic_ldo_cfg(pmic_t* pmic, int ldo, enum ldo_mode ldo_mode, int milli_volt)
132{
133    if (!ldo_valid(pmic, ldo)) {
134        return -1;
135    } else {
136        const struct max77_config* cfg;
137        uint8_t v;
138        cfg = pmic_get_priv(pmic);
139        assert(cfg);
140        /* Generate the register data */
141        v = LDO_MV(milli_volt);
142        switch (ldo_mode) {
143        case LDO_OFF:
144            v |= LDOMODE_OFF;
145            break;
146        case LDO_STANDBY:
147            v |= LDOMODE_STANDBY;
148            break;
149        case LDO_LOWPWR:
150            v |= LDOMODE_LOWPWR;
151            break;
152        case LDO_ON:
153            v |= LDOMODE_ON;
154            break;
155        default:
156            /* Invalid mode */
157            return -1;
158        }
159        /* Write the register */
160        if (pmic_reg_write(pmic, cfg->ctrl1_start + ldo - 1, &v, 1)) {
161            return -1;
162        } else {
163            return LDO_GET_MV(v);
164        }
165    }
166}
167
168int
169pmic_ldo_get_cfg(pmic_t* pmic, int ldo, enum ldo_mode* ldo_mode)
170{
171    if (!ldo_valid(pmic, ldo)) {
172        return -1;
173    } else {
174        const struct max77_config* cfg;
175        uint8_t v;
176        cfg = pmic_get_priv(pmic);
177        assert(cfg);
178        /* Read in the register */
179        if (pmic_reg_read(pmic, cfg->ctrl1_start + ldo - 1, &v, 1)) {
180            return -1;
181        }
182        /* Decode the mode */
183        if (ldo_mode != NULL) {
184            switch (v & LDOMODE_MASK) {
185            case LDOMODE_OFF:
186                *ldo_mode = LDO_OFF;
187                break;
188            case LDOMODE_STANDBY:
189                *ldo_mode = LDO_STANDBY;
190                break;
191            case LDOMODE_LOWPWR:
192                *ldo_mode = LDO_LOWPWR;
193                break;
194            case LDOMODE_ON:
195                *ldo_mode = LDO_ON;
196                break;
197            default:
198                /* Should never get here */
199                return -1;
200            }
201        }
202        return LDO_GET_MV(v);
203    }
204}
205
206int
207pmic_get_reset_delay(pmic_t* pmic)
208{
209    uint8_t data;
210    if (pmic_reg_read(pmic, REG_RESET_DELAY, &data, 1)) {
211        return -1;
212    } else {
213        return 1000 * (data >> 1);
214    }
215}
216
217int
218pmic_set_reset_delay(pmic_t* pmic, int ms)
219{
220    uint8_t data;
221    /* Clip ms value */
222    if (ms < 0) {
223        ms = 0;
224    } else if (ms > 10000) {
225        ms = 10000;
226    }
227    /* Write the data */
228    data = (ms / 1000) << 1;
229    if (pmic_reg_write(pmic, REG_RESET_DELAY, &data, 1)) {
230        return -1;
231    } else {
232        return data;
233    }
234}
235
236void
237pmic_print_status(pmic_t* pmic)
238{
239    uint8_t data;
240    int nldo;
241    int err;
242    int i;
243    printf("### PMIC ###\n");
244    err = pmic_reg_read(pmic, REG_CHIPID, &data, 1);
245    assert(!err);
246    if (err) {
247        return;
248    }
249    switch (data) {
250    case MAX77686_CHIPID:
251        printf("MAX77686");
252        break;
253    case MAX77802_CHIPID:
254        printf("MAX77802");
255        break;
256    case MAXXXXXX_CHIPID:
257        printf("MAXXXXXx");
258        break;
259    default:
260        printf("Unknown CHIP");
261    }
262    nldo = pmic_nldo(pmic);
263    printf(": %d LDOs\n", nldo);
264    for (i = 1; i < nldo; i++) {
265        int mv, v;
266        enum ldo_mode mode;
267        mv = pmic_ldo_get_cfg(pmic, i, &mode);
268        v = mv / 1000;
269        mv -= v * 1000;
270        printf("LDO%02d: %d.%03dV (", i, v, mv);
271        switch (mode) {
272        case LDO_OFF:
273            printf("Off");
274            break;
275        case LDO_STANDBY:
276            printf("Standby");
277            break;
278        case LDO_LOWPWR:
279            printf("Low power");
280            break;
281        case LDO_ON:
282            printf("On");
283            break;
284        default:
285            printf("Unknown state");
286        }
287        printf(")\n");
288    }
289    printf("Reset delay: %d ms\n", pmic_get_reset_delay(pmic));
290}
291