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