1f7ce69c9SNicolas Souchu /*- 2f7ce69c9SNicolas Souchu * Copyright (c) 1998 Nicolas Souchu, Marc Bouget 3f7ce69c9SNicolas Souchu * All rights reserved. 4f7ce69c9SNicolas Souchu * 5f7ce69c9SNicolas Souchu * Redistribution and use in source and binary forms, with or without 6f7ce69c9SNicolas Souchu * modification, are permitted provided that the following conditions 7f7ce69c9SNicolas Souchu * are met: 8f7ce69c9SNicolas Souchu * 1. Redistributions of source code must retain the above copyright 9f7ce69c9SNicolas Souchu * notice, this list of conditions and the following disclaimer. 10f7ce69c9SNicolas Souchu * 2. Redistributions in binary form must reproduce the above copyright 11f7ce69c9SNicolas Souchu * notice, this list of conditions and the following disclaimer in the 12f7ce69c9SNicolas Souchu * documentation and/or other materials provided with the distribution. 13f7ce69c9SNicolas Souchu * 14f7ce69c9SNicolas Souchu * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15f7ce69c9SNicolas Souchu * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16f7ce69c9SNicolas Souchu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17f7ce69c9SNicolas Souchu * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18f7ce69c9SNicolas Souchu * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19f7ce69c9SNicolas Souchu * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20f7ce69c9SNicolas Souchu * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21f7ce69c9SNicolas Souchu * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22f7ce69c9SNicolas Souchu * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23f7ce69c9SNicolas Souchu * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24f7ce69c9SNicolas Souchu * SUCH DAMAGE. 25f7ce69c9SNicolas Souchu * 26c3aac50fSPeter Wemm * $FreeBSD$ 27f7ce69c9SNicolas Souchu * 28f7ce69c9SNicolas Souchu */ 29f7ce69c9SNicolas Souchu 30f7ce69c9SNicolas Souchu /* 31f7ce69c9SNicolas Souchu * I2C Bit-Banging over parallel port 32f7ce69c9SNicolas Souchu * 33f7ce69c9SNicolas Souchu * See the Official Philips interface description in lpbb(4) 34f7ce69c9SNicolas Souchu */ 35f7ce69c9SNicolas Souchu 36f7ce69c9SNicolas Souchu #include <sys/param.h> 37f7ce69c9SNicolas Souchu #include <sys/kernel.h> 38f7ce69c9SNicolas Souchu #include <sys/systm.h> 39f7ce69c9SNicolas Souchu #include <sys/module.h> 40f7ce69c9SNicolas Souchu #include <sys/bus.h> 41f7ce69c9SNicolas Souchu #include <sys/conf.h> 42f7ce69c9SNicolas Souchu #include <sys/buf.h> 43f7ce69c9SNicolas Souchu #include <sys/uio.h> 44f7ce69c9SNicolas Souchu #include <sys/malloc.h> 45f7ce69c9SNicolas Souchu 46f7ce69c9SNicolas Souchu #include <machine/clock.h> 47f7ce69c9SNicolas Souchu 48f7ce69c9SNicolas Souchu #include <dev/ppbus/ppbconf.h> 49f7ce69c9SNicolas Souchu 50f7ce69c9SNicolas Souchu #include <dev/iicbus/iiconf.h> 51f7ce69c9SNicolas Souchu #include <dev/iicbus/iicbus.h> 52f7ce69c9SNicolas Souchu 53f7ce69c9SNicolas Souchu #include "iicbb_if.h" 54f7ce69c9SNicolas Souchu 55f7ce69c9SNicolas Souchu /* iicbus softc */ 56f7ce69c9SNicolas Souchu struct lpbb_softc { 57f7ce69c9SNicolas Souchu 58f7ce69c9SNicolas Souchu struct ppb_device lpbb_dev; 59f7ce69c9SNicolas Souchu }; 60f7ce69c9SNicolas Souchu 61f7ce69c9SNicolas Souchu static int lpbb_detect(struct lpbb_softc *); 62f7ce69c9SNicolas Souchu 63f7ce69c9SNicolas Souchu static int lpbb_probe(device_t); 64f7ce69c9SNicolas Souchu static int lpbb_attach(device_t); 65f7ce69c9SNicolas Souchu 66f7ce69c9SNicolas Souchu static int lpbb_callback(device_t, int, caddr_t *); 67f7ce69c9SNicolas Souchu static void lpbb_setlines(device_t, int, int); 68f7ce69c9SNicolas Souchu static int lpbb_getdataline(device_t); 69f7ce69c9SNicolas Souchu static int lpbb_reset(device_t, u_char, u_char, u_char *); 70f7ce69c9SNicolas Souchu 71f7ce69c9SNicolas Souchu static devclass_t lpbb_devclass; 72f7ce69c9SNicolas Souchu 73f7ce69c9SNicolas Souchu static device_method_t lpbb_methods[] = { 74f7ce69c9SNicolas Souchu /* device interface */ 75f7ce69c9SNicolas Souchu DEVMETHOD(device_probe, lpbb_probe), 76f7ce69c9SNicolas Souchu DEVMETHOD(device_attach, lpbb_attach), 77f7ce69c9SNicolas Souchu 78f7ce69c9SNicolas Souchu /* bus interface */ 7915317dd8SMatthew N. Dodd DEVMETHOD(bus_print_child, bus_generic_print_child), 80f7ce69c9SNicolas Souchu 81f7ce69c9SNicolas Souchu /* iicbb interface */ 82f7ce69c9SNicolas Souchu DEVMETHOD(iicbb_callback, lpbb_callback), 83f7ce69c9SNicolas Souchu DEVMETHOD(iicbb_setlines, lpbb_setlines), 84f7ce69c9SNicolas Souchu DEVMETHOD(iicbb_getdataline, lpbb_getdataline), 85f7ce69c9SNicolas Souchu DEVMETHOD(iicbb_reset, lpbb_reset), 86f7ce69c9SNicolas Souchu 87f7ce69c9SNicolas Souchu { 0, 0 } 88f7ce69c9SNicolas Souchu }; 89f7ce69c9SNicolas Souchu 90f7ce69c9SNicolas Souchu static driver_t lpbb_driver = { 91f7ce69c9SNicolas Souchu "lpbb", 92f7ce69c9SNicolas Souchu lpbb_methods, 93f7ce69c9SNicolas Souchu sizeof(struct lpbb_softc), 94f7ce69c9SNicolas Souchu }; 95f7ce69c9SNicolas Souchu 96f7ce69c9SNicolas Souchu /* 97f7ce69c9SNicolas Souchu * Make ourselves visible as a ppbus driver 98f7ce69c9SNicolas Souchu */ 99f7ce69c9SNicolas Souchu static struct ppb_device *lpbb_ppb_probe(struct ppb_data *ppb); 100f7ce69c9SNicolas Souchu static int lpbb_ppb_attach(struct ppb_device *dev); 101f7ce69c9SNicolas Souchu 102f7ce69c9SNicolas Souchu #define MAXLPBB 8 /* XXX not much better! */ 103f7ce69c9SNicolas Souchu static struct lpbb_softc *lpbbdata[MAXLPBB]; 104f7ce69c9SNicolas Souchu static int nlpbb = 0; 105f7ce69c9SNicolas Souchu 106f7ce69c9SNicolas Souchu #ifdef KERNEL 107f7ce69c9SNicolas Souchu 108f7ce69c9SNicolas Souchu static struct ppb_driver lpbbdriver = { 109f7ce69c9SNicolas Souchu lpbb_ppb_probe, lpbb_ppb_attach, "lpbb" 110f7ce69c9SNicolas Souchu }; 111f7ce69c9SNicolas Souchu DATA_SET(ppbdriver_set, lpbbdriver); 112f7ce69c9SNicolas Souchu 113f7ce69c9SNicolas Souchu #endif /* KERNEL */ 114f7ce69c9SNicolas Souchu 115f7ce69c9SNicolas Souchu static int 116f7ce69c9SNicolas Souchu lpbb_probe(device_t dev) 117f7ce69c9SNicolas Souchu { 118f7ce69c9SNicolas Souchu struct lpbb_softc *sc = lpbbdata[device_get_unit(dev)]; 119f7ce69c9SNicolas Souchu struct lpbb_softc *scdst = (struct lpbb_softc *)device_get_softc(dev); 120f7ce69c9SNicolas Souchu 121f7ce69c9SNicolas Souchu /* XXX copy softc. Yet, ppbus device is sc->lpbb_dev, but will be 122f7ce69c9SNicolas Souchu * dev->parent when ppbus will be ported to the new bus architecture */ 123f7ce69c9SNicolas Souchu bcopy(sc, scdst, sizeof(struct lpbb_softc)); 124f7ce69c9SNicolas Souchu 125f7ce69c9SNicolas Souchu device_set_desc(dev, "parallel I2C bit-banging interface"); 126f7ce69c9SNicolas Souchu 127f7ce69c9SNicolas Souchu /* probe done by ppbus initialization */ 128f7ce69c9SNicolas Souchu return (0); 129f7ce69c9SNicolas Souchu } 130f7ce69c9SNicolas Souchu 131f7ce69c9SNicolas Souchu static int 132f7ce69c9SNicolas Souchu lpbb_attach(device_t dev) 133f7ce69c9SNicolas Souchu { 134f7ce69c9SNicolas Souchu device_t bitbang, iicbus; 135f7ce69c9SNicolas Souchu 136f7ce69c9SNicolas Souchu /* add generic bit-banging code */ 137f7ce69c9SNicolas Souchu bitbang = device_add_child(dev, "iicbb", -1, NULL); 138f7ce69c9SNicolas Souchu 139f7ce69c9SNicolas Souchu /* add the iicbus to the tree */ 140f7ce69c9SNicolas Souchu iicbus = iicbus_alloc_bus(bitbang); 141f7ce69c9SNicolas Souchu 142f7ce69c9SNicolas Souchu device_probe_and_attach(bitbang); 143f7ce69c9SNicolas Souchu 144f7ce69c9SNicolas Souchu /* XXX should be in iicbb_attach! */ 145f7ce69c9SNicolas Souchu device_probe_and_attach(iicbus); 146f7ce69c9SNicolas Souchu 147f7ce69c9SNicolas Souchu return (0); 148f7ce69c9SNicolas Souchu } 149f7ce69c9SNicolas Souchu 150f7ce69c9SNicolas Souchu /* 151f7ce69c9SNicolas Souchu * lppbb_ppb_probe() 152f7ce69c9SNicolas Souchu */ 153f7ce69c9SNicolas Souchu static struct ppb_device * 154f7ce69c9SNicolas Souchu lpbb_ppb_probe(struct ppb_data *ppb) 155f7ce69c9SNicolas Souchu { 156f7ce69c9SNicolas Souchu struct lpbb_softc *sc; 157f7ce69c9SNicolas Souchu 158f7ce69c9SNicolas Souchu sc = (struct lpbb_softc *) malloc(sizeof(struct lpbb_softc), 159f7ce69c9SNicolas Souchu M_TEMP, M_NOWAIT); 160f7ce69c9SNicolas Souchu if (!sc) { 161f7ce69c9SNicolas Souchu printf("lpbb: cannot malloc!\n"); 162f7ce69c9SNicolas Souchu return (0); 163f7ce69c9SNicolas Souchu } 164f7ce69c9SNicolas Souchu bzero(sc, sizeof(struct lpbb_softc)); 165f7ce69c9SNicolas Souchu 166f7ce69c9SNicolas Souchu lpbbdata[nlpbb] = sc; 167f7ce69c9SNicolas Souchu 168f7ce69c9SNicolas Souchu /* 169f7ce69c9SNicolas Souchu * ppbus dependent initialisation. 170f7ce69c9SNicolas Souchu */ 171f7ce69c9SNicolas Souchu sc->lpbb_dev.id_unit = nlpbb; 172f7ce69c9SNicolas Souchu sc->lpbb_dev.name = lpbbdriver.name; 173f7ce69c9SNicolas Souchu sc->lpbb_dev.ppb = ppb; 174f7ce69c9SNicolas Souchu sc->lpbb_dev.intr = 0; 175f7ce69c9SNicolas Souchu 176f7ce69c9SNicolas Souchu if (!lpbb_detect(sc)) { 177f7ce69c9SNicolas Souchu free(sc, M_TEMP); 178f7ce69c9SNicolas Souchu return (NULL); 179f7ce69c9SNicolas Souchu } 180f7ce69c9SNicolas Souchu 181f7ce69c9SNicolas Souchu /* Ok, go to next device on next probe */ 182f7ce69c9SNicolas Souchu nlpbb ++; 183f7ce69c9SNicolas Souchu 184f7ce69c9SNicolas Souchu /* XXX wrong according to new bus architecture. ppbus needs to be 185f7ce69c9SNicolas Souchu * ported 186f7ce69c9SNicolas Souchu */ 187f7ce69c9SNicolas Souchu return (&sc->lpbb_dev); 188f7ce69c9SNicolas Souchu } 189f7ce69c9SNicolas Souchu 190f7ce69c9SNicolas Souchu static int 191f7ce69c9SNicolas Souchu lpbb_ppb_attach(struct ppb_device *dev) 192f7ce69c9SNicolas Souchu { 193f7ce69c9SNicolas Souchu /* add the parallel port I2C interface to the bus tree */ 194f7ce69c9SNicolas Souchu if (!device_add_child(root_bus, "lpbb", dev->id_unit, NULL)) 195f7ce69c9SNicolas Souchu return (0); 196f7ce69c9SNicolas Souchu 197f7ce69c9SNicolas Souchu return (1); 198f7ce69c9SNicolas Souchu } 199f7ce69c9SNicolas Souchu 200f7ce69c9SNicolas Souchu static int 201f7ce69c9SNicolas Souchu lpbb_callback(device_t dev, int index, caddr_t *data) 202f7ce69c9SNicolas Souchu { 203f7ce69c9SNicolas Souchu struct lpbb_softc *sc = (struct lpbb_softc *)device_get_softc(dev); 204f7ce69c9SNicolas Souchu int error = 0; 205f7ce69c9SNicolas Souchu int how; 206f7ce69c9SNicolas Souchu 207f7ce69c9SNicolas Souchu switch (index) { 208f7ce69c9SNicolas Souchu case IIC_REQUEST_BUS: 209f7ce69c9SNicolas Souchu /* request the ppbus */ 210f7ce69c9SNicolas Souchu how = *(int *)data; 211f7ce69c9SNicolas Souchu error = ppb_request_bus(&sc->lpbb_dev, how); 212f7ce69c9SNicolas Souchu break; 213f7ce69c9SNicolas Souchu 214f7ce69c9SNicolas Souchu case IIC_RELEASE_BUS: 215f7ce69c9SNicolas Souchu /* release the ppbus */ 216f7ce69c9SNicolas Souchu error = ppb_release_bus(&sc->lpbb_dev); 217f7ce69c9SNicolas Souchu break; 218f7ce69c9SNicolas Souchu 219f7ce69c9SNicolas Souchu default: 220f7ce69c9SNicolas Souchu error = EINVAL; 221f7ce69c9SNicolas Souchu } 222f7ce69c9SNicolas Souchu 223f7ce69c9SNicolas Souchu return (error); 224f7ce69c9SNicolas Souchu } 225f7ce69c9SNicolas Souchu 226f7ce69c9SNicolas Souchu #define SDA_out 0x80 227f7ce69c9SNicolas Souchu #define SCL_out 0x08 228f7ce69c9SNicolas Souchu #define SDA_in 0x80 229f7ce69c9SNicolas Souchu #define SCL_in 0x08 230f7ce69c9SNicolas Souchu #define ALIM 0x20 231f7ce69c9SNicolas Souchu #define I2CKEY 0x50 232f7ce69c9SNicolas Souchu 233f7ce69c9SNicolas Souchu static int getSDA(struct lpbb_softc *sc) 234f7ce69c9SNicolas Souchu { 235f7ce69c9SNicolas Souchu if((ppb_rstr(&sc->lpbb_dev)&SDA_in)==SDA_in) 236f7ce69c9SNicolas Souchu return 1; 237f7ce69c9SNicolas Souchu else 238f7ce69c9SNicolas Souchu return 0; 239f7ce69c9SNicolas Souchu } 240f7ce69c9SNicolas Souchu 241f7ce69c9SNicolas Souchu static void setSDA(struct lpbb_softc *sc, char val) 242f7ce69c9SNicolas Souchu { 243f7ce69c9SNicolas Souchu if(val==0) 244f7ce69c9SNicolas Souchu ppb_wdtr(&sc->lpbb_dev, (u_char)SDA_out); 245f7ce69c9SNicolas Souchu else 246f7ce69c9SNicolas Souchu ppb_wdtr(&sc->lpbb_dev, (u_char)~SDA_out); 247f7ce69c9SNicolas Souchu } 248f7ce69c9SNicolas Souchu 249f7ce69c9SNicolas Souchu static void setSCL(struct lpbb_softc *sc, unsigned char val) 250f7ce69c9SNicolas Souchu { 251f7ce69c9SNicolas Souchu if(val==0) 252f7ce69c9SNicolas Souchu ppb_wctr(&sc->lpbb_dev, (u_char)(ppb_rctr(&sc->lpbb_dev)&~SCL_out)); 253f7ce69c9SNicolas Souchu else 254f7ce69c9SNicolas Souchu ppb_wctr(&sc->lpbb_dev, (u_char)(ppb_rctr(&sc->lpbb_dev)|SCL_out)); 255f7ce69c9SNicolas Souchu } 256f7ce69c9SNicolas Souchu 257f7ce69c9SNicolas Souchu static int lpbb_detect(struct lpbb_softc *sc) 258f7ce69c9SNicolas Souchu { 259f7ce69c9SNicolas Souchu if (ppb_request_bus(&sc->lpbb_dev, PPB_DONTWAIT)) { 260f7ce69c9SNicolas Souchu printf("lpbb: can't allocate ppbus\n"); 261f7ce69c9SNicolas Souchu return (0); 262f7ce69c9SNicolas Souchu } 263f7ce69c9SNicolas Souchu 264f7ce69c9SNicolas Souchu /* reset bus */ 265f7ce69c9SNicolas Souchu setSDA(sc, 1); 266f7ce69c9SNicolas Souchu setSCL(sc, 1); 267f7ce69c9SNicolas Souchu 268f7ce69c9SNicolas Souchu if ((ppb_rstr(&sc->lpbb_dev) & I2CKEY) || 269a6530aceSNicolas Souchu ((ppb_rstr(&sc->lpbb_dev) & ALIM) != ALIM)) { 270a6530aceSNicolas Souchu 271a6530aceSNicolas Souchu ppb_release_bus(&sc->lpbb_dev); 272f7ce69c9SNicolas Souchu return (0); 273a6530aceSNicolas Souchu } 274f7ce69c9SNicolas Souchu 275f7ce69c9SNicolas Souchu ppb_release_bus(&sc->lpbb_dev); 276f7ce69c9SNicolas Souchu 277f7ce69c9SNicolas Souchu return (1); 278f7ce69c9SNicolas Souchu } 279f7ce69c9SNicolas Souchu 280f7ce69c9SNicolas Souchu static int 281f7ce69c9SNicolas Souchu lpbb_reset(device_t dev, u_char speed, u_char addr, u_char * oldaddr) 282f7ce69c9SNicolas Souchu { 283f7ce69c9SNicolas Souchu struct lpbb_softc *sc = (struct lpbb_softc *)device_get_softc(dev); 284f7ce69c9SNicolas Souchu 285f7ce69c9SNicolas Souchu /* reset bus */ 286f7ce69c9SNicolas Souchu setSDA(sc, 1); 287f7ce69c9SNicolas Souchu setSCL(sc, 1); 288f7ce69c9SNicolas Souchu 289f7ce69c9SNicolas Souchu return (IIC_ENOADDR); 290f7ce69c9SNicolas Souchu } 291f7ce69c9SNicolas Souchu 292f7ce69c9SNicolas Souchu static void 293f7ce69c9SNicolas Souchu lpbb_setlines(device_t dev, int ctrl, int data) 294f7ce69c9SNicolas Souchu { 295f7ce69c9SNicolas Souchu struct lpbb_softc *sc = (struct lpbb_softc *)device_get_softc(dev); 296f7ce69c9SNicolas Souchu 297f7ce69c9SNicolas Souchu setSCL(sc, ctrl); 298f7ce69c9SNicolas Souchu setSDA(sc, data); 299f7ce69c9SNicolas Souchu } 300f7ce69c9SNicolas Souchu 301f7ce69c9SNicolas Souchu static int 302f7ce69c9SNicolas Souchu lpbb_getdataline(device_t dev) 303f7ce69c9SNicolas Souchu { 304f7ce69c9SNicolas Souchu struct lpbb_softc *sc = (struct lpbb_softc *)device_get_softc(dev); 305f7ce69c9SNicolas Souchu 306f7ce69c9SNicolas Souchu return (getSDA(sc)); 307f7ce69c9SNicolas Souchu } 308f7ce69c9SNicolas Souchu 309f7ce69c9SNicolas Souchu DRIVER_MODULE(lpbb, root, lpbb_driver, lpbb_devclass, 0, 0); 310