1f7ce69c9SNicolas Souchu /*- 2c17d4340SNicolas Souchu * Copyright (c) 1998, 2001 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 * 27f7ce69c9SNicolas Souchu */ 28f7ce69c9SNicolas Souchu 29aad970f1SDavid E. O'Brien #include <sys/cdefs.h> 30aad970f1SDavid E. O'Brien __FBSDID("$FreeBSD$"); 31aad970f1SDavid E. O'Brien 32f7ce69c9SNicolas Souchu /* 33f7ce69c9SNicolas Souchu * I2C Bit-Banging over parallel port 34f7ce69c9SNicolas Souchu * 35f7ce69c9SNicolas Souchu * See the Official Philips interface description in lpbb(4) 36f7ce69c9SNicolas Souchu */ 37f7ce69c9SNicolas Souchu 38f7ce69c9SNicolas Souchu #include <sys/param.h> 39f7ce69c9SNicolas Souchu #include <sys/bus.h> 4013e3657bSJohn Baldwin #include <sys/lock.h> 4113e3657bSJohn Baldwin #include <sys/kernel.h> 4213e3657bSJohn Baldwin #include <sys/module.h> 4313e3657bSJohn Baldwin #include <sys/mutex.h> 4413e3657bSJohn Baldwin #include <sys/systm.h> 45f7ce69c9SNicolas Souchu #include <sys/uio.h> 46f7ce69c9SNicolas Souchu 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 570f210c92SNicolas Souchu static int lpbb_detect(device_t dev); 58f7ce69c9SNicolas Souchu 590f063508SPeter Wemm static void 600f063508SPeter Wemm lpbb_identify(driver_t *driver, device_t parent) 610f063508SPeter Wemm { 62f7ce69c9SNicolas Souchu 63a5c7e3bbSGuido van Rooij device_t dev; 64a5c7e3bbSGuido van Rooij 65ae6b868aSJohn Baldwin dev = device_find_child(parent, "lpbb", -1); 66a5c7e3bbSGuido van Rooij if (!dev) 67338cad62SBernd Walter BUS_ADD_CHILD(parent, 0, "lpbb", -1); 680f063508SPeter Wemm } 69f7ce69c9SNicolas Souchu 70f7ce69c9SNicolas Souchu static int 71f7ce69c9SNicolas Souchu lpbb_probe(device_t dev) 72f7ce69c9SNicolas Souchu { 73f7ce69c9SNicolas Souchu 740f063508SPeter Wemm /* Perhaps call this during identify instead? */ 750f210c92SNicolas Souchu if (!lpbb_detect(dev)) 760f210c92SNicolas Souchu return (ENXIO); 77f7ce69c9SNicolas Souchu 780f063508SPeter Wemm device_set_desc(dev, "Parallel I2C bit-banging interface"); 790f063508SPeter Wemm 80f7ce69c9SNicolas Souchu return (0); 81f7ce69c9SNicolas Souchu } 82f7ce69c9SNicolas Souchu 83f7ce69c9SNicolas Souchu static int 84f7ce69c9SNicolas Souchu lpbb_attach(device_t dev) 85f7ce69c9SNicolas Souchu { 86c17d4340SNicolas Souchu device_t bitbang; 87f7ce69c9SNicolas Souchu 88f7ce69c9SNicolas Souchu /* add generic bit-banging code */ 89fe0d4089SMatthew N. Dodd bitbang = device_add_child(dev, "iicbb", -1); 90f7ce69c9SNicolas Souchu device_probe_and_attach(bitbang); 91f7ce69c9SNicolas Souchu 92f7ce69c9SNicolas Souchu return (0); 93f7ce69c9SNicolas Souchu } 94f7ce69c9SNicolas Souchu 95f7ce69c9SNicolas Souchu static int 9634a53f3fSWarner Losh lpbb_callback(device_t dev, int index, caddr_t data) 97f7ce69c9SNicolas Souchu { 980f210c92SNicolas Souchu device_t ppbus = device_get_parent(dev); 99f7ce69c9SNicolas Souchu int error = 0; 100f7ce69c9SNicolas Souchu int how; 101f7ce69c9SNicolas Souchu 102f7ce69c9SNicolas Souchu switch (index) { 103f7ce69c9SNicolas Souchu case IIC_REQUEST_BUS: 104f7ce69c9SNicolas Souchu /* request the ppbus */ 105f7ce69c9SNicolas Souchu how = *(int *)data; 1062067d312SJohn Baldwin ppb_lock(ppbus); 1070f210c92SNicolas Souchu error = ppb_request_bus(ppbus, dev, how); 1082067d312SJohn Baldwin ppb_unlock(ppbus); 109f7ce69c9SNicolas Souchu break; 110f7ce69c9SNicolas Souchu 111f7ce69c9SNicolas Souchu case IIC_RELEASE_BUS: 112f7ce69c9SNicolas Souchu /* release the ppbus */ 1132067d312SJohn Baldwin ppb_lock(ppbus); 1140f210c92SNicolas Souchu error = ppb_release_bus(ppbus, dev); 1152067d312SJohn Baldwin ppb_unlock(ppbus); 116f7ce69c9SNicolas Souchu break; 117f7ce69c9SNicolas Souchu 118f7ce69c9SNicolas Souchu default: 119f7ce69c9SNicolas Souchu error = EINVAL; 120f7ce69c9SNicolas Souchu } 121f7ce69c9SNicolas Souchu 122f7ce69c9SNicolas Souchu return (error); 123f7ce69c9SNicolas Souchu } 124f7ce69c9SNicolas Souchu 125f7ce69c9SNicolas Souchu #define SDA_out 0x80 126f7ce69c9SNicolas Souchu #define SCL_out 0x08 127f7ce69c9SNicolas Souchu #define SDA_in 0x80 128f7ce69c9SNicolas Souchu #define SCL_in 0x08 129f7ce69c9SNicolas Souchu #define ALIM 0x20 130f7ce69c9SNicolas Souchu #define I2CKEY 0x50 131f7ce69c9SNicolas Souchu 1322067d312SJohn Baldwin /* Reset bus by setting SDA first and then SCL. */ 1332067d312SJohn Baldwin static void 1342067d312SJohn Baldwin lpbb_reset_bus(device_t dev) 1352067d312SJohn Baldwin { 1362067d312SJohn Baldwin device_t ppbus = device_get_parent(dev); 1372067d312SJohn Baldwin 1382067d312SJohn Baldwin ppb_assert_locked(ppbus); 1392067d312SJohn Baldwin ppb_wdtr(ppbus, (u_char)~SDA_out); 1402067d312SJohn Baldwin ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus) | SCL_out)); 1412067d312SJohn Baldwin } 1422067d312SJohn Baldwin 14313e3657bSJohn Baldwin static int 14413e3657bSJohn Baldwin lpbb_getscl(device_t dev) 145f7ce69c9SNicolas Souchu { 1462067d312SJohn Baldwin device_t ppbus = device_get_parent(dev); 14713e3657bSJohn Baldwin int rval; 14813e3657bSJohn Baldwin 1492067d312SJohn Baldwin ppb_lock(ppbus); 1502067d312SJohn Baldwin rval = ((ppb_rstr(ppbus) & SCL_in) == SCL_in); 1512067d312SJohn Baldwin ppb_unlock(ppbus); 15213e3657bSJohn Baldwin return (rval); 153f7ce69c9SNicolas Souchu } 154f7ce69c9SNicolas Souchu 15513e3657bSJohn Baldwin static int 15613e3657bSJohn Baldwin lpbb_getsda(device_t dev) 157f7ce69c9SNicolas Souchu { 1582067d312SJohn Baldwin device_t ppbus = device_get_parent(dev); 15913e3657bSJohn Baldwin int rval; 16013e3657bSJohn Baldwin 1612067d312SJohn Baldwin ppb_lock(ppbus); 1622067d312SJohn Baldwin rval = ((ppb_rstr(ppbus) & SDA_in) == SDA_in); 1632067d312SJohn Baldwin ppb_unlock(ppbus); 16413e3657bSJohn Baldwin return (rval); 165c17d4340SNicolas Souchu } 166c17d4340SNicolas Souchu 16713e3657bSJohn Baldwin static void 16834a53f3fSWarner Losh lpbb_setsda(device_t dev, int val) 169c17d4340SNicolas Souchu { 170c17d4340SNicolas Souchu device_t ppbus = device_get_parent(dev); 171c17d4340SNicolas Souchu 1722067d312SJohn Baldwin ppb_lock(ppbus); 173f7ce69c9SNicolas Souchu if (val == 0) 1740f210c92SNicolas Souchu ppb_wdtr(ppbus, (u_char)SDA_out); 175f7ce69c9SNicolas Souchu else 1760f210c92SNicolas Souchu ppb_wdtr(ppbus, (u_char)~SDA_out); 1772067d312SJohn Baldwin ppb_unlock(ppbus); 178f7ce69c9SNicolas Souchu } 179f7ce69c9SNicolas Souchu 18013e3657bSJohn Baldwin static void 18134a53f3fSWarner Losh lpbb_setscl(device_t dev, int val) 182f7ce69c9SNicolas Souchu { 183c17d4340SNicolas Souchu device_t ppbus = device_get_parent(dev); 184c17d4340SNicolas Souchu 1852067d312SJohn Baldwin ppb_lock(ppbus); 186f7ce69c9SNicolas Souchu if (val == 0) 1870f210c92SNicolas Souchu ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus) & ~SCL_out)); 188f7ce69c9SNicolas Souchu else 1890f210c92SNicolas Souchu ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus) | SCL_out)); 1902067d312SJohn Baldwin ppb_unlock(ppbus); 191f7ce69c9SNicolas Souchu } 192f7ce69c9SNicolas Souchu 19313e3657bSJohn Baldwin static int 19413e3657bSJohn Baldwin lpbb_detect(device_t dev) 195f7ce69c9SNicolas Souchu { 1960f210c92SNicolas Souchu device_t ppbus = device_get_parent(dev); 1970f210c92SNicolas Souchu 1982067d312SJohn Baldwin ppb_lock(ppbus); 1990f210c92SNicolas Souchu if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) { 2002067d312SJohn Baldwin ppb_unlock(ppbus); 2010f210c92SNicolas Souchu device_printf(dev, "can't allocate ppbus\n"); 202f7ce69c9SNicolas Souchu return (0); 203f7ce69c9SNicolas Souchu } 204f7ce69c9SNicolas Souchu 2052067d312SJohn Baldwin lpbb_reset_bus(dev); 206f7ce69c9SNicolas Souchu 2070f210c92SNicolas Souchu if ((ppb_rstr(ppbus) & I2CKEY) || 2080f210c92SNicolas Souchu ((ppb_rstr(ppbus) & ALIM) != ALIM)) { 2090f210c92SNicolas Souchu ppb_release_bus(ppbus, dev); 2102067d312SJohn Baldwin ppb_unlock(ppbus); 211f7ce69c9SNicolas Souchu return (0); 212a6530aceSNicolas Souchu } 213f7ce69c9SNicolas Souchu 2140f210c92SNicolas Souchu ppb_release_bus(ppbus, dev); 2152067d312SJohn Baldwin ppb_unlock(ppbus); 216f7ce69c9SNicolas Souchu 217f7ce69c9SNicolas Souchu return (1); 218f7ce69c9SNicolas Souchu } 219f7ce69c9SNicolas Souchu 220f7ce69c9SNicolas Souchu static int 221f7ce69c9SNicolas Souchu lpbb_reset(device_t dev, u_char speed, u_char addr, u_char * oldaddr) 222f7ce69c9SNicolas Souchu { 2230f210c92SNicolas Souchu device_t ppbus = device_get_parent(dev); 224f7ce69c9SNicolas Souchu 2252067d312SJohn Baldwin ppb_lock(ppbus); 226c17d4340SNicolas Souchu if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) { 2272067d312SJohn Baldwin ppb_unlock(ppbus); 228c17d4340SNicolas Souchu device_printf(dev, "can't allocate ppbus\n"); 229c17d4340SNicolas Souchu return (0); 230c17d4340SNicolas Souchu } 231c17d4340SNicolas Souchu 2322067d312SJohn Baldwin lpbb_reset_bus(dev); 233c17d4340SNicolas Souchu 234c17d4340SNicolas Souchu ppb_release_bus(ppbus, dev); 2352067d312SJohn Baldwin ppb_unlock(ppbus); 236f7ce69c9SNicolas Souchu 237f7ce69c9SNicolas Souchu return (IIC_ENOADDR); 238f7ce69c9SNicolas Souchu } 239f7ce69c9SNicolas Souchu 2400f063508SPeter Wemm static devclass_t lpbb_devclass; 2410f063508SPeter Wemm 2420f063508SPeter Wemm static device_method_t lpbb_methods[] = { 2430f063508SPeter Wemm /* device interface */ 2440f063508SPeter Wemm DEVMETHOD(device_identify, lpbb_identify), 2450f063508SPeter Wemm DEVMETHOD(device_probe, lpbb_probe), 2460f063508SPeter Wemm DEVMETHOD(device_attach, lpbb_attach), 2470f063508SPeter Wemm 2480f063508SPeter Wemm /* iicbb interface */ 2490f063508SPeter Wemm DEVMETHOD(iicbb_callback, lpbb_callback), 250c17d4340SNicolas Souchu DEVMETHOD(iicbb_setsda, lpbb_setsda), 251c17d4340SNicolas Souchu DEVMETHOD(iicbb_setscl, lpbb_setscl), 252c17d4340SNicolas Souchu DEVMETHOD(iicbb_getsda, lpbb_getsda), 253c17d4340SNicolas Souchu DEVMETHOD(iicbb_getscl, lpbb_getscl), 2540f063508SPeter Wemm DEVMETHOD(iicbb_reset, lpbb_reset), 2550f063508SPeter Wemm 256*4b7ec270SMarius Strobl DEVMETHOD_END 2570f063508SPeter Wemm }; 2580f063508SPeter Wemm 2590f063508SPeter Wemm static driver_t lpbb_driver = { 2600f063508SPeter Wemm "lpbb", 2610f063508SPeter Wemm lpbb_methods, 2620f063508SPeter Wemm 1, 2630f063508SPeter Wemm }; 2640f063508SPeter Wemm 2650f210c92SNicolas Souchu DRIVER_MODULE(lpbb, ppbus, lpbb_driver, lpbb_devclass, 0, 0); 26613e3657bSJohn Baldwin DRIVER_MODULE(iicbb, lpbb, iicbb_driver, iicbb_devclass, 0, 0); 267f5fd5611SRuslan Ermilov MODULE_DEPEND(lpbb, ppbus, 1, 1, 1); 268c17d4340SNicolas Souchu MODULE_DEPEND(lpbb, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER); 269c17d4340SNicolas Souchu MODULE_VERSION(lpbb, 1); 270