14e190a62SNicolas Souchu /*- 24e190a62SNicolas Souchu * Copyright (c) 1998 Nicolas Souchu, Marc Bouget 34e190a62SNicolas Souchu * All rights reserved. 44e190a62SNicolas Souchu * 54e190a62SNicolas Souchu * Redistribution and use in source and binary forms, with or without 64e190a62SNicolas Souchu * modification, are permitted provided that the following conditions 74e190a62SNicolas Souchu * are met: 84e190a62SNicolas Souchu * 1. Redistributions of source code must retain the above copyright 94e190a62SNicolas Souchu * notice, this list of conditions and the following disclaimer. 104e190a62SNicolas Souchu * 2. Redistributions in binary form must reproduce the above copyright 114e190a62SNicolas Souchu * notice, this list of conditions and the following disclaimer in the 124e190a62SNicolas Souchu * documentation and/or other materials provided with the distribution. 134e190a62SNicolas Souchu * 144e190a62SNicolas Souchu * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 154e190a62SNicolas Souchu * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 164e190a62SNicolas Souchu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 174e190a62SNicolas Souchu * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 184e190a62SNicolas Souchu * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 194e190a62SNicolas Souchu * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 204e190a62SNicolas Souchu * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 214e190a62SNicolas Souchu * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 224e190a62SNicolas Souchu * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 234e190a62SNicolas Souchu * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 244e190a62SNicolas Souchu * SUCH DAMAGE. 254e190a62SNicolas Souchu * 26c3aac50fSPeter Wemm * $FreeBSD$ 274e190a62SNicolas Souchu * 284e190a62SNicolas Souchu */ 294e190a62SNicolas Souchu #include <sys/param.h> 304e190a62SNicolas Souchu #include <sys/systm.h> 314e190a62SNicolas Souchu #include <sys/kernel.h> 324e190a62SNicolas Souchu #include <sys/module.h> 334e190a62SNicolas Souchu #include <sys/bus.h> 344e190a62SNicolas Souchu 350f210c92SNicolas Souchu #include <machine/bus.h> 360f210c92SNicolas Souchu #include <machine/resource.h> 370f210c92SNicolas Souchu #include <sys/rman.h> 380f210c92SNicolas Souchu 390f210c92SNicolas Souchu #include <isa/isareg.h> 400f210c92SNicolas Souchu #include <isa/isavar.h> 414e190a62SNicolas Souchu 424e190a62SNicolas Souchu #include <i386/isa/isa_device.h> 434e190a62SNicolas Souchu 444e190a62SNicolas Souchu #include <dev/iicbus/iiconf.h> 454e190a62SNicolas Souchu #include "iicbus_if.h" 464e190a62SNicolas Souchu 470f210c92SNicolas Souchu #define IO_PCFSIZE 2 480f210c92SNicolas Souchu 49af548787SNicolas Souchu #define TIMEOUT 9999 /* XXX */ 504e190a62SNicolas Souchu 514e190a62SNicolas Souchu /* Status bits of S1 register (read only) */ 524e190a62SNicolas Souchu #define nBB 0x01 /* busy when low set/reset by STOP/START*/ 534e190a62SNicolas Souchu #define LAB 0x02 /* lost arbitration bit in multi-master mode */ 544e190a62SNicolas Souchu #define AAS 0x04 /* addressed as slave */ 554e190a62SNicolas Souchu #define LRB 0x08 /* last received byte when not AAS */ 564e190a62SNicolas Souchu #define AD0 0x08 /* general call received when AAS */ 574e190a62SNicolas Souchu #define BER 0x10 /* bus error, misplaced START or STOP */ 584e190a62SNicolas Souchu #define STS 0x20 /* STOP detected in slave receiver mode */ 594e190a62SNicolas Souchu #define PIN 0x80 /* pending interrupt not (r/w) */ 604e190a62SNicolas Souchu 614e190a62SNicolas Souchu /* Control bits of S1 register (write only) */ 624e190a62SNicolas Souchu #define ACK 0x01 634e190a62SNicolas Souchu #define STO 0x02 644e190a62SNicolas Souchu #define STA 0x04 654e190a62SNicolas Souchu #define ENI 0x08 664e190a62SNicolas Souchu #define ES2 0x10 674e190a62SNicolas Souchu #define ES1 0x20 684e190a62SNicolas Souchu #define ES0 0x40 694e190a62SNicolas Souchu 704e190a62SNicolas Souchu #define BUFSIZE 2048 714e190a62SNicolas Souchu 724e190a62SNicolas Souchu #define SLAVE_TRANSMITTER 0x1 734e190a62SNicolas Souchu #define SLAVE_RECEIVER 0x2 744e190a62SNicolas Souchu 75af548787SNicolas Souchu #define PCF_DEFAULT_ADDR 0xaa 76af548787SNicolas Souchu 774e190a62SNicolas Souchu struct pcf_softc { 784e190a62SNicolas Souchu 794e190a62SNicolas Souchu int pcf_base; /* isa port */ 800f210c92SNicolas Souchu int pcf_flags; 81af548787SNicolas Souchu u_char pcf_addr; /* interface I2C address */ 824e190a62SNicolas Souchu 834e190a62SNicolas Souchu int pcf_slave_mode; /* receiver or transmitter */ 84af548787SNicolas Souchu int pcf_started; /* 1 if start condition sent */ 854e190a62SNicolas Souchu 864e190a62SNicolas Souchu device_t iicbus; /* the corresponding iicbus */ 874e190a62SNicolas Souchu 880f210c92SNicolas Souchu int rid_irq, rid_ioport; 890f210c92SNicolas Souchu struct resource *res_irq, *res_ioport; 900f210c92SNicolas Souchu void *intr_cookie; 914e190a62SNicolas Souchu }; 924e190a62SNicolas Souchu 934e190a62SNicolas Souchu static int pcf_probe(device_t); 944e190a62SNicolas Souchu static int pcf_attach(device_t); 950f210c92SNicolas Souchu static void pcfintr(void *arg); 960f210c92SNicolas Souchu 9715317dd8SMatthew N. Dodd static int pcf_print_child(device_t, device_t); 984e190a62SNicolas Souchu 99af548787SNicolas Souchu static int pcf_repeated_start(device_t, u_char, int); 100af548787SNicolas Souchu static int pcf_start(device_t, u_char, int); 1014e190a62SNicolas Souchu static int pcf_stop(device_t); 102e6b14871SPeter Wemm static int pcf_write(device_t, char *, int, int *, int); 103e6b14871SPeter Wemm static int pcf_read(device_t, char *, int, int *, int, int); 104e6b14871SPeter Wemm static int pcf_rst_card(device_t, u_char, u_char, u_char *); 1054e190a62SNicolas Souchu 1064e190a62SNicolas Souchu static device_method_t pcf_methods[] = { 1074e190a62SNicolas Souchu /* device interface */ 1084e190a62SNicolas Souchu DEVMETHOD(device_probe, pcf_probe), 1094e190a62SNicolas Souchu DEVMETHOD(device_attach, pcf_attach), 1104e190a62SNicolas Souchu 1114e190a62SNicolas Souchu /* bus interface */ 1124e190a62SNicolas Souchu DEVMETHOD(bus_print_child, pcf_print_child), 1134e190a62SNicolas Souchu 1144e190a62SNicolas Souchu /* iicbus interface */ 115af548787SNicolas Souchu DEVMETHOD(iicbus_callback, iicbus_null_callback), 1164e190a62SNicolas Souchu DEVMETHOD(iicbus_repeated_start, pcf_repeated_start), 1174e190a62SNicolas Souchu DEVMETHOD(iicbus_start, pcf_start), 1184e190a62SNicolas Souchu DEVMETHOD(iicbus_stop, pcf_stop), 1194e190a62SNicolas Souchu DEVMETHOD(iicbus_write, pcf_write), 1204e190a62SNicolas Souchu DEVMETHOD(iicbus_read, pcf_read), 1214e190a62SNicolas Souchu DEVMETHOD(iicbus_reset, pcf_rst_card), 1224e190a62SNicolas Souchu 1234e190a62SNicolas Souchu { 0, 0 } 1244e190a62SNicolas Souchu }; 1254e190a62SNicolas Souchu 1264e190a62SNicolas Souchu static driver_t pcf_driver = { 1274e190a62SNicolas Souchu "pcf", 1284e190a62SNicolas Souchu pcf_methods, 1294e190a62SNicolas Souchu sizeof(struct pcf_softc), 1304e190a62SNicolas Souchu }; 1314e190a62SNicolas Souchu 1324e190a62SNicolas Souchu static devclass_t pcf_devclass; 1334e190a62SNicolas Souchu 1344e190a62SNicolas Souchu #define DEVTOSOFTC(dev) ((struct pcf_softc *)device_get_softc(dev)) 1354e190a62SNicolas Souchu 1364e190a62SNicolas Souchu static int 1374e190a62SNicolas Souchu pcf_probe(device_t pcfdev) 1384e190a62SNicolas Souchu { 1390f210c92SNicolas Souchu struct pcf_softc *pcf = DEVTOSOFTC(pcfdev); 1400f210c92SNicolas Souchu device_t parent = device_get_parent(pcfdev); 141af548787SNicolas Souchu 1420f210c92SNicolas Souchu device_set_desc(pcfdev, "PCF8584 I2C bus controller"); 1430f210c92SNicolas Souchu 1440f210c92SNicolas Souchu pcf = DEVTOSOFTC(pcfdev); 1450f210c92SNicolas Souchu bzero(pcf, sizeof(struct pcf_softc)); 1460f210c92SNicolas Souchu 1470f210c92SNicolas Souchu pcf->rid_irq = pcf->rid_ioport = 0; 1480f210c92SNicolas Souchu pcf->res_irq = pcf->res_ioport = 0; 1490f210c92SNicolas Souchu 1500f210c92SNicolas Souchu /* IO port is mandatory */ 1510f210c92SNicolas Souchu pcf->res_ioport = bus_alloc_resource(pcfdev, SYS_RES_IOPORT, 1520f210c92SNicolas Souchu &pcf->rid_ioport, 0ul, ~0ul, 1530f210c92SNicolas Souchu IO_PCFSIZE, RF_ACTIVE); 1540f210c92SNicolas Souchu if (pcf->res_ioport == 0) { 1550f210c92SNicolas Souchu device_printf(pcfdev, "cannot reserve I/O port range\n"); 1560f210c92SNicolas Souchu goto error; 1570f210c92SNicolas Souchu } 1580f210c92SNicolas Souchu BUS_READ_IVAR(parent, pcfdev, ISA_IVAR_PORT, &pcf->pcf_base); 1590f210c92SNicolas Souchu 1600f210c92SNicolas Souchu pcf->pcf_flags = device_get_flags(pcfdev); 1610f210c92SNicolas Souchu 1620f210c92SNicolas Souchu if (!(pcf->pcf_flags & IIC_POLLED)) { 1630f210c92SNicolas Souchu pcf->res_irq = bus_alloc_resource(pcfdev, SYS_RES_IRQ, &pcf->rid_irq, 1640f210c92SNicolas Souchu 0ul, ~0ul, 1, RF_ACTIVE); 1650f210c92SNicolas Souchu if (pcf->res_irq == 0) { 1660f210c92SNicolas Souchu device_printf(pcfdev, "can't reserve irq, polled mode.\n"); 1670f210c92SNicolas Souchu pcf->pcf_flags |= IIC_POLLED; 1680f210c92SNicolas Souchu } 1690f210c92SNicolas Souchu } 170af548787SNicolas Souchu 171af548787SNicolas Souchu /* reset the chip */ 172af548787SNicolas Souchu pcf_rst_card(pcfdev, IIC_FASTEST, PCF_DEFAULT_ADDR, NULL); 1734e190a62SNicolas Souchu 1744e190a62SNicolas Souchu return (0); 1750f210c92SNicolas Souchu error: 1760f210c92SNicolas Souchu if (pcf->res_ioport != 0) { 1770f210c92SNicolas Souchu bus_deactivate_resource(pcfdev, SYS_RES_IOPORT, pcf->rid_ioport, 1780f210c92SNicolas Souchu pcf->res_ioport); 1790f210c92SNicolas Souchu bus_release_resource(pcfdev, SYS_RES_IOPORT, pcf->rid_ioport, 1800f210c92SNicolas Souchu pcf->res_ioport); 1810f210c92SNicolas Souchu } 1820f210c92SNicolas Souchu return (ENXIO); 1834e190a62SNicolas Souchu } 1844e190a62SNicolas Souchu 1854e190a62SNicolas Souchu static int 1864e190a62SNicolas Souchu pcf_attach(device_t pcfdev) 1874e190a62SNicolas Souchu { 1880f210c92SNicolas Souchu struct pcf_softc *pcf = DEVTOSOFTC(pcfdev); 1890f210c92SNicolas Souchu device_t parent = device_get_parent(pcfdev); 1900f210c92SNicolas Souchu int error = 0; 1910f210c92SNicolas Souchu 1920f210c92SNicolas Souchu if (pcf->res_irq) { 1930f210c92SNicolas Souchu /* default to the tty mask for registration */ /* XXX */ 1940f210c92SNicolas Souchu error = BUS_SETUP_INTR(parent, pcfdev, pcf->res_irq, INTR_TYPE_NET, 1950f210c92SNicolas Souchu pcfintr, pcfdev, &pcf->intr_cookie); 1960f210c92SNicolas Souchu if (error) 1970f210c92SNicolas Souchu return (error); 1980f210c92SNicolas Souchu } 1994e190a62SNicolas Souchu 200ea4122d2SNicolas Souchu pcf->iicbus = device_add_child(pcfdev, "iicbus", -1); 201af548787SNicolas Souchu 2024e190a62SNicolas Souchu /* probe and attach the iicbus */ 203ea4122d2SNicolas Souchu bus_generic_attach(pcfdev); 2044e190a62SNicolas Souchu 2054e190a62SNicolas Souchu return (0); 2064e190a62SNicolas Souchu } 2074e190a62SNicolas Souchu 20815317dd8SMatthew N. Dodd static int 2094e190a62SNicolas Souchu pcf_print_child(device_t bus, device_t dev) 2104e190a62SNicolas Souchu { 211af548787SNicolas Souchu struct pcf_softc *pcf = (struct pcf_softc *)device_get_softc(bus); 21215317dd8SMatthew N. Dodd int retval = 0; 213af548787SNicolas Souchu 21415317dd8SMatthew N. Dodd retval += bus_print_child_header(bus, dev); 21515317dd8SMatthew N. Dodd retval += printf(" on %s addr 0x%x\n", device_get_nameunit(bus), 21615317dd8SMatthew N. Dodd (int)pcf->pcf_addr); 2174e190a62SNicolas Souchu 21815317dd8SMatthew N. Dodd return (retval); 2194e190a62SNicolas Souchu } 2204e190a62SNicolas Souchu 2214e190a62SNicolas Souchu /* 2224e190a62SNicolas Souchu * PCF8584 datasheet : when operate at 8 MHz or more, a minimun time of 2234e190a62SNicolas Souchu * 6 clocks cycles must be left between two consecutives access 2244e190a62SNicolas Souchu */ 2254e190a62SNicolas Souchu #define pcf_nops() DELAY(10) 2264e190a62SNicolas Souchu 2274e190a62SNicolas Souchu #define dummy_read(pcf) PCF_GET_S0(pcf) 2284e190a62SNicolas Souchu #define dummy_write(pcf) PCF_SET_S0(pcf, 0) 2294e190a62SNicolas Souchu 2304e190a62SNicolas Souchu /* 2314e190a62SNicolas Souchu * Specific register access to PCF8584 2324e190a62SNicolas Souchu */ 2334e190a62SNicolas Souchu static void PCF_SET_S0(struct pcf_softc *pcf, int data) 2344e190a62SNicolas Souchu { 2354e190a62SNicolas Souchu outb(pcf->pcf_base, data); 2364e190a62SNicolas Souchu pcf_nops(); 2374e190a62SNicolas Souchu } 2384e190a62SNicolas Souchu 2394e190a62SNicolas Souchu static void PCF_SET_S1(struct pcf_softc *pcf, int data) 2404e190a62SNicolas Souchu { 2414e190a62SNicolas Souchu outb(pcf->pcf_base+1, data); 2424e190a62SNicolas Souchu pcf_nops(); 2434e190a62SNicolas Souchu } 2444e190a62SNicolas Souchu 2454e190a62SNicolas Souchu static char PCF_GET_S0(struct pcf_softc *pcf) 2464e190a62SNicolas Souchu { 2474e190a62SNicolas Souchu char data; 2484e190a62SNicolas Souchu 2494e190a62SNicolas Souchu data = inb(pcf->pcf_base); 2504e190a62SNicolas Souchu pcf_nops(); 2514e190a62SNicolas Souchu 2524e190a62SNicolas Souchu return (data); 2534e190a62SNicolas Souchu } 2544e190a62SNicolas Souchu 2554e190a62SNicolas Souchu static char PCF_GET_S1(struct pcf_softc *pcf) 2564e190a62SNicolas Souchu { 2574e190a62SNicolas Souchu char data; 2584e190a62SNicolas Souchu 2594e190a62SNicolas Souchu data = inb(pcf->pcf_base+1); 2604e190a62SNicolas Souchu pcf_nops(); 2614e190a62SNicolas Souchu 2624e190a62SNicolas Souchu return (data); 2634e190a62SNicolas Souchu } 2644e190a62SNicolas Souchu 2654e190a62SNicolas Souchu /* 2664e190a62SNicolas Souchu * Polling mode for master operations wait for a new 2674e190a62SNicolas Souchu * byte incomming or outgoing 2684e190a62SNicolas Souchu */ 2694e190a62SNicolas Souchu static int pcf_wait_byte(struct pcf_softc *pcf) 2704e190a62SNicolas Souchu { 2714e190a62SNicolas Souchu int counter = TIMEOUT; 2724e190a62SNicolas Souchu 2734e190a62SNicolas Souchu while (counter--) { 2744e190a62SNicolas Souchu 2754e190a62SNicolas Souchu if ((PCF_GET_S1(pcf) & PIN) == 0) 2764e190a62SNicolas Souchu return (0); 2774e190a62SNicolas Souchu } 2784e190a62SNicolas Souchu 2794e190a62SNicolas Souchu return (IIC_ETIMEOUT); 2804e190a62SNicolas Souchu } 2814e190a62SNicolas Souchu 2824e190a62SNicolas Souchu static int pcf_stop(device_t pcfdev) 2834e190a62SNicolas Souchu { 2844e190a62SNicolas Souchu struct pcf_softc *pcf = DEVTOSOFTC(pcfdev); 2854e190a62SNicolas Souchu 286af548787SNicolas Souchu /* 287af548787SNicolas Souchu * Send STOP condition iff the START condition was previously sent. 2889d5abbddSJens Schweikhardt * STOP is sent only once even if an iicbus_stop() is called after 289af548787SNicolas Souchu * an iicbus_read()... see pcf_read(): the pcf needs to send the stop 290af548787SNicolas Souchu * before the last char is read. 291af548787SNicolas Souchu */ 292af548787SNicolas Souchu if (pcf->pcf_started) { 2934e190a62SNicolas Souchu /* set stop condition and enable IT */ 2944e190a62SNicolas Souchu PCF_SET_S1(pcf, PIN|ES0|ENI|STO|ACK); 2954e190a62SNicolas Souchu 296af548787SNicolas Souchu pcf->pcf_started = 0; 297af548787SNicolas Souchu } 298af548787SNicolas Souchu 2994e190a62SNicolas Souchu return (0); 3004e190a62SNicolas Souchu } 3014e190a62SNicolas Souchu 302af548787SNicolas Souchu 303af548787SNicolas Souchu static int pcf_noack(struct pcf_softc *pcf, int timeout) 304af548787SNicolas Souchu { 305af548787SNicolas Souchu int noack; 306af548787SNicolas Souchu int k = timeout/10; 307af548787SNicolas Souchu 308af548787SNicolas Souchu do { 309af548787SNicolas Souchu noack = PCF_GET_S1(pcf) & LRB; 310af548787SNicolas Souchu if (!noack) 311af548787SNicolas Souchu break; 312af548787SNicolas Souchu DELAY(10); /* XXX wait 10 us */ 313af548787SNicolas Souchu } while (k--); 314af548787SNicolas Souchu 315af548787SNicolas Souchu return (noack); 316af548787SNicolas Souchu } 317af548787SNicolas Souchu 318af548787SNicolas Souchu static int pcf_repeated_start(device_t pcfdev, u_char slave, int timeout) 3194e190a62SNicolas Souchu { 3204e190a62SNicolas Souchu struct pcf_softc *pcf = DEVTOSOFTC(pcfdev); 3214e190a62SNicolas Souchu int error = 0; 3224e190a62SNicolas Souchu 3234e190a62SNicolas Souchu /* repeated start */ 3244e190a62SNicolas Souchu PCF_SET_S1(pcf, ES0|STA|STO|ACK); 3254e190a62SNicolas Souchu 3264e190a62SNicolas Souchu /* set slave address to PCF. Last bit (LSB) must be set correctly 3274e190a62SNicolas Souchu * according to transfer direction */ 3284e190a62SNicolas Souchu PCF_SET_S0(pcf, slave); 3294e190a62SNicolas Souchu 3304e190a62SNicolas Souchu /* wait for address sent, polling */ 3314e190a62SNicolas Souchu if ((error = pcf_wait_byte(pcf))) 3324e190a62SNicolas Souchu goto error; 3334e190a62SNicolas Souchu 334af548787SNicolas Souchu /* check for ack */ 335af548787SNicolas Souchu if (pcf_noack(pcf, timeout)) { 3364e190a62SNicolas Souchu error = IIC_ENOACK; 3374e190a62SNicolas Souchu goto error; 3384e190a62SNicolas Souchu } 3394e190a62SNicolas Souchu 3404e190a62SNicolas Souchu return (0); 3414e190a62SNicolas Souchu 3424e190a62SNicolas Souchu error: 3434e190a62SNicolas Souchu pcf_stop(pcfdev); 3444e190a62SNicolas Souchu return (error); 3454e190a62SNicolas Souchu } 3464e190a62SNicolas Souchu 347af548787SNicolas Souchu static int pcf_start(device_t pcfdev, u_char slave, int timeout) 3484e190a62SNicolas Souchu { 3494e190a62SNicolas Souchu struct pcf_softc *pcf = DEVTOSOFTC(pcfdev); 3504e190a62SNicolas Souchu int error = 0; 3514e190a62SNicolas Souchu 352f79cfdd9SPeter Wemm if ((PCF_GET_S1(pcf) & nBB) == 0) 3534e190a62SNicolas Souchu return (IIC_EBUSBSY); 3544e190a62SNicolas Souchu 3554e190a62SNicolas Souchu /* set slave address to PCF. Last bit (LSB) must be set correctly 3564e190a62SNicolas Souchu * according to transfer direction */ 3574e190a62SNicolas Souchu PCF_SET_S0(pcf, slave); 3584e190a62SNicolas Souchu 3594e190a62SNicolas Souchu /* START only */ 3604e190a62SNicolas Souchu PCF_SET_S1(pcf, PIN|ES0|STA|ACK); 3614e190a62SNicolas Souchu 362af548787SNicolas Souchu pcf->pcf_started = 1; 363af548787SNicolas Souchu 3644e190a62SNicolas Souchu /* wait for address sent, polling */ 3654e190a62SNicolas Souchu if ((error = pcf_wait_byte(pcf))) 3664e190a62SNicolas Souchu goto error; 3674e190a62SNicolas Souchu 368af548787SNicolas Souchu /* check for ACK */ 369af548787SNicolas Souchu if (pcf_noack(pcf, timeout)) { 3704e190a62SNicolas Souchu error = IIC_ENOACK; 3714e190a62SNicolas Souchu goto error; 3724e190a62SNicolas Souchu } 3734e190a62SNicolas Souchu 3744e190a62SNicolas Souchu return (0); 3754e190a62SNicolas Souchu 3764e190a62SNicolas Souchu error: 3774e190a62SNicolas Souchu pcf_stop(pcfdev); 3784e190a62SNicolas Souchu return (error); 3794e190a62SNicolas Souchu } 3804e190a62SNicolas Souchu 381fe310de8SBruce Evans static void 3820f210c92SNicolas Souchu pcfintr(void *arg) 3834e190a62SNicolas Souchu { 3840f210c92SNicolas Souchu device_t pcfdev = (device_t)arg; 3850f210c92SNicolas Souchu struct pcf_softc *pcf = DEVTOSOFTC(pcfdev); 3864e190a62SNicolas Souchu 3874e190a62SNicolas Souchu char data, status, addr; 3884e190a62SNicolas Souchu char error = 0; 3894e190a62SNicolas Souchu 3904e190a62SNicolas Souchu status = PCF_GET_S1(pcf); 3914e190a62SNicolas Souchu 3924e190a62SNicolas Souchu if (status & PIN) { 3930f210c92SNicolas Souchu device_printf(pcfdev, "spurious interrupt, status=0x%x\n", status & 0xff); 3944e190a62SNicolas Souchu 3954e190a62SNicolas Souchu goto error; 3964e190a62SNicolas Souchu } 3974e190a62SNicolas Souchu 3984e190a62SNicolas Souchu if (status & LAB) 3990f210c92SNicolas Souchu device_printf(pcfdev, "bus arbitration lost!\n"); 4004e190a62SNicolas Souchu 4014e190a62SNicolas Souchu if (status & BER) { 4024e190a62SNicolas Souchu error = IIC_EBUSERR; 4034e190a62SNicolas Souchu iicbus_intr(pcf->iicbus, INTR_ERROR, &error); 4044e190a62SNicolas Souchu 4054e190a62SNicolas Souchu goto error; 4064e190a62SNicolas Souchu } 4074e190a62SNicolas Souchu 4084e190a62SNicolas Souchu do { 4094e190a62SNicolas Souchu status = PCF_GET_S1(pcf); 4104e190a62SNicolas Souchu 4114e190a62SNicolas Souchu switch(pcf->pcf_slave_mode) { 4124e190a62SNicolas Souchu 4134e190a62SNicolas Souchu case SLAVE_TRANSMITTER: 4144e190a62SNicolas Souchu if (status & LRB) { 4154e190a62SNicolas Souchu /* ack interrupt line */ 4164e190a62SNicolas Souchu dummy_write(pcf); 4174e190a62SNicolas Souchu 4184e190a62SNicolas Souchu /* no ack, don't send anymore */ 4194e190a62SNicolas Souchu pcf->pcf_slave_mode = SLAVE_RECEIVER; 4204e190a62SNicolas Souchu 4214e190a62SNicolas Souchu iicbus_intr(pcf->iicbus, INTR_NOACK, NULL); 4224e190a62SNicolas Souchu break; 4234e190a62SNicolas Souchu } 4244e190a62SNicolas Souchu 4254e190a62SNicolas Souchu /* get data from upper code */ 4264e190a62SNicolas Souchu iicbus_intr(pcf->iicbus, INTR_TRANSMIT, &data); 4274e190a62SNicolas Souchu 4284e190a62SNicolas Souchu PCF_SET_S0(pcf, data); 4294e190a62SNicolas Souchu break; 4304e190a62SNicolas Souchu 4314e190a62SNicolas Souchu case SLAVE_RECEIVER: 4324e190a62SNicolas Souchu if (status & AAS) { 4334e190a62SNicolas Souchu addr = PCF_GET_S0(pcf); 4344e190a62SNicolas Souchu 4354e190a62SNicolas Souchu if (status & AD0) 4364e190a62SNicolas Souchu iicbus_intr(pcf->iicbus, INTR_GENERAL, &addr); 4374e190a62SNicolas Souchu else 4384e190a62SNicolas Souchu iicbus_intr(pcf->iicbus, INTR_START, &addr); 4394e190a62SNicolas Souchu 4404e190a62SNicolas Souchu if (addr & LSB) { 4414e190a62SNicolas Souchu pcf->pcf_slave_mode = SLAVE_TRANSMITTER; 4424e190a62SNicolas Souchu 4434e190a62SNicolas Souchu /* get the first char from upper code */ 4444e190a62SNicolas Souchu iicbus_intr(pcf->iicbus, INTR_TRANSMIT, &data); 4454e190a62SNicolas Souchu 4464e190a62SNicolas Souchu /* send first data byte */ 4474e190a62SNicolas Souchu PCF_SET_S0(pcf, data); 4484e190a62SNicolas Souchu } 4494e190a62SNicolas Souchu 4504e190a62SNicolas Souchu break; 4514e190a62SNicolas Souchu } 4524e190a62SNicolas Souchu 4534e190a62SNicolas Souchu /* stop condition received? */ 4544e190a62SNicolas Souchu if (status & STS) { 4554e190a62SNicolas Souchu /* ack interrupt line */ 4564e190a62SNicolas Souchu dummy_read(pcf); 4574e190a62SNicolas Souchu 4584e190a62SNicolas Souchu /* emulate intr stop condition */ 4594e190a62SNicolas Souchu iicbus_intr(pcf->iicbus, INTR_STOP, NULL); 4604e190a62SNicolas Souchu 4614e190a62SNicolas Souchu } else { 4624e190a62SNicolas Souchu /* get data, ack interrupt line */ 4634e190a62SNicolas Souchu data = PCF_GET_S0(pcf); 4644e190a62SNicolas Souchu 4654e190a62SNicolas Souchu /* deliver the character */ 4664e190a62SNicolas Souchu iicbus_intr(pcf->iicbus, INTR_RECEIVE, &data); 4674e190a62SNicolas Souchu } 4684e190a62SNicolas Souchu break; 4694e190a62SNicolas Souchu 4704e190a62SNicolas Souchu default: 4716e551fb6SDavid E. O'Brien panic("%s: unknown slave mode (%d)!", __func__, 4724e190a62SNicolas Souchu pcf->pcf_slave_mode); 4734e190a62SNicolas Souchu } 4744e190a62SNicolas Souchu 4754e190a62SNicolas Souchu } while ((PCF_GET_S1(pcf) & PIN) == 0); 4764e190a62SNicolas Souchu 4774e190a62SNicolas Souchu return; 4784e190a62SNicolas Souchu 4794e190a62SNicolas Souchu error: 4804e190a62SNicolas Souchu /* unknown event on bus...reset PCF */ 4814e190a62SNicolas Souchu PCF_SET_S1(pcf, PIN|ES0|ENI|ACK); 4824e190a62SNicolas Souchu 4834e190a62SNicolas Souchu pcf->pcf_slave_mode = SLAVE_RECEIVER; 4844e190a62SNicolas Souchu 4854e190a62SNicolas Souchu return; 4864e190a62SNicolas Souchu } 4874e190a62SNicolas Souchu 488af548787SNicolas Souchu static int pcf_rst_card(device_t pcfdev, u_char speed, u_char addr, u_char *oldaddr) 4894e190a62SNicolas Souchu { 4904e190a62SNicolas Souchu struct pcf_softc *pcf = DEVTOSOFTC(pcfdev); 491af548787SNicolas Souchu 492af548787SNicolas Souchu if (oldaddr) 493af548787SNicolas Souchu *oldaddr = pcf->pcf_addr; 4944e190a62SNicolas Souchu 4954e190a62SNicolas Souchu /* retrieve own address from bus level */ 496af548787SNicolas Souchu if (!addr) 497af548787SNicolas Souchu pcf->pcf_addr = PCF_DEFAULT_ADDR; 498af548787SNicolas Souchu else 499af548787SNicolas Souchu pcf->pcf_addr = addr; 5004e190a62SNicolas Souchu 5014e190a62SNicolas Souchu PCF_SET_S1(pcf, PIN); /* initialize S1 */ 5024e190a62SNicolas Souchu 5034e190a62SNicolas Souchu /* own address S'O<>0 */ 504af548787SNicolas Souchu PCF_SET_S0(pcf, pcf->pcf_addr >> 1); 5054e190a62SNicolas Souchu 5064e190a62SNicolas Souchu /* select clock register */ 5074e190a62SNicolas Souchu PCF_SET_S1(pcf, PIN|ES1); 5084e190a62SNicolas Souchu 5094e190a62SNicolas Souchu /* select bus speed : 18=90kb, 19=45kb, 1A=11kb, 1B=1.5kb */ 5104e190a62SNicolas Souchu switch (speed) { 5114e190a62SNicolas Souchu case IIC_SLOW: 5124e190a62SNicolas Souchu PCF_SET_S0(pcf, 0x1b); 5134e190a62SNicolas Souchu break; 5144e190a62SNicolas Souchu 5154e190a62SNicolas Souchu case IIC_FAST: 5164e190a62SNicolas Souchu PCF_SET_S0(pcf, 0x19); 5174e190a62SNicolas Souchu break; 5184e190a62SNicolas Souchu 5194e190a62SNicolas Souchu case IIC_UNKNOWN: 5204e190a62SNicolas Souchu case IIC_FASTEST: 5214e190a62SNicolas Souchu default: 5224e190a62SNicolas Souchu PCF_SET_S0(pcf, 0x18); 5234e190a62SNicolas Souchu break; 5244e190a62SNicolas Souchu } 5254e190a62SNicolas Souchu 5264e190a62SNicolas Souchu /* set bus on, ack=yes, INT=yes */ 5274e190a62SNicolas Souchu PCF_SET_S1(pcf, PIN|ES0|ENI|ACK); 5284e190a62SNicolas Souchu 5294e190a62SNicolas Souchu pcf->pcf_slave_mode = SLAVE_RECEIVER; 5304e190a62SNicolas Souchu 5314e190a62SNicolas Souchu return (0); 5324e190a62SNicolas Souchu } 5334e190a62SNicolas Souchu 5344e190a62SNicolas Souchu static int 535af548787SNicolas Souchu pcf_write(device_t pcfdev, char *buf, int len, int *sent, int timeout /* us */) 5364e190a62SNicolas Souchu { 5374e190a62SNicolas Souchu struct pcf_softc *pcf = DEVTOSOFTC(pcfdev); 5384e190a62SNicolas Souchu int bytes, error = 0; 5394e190a62SNicolas Souchu 5404e190a62SNicolas Souchu #ifdef PCFDEBUG 5414e190a62SNicolas Souchu printf("pcf%d: >> writing %d bytes\n", device_get_unit(pcfdev), len); 5424e190a62SNicolas Souchu #endif 5434e190a62SNicolas Souchu 5444e190a62SNicolas Souchu bytes = 0; 5454e190a62SNicolas Souchu while (len) { 5464e190a62SNicolas Souchu 5474e190a62SNicolas Souchu PCF_SET_S0(pcf, *buf++); 5484e190a62SNicolas Souchu 549af548787SNicolas Souchu /* wait for the byte to be send */ 5504e190a62SNicolas Souchu if ((error = pcf_wait_byte(pcf))) 5514e190a62SNicolas Souchu goto error; 5524e190a62SNicolas Souchu 553af548787SNicolas Souchu /* check if ack received */ 554af548787SNicolas Souchu if (pcf_noack(pcf, timeout)) { 5554e190a62SNicolas Souchu error = IIC_ENOACK; 5564e190a62SNicolas Souchu goto error; 5574e190a62SNicolas Souchu } 5584e190a62SNicolas Souchu 5594e190a62SNicolas Souchu len --; 5604e190a62SNicolas Souchu bytes ++; 5614e190a62SNicolas Souchu } 5624e190a62SNicolas Souchu 5634e190a62SNicolas Souchu error: 5644e190a62SNicolas Souchu *sent = bytes; 5654e190a62SNicolas Souchu 5664e190a62SNicolas Souchu #ifdef PCFDEBUG 5674e190a62SNicolas Souchu printf("pcf%d: >> %d bytes written (%d)\n", 5684e190a62SNicolas Souchu device_get_unit(pcfdev), bytes, error); 5694e190a62SNicolas Souchu #endif 5704e190a62SNicolas Souchu 5714e190a62SNicolas Souchu return (error); 5724e190a62SNicolas Souchu } 5734e190a62SNicolas Souchu 5744e190a62SNicolas Souchu static int 575af548787SNicolas Souchu pcf_read(device_t pcfdev, char *buf, int len, int *read, int last, 576af548787SNicolas Souchu int delay /* us */) 5774e190a62SNicolas Souchu { 5784e190a62SNicolas Souchu struct pcf_softc *pcf = DEVTOSOFTC(pcfdev); 5794e190a62SNicolas Souchu int bytes, error = 0; 5804e190a62SNicolas Souchu 5814e190a62SNicolas Souchu #ifdef PCFDEBUG 5824e190a62SNicolas Souchu printf("pcf%d: << reading %d bytes\n", device_get_unit(pcfdev), len); 5834e190a62SNicolas Souchu #endif 5844e190a62SNicolas Souchu 5854e190a62SNicolas Souchu /* trig the bus to get the first data byte in S0 */ 5864e190a62SNicolas Souchu if (len) { 587af548787SNicolas Souchu if (len == 1 && last) 5884e190a62SNicolas Souchu /* just one byte to read */ 5894e190a62SNicolas Souchu PCF_SET_S1(pcf, ES0); /* no ack */ 5904e190a62SNicolas Souchu 5914e190a62SNicolas Souchu dummy_read(pcf); 5924e190a62SNicolas Souchu } 5934e190a62SNicolas Souchu 5944e190a62SNicolas Souchu bytes = 0; 5954e190a62SNicolas Souchu while (len) { 5964e190a62SNicolas Souchu 597af548787SNicolas Souchu /* XXX delay needed here */ 598af548787SNicolas Souchu 599af548787SNicolas Souchu /* wait for trigged byte */ 6004e190a62SNicolas Souchu if ((error = pcf_wait_byte(pcf))) { 6014e190a62SNicolas Souchu pcf_stop(pcfdev); 6024e190a62SNicolas Souchu goto error; 6034e190a62SNicolas Souchu } 6044e190a62SNicolas Souchu 605af548787SNicolas Souchu if (len == 1 && last) 606af548787SNicolas Souchu /* ok, last data byte already in S0, no I2C activity 607af548787SNicolas Souchu * on next PCF_GET_S0() */ 6084e190a62SNicolas Souchu pcf_stop(pcfdev); 6094e190a62SNicolas Souchu 610af548787SNicolas Souchu else if (len == 2 && last) 6114e190a62SNicolas Souchu /* next trigged byte with no ack */ 6124e190a62SNicolas Souchu PCF_SET_S1(pcf, ES0); 6134e190a62SNicolas Souchu 614af548787SNicolas Souchu /* receive byte, trig next byte */ 6154e190a62SNicolas Souchu *buf++ = PCF_GET_S0(pcf); 6164e190a62SNicolas Souchu 6174e190a62SNicolas Souchu len --; 6184e190a62SNicolas Souchu bytes ++; 6194e190a62SNicolas Souchu }; 6204e190a62SNicolas Souchu 6214e190a62SNicolas Souchu error: 6224e190a62SNicolas Souchu *read = bytes; 6234e190a62SNicolas Souchu 6244e190a62SNicolas Souchu #ifdef PCFDEBUG 6254e190a62SNicolas Souchu printf("pcf%d: << %d bytes read (%d)\n", 6264e190a62SNicolas Souchu device_get_unit(pcfdev), bytes, error); 6274e190a62SNicolas Souchu #endif 6284e190a62SNicolas Souchu 6294e190a62SNicolas Souchu return (error); 6304e190a62SNicolas Souchu } 6314e190a62SNicolas Souchu 6320f210c92SNicolas Souchu DRIVER_MODULE(pcf, isa, pcf_driver, pcf_devclass, 0, 0); 633