1 /* $NetBSD: as3722.c,v 1.4 2015/12/13 17:15:06 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: as3722.c,v 1.4 2015/12/13 17:15:06 jmcneill Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/kernel.h> 35 #include <sys/device.h> 36 #include <sys/conf.h> 37 #include <sys/bus.h> 38 #include <sys/kmem.h> 39 #include <sys/wdog.h> 40 41 #include <dev/sysmon/sysmonvar.h> 42 43 #include <dev/i2c/i2cvar.h> 44 #include <dev/i2c/as3722.h> 45 46 #define AS3722_GPIO0_CTRL_REG 0x08 47 #define AS3722_GPIO0_CTRL_INVERT __BIT(7) 48 #define AS3722_GPIO0_CTRL_IOSF __BITS(6,3) 49 #define AS3722_GPIO0_CTRL_IOSF_GPIO 0 50 #define AS3722_GPIO0_CTRL_IOSF_WATCHDOG 9 51 #define AS3722_GPIO0_CTRL_MODE __BITS(2,0) 52 #define AS3722_GPIO0_CTRL_MODE_PULLDOWN 5 53 54 #define AS3722_RESET_CTRL_REG 0x36 55 #define AS3722_RESET_CTRL_POWER_OFF __BIT(1) 56 #define AS3722_RESET_CTRL_FORCE_RESET __BIT(0) 57 58 #define AS3722_WATCHDOG_CTRL_REG 0x38 59 #define AS3722_WATCHDOG_CTRL_MODE __BITS(2,1) 60 #define AS3722_WATCHDOG_CTRL_ON __BIT(0) 61 62 #define AS3722_WATCHDOG_TIMER_REG 0x46 63 #define AS3722_WATCHDOG_TIMER_TIMER __BITS(6,0) 64 65 #define AS3722_WATCHDOG_SIGNAL_REG 0x48 66 #define AS3722_WATCHDOG_SIGNAL_PWM_DIV __BITS(7,6) 67 #define AS3722_WATCHDOG_SIGNAL_SW_SIG __BIT(0) 68 69 #define AS3722_ASIC_ID1_REG 0x90 70 #define AS3722_ASIC_ID2_REG 0x91 71 72 struct as3722_softc { 73 device_t sc_dev; 74 i2c_tag_t sc_i2c; 75 i2c_addr_t sc_addr; 76 77 struct sysmon_wdog sc_smw; 78 }; 79 80 #define AS3722_WATCHDOG_DEFAULT_PERIOD 10 81 82 static int as3722_match(device_t, cfdata_t, void *); 83 static void as3722_attach(device_t, device_t, void *); 84 85 static int as3722_wdt_setmode(struct sysmon_wdog *); 86 static int as3722_wdt_tickle(struct sysmon_wdog *); 87 88 static int as3722_read(struct as3722_softc *, uint8_t, uint8_t *, int); 89 static int as3722_write(struct as3722_softc *, uint8_t, uint8_t, int); 90 static int as3722_set_clear(struct as3722_softc *, uint8_t, uint8_t, 91 uint8_t, int); 92 93 CFATTACH_DECL_NEW(as3722pmic, sizeof(struct as3722_softc), 94 as3722_match, as3722_attach, NULL, NULL); 95 96 static const char * as3722_compats[] = { 97 "ams,as3722", 98 NULL 99 }; 100 101 static int 102 as3722_match(device_t parent, cfdata_t match, void *aux) 103 { 104 struct i2c_attach_args *ia = aux; 105 uint8_t reg, id1; 106 int error; 107 108 if (ia->ia_name == NULL) { 109 iic_acquire_bus(ia->ia_tag, I2C_F_POLL); 110 reg = AS3722_ASIC_ID1_REG; 111 error = iic_exec(ia->ia_tag, I2C_OP_READ_WITH_STOP, ia->ia_addr, 112 ®, 1, &id1, 1, I2C_F_POLL); 113 iic_release_bus(ia->ia_tag, I2C_F_POLL); 114 115 if (error == 0 && id1 == 0x0c) 116 return 1; 117 118 return 0; 119 } else { 120 return iic_compat_match(ia, as3722_compats); 121 } 122 } 123 124 static void 125 as3722_attach(device_t parent, device_t self, void *aux) 126 { 127 struct as3722_softc * const sc = device_private(self); 128 struct i2c_attach_args *ia = aux; 129 int error; 130 131 sc->sc_dev = self; 132 sc->sc_i2c = ia->ia_tag; 133 sc->sc_addr = ia->ia_addr; 134 135 aprint_naive("\n"); 136 aprint_normal(": AMS AS3822\n"); 137 138 iic_acquire_bus(sc->sc_i2c, I2C_F_POLL); 139 error = as3722_write(sc, AS3722_GPIO0_CTRL_REG, 140 __SHIFTIN(AS3722_GPIO0_CTRL_IOSF_GPIO, 141 AS3722_GPIO0_CTRL_IOSF) | 142 __SHIFTIN(AS3722_GPIO0_CTRL_MODE_PULLDOWN, 143 AS3722_GPIO0_CTRL_MODE), 144 I2C_F_POLL); 145 error += as3722_set_clear(sc, AS3722_WATCHDOG_CTRL_REG, 146 __SHIFTIN(1, AS3722_WATCHDOG_CTRL_MODE), 0, I2C_F_POLL); 147 iic_release_bus(sc->sc_i2c, I2C_F_POLL); 148 149 if (error) 150 aprint_error_dev(self, "couldn't setup watchdog\n"); 151 152 sc->sc_smw.smw_name = device_xname(self); 153 sc->sc_smw.smw_cookie = sc; 154 sc->sc_smw.smw_setmode = as3722_wdt_setmode; 155 sc->sc_smw.smw_tickle = as3722_wdt_tickle; 156 sc->sc_smw.smw_period = AS3722_WATCHDOG_DEFAULT_PERIOD; 157 158 aprint_normal_dev(self, "default watchdog period is %u seconds\n", 159 sc->sc_smw.smw_period); 160 161 if (sysmon_wdog_register(&sc->sc_smw) != 0) 162 aprint_error_dev(self, "couldn't register with sysmon\n"); 163 } 164 165 static int 166 as3722_read(struct as3722_softc *sc, uint8_t reg, uint8_t *val, int flags) 167 { 168 return iic_exec(sc->sc_i2c, I2C_OP_READ_WITH_STOP, sc->sc_addr, 169 ®, 1, val, 1, flags); 170 } 171 172 static int 173 as3722_write(struct as3722_softc *sc, uint8_t reg, uint8_t val, int flags) 174 { 175 uint8_t buf[2] = { reg, val }; 176 return iic_exec(sc->sc_i2c, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, 177 NULL, 0, buf, 2, flags); 178 } 179 180 static int 181 as3722_set_clear(struct as3722_softc *sc, uint8_t reg, uint8_t set, 182 uint8_t clr, int flags) 183 { 184 uint8_t old, new; 185 int error; 186 187 error = as3722_read(sc, reg, &old, flags); 188 if (error) { 189 return error; 190 } 191 new = set | (old & ~clr); 192 193 return as3722_write(sc, reg, new, flags); 194 } 195 196 static int 197 as3722_wdt_setmode(struct sysmon_wdog *smw) 198 { 199 struct as3722_softc * const sc = smw->smw_cookie; 200 int error; 201 202 const int flags = (cold ? I2C_F_POLL : 0); 203 204 if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) { 205 iic_acquire_bus(sc->sc_i2c, flags); 206 error = as3722_set_clear(sc, AS3722_WATCHDOG_CTRL_REG, 207 0, AS3722_WATCHDOG_CTRL_ON, flags); 208 iic_release_bus(sc->sc_i2c, flags); 209 return error; 210 } 211 212 if (smw->smw_period == WDOG_PERIOD_DEFAULT) { 213 smw->smw_period = AS3722_WATCHDOG_DEFAULT_PERIOD; 214 } 215 if (smw->smw_period < 1 || smw->smw_period > 128) { 216 return EINVAL; 217 } 218 sc->sc_smw.smw_period = smw->smw_period; 219 220 iic_acquire_bus(sc->sc_i2c, flags); 221 error = as3722_set_clear(sc, AS3722_WATCHDOG_TIMER_REG, 222 __SHIFTIN(sc->sc_smw.smw_period - 1, AS3722_WATCHDOG_TIMER_TIMER), 223 AS3722_WATCHDOG_TIMER_TIMER, flags); 224 if (error == 0) { 225 error = as3722_set_clear(sc, AS3722_WATCHDOG_CTRL_REG, 226 AS3722_WATCHDOG_CTRL_ON, 0, flags); 227 } 228 iic_release_bus(sc->sc_i2c, flags); 229 230 return error; 231 } 232 233 static int 234 as3722_wdt_tickle(struct sysmon_wdog *smw) 235 { 236 struct as3722_softc * const sc = smw->smw_cookie; 237 int error; 238 239 const int flags = (cold ? I2C_F_POLL : 0); 240 241 iic_acquire_bus(sc->sc_i2c, flags); 242 error = as3722_set_clear(sc, AS3722_WATCHDOG_SIGNAL_REG, 243 AS3722_WATCHDOG_SIGNAL_SW_SIG, 0, flags); 244 iic_release_bus(sc->sc_i2c, flags); 245 246 return error; 247 } 248 249 int 250 as3722_poweroff(device_t dev) 251 { 252 struct as3722_softc * const sc = device_private(dev); 253 int error; 254 255 const int flags = I2C_F_POLL; 256 257 iic_acquire_bus(sc->sc_i2c, flags); 258 error = as3722_write(sc, AS3722_RESET_CTRL_REG, 259 AS3722_RESET_CTRL_POWER_OFF, flags); 260 iic_release_bus(sc->sc_i2c, flags); 261 262 return error; 263 } 264 265 int 266 as3722_reboot(device_t dev) 267 { 268 struct as3722_softc * const sc = device_private(dev); 269 int error; 270 271 const int flags = I2C_F_POLL; 272 273 iic_acquire_bus(sc->sc_i2c, flags); 274 error = as3722_write(sc, AS3722_RESET_CTRL_REG, 275 AS3722_RESET_CTRL_FORCE_RESET, flags); 276 iic_release_bus(sc->sc_i2c, flags); 277 278 return error; 279 } 280