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 * 26f7ce69c9SNicolas Souchu * $Id$ 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 static void lpbb_print_child(device_t, device_t); 66f7ce69c9SNicolas Souchu 67f7ce69c9SNicolas Souchu static int lpbb_callback(device_t, int, caddr_t *); 68f7ce69c9SNicolas Souchu static void lpbb_setlines(device_t, int, int); 69f7ce69c9SNicolas Souchu static int lpbb_getdataline(device_t); 70f7ce69c9SNicolas Souchu static int lpbb_reset(device_t, u_char, u_char, u_char *); 71f7ce69c9SNicolas Souchu 72f7ce69c9SNicolas Souchu static devclass_t lpbb_devclass; 73f7ce69c9SNicolas Souchu 74f7ce69c9SNicolas Souchu static device_method_t lpbb_methods[] = { 75f7ce69c9SNicolas Souchu /* device interface */ 76f7ce69c9SNicolas Souchu DEVMETHOD(device_probe, lpbb_probe), 77f7ce69c9SNicolas Souchu DEVMETHOD(device_attach, lpbb_attach), 78f7ce69c9SNicolas Souchu 79f7ce69c9SNicolas Souchu /* bus interface */ 80f7ce69c9SNicolas Souchu DEVMETHOD(bus_print_child, lpbb_print_child), 81f7ce69c9SNicolas Souchu 82f7ce69c9SNicolas Souchu /* iicbb interface */ 83f7ce69c9SNicolas Souchu DEVMETHOD(iicbb_callback, lpbb_callback), 84f7ce69c9SNicolas Souchu DEVMETHOD(iicbb_setlines, lpbb_setlines), 85f7ce69c9SNicolas Souchu DEVMETHOD(iicbb_getdataline, lpbb_getdataline), 86f7ce69c9SNicolas Souchu DEVMETHOD(iicbb_reset, lpbb_reset), 87f7ce69c9SNicolas Souchu 88f7ce69c9SNicolas Souchu { 0, 0 } 89f7ce69c9SNicolas Souchu }; 90f7ce69c9SNicolas Souchu 91f7ce69c9SNicolas Souchu static driver_t lpbb_driver = { 92f7ce69c9SNicolas Souchu "lpbb", 93f7ce69c9SNicolas Souchu lpbb_methods, 94f7ce69c9SNicolas Souchu DRIVER_TYPE_MISC, 95f7ce69c9SNicolas Souchu sizeof(struct lpbb_softc), 96f7ce69c9SNicolas Souchu }; 97f7ce69c9SNicolas Souchu 98f7ce69c9SNicolas Souchu /* 99f7ce69c9SNicolas Souchu * Make ourselves visible as a ppbus driver 100f7ce69c9SNicolas Souchu */ 101f7ce69c9SNicolas Souchu static struct ppb_device *lpbb_ppb_probe(struct ppb_data *ppb); 102f7ce69c9SNicolas Souchu static int lpbb_ppb_attach(struct ppb_device *dev); 103f7ce69c9SNicolas Souchu 104f7ce69c9SNicolas Souchu #define MAXLPBB 8 /* XXX not much better! */ 105f7ce69c9SNicolas Souchu static struct lpbb_softc *lpbbdata[MAXLPBB]; 106f7ce69c9SNicolas Souchu static int nlpbb = 0; 107f7ce69c9SNicolas Souchu 108f7ce69c9SNicolas Souchu #ifdef KERNEL 109f7ce69c9SNicolas Souchu 110f7ce69c9SNicolas Souchu static struct ppb_driver lpbbdriver = { 111f7ce69c9SNicolas Souchu lpbb_ppb_probe, lpbb_ppb_attach, "lpbb" 112f7ce69c9SNicolas Souchu }; 113f7ce69c9SNicolas Souchu DATA_SET(ppbdriver_set, lpbbdriver); 114f7ce69c9SNicolas Souchu 115f7ce69c9SNicolas Souchu #endif /* KERNEL */ 116f7ce69c9SNicolas Souchu 117f7ce69c9SNicolas Souchu static int 118f7ce69c9SNicolas Souchu lpbb_probe(device_t dev) 119f7ce69c9SNicolas Souchu { 120f7ce69c9SNicolas Souchu struct lpbb_softc *sc = lpbbdata[device_get_unit(dev)]; 121f7ce69c9SNicolas Souchu struct lpbb_softc *scdst = (struct lpbb_softc *)device_get_softc(dev); 122f7ce69c9SNicolas Souchu 123f7ce69c9SNicolas Souchu /* XXX copy softc. Yet, ppbus device is sc->lpbb_dev, but will be 124f7ce69c9SNicolas Souchu * dev->parent when ppbus will be ported to the new bus architecture */ 125f7ce69c9SNicolas Souchu bcopy(sc, scdst, sizeof(struct lpbb_softc)); 126f7ce69c9SNicolas Souchu 127f7ce69c9SNicolas Souchu device_set_desc(dev, "parallel I2C bit-banging interface"); 128f7ce69c9SNicolas Souchu 129f7ce69c9SNicolas Souchu /* probe done by ppbus initialization */ 130f7ce69c9SNicolas Souchu return (0); 131f7ce69c9SNicolas Souchu } 132f7ce69c9SNicolas Souchu 133f7ce69c9SNicolas Souchu static int 134f7ce69c9SNicolas Souchu lpbb_attach(device_t dev) 135f7ce69c9SNicolas Souchu { 136f7ce69c9SNicolas Souchu struct lpbb_softc *sc = (struct lpbb_softc *)device_get_softc(dev); 137f7ce69c9SNicolas Souchu device_t bitbang, iicbus; 138f7ce69c9SNicolas Souchu 139f7ce69c9SNicolas Souchu /* add generic bit-banging code */ 140f7ce69c9SNicolas Souchu bitbang = device_add_child(dev, "iicbb", -1, NULL); 141f7ce69c9SNicolas Souchu 142f7ce69c9SNicolas Souchu /* add the iicbus to the tree */ 143f7ce69c9SNicolas Souchu iicbus = iicbus_alloc_bus(bitbang); 144f7ce69c9SNicolas Souchu 145f7ce69c9SNicolas Souchu device_probe_and_attach(bitbang); 146f7ce69c9SNicolas Souchu 147f7ce69c9SNicolas Souchu /* XXX should be in iicbb_attach! */ 148f7ce69c9SNicolas Souchu device_probe_and_attach(iicbus); 149f7ce69c9SNicolas Souchu 150f7ce69c9SNicolas Souchu return (0); 151f7ce69c9SNicolas Souchu } 152f7ce69c9SNicolas Souchu 153f7ce69c9SNicolas Souchu /* 154f7ce69c9SNicolas Souchu * lppbb_ppb_probe() 155f7ce69c9SNicolas Souchu */ 156f7ce69c9SNicolas Souchu static struct ppb_device * 157f7ce69c9SNicolas Souchu lpbb_ppb_probe(struct ppb_data *ppb) 158f7ce69c9SNicolas Souchu { 159f7ce69c9SNicolas Souchu struct lpbb_softc *sc; 160f7ce69c9SNicolas Souchu 161f7ce69c9SNicolas Souchu sc = (struct lpbb_softc *) malloc(sizeof(struct lpbb_softc), 162f7ce69c9SNicolas Souchu M_TEMP, M_NOWAIT); 163f7ce69c9SNicolas Souchu if (!sc) { 164f7ce69c9SNicolas Souchu printf("lpbb: cannot malloc!\n"); 165f7ce69c9SNicolas Souchu return (0); 166f7ce69c9SNicolas Souchu } 167f7ce69c9SNicolas Souchu bzero(sc, sizeof(struct lpbb_softc)); 168f7ce69c9SNicolas Souchu 169f7ce69c9SNicolas Souchu lpbbdata[nlpbb] = sc; 170f7ce69c9SNicolas Souchu 171f7ce69c9SNicolas Souchu /* 172f7ce69c9SNicolas Souchu * ppbus dependent initialisation. 173f7ce69c9SNicolas Souchu */ 174f7ce69c9SNicolas Souchu sc->lpbb_dev.id_unit = nlpbb; 175f7ce69c9SNicolas Souchu sc->lpbb_dev.name = lpbbdriver.name; 176f7ce69c9SNicolas Souchu sc->lpbb_dev.ppb = ppb; 177f7ce69c9SNicolas Souchu sc->lpbb_dev.intr = 0; 178f7ce69c9SNicolas Souchu 179f7ce69c9SNicolas Souchu if (!lpbb_detect(sc)) { 180f7ce69c9SNicolas Souchu free(sc, M_TEMP); 181f7ce69c9SNicolas Souchu return (NULL); 182f7ce69c9SNicolas Souchu } 183f7ce69c9SNicolas Souchu 184f7ce69c9SNicolas Souchu /* Ok, go to next device on next probe */ 185f7ce69c9SNicolas Souchu nlpbb ++; 186f7ce69c9SNicolas Souchu 187f7ce69c9SNicolas Souchu /* XXX wrong according to new bus architecture. ppbus needs to be 188f7ce69c9SNicolas Souchu * ported 189f7ce69c9SNicolas Souchu */ 190f7ce69c9SNicolas Souchu return (&sc->lpbb_dev); 191f7ce69c9SNicolas Souchu } 192f7ce69c9SNicolas Souchu 193f7ce69c9SNicolas Souchu static int 194f7ce69c9SNicolas Souchu lpbb_ppb_attach(struct ppb_device *dev) 195f7ce69c9SNicolas Souchu { 196f7ce69c9SNicolas Souchu /* add the parallel port I2C interface to the bus tree */ 197f7ce69c9SNicolas Souchu if (!device_add_child(root_bus, "lpbb", dev->id_unit, NULL)) 198f7ce69c9SNicolas Souchu return (0); 199f7ce69c9SNicolas Souchu 200f7ce69c9SNicolas Souchu return (1); 201f7ce69c9SNicolas Souchu } 202f7ce69c9SNicolas Souchu 203f7ce69c9SNicolas Souchu static int 204f7ce69c9SNicolas Souchu lpbb_callback(device_t dev, int index, caddr_t *data) 205f7ce69c9SNicolas Souchu { 206f7ce69c9SNicolas Souchu struct lpbb_softc *sc = (struct lpbb_softc *)device_get_softc(dev); 207f7ce69c9SNicolas Souchu int error = 0; 208f7ce69c9SNicolas Souchu int how; 209f7ce69c9SNicolas Souchu 210f7ce69c9SNicolas Souchu switch (index) { 211f7ce69c9SNicolas Souchu case IIC_REQUEST_BUS: 212f7ce69c9SNicolas Souchu /* request the ppbus */ 213f7ce69c9SNicolas Souchu how = *(int *)data; 214f7ce69c9SNicolas Souchu error = ppb_request_bus(&sc->lpbb_dev, how); 215f7ce69c9SNicolas Souchu break; 216f7ce69c9SNicolas Souchu 217f7ce69c9SNicolas Souchu case IIC_RELEASE_BUS: 218f7ce69c9SNicolas Souchu /* release the ppbus */ 219f7ce69c9SNicolas Souchu error = ppb_release_bus(&sc->lpbb_dev); 220f7ce69c9SNicolas Souchu break; 221f7ce69c9SNicolas Souchu 222f7ce69c9SNicolas Souchu default: 223f7ce69c9SNicolas Souchu error = EINVAL; 224f7ce69c9SNicolas Souchu } 225f7ce69c9SNicolas Souchu 226f7ce69c9SNicolas Souchu return (error); 227f7ce69c9SNicolas Souchu } 228f7ce69c9SNicolas Souchu 229f7ce69c9SNicolas Souchu #define SDA_out 0x80 230f7ce69c9SNicolas Souchu #define SCL_out 0x08 231f7ce69c9SNicolas Souchu #define SDA_in 0x80 232f7ce69c9SNicolas Souchu #define SCL_in 0x08 233f7ce69c9SNicolas Souchu #define ALIM 0x20 234f7ce69c9SNicolas Souchu #define I2CKEY 0x50 235f7ce69c9SNicolas Souchu 236f7ce69c9SNicolas Souchu static int getSDA(struct lpbb_softc *sc) 237f7ce69c9SNicolas Souchu { 238f7ce69c9SNicolas Souchu if((ppb_rstr(&sc->lpbb_dev)&SDA_in)==SDA_in) 239f7ce69c9SNicolas Souchu return 1; 240f7ce69c9SNicolas Souchu else 241f7ce69c9SNicolas Souchu return 0; 242f7ce69c9SNicolas Souchu } 243f7ce69c9SNicolas Souchu 244f7ce69c9SNicolas Souchu static int getSCL(struct lpbb_softc *sc) 245f7ce69c9SNicolas Souchu { 246f7ce69c9SNicolas Souchu if((ppb_rstr(&sc->lpbb_dev)&SCL_in)==SCL_in) 247f7ce69c9SNicolas Souchu return 1; 248f7ce69c9SNicolas Souchu else 249f7ce69c9SNicolas Souchu return 0; 250f7ce69c9SNicolas Souchu } 251f7ce69c9SNicolas Souchu 252f7ce69c9SNicolas Souchu static void setSDA(struct lpbb_softc *sc, char val) 253f7ce69c9SNicolas Souchu { 254f7ce69c9SNicolas Souchu if(val==0) 255f7ce69c9SNicolas Souchu ppb_wdtr(&sc->lpbb_dev, (u_char)SDA_out); 256f7ce69c9SNicolas Souchu else 257f7ce69c9SNicolas Souchu ppb_wdtr(&sc->lpbb_dev, (u_char)~SDA_out); 258f7ce69c9SNicolas Souchu } 259f7ce69c9SNicolas Souchu 260f7ce69c9SNicolas Souchu static void setSCL(struct lpbb_softc *sc, unsigned char val) 261f7ce69c9SNicolas Souchu { 262f7ce69c9SNicolas Souchu if(val==0) 263f7ce69c9SNicolas Souchu ppb_wctr(&sc->lpbb_dev, (u_char)(ppb_rctr(&sc->lpbb_dev)&~SCL_out)); 264f7ce69c9SNicolas Souchu else 265f7ce69c9SNicolas Souchu ppb_wctr(&sc->lpbb_dev, (u_char)(ppb_rctr(&sc->lpbb_dev)|SCL_out)); 266f7ce69c9SNicolas Souchu } 267f7ce69c9SNicolas Souchu 268f7ce69c9SNicolas Souchu static int lpbb_detect(struct lpbb_softc *sc) 269f7ce69c9SNicolas Souchu { 270f7ce69c9SNicolas Souchu if (ppb_request_bus(&sc->lpbb_dev, PPB_DONTWAIT)) { 271f7ce69c9SNicolas Souchu printf("lpbb: can't allocate ppbus\n"); 272f7ce69c9SNicolas Souchu return (0); 273f7ce69c9SNicolas Souchu } 274f7ce69c9SNicolas Souchu 275f7ce69c9SNicolas Souchu /* reset bus */ 276f7ce69c9SNicolas Souchu setSDA(sc, 1); 277f7ce69c9SNicolas Souchu setSCL(sc, 1); 278f7ce69c9SNicolas Souchu 279f7ce69c9SNicolas Souchu if ((ppb_rstr(&sc->lpbb_dev) & I2CKEY) || 280f7ce69c9SNicolas Souchu ((ppb_rstr(&sc->lpbb_dev) & ALIM) != ALIM)) 281f7ce69c9SNicolas Souchu return (0); 282f7ce69c9SNicolas Souchu 283f7ce69c9SNicolas Souchu ppb_release_bus(&sc->lpbb_dev); 284f7ce69c9SNicolas Souchu 285f7ce69c9SNicolas Souchu return (1); 286f7ce69c9SNicolas Souchu } 287f7ce69c9SNicolas Souchu 288f7ce69c9SNicolas Souchu static int 289f7ce69c9SNicolas Souchu lpbb_reset(device_t dev, u_char speed, u_char addr, u_char * oldaddr) 290f7ce69c9SNicolas Souchu { 291f7ce69c9SNicolas Souchu struct lpbb_softc *sc = (struct lpbb_softc *)device_get_softc(dev); 292f7ce69c9SNicolas Souchu 293f7ce69c9SNicolas Souchu /* reset bus */ 294f7ce69c9SNicolas Souchu setSDA(sc, 1); 295f7ce69c9SNicolas Souchu setSCL(sc, 1); 296f7ce69c9SNicolas Souchu 297f7ce69c9SNicolas Souchu return (IIC_ENOADDR); 298f7ce69c9SNicolas Souchu } 299f7ce69c9SNicolas Souchu 300f7ce69c9SNicolas Souchu static void 301f7ce69c9SNicolas Souchu lpbb_setlines(device_t dev, int ctrl, int data) 302f7ce69c9SNicolas Souchu { 303f7ce69c9SNicolas Souchu struct lpbb_softc *sc = (struct lpbb_softc *)device_get_softc(dev); 304f7ce69c9SNicolas Souchu 305f7ce69c9SNicolas Souchu setSCL(sc, ctrl); 306f7ce69c9SNicolas Souchu setSDA(sc, data); 307f7ce69c9SNicolas Souchu } 308f7ce69c9SNicolas Souchu 309f7ce69c9SNicolas Souchu static int 310f7ce69c9SNicolas Souchu lpbb_getdataline(device_t dev) 311f7ce69c9SNicolas Souchu { 312f7ce69c9SNicolas Souchu struct lpbb_softc *sc = (struct lpbb_softc *)device_get_softc(dev); 313f7ce69c9SNicolas Souchu 314f7ce69c9SNicolas Souchu return (getSDA(sc)); 315f7ce69c9SNicolas Souchu } 316f7ce69c9SNicolas Souchu 317f7ce69c9SNicolas Souchu static void 318f7ce69c9SNicolas Souchu lpbb_print_child(device_t bus, device_t dev) 319f7ce69c9SNicolas Souchu { 320f7ce69c9SNicolas Souchu printf(" on %s%d", device_get_name(bus), device_get_unit(bus)); 321f7ce69c9SNicolas Souchu 322f7ce69c9SNicolas Souchu return; 323f7ce69c9SNicolas Souchu } 324f7ce69c9SNicolas Souchu 325f7ce69c9SNicolas Souchu DRIVER_MODULE(lpbb, root, lpbb_driver, lpbb_devclass, 0, 0); 326