1 /* $NetBSD: icap_ebus.c,v 1.5 2014/03/16 05:20:23 dholland 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.5 2014/03/16 05:20:23 dholland 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_flag = 0 163 }; 164 165 /* 166 * Handle an open request on the device. 167 */ 168 static int 169 icapopen(dev_t device, int flags, int fmt, struct lwp *process) 170 { 171 struct icap_softc *sc; 172 173 DEBUG_PRINT(("icapopen\n"), DEBUG_FUNCS); 174 sc = device_lookup_private(&icap_cd, minor(device)); 175 if (sc == NULL) 176 return (ENXIO); 177 178 return 0; 179 } 180 181 /* 182 * Handle the close request for the device. 183 */ 184 static int 185 icapclose(dev_t device, int flags, int fmt, struct lwp *process) 186 { 187 DEBUG_PRINT(("icapclose\n"), DEBUG_FUNCS); 188 return 0; /* this always succeeds */ 189 } 190 191 /* 192 * Handle the read request for the device. 193 */ 194 static int 195 icapread(dev_t dev, struct uio *uio, int flags) 196 { 197 DEBUG_PRINT(("icapread\n"), DEBUG_READS); 198 return (physio(icapstrategy, NULL, dev, B_READ, minphys, uio)); 199 } 200 201 /* 202 * Handle the write request for the device. 203 */ 204 static int 205 icapwrite(dev_t dev, struct uio *uio, int flags) 206 { 207 DEBUG_PRINT(("icapwrite\n"), DEBUG_WRITES); 208 return (physio(icapstrategy, NULL, dev, B_WRITE, minphys, uio)); 209 } 210 211 /* 212 * Handle the ioctl request for the device. 213 */ 214 static int 215 icapioctl(dev_t dev, u_long xfer, void *addr, int flag, struct lwp *l) 216 { 217 218 return ENOTTY; 219 } 220 221 /* 222 * Strategy function for the device. 223 */ 224 static void 225 icapstrategy(struct buf *bp) 226 { 227 struct icap_softc *sc; 228 int s; 229 230 DEBUG_PRINT(("icapstrategy\n"), DEBUG_FUNCS); 231 232 /* We did nothing lest we did */ 233 bp->b_resid = bp->b_bcount; 234 235 /* Do we know you. */ 236 sc = device_lookup_private(&icap_cd, minor(bp->b_dev)); 237 if (sc == NULL) { 238 DEBUG_PRINT(("icapstrategy: nodev %" PRIx64 "\n",bp->b_dev), 239 DEBUG_ERRORS); 240 bp->b_error = ENXIO; 241 biodone(bp); 242 return; 243 } 244 245 /* Add to Q. If Q was empty get it started */ 246 s = splbio(); 247 bufq_put(sc->sc_buflist, bp); 248 if (bufq_peek(sc->sc_buflist) == bp) { 249 icapstart(sc); 250 } 251 splx(s); 252 } 253 254 /* 255 * Get the next I/O request started 256 */ 257 static void 258 icapstart(struct icap_softc *sc) 259 { 260 paddr_t phys, phys2; 261 vaddr_t virt; 262 size_t count; 263 uint32_t fl; 264 struct buf *bp = sc->sc_bp; 265 266 DEBUG_PRINT(("icapstart %p %p\n",sc,bp), DEBUG_FUNCS); 267 268 /* Were we idle? 269 */ 270 recheck: 271 if (bp == NULL) { 272 273 /* Yes, get the next request if any 274 */ 275 bp = bufq_get(sc->sc_buflist); 276 DEBUG_PRINT(("icapnext: %p\n",bp), DEBUG_XFERS); 277 if (bp == NULL) 278 return; 279 } 280 281 /* Done with this request? 282 */ 283 if ((bp->b_resid == 0) || bp->b_error) { 284 285 /* Yes, complete and move to next, if any 286 */ 287 sc->sc_bp = NULL; 288 biodone(bp); 289 DEBUG_PRINT(("icapdone %p\n",bp), DEBUG_XFERS); 290 bp = NULL; 291 goto recheck; 292 } 293 294 /* If new request init the xfer info 295 */ 296 if (sc->sc_bp == NULL) { 297 sc->sc_bp = bp; 298 sc->sc_data = bp->b_data; 299 sc->sc_count = bp->b_resid; 300 } 301 302 /* Loop filling as many buffers as will fit in the FIFO 303 */ 304 fl = (bp->b_flags & B_READ) ? ICAPS_F_RECV : ICAPS_F_XMIT; 305 for (;;) { 306 307 /* Make sure there's still room in the FIFO, no errors. 308 */ 309 if (sc->sc_dp->Control & (ICAPC_IF_FULL|ICAPC_ERROR)) 310 break; 311 312 /* How much data do we xfer and where 313 */ 314 virt = (vaddr_t)sc->sc_data; 315 phys = kvtophys(virt); 316 count = round_page(virt) - virt; 317 if (count == 0) count = PAGE_SIZE;/* could(will) be aligned */ 318 319 /* How much of it is contiguous 320 */ 321 while (count < sc->sc_count) { 322 phys2 = kvtophys(virt + count); 323 if (phys2 != (phys + count)) { 324 325 /* No longer contig, ship it 326 */ 327 break; 328 } 329 count += PAGE_SIZE; 330 } 331 332 /* Trim if we went too far 333 */ 334 if (count > sc->sc_count) 335 count = sc->sc_count; 336 337 /* Ship it 338 */ 339 DEBUG_PRINT(("icapship %" PRIxPADDR " %d\n",phys,count), DEBUG_XFERS); 340 sc->sc_dp->SizeAndFlags = fl | count; 341 sc->sc_dp->BufferAddressHi32 = 0; /* BUGBUG 64bit */ 342 sc->sc_dp->BufferAddressLo32 = phys; /* this pushes the fifo */ 343 344 /* Adjust pointers and continue 345 */ 346 sc->sc_data += count; 347 sc->sc_count -= count; 348 349 if (sc->sc_count <= 0) 350 break; 351 } 352 } 353 354 /* 355 * Interrupt handler 356 */ 357 static int 358 icap_ebus_intr(void *cookie, void *f) 359 { 360 struct icap_softc *sc = cookie; 361 struct buf *bp = sc->sc_bp; 362 u_int32_t isr, saf = 0, hi, lo; 363 364 isr = sc->sc_dp->Control; 365 366 DEBUG_PRINT(("i %x\n",isr), DEBUG_INTR); 367 368 /* Make sure there is an interrupt and that we should take it 369 */ 370 if ((isr & (ICAPC_INTEN|ICAPC_DONE)) != (ICAPC_INTEN|ICAPC_DONE)) 371 return (0); 372 373 /* Pull out all completed buffers 374 */ 375 while ((isr & ICAPC_OF_EMPTY) == 0) { 376 377 if (isr & ICAPC_ERROR) { 378 printf("%s: internal error (%x)\n", device_xname(sc->sc_dev),isr); 379 icap_reset(sc); 380 if (bp) { 381 bp->b_error = EIO; 382 icapstart(sc); 383 } 384 return (1); 385 } 386 387 /* Beware, order matters */ 388 saf = sc->sc_dp->SizeAndFlags; 389 hi = sc->sc_dp->BufferAddressHi32; /* BUGBUG 64bit */ 390 lo = sc->sc_dp->BufferAddressLo32; /* this pops the fifo */ 391 __USE(hi); 392 __USE(lo); 393 394 /* Say its done that much (and sanity) 395 */ 396 if (bp) { 397 size_t count = saf & ICAPS_S_MASK; 398 /* more sanity */ 399 if (count > bp->b_resid) 400 count = bp->b_resid; 401 bp->b_resid -= count; 402 } 403 404 /* More? */ 405 isr = sc->sc_dp->Control; 406 } 407 408 /* Did we pop at least one */ 409 if (saf) 410 icapstart(sc); 411 412 return (1); 413 } 414 415 /* 416 * HW (re)Initialization 417 */ 418 static void 419 icap_reset(struct icap_softc *sc) 420 { 421 DEBUG_PRINT(("icap_reset %x\n",sc->sc_dp->Control), DEBUG_STATUS); 422 sc->sc_dp->Control = ICAPC_RESET; 423 DELAY(2); 424 sc->sc_dp->Control = ICAPC_INTEN; 425 } 426