1 /* $NetBSD: icap_ebus.c,v 1.7 2018/03/04 21:41:48 mrg Exp $ */ 2 3 /*- 4 * Copyright (c) 2010 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code was written by Alessandro Forin and Neil Pittman 8 * at Microsoft Research and contributed to The NetBSD Foundation 9 * by Microsoft Corporation. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <sys/cdefs.h> /* RCS ID & Copyright macro defns */ 34 __KERNEL_RCSID(0, "$NetBSD: icap_ebus.c,v 1.7 2018/03/04 21:41:48 mrg Exp $"); 35 36 #include <sys/param.h> 37 #include <sys/systm.h> 38 #include <sys/buf.h> 39 #include <sys/bufq.h> 40 #include <sys/proc.h> 41 #include <sys/errno.h> 42 #include <sys/ioctl.h> 43 #include <sys/device.h> 44 #include <sys/conf.h> 45 #include <uvm/uvm_param.h> 46 47 #include <emips/ebus/ebusvar.h> 48 #include <emips/emips/machdep.h> 49 #include <machine/emipsreg.h> 50 51 #define DEBUG_INTR 0x01 52 #define DEBUG_XFERS 0x02 53 #define DEBUG_STATUS 0x04 54 #define DEBUG_FUNCS 0x08 55 #define DEBUG_PROBE 0x10 56 #define DEBUG_WRITES 0x20 57 #define DEBUG_READS 0x40 58 #define DEBUG_ERRORS 0x80 59 #ifdef DEBUG 60 int icap_debug = DEBUG_ERRORS; 61 #define ICAP_DEBUG(x) (icap_debug & (x)) 62 #define DBGME(_lev_,_x_) if ((_lev_) & icap_debug) _x_ 63 #else 64 #define ICAP_DEBUG(x) (0) 65 #define DBGME(_lev_,_x_) 66 #endif 67 #define DEBUG_PRINT(_args_,_lev_) DBGME(_lev_,printf _args_) 68 69 /* 70 * Device softc 71 */ 72 struct icap_softc { 73 device_t sc_dev; 74 struct _Icap *sc_dp; 75 struct bufq_state *sc_buflist; 76 struct buf *sc_bp; 77 char *sc_data; 78 int sc_count; 79 }; 80 81 /* Required funcs 82 */ 83 static int icap_ebus_match (device_t, cfdata_t, void *); 84 static void icap_ebus_attach (device_t, device_t, void *); 85 86 static dev_type_open(icapopen); 87 static dev_type_close(icapclose); 88 static dev_type_read(icapread); 89 static dev_type_write(icapwrite); 90 static dev_type_ioctl(icapioctl); 91 static dev_type_strategy(icapstrategy); 92 93 /* Other functions 94 */ 95 extern paddr_t kvtophys(vaddr_t); 96 static void icapstart(struct icap_softc *sc); 97 static int icap_ebus_intr(void *cookie, void *f); 98 static void icap_reset(struct icap_softc *sc); 99 100 /* Config stuff 101 */ 102 extern struct cfdriver icap_cd; 103 104 CFATTACH_DECL_NEW(icap_ebus, sizeof (struct icap_softc), 105 icap_ebus_match, icap_ebus_attach, NULL, NULL); 106 107 static int 108 icap_ebus_match(device_t parent, cfdata_t match, void *aux) 109 { 110 struct ebus_attach_args *ia = aux; 111 struct _Icap *f = (struct _Icap *)ia->ia_vaddr; 112 113 DEBUG_PRINT(("icap_match %x\n", (f) ? f->Tag : 0), DEBUG_PROBE); 114 if (strcmp("icap", ia->ia_name) != 0) 115 return (0); 116 if ((f == NULL) || 117 (! (f->Tag == PMTTAG_ICAP))) 118 return (0); 119 120 return (1); 121 } 122 123 static void 124 icap_ebus_attach(device_t parent, device_t self, void *aux) 125 { 126 struct icap_softc *sc = device_private(self); 127 struct ebus_attach_args *ia =aux; 128 129 DEBUG_PRINT(("icap_attach %p\n", sc), DEBUG_PROBE); 130 131 sc->sc_dev = self; 132 sc->sc_dp = (struct _Icap*)ia->ia_vaddr; 133 bufq_alloc(&sc->sc_buflist, "fcfs", 0); 134 sc->sc_bp = NULL; 135 sc->sc_data = NULL; 136 sc->sc_count = 0; 137 138 #if DEBUG 139 printf(" virt=%p", (void*)sc->sc_dp); 140 #endif 141 printf(": %s\n", "Internal Configuration Access Port"); 142 143 ebus_intr_establish(parent, (void*)ia->ia_cookie, IPL_BIO, 144 icap_ebus_intr, sc); 145 146 icap_reset(sc); 147 } 148 149 /* The character device handlers 150 */ 151 const struct cdevsw icap_cdevsw = { 152 .d_open = icapopen, 153 .d_close = icapclose, 154 .d_read = icapread, 155 .d_write = icapwrite, 156 .d_ioctl = icapioctl, 157 .d_stop = nostop, 158 .d_tty = notty, 159 .d_poll = nopoll, 160 .d_mmap = nommap, 161 .d_kqfilter = nokqfilter, 162 .d_discard = nodiscard, 163 .d_flag = 0 164 }; 165 166 /* 167 * Handle an open request on the device. 168 */ 169 static int 170 icapopen(dev_t device, int flags, int fmt, struct lwp *process) 171 { 172 struct icap_softc *sc; 173 174 DEBUG_PRINT(("icapopen\n"), DEBUG_FUNCS); 175 sc = device_lookup_private(&icap_cd, minor(device)); 176 if (sc == NULL) 177 return (ENXIO); 178 179 return 0; 180 } 181 182 /* 183 * Handle the close request for the device. 184 */ 185 static int 186 icapclose(dev_t device, int flags, int fmt, struct lwp *process) 187 { 188 DEBUG_PRINT(("icapclose\n"), DEBUG_FUNCS); 189 return 0; /* this always succeeds */ 190 } 191 192 /* 193 * Handle the read request for the device. 194 */ 195 static int 196 icapread(dev_t dev, struct uio *uio, int flags) 197 { 198 DEBUG_PRINT(("icapread\n"), DEBUG_READS); 199 return (physio(icapstrategy, NULL, dev, B_READ, minphys, uio)); 200 } 201 202 /* 203 * Handle the write request for the device. 204 */ 205 static int 206 icapwrite(dev_t dev, struct uio *uio, int flags) 207 { 208 DEBUG_PRINT(("icapwrite\n"), DEBUG_WRITES); 209 return (physio(icapstrategy, NULL, dev, B_WRITE, minphys, uio)); 210 } 211 212 /* 213 * Handle the ioctl request for the device. 214 */ 215 static int 216 icapioctl(dev_t dev, u_long xfer, void *addr, int flag, struct lwp *l) 217 { 218 219 return ENOTTY; 220 } 221 222 /* 223 * Strategy function for the device. 224 */ 225 static void 226 icapstrategy(struct buf *bp) 227 { 228 struct icap_softc *sc; 229 int s; 230 231 DEBUG_PRINT(("icapstrategy\n"), DEBUG_FUNCS); 232 233 /* We did nothing lest we did */ 234 bp->b_resid = bp->b_bcount; 235 236 /* Do we know you. */ 237 sc = device_lookup_private(&icap_cd, minor(bp->b_dev)); 238 if (sc == NULL) { 239 DEBUG_PRINT(("icapstrategy: nodev %" PRIx64 "\n",bp->b_dev), 240 DEBUG_ERRORS); 241 bp->b_error = ENXIO; 242 biodone(bp); 243 return; 244 } 245 246 /* Add to Q. If Q was empty get it started */ 247 s = splbio(); 248 bufq_put(sc->sc_buflist, bp); 249 if (bufq_peek(sc->sc_buflist) == bp) { 250 icapstart(sc); 251 } 252 splx(s); 253 } 254 255 /* 256 * Get the next I/O request started 257 */ 258 static void 259 icapstart(struct icap_softc *sc) 260 { 261 paddr_t phys, phys2; 262 vaddr_t virt; 263 size_t count; 264 uint32_t fl; 265 struct buf *bp = sc->sc_bp; 266 267 DEBUG_PRINT(("icapstart %p %p\n",sc,bp), DEBUG_FUNCS); 268 269 /* Were we idle? 270 */ 271 recheck: 272 if (bp == NULL) { 273 274 /* Yes, get the next request if any 275 */ 276 bp = bufq_get(sc->sc_buflist); 277 DEBUG_PRINT(("icapnext: %p\n",bp), DEBUG_XFERS); 278 if (bp == NULL) 279 return; 280 } 281 282 /* Done with this request? 283 */ 284 if ((bp->b_resid == 0) || bp->b_error) { 285 286 /* Yes, complete and move to next, if any 287 */ 288 sc->sc_bp = NULL; 289 biodone(bp); 290 DEBUG_PRINT(("icapdone %p\n",bp), DEBUG_XFERS); 291 bp = NULL; 292 goto recheck; 293 } 294 295 /* If new request init the xfer info 296 */ 297 if (sc->sc_bp == NULL) { 298 sc->sc_bp = bp; 299 sc->sc_data = bp->b_data; 300 sc->sc_count = bp->b_resid; 301 } 302 303 /* Loop filling as many buffers as will fit in the FIFO 304 */ 305 fl = (bp->b_flags & B_READ) ? ICAPS_F_RECV : ICAPS_F_XMIT; 306 for (;;) { 307 308 /* Make sure there's still room in the FIFO, no errors. 309 */ 310 if (sc->sc_dp->Control & (ICAPC_IF_FULL|ICAPC_ERROR)) 311 break; 312 313 /* How much data do we xfer and where 314 */ 315 virt = (vaddr_t)sc->sc_data; 316 phys = kvtophys(virt); 317 count = round_page(virt) - virt; 318 if (count == 0) count = PAGE_SIZE;/* could(will) be aligned */ 319 320 /* How much of it is contiguous 321 */ 322 while (count < sc->sc_count) { 323 phys2 = kvtophys(virt + count); 324 if (phys2 != (phys + count)) { 325 326 /* No longer contig, ship it 327 */ 328 break; 329 } 330 count += PAGE_SIZE; 331 } 332 333 /* Trim if we went too far 334 */ 335 if (count > sc->sc_count) 336 count = sc->sc_count; 337 338 /* Ship it 339 */ 340 DEBUG_PRINT(("icapship %" PRIxPADDR " %d\n",phys,count), DEBUG_XFERS); 341 sc->sc_dp->SizeAndFlags = fl | count; 342 sc->sc_dp->BufferAddressHi32 = 0; /* BUGBUG 64bit */ 343 sc->sc_dp->BufferAddressLo32 = phys; /* this pushes the fifo */ 344 345 /* Adjust pointers and continue 346 */ 347 sc->sc_data += count; 348 sc->sc_count -= count; 349 350 if (sc->sc_count <= 0) 351 break; 352 } 353 } 354 355 /* 356 * Interrupt handler 357 */ 358 static int 359 icap_ebus_intr(void *cookie, void *f) 360 { 361 struct icap_softc *sc = cookie; 362 struct buf *bp = sc->sc_bp; 363 u_int32_t isr, saf = 0, hi, lo; 364 365 isr = sc->sc_dp->Control; 366 367 DEBUG_PRINT(("i %x\n",isr), DEBUG_INTR); 368 369 /* Make sure there is an interrupt and that we should take it 370 */ 371 if ((isr & (ICAPC_INTEN|ICAPC_DONE)) != (ICAPC_INTEN|ICAPC_DONE)) 372 return (0); 373 374 /* Pull out all completed buffers 375 */ 376 while ((isr & ICAPC_OF_EMPTY) == 0) { 377 378 if (isr & ICAPC_ERROR) { 379 printf("%s: internal error (%x)\n", device_xname(sc->sc_dev),isr); 380 icap_reset(sc); 381 if (bp) { 382 bp->b_error = EIO; 383 icapstart(sc); 384 } 385 return (1); 386 } 387 388 /* Beware, order matters */ 389 saf = sc->sc_dp->SizeAndFlags; 390 hi = sc->sc_dp->BufferAddressHi32; /* BUGBUG 64bit */ 391 lo = sc->sc_dp->BufferAddressLo32; /* this pops the fifo */ 392 __USE(hi); 393 __USE(lo); 394 395 /* Say its done that much (and sanity) 396 */ 397 if (bp) { 398 size_t count = saf & ICAPS_S_MASK; 399 /* more sanity */ 400 if (count > bp->b_resid) 401 count = bp->b_resid; 402 bp->b_resid -= count; 403 } 404 405 /* More? */ 406 isr = sc->sc_dp->Control; 407 } 408 409 /* Did we pop at least one */ 410 if (saf) 411 icapstart(sc); 412 413 return (1); 414 } 415 416 /* 417 * HW (re)Initialization 418 */ 419 static void 420 icap_reset(struct icap_softc *sc) 421 { 422 DEBUG_PRINT(("icap_reset %x\n",sc->sc_dp->Control), DEBUG_STATUS); 423 sc->sc_dp->Control = ICAPC_RESET; 424 DELAY(2); 425 sc->sc_dp->Control = ICAPC_INTEN; 426 } 427