DF-5521 / df5521_rig.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 | /* * DF-5521/5522/5523/5525 rig: software I2C controller on isa. * * Registers a stateless "dfa_iic" controller driver on the isa bus and an * "iicbus" child on it, so that the production chain * * dfa_iic0 (isa) -> iicbus0 -> iicsmb0 -> smbus0 -> smb0 (/dev/smb0) * * comes up with 100% production code (iicbus.ko, iicsmb.ko, smbus.ko, * smb.ko are the stock modules). Only the wire is software: * * - every START/WRITE/READ/STOP is kprintf'ed with its bytes so the * iicsmb bridge's wire protocol is directly observable; * - reads can sleep a configurable delay (hw.dfa.rdelay ms) to hold an * SMBus transaction in flight (kldunload race window); * - the READ log line shows the requested length, which is exactly the * bridge's (int)*count -- i.e. /dev/smb's *uninitialized* bcount; * - hw.dfa.fire_event=N injects iicbus_intr(iicbus0, N, &c) the way a * real controller ISR would (pcfintr calls iicbus_intr on 8 paths). */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/systm.h> #include <sys/bus.h> #include <sys/sysctl.h> #include <bus/iicbus/iiconf.h> #include <bus/iicbus/iicbus.h> #include "iicbus_if.h" /* iicbus_driver/iicbus_devclass are declared in <bus/iicbus/iicbus.h> */ #define DF_SLAVE_CNT 8 /* emulated SMBus slave block count byte */ struct dfa_softc { device_t iicbus; int unit; int rxoff; /* slave TX stream offset within a txn */ }; static struct dfa_softc *g_sc; static int dfa_rdelay = 0; /* ms to sleep inside READ (race window) */ static int dfa_probe(device_t); static int dfa_attach(device_t); static int dfa_detach(device_t); static int dfa_callback(device_t, int, caddr_t); static int dfa_reset(device_t, u_char, u_char, u_char *); static int dfa_start(device_t, u_char, int); static int dfa_rstart(device_t, u_char, int); static int dfa_stop(device_t); static int dfa_write(device_t, const char *, int, int *, int); static int dfa_read(device_t, char *, int, int *, int, int); static device_method_t dfa_methods[] = { /* device interface */ DEVMETHOD(device_probe, dfa_probe), DEVMETHOD(device_attach, dfa_attach), DEVMETHOD(device_detach, dfa_detach), /* iicbus interface (this driver IS an i2c controller) */ DEVMETHOD(iicbus_callback, dfa_callback), DEVMETHOD(iicbus_reset, dfa_reset), DEVMETHOD(iicbus_start, dfa_start), DEVMETHOD(iicbus_repeated_start, dfa_rstart), DEVMETHOD(iicbus_stop, dfa_stop), DEVMETHOD(iicbus_write, dfa_write), DEVMETHOD(iicbus_read, dfa_read), DEVMETHOD_END }; static driver_t dfa_driver = { "dfa_iic", dfa_methods, sizeof(struct dfa_softc), }; static devclass_t dfa_devclass; DRIVER_MODULE(dfa_iic, isa, dfa_driver, dfa_devclass, NULL, NULL); DRIVER_MODULE(iicbus, dfa_iic, iicbus_driver, iicbus_devclass, NULL, NULL); MODULE_DEPEND(dfa_iic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); static int dfa_probe(device_t dev) { device_set_desc(dev, "df5521 software I2C controller (wire-logged)"); return (0); } static int dfa_attach(device_t dev) { struct dfa_softc *sc = device_get_softc(dev); sc->unit = device_get_unit(dev); sc->iicbus = device_add_child(dev, "iicbus", -1); if (sc->iicbus == NULL) { kprintf("df5521: device_add_child(iicbus) failed\n"); return (ENXIO); } g_sc = sc; /* iicbus attach adds ic/iic/iicsmb children (iicbus.c:144-146); * iicsmb attach adds smbus (iicsmb.c:156); smbus attach probes the * bus (all probes fail: iicsmb has no smbus_trans) and smb attaches * -> /dev/smb0. */ kprintf("df5521: dfa_iic%d attached, attaching iicbus chain\n", sc->unit); bus_generic_attach(dev); kprintf("df5521: chain up: iicbus=%s\n", device_get_nameunit(sc->iicbus)); return (0); } static int dfa_detach(device_t dev) { struct dfa_softc *sc = device_get_softc(dev); if (sc->iicbus) { bus_generic_detach(dev); device_delete_child(dev, sc->iicbus); } g_sc = NULL; return (0); } static int dfa_callback(device_t dev, int index, caddr_t data) { return (0); } static int dfa_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) { return (0); } static int dfa_start(device_t dev, u_char slave, int timeout) { struct dfa_softc *sc = device_get_softc(dev); sc->rxoff = 0; /* slave restarts its TX stream per txn */ kprintf("df5521-wire: START slave=0x%02x %s\n", slave, (slave & LSB) ? "READ" : "WRITE"); return (0); /* always ACK */ } static int dfa_rstart(device_t dev, u_char slave, int timeout) { kprintf("df5521-wire: REPEATED_START slave=0x%02x %s\n", slave, (slave & LSB) ? "READ" : "WRITE"); return (0); } static int dfa_stop(device_t dev) { kprintf("df5521-wire: STOP\n"); return (0); } static int dfa_write(device_t dev, const char *buf, int len, int *bytes, int timeout) { int i; kprintf("df5521-wire: WRITE len=%d bytes:", len); for (i = 0; i < len && i < 64; i++) kprintf(" %02x", (u_char)buf[i]); if (len > 64) kprintf(" ..."); kprintf("\n"); *bytes = len; return (0); } static int dfa_read(device_t dev, char *buf, int len, int *bytes, int last, int delay) { struct dfa_softc *sc = device_get_softc(dev); int i; /* len here == the bridge's (int)*count == /dev/smb's `bcount` * (smb.c:217 is NEVER initialized before smbus_bread) */ kprintf("df5521-wire: READ req_len=%d <== bridge *count\n", len); if (dfa_rdelay > 0) tsleep(&dfa_rdelay, 0, "dfard", dfa_rdelay * hz / 1000); /* emulated spec-compliant SMBus block slave: the byte stream within * one transaction is [count][data...] and CONTINUES across reads */ for (i = 0; i < len; i++, sc->rxoff++) buf[i] = (sc->rxoff == 0) ? DF_SLAVE_CNT : (char)(0xa0 + sc->rxoff - 1); *bytes = len; kprintf("df5521-wire: READ gave %d bytes: %02x %02x %02x %02x ...\n", len, (u_char)buf[0], (u_char)buf[1], (u_char)buf[2], (u_char)buf[3]); return (0); } /* * sysctls */ SYSCTL_NODE(_hw, OID_AUTO, dfa, CTLFLAG_RW, 0, "df5521 rig"); SYSCTL_INT(_hw_dfa, OID_AUTO, rdelay, CTLFLAG_RW, &dfa_rdelay, 0, "ms to sleep inside controller READ (in-flight window)"); static int dfa_fire_event_sysctl(SYSCTL_HANDLER_ARGS) { int error, v = 0; error = SYSCTL_OUT(req, &v, sizeof(v)); if (error != 0 || req->newptr == NULL) return (error); error = SYSCTL_IN(req, &v, sizeof(v)); if (error != 0) return (error); if (g_sc && g_sc->iicbus) { char c = IIC_EBUSERR; kprintf("df5521: firing iicbus_intr(%s, event=0x%x, &c)\n", device_get_nameunit(g_sc->iicbus), v); iicbus_intr(g_sc->iicbus, v, &c); kprintf("df5521: iicbus_intr returned (no fault)\n"); } else { kprintf("df5521: no iicbus child\n"); } return (0); } SYSCTL_PROC(_hw_dfa, OID_AUTO, fire_event, CTLTYPE_INT | CTLFLAG_RW, 0, 0, dfa_fire_event_sysctl, "I", "fire iicbus_intr with event value"); /* * Instantiate the controller the way isahint(4) does for real ISA devices * (same newbus API sequence the DF-5490 rig used for pcf). */ static int dfa_modevent(module_t m, int what, void *arg) { devclass_t dc; device_t isa, child; switch (what) { case MOD_LOAD: dc = devclass_find("isa"); if (dc == NULL) { kprintf("df5521: no isa devclass\n"); return (ENXIO); } isa = devclass_get_device(dc, 1); if (isa == NULL) isa = devclass_get_device(dc, 0); if (isa == NULL) { kprintf("df5521: no isa bus device\n"); return (ENXIO); } child = BUS_ADD_CHILD(isa, isa, 90, "dfa_iic", -1); if (child == NULL) { kprintf("df5521: BUS_ADD_CHILD failed\n"); return (ENXIO); } kprintf("df5521: added %s, probing+attaching\n", device_get_nameunit(child)); if (device_probe_and_attach(child) != 0) kprintf("df5521: device_probe_and_attach FAILED\n"); break; default: break; } return (0); } static moduledata_t dfa_mod = { "df5521_rig", dfa_modevent, NULL }; /* SI_ORDER_MIDDLE+1: run AFTER the DRIVER_MODULE handlers (SI_ORDER_MIDDLE) * so dfa_iic-on-isa and iicbus-on-dfa_iic are registered when we instantiate. */ DECLARE_MODULE(df5521_rig, dfa_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE + 1); MODULE_VERSION(df5521_rig, 1); |