1 /* $OpenBSD: fanpwr.c,v 1.3 2019/05/24 18:54:12 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_NSEL_MASK 0x3f 34 #define FAN53555_CONTROL 0x02 35 #define FAN53555_CONTROL_SLEW_MASK (0x7 << 4) 36 #define FAN53555_CONTROL_SLEW_SHIFT 4 37 #define FAN53555_ID1 0x03 38 #define FAN53555_ID2 0x04 39 40 /* Distinguish between Failrchild original and Silergy clones. */ 41 enum fanpwr_id { 42 FANPWR_FAN53555, /* Fairchild FAN53555 */ 43 FANPWR_SYR827, /* Silergy SYR827 */ 44 FANPWR_SYR828 /* Silergy SYR828 */ 45 }; 46 47 struct fanpwr_softc { 48 struct device sc_dev; 49 i2c_tag_t sc_tag; 50 i2c_addr_t sc_addr; 51 52 enum fanpwr_id sc_id; 53 uint8_t sc_vsel; 54 55 struct regulator_device sc_rd; 56 uint32_t sc_vbase; 57 uint32_t sc_vstep; 58 }; 59 60 int fanpwr_match(struct device *, void *, void *); 61 void fanpwr_attach(struct device *, struct device *, void *); 62 63 struct cfattach fanpwr_ca = { 64 sizeof(struct fanpwr_softc), fanpwr_match, fanpwr_attach 65 }; 66 67 struct cfdriver fanpwr_cd = { 68 NULL, "fanpwr", DV_DULL 69 }; 70 71 uint8_t fanpwr_read(struct fanpwr_softc *, int); 72 void fanpwr_write(struct fanpwr_softc *, int, uint8_t); 73 uint32_t fanpwr_get_voltage(void *); 74 int fanpwr_set_voltage(void *, uint32_t); 75 76 int 77 fanpwr_match(struct device *parent, void *match, void *aux) 78 { 79 struct i2c_attach_args *ia = aux; 80 int node = *(int *)ia->ia_cookie; 81 82 return (OF_is_compatible(node, "fcs,fan53555") || 83 OF_is_compatible(node, "silergy,syr827") || 84 OF_is_compatible(node, "silergy,syr828")); 85 } 86 87 void 88 fanpwr_attach(struct device *parent, struct device *self, void *aux) 89 { 90 struct fanpwr_softc *sc = (struct fanpwr_softc *)self; 91 struct i2c_attach_args *ia = aux; 92 int node = *(int *)ia->ia_cookie; 93 uint32_t voltage, ramp_delay; 94 uint8_t id1, id2; 95 96 pinctrl_byname(node, "default"); 97 98 sc->sc_tag = ia->ia_tag; 99 sc->sc_addr = ia->ia_addr; 100 101 if (OF_getpropint(node, "fcs,suspend-voltage-selector", 0)) 102 sc->sc_vsel = FAN53555_VSEL0; 103 else 104 sc->sc_vsel = FAN53555_VSEL1; 105 106 if (OF_is_compatible(node, "silergy,syr827")) { 107 printf(": SYR827"); 108 sc->sc_id = FANPWR_SYR827; 109 } else if (OF_is_compatible(node, "silergy,syr828")) { 110 printf(": SYR828"); 111 sc->sc_id = FANPWR_SYR828; 112 } else { 113 printf(": FAN53555"); 114 sc->sc_id = FANPWR_FAN53555; 115 } 116 117 id1 = fanpwr_read(sc, FAN53555_ID1); 118 id2 = fanpwr_read(sc, FAN53555_ID2); 119 120 switch (sc->sc_id) { 121 case FANPWR_FAN53555: 122 switch (id1 << 8 | id2) { 123 case 0x8003: /* 00 Option */ 124 case 0x8103: /* 01 Option */ 125 case 0x8303: /* 03 Option */ 126 case 0x8503: /* 05 Option */ 127 case 0x8801: /* 08, 18 Options */ 128 case 0x880f: /* BUC08, BUC18 Options */ 129 case 0x8108: /* 79 Option */ 130 sc->sc_vbase = 600000; 131 sc->sc_vstep = 10000; 132 break; 133 case 0x840f: /* 04 Option */ 134 case 0x8c0f: /* 09 Option */ 135 sc->sc_vbase = 603000; 136 sc->sc_vstep = 12826; 137 break; 138 case 0x800f: /* 13 Option */ 139 sc->sc_vbase = 800000; 140 sc->sc_vstep = 10000; 141 break; 142 case 0x800c: /* 23 Option */ 143 sc->sc_vbase = 600000; 144 sc->sc_vstep = 12500; 145 break; 146 case 0x8004: /* 24 Option */ 147 sc->sc_vbase = 603000; 148 sc->sc_vstep = 12967; 149 break; 150 default: 151 printf(", unknown ID1 0x%02x ID2 0x%02x\n", id1, id2); 152 return; 153 } 154 break; 155 case FANPWR_SYR827: 156 case FANPWR_SYR828: 157 sc->sc_vbase = 712500; 158 sc->sc_vstep = 12500; 159 break; 160 } 161 162 voltage = fanpwr_get_voltage(sc); 163 printf(", %d.%02d VDC", voltage / 1000000, 164 (voltage % 1000000) / 10000); 165 166 ramp_delay = OF_getpropint(node, "regulator-ramp-delay", 0); 167 if (ramp_delay > 0) { 168 uint8_t ctrl, slew; 169 170 for (slew = 7; slew > 0; slew--) 171 if ((64000 >> slew) >= ramp_delay) 172 break; 173 ctrl = fanpwr_read(sc, FAN53555_CONTROL); 174 ctrl &= ~FAN53555_CONTROL_SLEW_MASK; 175 ctrl |= slew << FAN53555_CONTROL_SLEW_SHIFT; 176 fanpwr_write(sc, FAN53555_CONTROL, ctrl); 177 } 178 179 sc->sc_rd.rd_node = node; 180 sc->sc_rd.rd_cookie = sc; 181 sc->sc_rd.rd_get_voltage = fanpwr_get_voltage; 182 sc->sc_rd.rd_set_voltage = fanpwr_set_voltage; 183 regulator_register(&sc->sc_rd); 184 185 printf("\n"); 186 } 187 188 uint8_t 189 fanpwr_read(struct fanpwr_softc *sc, int reg) 190 { 191 uint8_t cmd = reg; 192 uint8_t val; 193 int error; 194 195 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 196 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 197 &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL); 198 iic_release_bus(sc->sc_tag, I2C_F_POLL); 199 200 if (error) { 201 printf("error %d\n", error); 202 printf("%s: can't read register 0x%02x\n", 203 sc->sc_dev.dv_xname, reg); 204 val = 0xff; 205 } 206 207 return val; 208 } 209 210 void 211 fanpwr_write(struct fanpwr_softc *sc, int reg, uint8_t val) 212 { 213 uint8_t cmd = reg; 214 int error; 215 216 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 217 error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, 218 &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL); 219 iic_release_bus(sc->sc_tag, I2C_F_POLL); 220 221 if (error) { 222 printf("%s: can't write register 0x%02x\n", 223 sc->sc_dev.dv_xname, reg); 224 } 225 } 226 227 uint32_t 228 fanpwr_get_voltage(void *cookie) 229 { 230 struct fanpwr_softc *sc = cookie; 231 uint8_t vsel; 232 233 vsel = fanpwr_read(sc, sc->sc_vsel); 234 return sc->sc_vbase + (vsel & FAN53555_VSEL_NSEL_MASK) * sc->sc_vstep; 235 } 236 237 int 238 fanpwr_set_voltage(void *cookie, uint32_t voltage) 239 { 240 struct fanpwr_softc *sc = cookie; 241 uint32_t vmin = sc->sc_vbase; 242 uint32_t vmax = vmin + FAN53555_VSEL_NSEL_MASK * sc->sc_vstep; 243 uint8_t vsel; 244 245 if (voltage < vmin || voltage > vmax) 246 return EINVAL; 247 248 vsel = fanpwr_read(sc, sc->sc_vsel); 249 vsel &= ~FAN53555_VSEL_NSEL_MASK; 250 vsel |= (voltage - sc->sc_vbase) / sc->sc_vstep; 251 fanpwr_write(sc, sc->sc_vsel, vsel); 252 253 return 0; 254 } 255