fanpwr.c revision 1.9
1/* $OpenBSD: fanpwr.c,v 1.9 2024/05/26 18:06:21 kettenis Exp $ */ 2/* 3 * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18#include <sys/param.h> 19#include <sys/systm.h> 20#include <sys/device.h> 21#include <sys/malloc.h> 22 23#include <dev/ofw/openfirm.h> 24#include <dev/ofw/ofw_pinctrl.h> 25#include <dev/ofw/ofw_regulator.h> 26#include <dev/ofw/fdt.h> 27 28#include <dev/i2c/i2cvar.h> 29 30/* Registers */ 31#define FAN53555_VSEL0 0x00 32#define FAN53555_VSEL1 0x01 33#define FAN53555_VSEL_BUCK_EN 0x80 34#define FAN53555_VSEL_NSEL_MASK 0x3f 35#define FAN53555_CONTROL 0x02 36#define FAN53555_CONTROL_SLEW_MASK (0x7 << 4) 37#define FAN53555_CONTROL_SLEW_SHIFT 4 38#define FAN53555_ID1 0x03 39#define FAN53555_ID2 0x04 40 41#define TCS4525_VSEL1 0x10 42#define TCS4525_VSEL0 0x11 43#define TCS4525_VSEL_NSEL_MASK 0x7f 44#define TCS4525_TIME 0x13 45#define TCS4525_TIME_SLEW_MASK (0x3 << 3) 46#define TCS4525_TIME_SLEW_SHIFT 3 47 48#define RK8602_VSEL0 0x06 49#define RK8602_VSEL1 0x07 50#define RK8602_VSEL_NSEL_MASK 0xff 51 52/* Distinguish between Fairchild original and Silergy clones. */ 53enum fanpwr_id { 54 FANPWR_FAN53555, /* Fairchild FAN53555 */ 55 FANPWR_RK8602, /* Rockchip RK8602 */ 56 FANPWR_SYR827, /* Silergy SYR827 */ 57 FANPWR_SYR828, /* Silergy SYR828 */ 58 FANPWR_TCS4525, /* TCS TCS4525 */ 59}; 60 61struct fanpwr_softc { 62 struct device sc_dev; 63 i2c_tag_t sc_tag; 64 i2c_addr_t sc_addr; 65 66 enum fanpwr_id sc_id; 67 uint8_t sc_vsel; 68 uint8_t sc_vsel_nsel_mask; 69 70 struct regulator_device sc_rd; 71 uint32_t sc_vbase; 72 uint32_t sc_vstep; 73}; 74 75int fanpwr_match(struct device *, void *, void *); 76void fanpwr_attach(struct device *, struct device *, void *); 77 78const struct cfattach fanpwr_ca = { 79 sizeof(struct fanpwr_softc), fanpwr_match, fanpwr_attach 80}; 81 82struct cfdriver fanpwr_cd = { 83 NULL, "fanpwr", DV_DULL 84}; 85 86uint8_t fanpwr_read(struct fanpwr_softc *, int); 87void fanpwr_write(struct fanpwr_softc *, int, uint8_t); 88uint32_t fanpwr_get_voltage(void *); 89int fanpwr_set_voltage(void *, uint32_t); 90 91int 92fanpwr_match(struct device *parent, void *match, void *aux) 93{ 94 struct i2c_attach_args *ia = aux; 95 96 return (strcmp(ia->ia_name, "fcs,fan53555") == 0 || 97 strcmp(ia->ia_name, "rockchip,rk8602") == 0 || 98 strcmp(ia->ia_name, "rockchip,rk8603") == 0 || 99 strcmp(ia->ia_name, "silergy,syr827") == 0 || 100 strcmp(ia->ia_name, "silergy,syr828") == 0 || 101 strcmp(ia->ia_name, "tcs,tcs4525") == 0); 102} 103 104void 105fanpwr_attach(struct device *parent, struct device *self, void *aux) 106{ 107 struct fanpwr_softc *sc = (struct fanpwr_softc *)self; 108 struct i2c_attach_args *ia = aux; 109 int node = *(int *)ia->ia_cookie; 110 uint32_t voltage, ramp_delay; 111 uint8_t vsel_sleep_en; 112 uint8_t vsel_sleep; 113 uint8_t id1, id2; 114 uint8_t vsel; 115 int snode; 116 117 pinctrl_byname(node, "default"); 118 119 sc->sc_tag = ia->ia_tag; 120 sc->sc_addr = ia->ia_addr; 121 122 if (OF_is_compatible(node, "rockchip,rk8602") || 123 OF_is_compatible(node, "rockchip,rk8603")) { 124 printf(": RK8602"); 125 sc->sc_id = FANPWR_RK8602; 126 } else if (OF_is_compatible(node, "silergy,syr827")) { 127 printf(": SYR827"); 128 sc->sc_id = FANPWR_SYR827; 129 } else if (OF_is_compatible(node, "silergy,syr828")) { 130 printf(": SYR828"); 131 sc->sc_id = FANPWR_SYR828; 132 } else if (OF_is_compatible(node, "tcs,tcs4525")) { 133 printf(": TCS4525"); 134 sc->sc_id = FANPWR_TCS4525; 135 } else { 136 printf(": FAN53555"); 137 sc->sc_id = FANPWR_FAN53555; 138 } 139 140 if (sc->sc_id == FANPWR_TCS4525) { 141 if (OF_getpropint(node, "fcs,suspend-voltage-selector", 0)) { 142 sc->sc_vsel = TCS4525_VSEL0; 143 vsel_sleep = TCS4525_VSEL1; 144 } else { 145 sc->sc_vsel = TCS4525_VSEL1; 146 vsel_sleep = TCS4525_VSEL0; 147 } 148 sc->sc_vsel_nsel_mask = TCS4525_VSEL_NSEL_MASK; 149 vsel_sleep_en = vsel_sleep; 150 } else if (sc->sc_id == FANPWR_RK8602) { 151 if (OF_getpropint(node, "fcs,suspend-voltage-selector", 0)) { 152 sc->sc_vsel = RK8602_VSEL0; 153 vsel_sleep = RK8602_VSEL1; 154 vsel_sleep_en = FAN53555_VSEL1; 155 } else { 156 sc->sc_vsel = RK8602_VSEL1; 157 vsel_sleep = RK8602_VSEL0; 158 vsel_sleep_en = FAN53555_VSEL0; 159 } 160 sc->sc_vsel_nsel_mask = RK8602_VSEL_NSEL_MASK; 161 } else { 162 if (OF_getpropint(node, "fcs,suspend-voltage-selector", 0)) { 163 sc->sc_vsel = FAN53555_VSEL0; 164 vsel_sleep = FAN53555_VSEL1; 165 } else { 166 sc->sc_vsel = FAN53555_VSEL1; 167 vsel_sleep = FAN53555_VSEL0; 168 } 169 sc->sc_vsel_nsel_mask = FAN53555_VSEL_NSEL_MASK; 170 vsel_sleep_en = vsel_sleep; 171 } 172 173 id1 = fanpwr_read(sc, FAN53555_ID1); 174 id2 = fanpwr_read(sc, FAN53555_ID2); 175 176 switch (sc->sc_id) { 177 case FANPWR_FAN53555: 178 switch (id1 << 8 | id2) { 179 case 0x8003: /* 00 Option */ 180 case 0x8103: /* 01 Option */ 181 case 0x8303: /* 03 Option */ 182 case 0x8503: /* 05 Option */ 183 case 0x8801: /* 08, 18 Options */ 184 case 0x880f: /* BUC08, BUC18 Options */ 185 case 0x8108: /* 79 Option */ 186 sc->sc_vbase = 600000; 187 sc->sc_vstep = 10000; 188 break; 189 case 0x840f: /* 04 Option */ 190 case 0x8c0f: /* 09 Option */ 191 sc->sc_vbase = 603000; 192 sc->sc_vstep = 12826; 193 break; 194 case 0x800f: /* 13 Option */ 195 sc->sc_vbase = 800000; 196 sc->sc_vstep = 10000; 197 break; 198 case 0x800c: /* 23 Option */ 199 sc->sc_vbase = 600000; 200 sc->sc_vstep = 12500; 201 break; 202 case 0x8004: /* 24 Option */ 203 sc->sc_vbase = 603000; 204 sc->sc_vstep = 12967; 205 break; 206 default: 207 printf(", unknown ID1 0x%02x ID2 0x%02x\n", id1, id2); 208 return; 209 } 210 break; 211 case FANPWR_RK8602: 212 sc->sc_vbase = 500000; 213 sc->sc_vstep = 6250; 214 break; 215 case FANPWR_SYR827: 216 case FANPWR_SYR828: 217 sc->sc_vbase = 712500; 218 sc->sc_vstep = 12500; 219 break; 220 case FANPWR_TCS4525: 221 sc->sc_vbase = 600000; 222 sc->sc_vstep = 6250; 223 break; 224 } 225 226 voltage = fanpwr_get_voltage(sc); 227 printf(", %d.%02d VDC", voltage / 1000000, 228 (voltage % 1000000) / 10000); 229 230 ramp_delay = OF_getpropint(node, "regulator-ramp-delay", 0); 231 if (ramp_delay > 0) { 232 if (sc->sc_id == FANPWR_TCS4525) { 233 uint8_t ctrl, slew; 234 235 if (ramp_delay >= 18700) 236 slew = 0; 237 else if (ramp_delay >= 9300) 238 slew = 1; 239 else if (ramp_delay >= 4600) 240 slew = 2; 241 else 242 slew = 3; 243 ctrl = fanpwr_read(sc, TCS4525_TIME); 244 ctrl &= ~TCS4525_TIME_SLEW_MASK; 245 ctrl |= slew << TCS4525_TIME_SLEW_SHIFT; 246 fanpwr_write(sc, TCS4525_TIME, ctrl); 247 } else { 248 uint8_t ctrl, slew; 249 250 for (slew = 7; slew > 0; slew--) 251 if ((64000 >> slew) >= ramp_delay) 252 break; 253 ctrl = fanpwr_read(sc, FAN53555_CONTROL); 254 ctrl &= ~FAN53555_CONTROL_SLEW_MASK; 255 ctrl |= slew << FAN53555_CONTROL_SLEW_SHIFT; 256 fanpwr_write(sc, FAN53555_CONTROL, ctrl); 257 } 258 } 259 260 sc->sc_rd.rd_node = node; 261 sc->sc_rd.rd_cookie = sc; 262 sc->sc_rd.rd_get_voltage = fanpwr_get_voltage; 263 sc->sc_rd.rd_set_voltage = fanpwr_set_voltage; 264 regulator_register(&sc->sc_rd); 265 266 printf("\n"); 267 268 snode = OF_getnodebyname(node, "regulator-state-mem"); 269 if (snode) { 270 vsel = fanpwr_read(sc, vsel_sleep_en); 271 if (OF_getpropbool(snode, "regulator-on-in-suspend")) 272 vsel |= FAN53555_VSEL_BUCK_EN; 273 if (OF_getpropbool(snode, "regulator-off-in-suspend")) 274 vsel &= ~FAN53555_VSEL_BUCK_EN; 275 fanpwr_write(sc, vsel_sleep_en, vsel); 276 277 voltage = OF_getpropint(snode, 278 "regulator-suspend-min-microvolt", 0); 279 voltage = OF_getpropint(snode, 280 "regulator-suspend-microvolt", voltage); 281 if (voltage > 0) { 282 vsel = fanpwr_read(sc, vsel_sleep); 283 vsel &= ~sc->sc_vsel_nsel_mask; 284 vsel |= (voltage - vsel_sleep) / sc->sc_vstep; 285 fanpwr_write(sc, vsel_sleep, vsel); 286 } 287 } 288} 289 290uint8_t 291fanpwr_read(struct fanpwr_softc *sc, int reg) 292{ 293 uint8_t cmd = reg; 294 uint8_t val; 295 int error; 296 297 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 298 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 299 &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL); 300 iic_release_bus(sc->sc_tag, I2C_F_POLL); 301 302 if (error) { 303 printf("error %d\n", error); 304 printf("%s: can't read register 0x%02x\n", 305 sc->sc_dev.dv_xname, reg); 306 val = 0xff; 307 } 308 309 return val; 310} 311 312void 313fanpwr_write(struct fanpwr_softc *sc, int reg, uint8_t val) 314{ 315 uint8_t cmd = reg; 316 int error; 317 318 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 319 error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, 320 &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL); 321 iic_release_bus(sc->sc_tag, I2C_F_POLL); 322 323 if (error) { 324 printf("%s: can't write register 0x%02x\n", 325 sc->sc_dev.dv_xname, reg); 326 } 327} 328 329uint32_t 330fanpwr_get_voltage(void *cookie) 331{ 332 struct fanpwr_softc *sc = cookie; 333 uint8_t vsel; 334 335 vsel = fanpwr_read(sc, sc->sc_vsel); 336 return sc->sc_vbase + (vsel & sc->sc_vsel_nsel_mask) * sc->sc_vstep; 337} 338 339int 340fanpwr_set_voltage(void *cookie, uint32_t voltage) 341{ 342 struct fanpwr_softc *sc = cookie; 343 uint32_t vmin = sc->sc_vbase; 344 uint32_t vmax = vmin + sc->sc_vsel_nsel_mask * sc->sc_vstep; 345 uint8_t vsel; 346 347 if (voltage < vmin || voltage > vmax) 348 return EINVAL; 349 350 vsel = fanpwr_read(sc, sc->sc_vsel); 351 vsel &= ~sc->sc_vsel_nsel_mask; 352 vsel |= (voltage - sc->sc_vbase) / sc->sc_vstep; 353 fanpwr_write(sc, sc->sc_vsel, vsel); 354 355 return 0; 356} 357