1 /* $OpenBSD: sxidog.c,v 1.2 2019/10/17 22:26:32 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2007,2009 Dale Rahn <drahn@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 22 #include <machine/bus.h> 23 #include <machine/fdt.h> 24 25 #include <dev/fdt/sunxireg.h> 26 27 #include <dev/ofw/openfirm.h> 28 #include <dev/ofw/fdt.h> 29 30 extern void (*cpuresetfn)(void); 31 32 /* Allwinner A10 registers */ 33 #define WDOG_CTRL_REG 0x00 34 #define WDOG_KEY (0x0a57 << 1) 35 #define WDOG_RSTART 0x01 36 #define WDOG_MODE_REG 0x04 37 #define WDOG_EN (1 << 0) 38 #define WDOG_RST_EN (1 << 1) /* system reset */ 39 #define WDOG_INTV_VALUE(x) ((x) << 3) 40 41 /* Allwinner A31 registers */ 42 #define WDOG0_CTRL_REG 0x10 43 #define WDOG0_KEY (0x0a57 << 1) 44 #define WDOG0_RSTART (1 << 0) 45 #define WDOG0_CFG_REG 0x14 46 #define WDOG0_RST_EN (1 << 0) 47 #define WDOG0_MODE_REG 0x18 48 #define WDOG0_EN (1 << 0) 49 #define WDOG0_INTV_VALUE(x) ((x) << 4) 50 51 struct sxidog_softc { 52 struct device sc_dev; 53 bus_space_tag_t sc_iot; 54 bus_space_handle_t sc_ioh; 55 int sc_type; 56 #define SXIDOG_A10 0 57 #define SXIDOG_A31 1 58 }; 59 60 struct sxidog_softc *sxidog_sc = NULL; /* for sxidog_reset() */ 61 62 int sxidog_match(struct device *, void *, void *); 63 void sxidog_attach(struct device *, struct device *, void *); 64 int sxidog_activate(struct device *, int); 65 int sxidog_callback(void *, int); 66 void sxidog_reset(void); 67 68 struct cfattach sxidog_ca = { 69 sizeof (struct sxidog_softc), sxidog_match, sxidog_attach, 70 NULL, sxidog_activate 71 }; 72 73 struct cfdriver sxidog_cd = { 74 NULL, "sxidog", DV_DULL 75 }; 76 77 int 78 sxidog_match(struct device *parent, void *match, void *aux) 79 { 80 struct fdt_attach_args *faa = aux; 81 82 return (OF_is_compatible(faa->fa_node, "allwinner,sun4i-a10-wdt") || 83 OF_is_compatible(faa->fa_node, "allwinner,sun6i-a31-wdt")); 84 } 85 86 void 87 sxidog_attach(struct device *parent, struct device *self, void *aux) 88 { 89 struct sxidog_softc *sc = (struct sxidog_softc *)self; 90 struct fdt_attach_args *faa = aux; 91 92 if (faa->fa_nreg < 1) 93 return; 94 95 sc->sc_iot = faa->fa_iot; 96 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 97 faa->fa_reg[0].size, 0, &sc->sc_ioh)) 98 panic("sxidog_attach: bus_space_map failed!"); 99 100 if (OF_is_compatible(faa->fa_node, "allwinner,sun6i-a31-wdt")) { 101 SXIWRITE4(sc, WDOG0_CFG_REG, WDOG0_RST_EN); 102 sc->sc_type = SXIDOG_A31; 103 } else 104 sc->sc_type = SXIDOG_A10; 105 106 sxidog_sc = sc; 107 cpuresetfn = sxidog_reset; 108 109 #ifndef SMALL_KERNEL 110 wdog_register(sxidog_callback, sc); 111 #endif 112 113 printf("\n"); 114 } 115 116 int 117 sxidog_activate(struct device *self, int act) 118 { 119 switch (act) { 120 case DVACT_POWERDOWN: 121 #ifndef SMALL_KERNEL 122 wdog_shutdown(self); 123 #endif 124 break; 125 } 126 127 return (0); 128 } 129 130 int 131 sxidog_callback(void *arg, int period) 132 { 133 struct sxidog_softc *sc = (struct sxidog_softc *)arg; 134 int enable; 135 136 if (period > 16) 137 period = 16; 138 else if (period < 0) 139 period = 0; 140 141 /* Convert to register encoding. */ 142 if (period > 6) 143 period = 6 + (period - 5) / 2; 144 145 switch (sc->sc_type) { 146 case SXIDOG_A10: 147 enable = (period > 0) ? WDOG_RST_EN : 0; 148 SXIWRITE4(sc, WDOG_MODE_REG, 149 enable | WDOG_EN | WDOG_INTV_VALUE(period)); 150 SXIWRITE4(sc, WDOG_CTRL_REG, WDOG_KEY | WDOG_RSTART); 151 break; 152 case SXIDOG_A31: 153 enable = (period > 0) ? WDOG0_EN : 0; 154 SXIWRITE4(sc, WDOG0_MODE_REG, 155 enable | WDOG0_INTV_VALUE(period)); 156 SXIWRITE4(sc, WDOG0_CTRL_REG, WDOG0_KEY | WDOG0_RSTART); 157 break; 158 } 159 160 /* Convert back to seconds. */ 161 if (period > 6) 162 period = 6 + (period - 6) * 2; 163 164 return period; 165 } 166 167 void 168 sxidog_reset(void) 169 { 170 if (sxidog_sc == NULL) 171 return; 172 173 sxidog_callback(sxidog_sc, 1); 174 delay(1500000); 175 } 176