1189251Ssam/*- 2189251Ssam * Copyright (c) 2000-2004 3189251Ssam * Diomidis D. Spinellis, Athens, Greece 4189251Ssam * All rights reserved. 5189251Ssam * 6189251Ssam * Redistribution and use in source and binary forms, with or without 7189251Ssam * modification, are permitted provided that the following conditions 8189251Ssam * are met: 9189251Ssam * 1. Redistributions of source code must retain the above copyright 10189251Ssam * notice, this list of conditions and the following disclaimer as 11189251Ssam * the first lines of this file unmodified. 12189251Ssam * 2. Redistributions in binary form must reproduce the above copyright 13189251Ssam * notice, this list of conditions and the following disclaimer in the 14189251Ssam * documentation and/or other materials provided with the distribution. 15189251Ssam * 16189251Ssam * THIS SOFTWARE IS PROVIDED BY Diomidis D. Spinellis ``AS IS'' AND ANY 17189251Ssam * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18189251Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19189251Ssam * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Diomidis D. Spinellis BE 20189251Ssam * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21189251Ssam * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22189251Ssam * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 23189251Ssam * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24189251Ssam * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25189251Ssam * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 26189251Ssam * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27189251Ssam * 28189251Ssam * $Id: pbio.c,v 1.12 2003/10/11 13:05:08 dds Exp dds $ 29189251Ssam */ 30189251Ssam 31189251Ssam#include <sys/cdefs.h> 32189251Ssam__FBSDID("$FreeBSD: releng/10.2/sys/dev/pbio/pbio.c 183397 2008-09-27 08:51:18Z ed $"); 33189251Ssam 34189251Ssam#include <sys/param.h> 35189251Ssam#include <sys/systm.h> 36189251Ssam#include <sys/kernel.h> /* SYSINIT stuff */ 37189251Ssam#include <sys/bus.h> 38189251Ssam#include <sys/resource.h> 39189251Ssam#include <sys/syslog.h> 40189251Ssam#include <sys/sysctl.h> 41189251Ssam#include <sys/conf.h> /* cdevsw stuff */ 42189251Ssam#include <sys/malloc.h> /* malloc region definitions */ 43189251Ssam#include <sys/module.h> 44189251Ssam#include <machine/bus.h> 45189251Ssam#include <machine/resource.h> 46189251Ssam#include <sys/rman.h> 47189251Ssam#include <dev/pbio/pbioio.h> /* pbio IOCTL definitions */ 48189251Ssam#include <sys/uio.h> 49189251Ssam#include <sys/fcntl.h> 50189251Ssam#include <isa/isavar.h> 51189251Ssam 52189251Ssam/* Function prototypes (these should all be static) */ 53189251Ssamstatic d_open_t pbioopen; 54189251Ssamstatic d_close_t pbioclose; 55189251Ssamstatic d_read_t pbioread; 56189251Ssamstatic d_write_t pbiowrite; 57189251Ssamstatic d_ioctl_t pbioioctl; 58189251Ssamstatic d_poll_t pbiopoll; 59189251Ssamstatic int pbioprobe(device_t); 60189251Ssamstatic int pbioattach(device_t); 61189251Ssam 62189251Ssam/* Device registers */ 63189251Ssam#define PBIO_PORTA 0 64189251Ssam#define PBIO_PORTB 1 65189251Ssam#define PBIO_PORTC 2 66189251Ssam#define PBIO_CFG 3 67189251Ssam#define PBIO_IOSIZE 4 68189251Ssam 69189251Ssam/* Per-port buffer size */ 70189251Ssam#define PBIO_BUFSIZ 64 71189251Ssam 72189251Ssam/* Number of /dev entries */ 73189251Ssam#define PBIO_NPORTS 4 74189251Ssam 75189251Ssam/* I/O port range */ 76189251Ssam#define IO_PBIOSIZE 4 77189251Ssam 78189251Ssamstatic char *port_names[] = {"a", "b", "ch", "cl"}; 79189251Ssam 80189251Ssam#define PBIO_PNAME(n) (port_names[(n)]) 81189251Ssam 82189251Ssam#define UNIT(dev) (dev2unit(dev) >> 2) 83189251Ssam#define PORT(dev) (dev2unit(dev) & 0x3) 84189251Ssam 85189251Ssam#define PBIOPRI ((PZERO + 5) | PCATCH) 86189251Ssam 87189251Ssamstatic struct cdevsw pbio_cdevsw = { 88189251Ssam .d_version = D_VERSION, 89189251Ssam .d_flags = D_NEEDGIANT, 90189251Ssam .d_open = pbioopen, 91189251Ssam .d_close = pbioclose, 92189251Ssam .d_read = pbioread, 93189251Ssam .d_write = pbiowrite, 94189251Ssam .d_ioctl = pbioioctl, 95189251Ssam .d_poll = pbiopoll, 96189251Ssam .d_name = "pbio" 97189251Ssam}; 98189251Ssam 99189251Ssam/* 100189251Ssam * Data specific to each I/O port 101189251Ssam */ 102189251Ssamstruct portdata { 103189251Ssam struct cdev *port; 104189251Ssam int diff; /* When true read only differences */ 105189251Ssam int ipace; /* Input pace */ 106189251Ssam int opace; /* Output pace */ 107189251Ssam char oldval; /* Last value read */ 108189251Ssam char buff[PBIO_BUFSIZ]; /* Per-port data buffer */ 109189251Ssam}; 110189251Ssam 111189251Ssam/* 112189251Ssam * One of these per allocated device 113189251Ssam */ 114189251Ssamstruct pbio_softc { 115189251Ssam struct portdata pd[PBIO_NPORTS];/* Per port data */ 116189251Ssam int iomode; /* Virtualized I/O mode port value */ 117189251Ssam /* The real port is write-only */ 118189251Ssam struct resource *res; 119189251Ssam bus_space_tag_t bst; 120189251Ssam bus_space_handle_t bsh; 121189251Ssam}; 122189251Ssam 123189251Ssamtypedef struct pbio_softc *sc_p; 124189251Ssam 125189251Ssamstatic device_method_t pbio_methods[] = { 126189251Ssam /* Device interface */ 127189251Ssam DEVMETHOD(device_probe, pbioprobe), 128189251Ssam DEVMETHOD(device_attach, pbioattach), 129189251Ssam { 0, 0 } 130189251Ssam}; 131189251Ssam 132189251Ssamstatic devclass_t pbio_devclass; 133189251Ssam#define pbio_addr(unit) \ 134189251Ssam ((struct pbio_softc *) devclass_get_softc(pbio_devclass, unit)) 135189251Ssam 136189251Ssamstatic char driver_name[] = "pbio"; 137189251Ssam 138189251Ssamstatic driver_t pbio_driver = { 139189251Ssam driver_name, 140189251Ssam pbio_methods, 141189251Ssam sizeof(struct pbio_softc), 142189251Ssam}; 143189251Ssam 144189251SsamDRIVER_MODULE(pbio, isa, pbio_driver, pbio_devclass, 0, 0); 145189251Ssam 146189251Ssamstatic __inline uint8_t 147189251Ssampbinb(struct pbio_softc *scp, int off) 148189251Ssam{ 149189251Ssam 150189251Ssam return bus_space_read_1(scp->bst, scp->bsh, off); 151189251Ssam} 152189251Ssam 153189251Ssamstatic __inline void 154189251Ssampboutb(struct pbio_softc *scp, int off, uint8_t val) 155189251Ssam{ 156189251Ssam 157189251Ssam bus_space_write_1(scp->bst, scp->bsh, off, val); 158189251Ssam} 159189251Ssam 160189251Ssam 161189251Ssamstatic int 162189251Ssampbioprobe(device_t dev) 163189251Ssam{ 164189251Ssam int rid; 165189251Ssam struct pbio_softc *scp = device_get_softc(dev); 166189251Ssam#ifdef GENERIC_PBIO_PROBE 167189251Ssam unsigned char val; 168189251Ssam#endif 169189251Ssam 170189251Ssam if (isa_get_logicalid(dev)) /* skip PnP probes */ 171189251Ssam return (ENXIO); 172189251Ssam rid = 0; 173189251Ssam scp->res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 174189251Ssam 0, ~0, IO_PBIOSIZE, RF_ACTIVE); 175189251Ssam if (scp->res == NULL) 176189251Ssam return (ENXIO); 177189251Ssam 178189251Ssam#ifdef GENERIC_PBIO_PROBE 179189251Ssam scp->bst = rman_get_bustag(scp->res); 180189251Ssam scp->bsh = rman_get_bushandle(scp->res); 181189251Ssam /* 182189251Ssam * try see if the device is there. 183189251Ssam * This probe works only if the device has no I/O attached to it 184189251Ssam * XXX Better allow flags to abort testing 185189251Ssam */ 186189251Ssam /* Set all ports to output */ 187189251Ssam pboutb(scp, PBIO_CFG, 0x80); 188189251Ssam printf("pbio val(CFG: 0x%03x)=0x%02x (should be 0x80)\n", 189189251Ssam rman_get_start(scp->res), pbinb(scp, PBIO_CFG)); 190189251Ssam pboutb(scp, PBIO_PORTA, 0xa5); 191189251Ssam val = pbinb(scp, PBIO_PORTA); 192189251Ssam printf("pbio val=0x%02x (should be 0xa5)\n", val); 193189251Ssam if (val != 0xa5) { 194189251Ssam bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->res); 195189251Ssam return (ENXIO); 196189251Ssam } 197189251Ssam pboutb(scp, PBIO_PORTA, 0x5a); 198189251Ssam val = pbinb(scp, PBIO_PORTA); 199189251Ssam printf("pbio val=0x%02x (should be 0x5a)\n", val); 200189251Ssam if (val != 0x5a) { 201189251Ssam bus_release_resource(dev, SYS_RES_IOPORT, rid, sc->res); 202189251Ssam return (ENXIO); 203189251Ssam } 204189251Ssam#endif 205189251Ssam device_set_desc(dev, "Intel 8255 PPI (basic mode)"); 206189251Ssam /* Set all ports to input */ 207189251Ssam /* pboutb(scp, PBIO_CFG, 0x9b); */ 208189251Ssam bus_release_resource(dev, SYS_RES_IOPORT, rid, scp->res); 209189251Ssam return (0); 210189251Ssam} 211189251Ssam 212189251Ssam/* 213189251Ssam * Called if the probe succeeded. 214189251Ssam * We can be destructive here as we know we have the device. 215189251Ssam * we can also trust the unit number. 216189251Ssam */ 217189251Ssamstatic int 218189251Ssampbioattach (device_t dev) 219189251Ssam{ 220189251Ssam int unit; 221189251Ssam int i; 222189251Ssam int rid; 223189251Ssam struct pbio_softc *sc; 224189251Ssam 225189251Ssam sc = device_get_softc(dev); 226189251Ssam unit = device_get_unit(dev); 227189251Ssam rid = 0; 228189251Ssam sc->res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 229189251Ssam 0, ~0, IO_PBIOSIZE, RF_ACTIVE); 230189251Ssam if (sc->res == NULL) 231189251Ssam return (ENXIO); 232189251Ssam sc->bst = rman_get_bustag(sc->res); 233189251Ssam sc->bsh = rman_get_bushandle(sc->res); 234189251Ssam 235189251Ssam /* 236189251Ssam * Store whatever seems wise. 237189251Ssam */ 238189251Ssam sc->iomode = 0x9b; /* All ports to input */ 239189251Ssam 240189251Ssam for (i = 0; i < PBIO_NPORTS; i++) 241189251Ssam sc->pd[i].port = make_dev(&pbio_cdevsw, (unit << 2) + i, 0, 0, 242189251Ssam 0600, "pbio%d%s", unit, PBIO_PNAME(i)); 243189251Ssam return (0); 244189251Ssam} 245189251Ssam 246189251Ssamstatic int 247189251Ssampbioioctl (struct cdev *dev, u_long cmd, caddr_t data, int flag, 248189251Ssam struct thread *td) 249189251Ssam{ 250189251Ssam struct pbio_softc *scp; 251189251Ssam int port, unit; 252189251Ssam 253189251Ssam unit = UNIT(dev); 254189251Ssam port = PORT(dev); 255189251Ssam scp = pbio_addr(unit); 256189251Ssam if (scp == NULL) 257189251Ssam return (ENODEV); 258189251Ssam switch (cmd) { 259189251Ssam case PBIO_SETDIFF: 260189251Ssam scp->pd[port].diff = *(int *)data; 261189251Ssam break; 262189251Ssam case PBIO_SETIPACE: 263189251Ssam scp->pd[port].ipace = *(int *)data; 264189251Ssam break; 265189251Ssam case PBIO_SETOPACE: 266189251Ssam scp->pd[port].opace = *(int *)data; 267189251Ssam break; 268189251Ssam case PBIO_GETDIFF: 269189251Ssam *(int *)data = scp->pd[port].diff; 270189251Ssam break; 271189251Ssam case PBIO_GETIPACE: 272189251Ssam *(int *)data = scp->pd[port].ipace; 273189251Ssam break; 274189251Ssam case PBIO_GETOPACE: 275189251Ssam *(int *)data = scp->pd[port].opace; 276189251Ssam break; 277189251Ssam default: 278189251Ssam return ENXIO; 279189251Ssam } 280189251Ssam return (0); 281189251Ssam} 282189251Ssam 283189251Ssamstatic int 284189251Ssampbioopen(struct cdev *dev, int oflags, int devtype, struct thread *td) 285189251Ssam{ 286189251Ssam struct pbio_softc *scp; 287189251Ssam int ocfg, port, unit; 288189251Ssam int portbit; /* Port configuration bit */ 289189251Ssam 290189251Ssam unit = UNIT(dev); 291189251Ssam port = PORT(dev); 292189251Ssam scp = pbio_addr(unit); 293189251Ssam if (scp == NULL) 294189251Ssam return (ENODEV); 295189251Ssam 296189251Ssam switch (port) { 297189251Ssam case 0: portbit = 0x10; break; /* Port A */ 298189251Ssam case 1: portbit = 0x02; break; /* Port B */ 299189251Ssam case 2: portbit = 0x08; break; /* Port CH */ 300189251Ssam case 3: portbit = 0x01; break; /* Port CL */ 301189251Ssam default: return (ENODEV); 302189251Ssam } 303189251Ssam ocfg = scp->iomode; 304189251Ssam 305189251Ssam if (oflags & FWRITE) 306189251Ssam /* Writing == output; zero the bit */ 307189251Ssam pboutb(scp, PBIO_CFG, scp->iomode = (ocfg & (~portbit))); 308189251Ssam else if (oflags & FREAD) 309189251Ssam /* Reading == input; set the bit */ 310189251Ssam pboutb(scp, PBIO_CFG, scp->iomode = (ocfg | portbit)); 311189251Ssam else 312189251Ssam return (EACCES); 313189251Ssam 314189251Ssam return (0); 315189251Ssam} 316189251Ssam 317189251Ssamstatic int 318189251Ssampbioclose(struct cdev *dev, int fflag, int devtype, struct thread *td) 319189251Ssam{ 320189251Ssam struct pbio_softc *scp; 321189251Ssam int unit; 322189251Ssam 323189251Ssam unit = UNIT(dev); 324189251Ssam scp = pbio_addr(unit); 325189251Ssam if (scp == NULL) 326189251Ssam return (ENODEV); 327189251Ssam 328189251Ssam return (0); 329189251Ssam} 330189251Ssam 331189251Ssam/* 332189251Ssam * Return the value of a given port on a given I/O base address 333189251Ssam * Handles the split C port nibbles and blocking 334189251Ssam */ 335189251Ssamstatic int 336189251Ssamportval(int port, struct pbio_softc *scp, char *val) 337189251Ssam{ 338189251Ssam int err; 339189251Ssam 340189251Ssam for (;;) { 341189251Ssam switch (port) { 342189251Ssam case 0: 343189251Ssam *val = pbinb(scp, PBIO_PORTA); 344189251Ssam break; 345189251Ssam case 1: 346189251Ssam *val = pbinb(scp, PBIO_PORTB); 347189251Ssam break; 348189251Ssam case 2: 349189251Ssam *val = (pbinb(scp, PBIO_PORTC) >> 4) & 0xf; 350189251Ssam break; 351189251Ssam case 3: 352189251Ssam *val = pbinb(scp, PBIO_PORTC) & 0xf; 353189251Ssam break; 354189251Ssam default: 355189251Ssam *val = 0; 356189251Ssam break; 357189251Ssam } 358189251Ssam if (scp->pd[port].diff) { 359189251Ssam if (*val != scp->pd[port].oldval) { 360 scp->pd[port].oldval = *val; 361 return (0); 362 } 363 err = tsleep((caddr_t)&(scp->pd[port].diff), PBIOPRI, 364 "pbiopl", max(1, scp->pd[port].ipace)); 365 if (err == EINTR) 366 return (EINTR); 367 } else 368 return (0); 369 } 370} 371 372static int 373pbioread(struct cdev *dev, struct uio *uio, int ioflag) 374{ 375 struct pbio_softc *scp; 376 int err, i, port, ret, toread, unit; 377 char val; 378 379 unit = UNIT(dev); 380 port = PORT(dev); 381 scp = pbio_addr(unit); 382 if (scp == NULL) 383 return (ENODEV); 384 385 while (uio->uio_resid > 0) { 386 toread = min(uio->uio_resid, PBIO_BUFSIZ); 387 if ((ret = uiomove(scp->pd[port].buff, toread, uio)) != 0) 388 return (ret); 389 for (i = 0; i < toread; i++) { 390 if ((err = portval(port, scp, &val)) != 0) 391 return (err); 392 scp->pd[port].buff[i] = val; 393 if (!scp->pd[port].diff && scp->pd[port].ipace) 394 tsleep((caddr_t)&(scp->pd[port].ipace), PBIOPRI, 395 "pbioip", scp->pd[port].ipace); 396 } 397 } 398 return 0; 399} 400 401static int 402pbiowrite(struct cdev *dev, struct uio *uio, int ioflag) 403{ 404 struct pbio_softc *scp; 405 int i, port, ret, towrite, unit; 406 char val, oval; 407 408 unit = UNIT(dev); 409 port = PORT(dev); 410 scp = pbio_addr(unit); 411 if (scp == NULL) 412 return (ENODEV); 413 414 while (uio->uio_resid > 0) { 415 towrite = min(uio->uio_resid, PBIO_BUFSIZ); 416 if ((ret = uiomove(scp->pd[port].buff, towrite, uio)) != 0) 417 return (ret); 418 for (i = 0; i < towrite; i++) { 419 val = scp->pd[port].buff[i]; 420 switch (port) { 421 case 0: 422 pboutb(scp, PBIO_PORTA, val); 423 break; 424 case 1: 425 pboutb(scp, PBIO_PORTB, val); 426 break; 427 case 2: 428 oval = pbinb(scp, PBIO_PORTC); 429 oval &= 0xf; 430 val <<= 4; 431 pboutb(scp, PBIO_PORTC, val | oval); 432 break; 433 case 3: 434 oval = pbinb(scp, PBIO_PORTC); 435 oval &= 0xf0; 436 val &= 0xf; 437 pboutb(scp, PBIO_PORTC, oval | val); 438 break; 439 } 440 if (scp->pd[port].opace) 441 tsleep((caddr_t)&(scp->pd[port].opace), 442 PBIOPRI, "pbioop", 443 scp->pd[port].opace); 444 } 445 } 446 return (0); 447} 448 449static int 450pbiopoll(struct cdev *dev, int which, struct thread *td) 451{ 452 struct pbio_softc *scp; 453 int unit; 454 455 unit = UNIT(dev); 456 scp = pbio_addr(unit); 457 if (scp == NULL) 458 return (ENODEV); 459 460 /* 461 * Do processing 462 */ 463 return (0); /* this is the wrong value I'm sure */ 464} 465