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> 490f210c92SNicolas Souchu #include "ppbus_if.h" 500f210c92SNicolas Souchu #include <dev/ppbus/ppbio.h> 51f7ce69c9SNicolas Souchu 52f7ce69c9SNicolas Souchu #include <dev/iicbus/iiconf.h> 53f7ce69c9SNicolas Souchu #include <dev/iicbus/iicbus.h> 54f7ce69c9SNicolas Souchu 55f7ce69c9SNicolas Souchu #include "iicbb_if.h" 56f7ce69c9SNicolas Souchu 57f7ce69c9SNicolas Souchu struct lpbb_softc { 580f210c92SNicolas Souchu int dummy; 59f7ce69c9SNicolas Souchu }; 60f7ce69c9SNicolas Souchu 610f210c92SNicolas Souchu static int lpbb_detect(device_t dev); 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 static int 97f7ce69c9SNicolas Souchu lpbb_probe(device_t dev) 98f7ce69c9SNicolas Souchu { 990f210c92SNicolas Souchu device_set_desc(dev, "Parallel I2C bit-banging interface"); 100f7ce69c9SNicolas Souchu 1010f210c92SNicolas Souchu if (!lpbb_detect(dev)) 1020f210c92SNicolas Souchu return (ENXIO); 103f7ce69c9SNicolas Souchu 104f7ce69c9SNicolas Souchu return (0); 105f7ce69c9SNicolas Souchu } 106f7ce69c9SNicolas Souchu 107f7ce69c9SNicolas Souchu static int 108f7ce69c9SNicolas Souchu lpbb_attach(device_t dev) 109f7ce69c9SNicolas Souchu { 110f7ce69c9SNicolas Souchu device_t bitbang, iicbus; 111f7ce69c9SNicolas Souchu 112f7ce69c9SNicolas Souchu /* add generic bit-banging code */ 113fe0d4089SMatthew N. Dodd bitbang = device_add_child(dev, "iicbb", -1); 114f7ce69c9SNicolas Souchu 115f7ce69c9SNicolas Souchu /* add the iicbus to the tree */ 116f7ce69c9SNicolas Souchu iicbus = iicbus_alloc_bus(bitbang); 117f7ce69c9SNicolas Souchu 118f7ce69c9SNicolas Souchu device_probe_and_attach(bitbang); 119f7ce69c9SNicolas Souchu 120f7ce69c9SNicolas Souchu /* XXX should be in iicbb_attach! */ 121f7ce69c9SNicolas Souchu device_probe_and_attach(iicbus); 122f7ce69c9SNicolas Souchu 123f7ce69c9SNicolas Souchu return (0); 124f7ce69c9SNicolas Souchu } 125f7ce69c9SNicolas Souchu 126f7ce69c9SNicolas Souchu static int 127f7ce69c9SNicolas Souchu lpbb_callback(device_t dev, int index, caddr_t *data) 128f7ce69c9SNicolas Souchu { 1290f210c92SNicolas Souchu device_t ppbus = device_get_parent(dev); 130f7ce69c9SNicolas Souchu int error = 0; 131f7ce69c9SNicolas Souchu int how; 132f7ce69c9SNicolas Souchu 133f7ce69c9SNicolas Souchu switch (index) { 134f7ce69c9SNicolas Souchu case IIC_REQUEST_BUS: 135f7ce69c9SNicolas Souchu /* request the ppbus */ 136f7ce69c9SNicolas Souchu how = *(int *)data; 1370f210c92SNicolas Souchu error = ppb_request_bus(ppbus, dev, how); 138f7ce69c9SNicolas Souchu break; 139f7ce69c9SNicolas Souchu 140f7ce69c9SNicolas Souchu case IIC_RELEASE_BUS: 141f7ce69c9SNicolas Souchu /* release the ppbus */ 1420f210c92SNicolas Souchu error = ppb_release_bus(ppbus, dev); 143f7ce69c9SNicolas Souchu break; 144f7ce69c9SNicolas Souchu 145f7ce69c9SNicolas Souchu default: 146f7ce69c9SNicolas Souchu error = EINVAL; 147f7ce69c9SNicolas Souchu } 148f7ce69c9SNicolas Souchu 149f7ce69c9SNicolas Souchu return (error); 150f7ce69c9SNicolas Souchu } 151f7ce69c9SNicolas Souchu 152f7ce69c9SNicolas Souchu #define SDA_out 0x80 153f7ce69c9SNicolas Souchu #define SCL_out 0x08 154f7ce69c9SNicolas Souchu #define SDA_in 0x80 155f7ce69c9SNicolas Souchu #define SCL_in 0x08 156f7ce69c9SNicolas Souchu #define ALIM 0x20 157f7ce69c9SNicolas Souchu #define I2CKEY 0x50 158f7ce69c9SNicolas Souchu 1590f210c92SNicolas Souchu static int getSDA(device_t ppbus) 160f7ce69c9SNicolas Souchu { 1610f210c92SNicolas Souchu if((ppb_rstr(ppbus)&SDA_in)==SDA_in) 162f7ce69c9SNicolas Souchu return 1; 163f7ce69c9SNicolas Souchu else 164f7ce69c9SNicolas Souchu return 0; 165f7ce69c9SNicolas Souchu } 166f7ce69c9SNicolas Souchu 1670f210c92SNicolas Souchu static void setSDA(device_t ppbus, char val) 168f7ce69c9SNicolas Souchu { 169f7ce69c9SNicolas Souchu if(val==0) 1700f210c92SNicolas Souchu ppb_wdtr(ppbus, (u_char)SDA_out); 171f7ce69c9SNicolas Souchu else 1720f210c92SNicolas Souchu ppb_wdtr(ppbus, (u_char)~SDA_out); 173f7ce69c9SNicolas Souchu } 174f7ce69c9SNicolas Souchu 1750f210c92SNicolas Souchu static void setSCL(device_t ppbus, unsigned char val) 176f7ce69c9SNicolas Souchu { 177f7ce69c9SNicolas Souchu if(val==0) 1780f210c92SNicolas Souchu ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus)&~SCL_out)); 179f7ce69c9SNicolas Souchu else 1800f210c92SNicolas Souchu ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus)|SCL_out)); 181f7ce69c9SNicolas Souchu } 182f7ce69c9SNicolas Souchu 1830f210c92SNicolas Souchu static int lpbb_detect(device_t dev) 184f7ce69c9SNicolas Souchu { 1850f210c92SNicolas Souchu device_t ppbus = device_get_parent(dev); 1860f210c92SNicolas Souchu 1870f210c92SNicolas Souchu if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) { 1880f210c92SNicolas Souchu device_printf(dev, "can't allocate ppbus\n"); 189f7ce69c9SNicolas Souchu return (0); 190f7ce69c9SNicolas Souchu } 191f7ce69c9SNicolas Souchu 192f7ce69c9SNicolas Souchu /* reset bus */ 1930f210c92SNicolas Souchu setSDA(ppbus, 1); 1940f210c92SNicolas Souchu setSCL(ppbus, 1); 195f7ce69c9SNicolas Souchu 1960f210c92SNicolas Souchu if ((ppb_rstr(ppbus) & I2CKEY) || 1970f210c92SNicolas Souchu ((ppb_rstr(ppbus) & ALIM) != ALIM)) { 198a6530aceSNicolas Souchu 1990f210c92SNicolas Souchu ppb_release_bus(ppbus, dev); 200f7ce69c9SNicolas Souchu return (0); 201a6530aceSNicolas Souchu } 202f7ce69c9SNicolas Souchu 2030f210c92SNicolas Souchu ppb_release_bus(ppbus, dev); 204f7ce69c9SNicolas Souchu 205f7ce69c9SNicolas Souchu return (1); 206f7ce69c9SNicolas Souchu } 207f7ce69c9SNicolas Souchu 208f7ce69c9SNicolas Souchu static int 209f7ce69c9SNicolas Souchu lpbb_reset(device_t dev, u_char speed, u_char addr, u_char * oldaddr) 210f7ce69c9SNicolas Souchu { 2110f210c92SNicolas Souchu device_t ppbus = device_get_parent(dev); 212f7ce69c9SNicolas Souchu 213f7ce69c9SNicolas Souchu /* reset bus */ 2140f210c92SNicolas Souchu setSDA(ppbus, 1); 2150f210c92SNicolas Souchu setSCL(ppbus, 1); 216f7ce69c9SNicolas Souchu 217f7ce69c9SNicolas Souchu return (IIC_ENOADDR); 218f7ce69c9SNicolas Souchu } 219f7ce69c9SNicolas Souchu 220f7ce69c9SNicolas Souchu static void 221f7ce69c9SNicolas Souchu lpbb_setlines(device_t dev, int ctrl, int data) 222f7ce69c9SNicolas Souchu { 2230f210c92SNicolas Souchu device_t ppbus = device_get_parent(dev); 224f7ce69c9SNicolas Souchu 2250f210c92SNicolas Souchu setSCL(ppbus, ctrl); 2260f210c92SNicolas Souchu setSDA(ppbus, data); 227f7ce69c9SNicolas Souchu } 228f7ce69c9SNicolas Souchu 229f7ce69c9SNicolas Souchu static int 230f7ce69c9SNicolas Souchu lpbb_getdataline(device_t dev) 231f7ce69c9SNicolas Souchu { 2320f210c92SNicolas Souchu device_t ppbus = device_get_parent(dev); 233f7ce69c9SNicolas Souchu 2340f210c92SNicolas Souchu return (getSDA(ppbus)); 235f7ce69c9SNicolas Souchu } 236f7ce69c9SNicolas Souchu 2370f210c92SNicolas Souchu DRIVER_MODULE(lpbb, ppbus, lpbb_driver, lpbb_devclass, 0, 0); 238