1f7ce69c9SNicolas Souchu /*- 2718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3718cf2ccSPedro F. Giffuni * 4c17d4340SNicolas Souchu * Copyright (c) 1998, 2001 Nicolas Souchu, Marc Bouget 5f7ce69c9SNicolas Souchu * All rights reserved. 6f7ce69c9SNicolas Souchu * 7f7ce69c9SNicolas Souchu * Redistribution and use in source and binary forms, with or without 8f7ce69c9SNicolas Souchu * modification, are permitted provided that the following conditions 9f7ce69c9SNicolas Souchu * are met: 10f7ce69c9SNicolas Souchu * 1. Redistributions of source code must retain the above copyright 11f7ce69c9SNicolas Souchu * notice, this list of conditions and the following disclaimer. 12f7ce69c9SNicolas Souchu * 2. Redistributions in binary form must reproduce the above copyright 13f7ce69c9SNicolas Souchu * notice, this list of conditions and the following disclaimer in the 14f7ce69c9SNicolas Souchu * documentation and/or other materials provided with the distribution. 15f7ce69c9SNicolas Souchu * 16f7ce69c9SNicolas Souchu * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17f7ce69c9SNicolas Souchu * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18f7ce69c9SNicolas Souchu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19f7ce69c9SNicolas Souchu * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20f7ce69c9SNicolas Souchu * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21f7ce69c9SNicolas Souchu * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22f7ce69c9SNicolas Souchu * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23f7ce69c9SNicolas Souchu * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24f7ce69c9SNicolas Souchu * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25f7ce69c9SNicolas Souchu * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26f7ce69c9SNicolas Souchu * SUCH DAMAGE. 27f7ce69c9SNicolas Souchu * 28f7ce69c9SNicolas Souchu * 29f7ce69c9SNicolas Souchu */ 30f7ce69c9SNicolas Souchu 31aad970f1SDavid E. O'Brien #include <sys/cdefs.h> 32aad970f1SDavid E. O'Brien __FBSDID("$FreeBSD$"); 33aad970f1SDavid E. O'Brien 34f7ce69c9SNicolas Souchu /* 35f7ce69c9SNicolas Souchu * I2C Bit-Banging over parallel port 36f7ce69c9SNicolas Souchu * 37f7ce69c9SNicolas Souchu * See the Official Philips interface description in lpbb(4) 38f7ce69c9SNicolas Souchu */ 39f7ce69c9SNicolas Souchu 40f7ce69c9SNicolas Souchu #include <sys/param.h> 41f7ce69c9SNicolas Souchu #include <sys/bus.h> 4213e3657bSJohn Baldwin #include <sys/lock.h> 4313e3657bSJohn Baldwin #include <sys/kernel.h> 4413e3657bSJohn Baldwin #include <sys/module.h> 4513e3657bSJohn Baldwin #include <sys/mutex.h> 4613e3657bSJohn Baldwin #include <sys/systm.h> 47f7ce69c9SNicolas Souchu #include <sys/uio.h> 48f7ce69c9SNicolas Souchu 49f7ce69c9SNicolas Souchu #include <dev/ppbus/ppbconf.h> 500f210c92SNicolas Souchu #include "ppbus_if.h" 510f210c92SNicolas Souchu #include <dev/ppbus/ppbio.h> 52f7ce69c9SNicolas Souchu 53f7ce69c9SNicolas Souchu #include <dev/iicbus/iiconf.h> 54f7ce69c9SNicolas Souchu #include <dev/iicbus/iicbus.h> 55f7ce69c9SNicolas Souchu 56f7ce69c9SNicolas Souchu #include "iicbb_if.h" 57f7ce69c9SNicolas Souchu 580f210c92SNicolas Souchu static int lpbb_detect(device_t dev); 59f7ce69c9SNicolas Souchu 600f063508SPeter Wemm static void 610f063508SPeter Wemm lpbb_identify(driver_t *driver, device_t parent) 620f063508SPeter Wemm { 63f7ce69c9SNicolas Souchu 64a5c7e3bbSGuido van Rooij device_t dev; 65a5c7e3bbSGuido van Rooij 66ae6b868aSJohn Baldwin dev = device_find_child(parent, "lpbb", -1); 67a5c7e3bbSGuido van Rooij if (!dev) 68338cad62SBernd Walter BUS_ADD_CHILD(parent, 0, "lpbb", -1); 690f063508SPeter Wemm } 70f7ce69c9SNicolas Souchu 71f7ce69c9SNicolas Souchu static int 72f7ce69c9SNicolas Souchu lpbb_probe(device_t dev) 73f7ce69c9SNicolas Souchu { 74f7ce69c9SNicolas Souchu 750f063508SPeter Wemm /* Perhaps call this during identify instead? */ 760f210c92SNicolas Souchu if (!lpbb_detect(dev)) 770f210c92SNicolas Souchu return (ENXIO); 78f7ce69c9SNicolas Souchu 790f063508SPeter Wemm device_set_desc(dev, "Parallel I2C bit-banging interface"); 800f063508SPeter Wemm 81f7ce69c9SNicolas Souchu return (0); 82f7ce69c9SNicolas Souchu } 83f7ce69c9SNicolas Souchu 84f7ce69c9SNicolas Souchu static int 85f7ce69c9SNicolas Souchu lpbb_attach(device_t dev) 86f7ce69c9SNicolas Souchu { 87c17d4340SNicolas Souchu device_t bitbang; 88f7ce69c9SNicolas Souchu 89f7ce69c9SNicolas Souchu /* add generic bit-banging code */ 90fe0d4089SMatthew N. Dodd bitbang = device_add_child(dev, "iicbb", -1); 91f7ce69c9SNicolas Souchu device_probe_and_attach(bitbang); 92f7ce69c9SNicolas Souchu 93f7ce69c9SNicolas Souchu return (0); 94f7ce69c9SNicolas Souchu } 95f7ce69c9SNicolas Souchu 96f7ce69c9SNicolas Souchu static int 9734a53f3fSWarner Losh lpbb_callback(device_t dev, int index, caddr_t data) 98f7ce69c9SNicolas Souchu { 990f210c92SNicolas Souchu device_t ppbus = device_get_parent(dev); 100f7ce69c9SNicolas Souchu int error = 0; 101f7ce69c9SNicolas Souchu int how; 102f7ce69c9SNicolas Souchu 103f7ce69c9SNicolas Souchu switch (index) { 104f7ce69c9SNicolas Souchu case IIC_REQUEST_BUS: 105f7ce69c9SNicolas Souchu /* request the ppbus */ 106f7ce69c9SNicolas Souchu how = *(int *)data; 1072067d312SJohn Baldwin ppb_lock(ppbus); 1080f210c92SNicolas Souchu error = ppb_request_bus(ppbus, dev, how); 1092067d312SJohn Baldwin ppb_unlock(ppbus); 110f7ce69c9SNicolas Souchu break; 111f7ce69c9SNicolas Souchu 112f7ce69c9SNicolas Souchu case IIC_RELEASE_BUS: 113f7ce69c9SNicolas Souchu /* release the ppbus */ 1142067d312SJohn Baldwin ppb_lock(ppbus); 1150f210c92SNicolas Souchu error = ppb_release_bus(ppbus, dev); 1162067d312SJohn Baldwin ppb_unlock(ppbus); 117f7ce69c9SNicolas Souchu break; 118f7ce69c9SNicolas Souchu 119f7ce69c9SNicolas Souchu default: 120f7ce69c9SNicolas Souchu error = EINVAL; 121f7ce69c9SNicolas Souchu } 122f7ce69c9SNicolas Souchu 123f7ce69c9SNicolas Souchu return (error); 124f7ce69c9SNicolas Souchu } 125f7ce69c9SNicolas Souchu 126f7ce69c9SNicolas Souchu #define SDA_out 0x80 127f7ce69c9SNicolas Souchu #define SCL_out 0x08 128f7ce69c9SNicolas Souchu #define SDA_in 0x80 129f7ce69c9SNicolas Souchu #define SCL_in 0x08 130f7ce69c9SNicolas Souchu #define ALIM 0x20 131f7ce69c9SNicolas Souchu #define I2CKEY 0x50 132f7ce69c9SNicolas Souchu 1332067d312SJohn Baldwin /* Reset bus by setting SDA first and then SCL. */ 1342067d312SJohn Baldwin static void 1352067d312SJohn Baldwin lpbb_reset_bus(device_t dev) 1362067d312SJohn Baldwin { 1372067d312SJohn Baldwin device_t ppbus = device_get_parent(dev); 1382067d312SJohn Baldwin 1392067d312SJohn Baldwin ppb_assert_locked(ppbus); 1402067d312SJohn Baldwin ppb_wdtr(ppbus, (u_char)~SDA_out); 1412067d312SJohn Baldwin ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus) | SCL_out)); 1422067d312SJohn Baldwin } 1432067d312SJohn Baldwin 14413e3657bSJohn Baldwin static int 14513e3657bSJohn Baldwin lpbb_getscl(device_t dev) 146f7ce69c9SNicolas Souchu { 1472067d312SJohn Baldwin device_t ppbus = device_get_parent(dev); 14813e3657bSJohn Baldwin int rval; 14913e3657bSJohn Baldwin 1502067d312SJohn Baldwin ppb_lock(ppbus); 1512067d312SJohn Baldwin rval = ((ppb_rstr(ppbus) & SCL_in) == SCL_in); 1522067d312SJohn Baldwin ppb_unlock(ppbus); 15313e3657bSJohn Baldwin return (rval); 154f7ce69c9SNicolas Souchu } 155f7ce69c9SNicolas Souchu 15613e3657bSJohn Baldwin static int 15713e3657bSJohn Baldwin lpbb_getsda(device_t dev) 158f7ce69c9SNicolas Souchu { 1592067d312SJohn Baldwin device_t ppbus = device_get_parent(dev); 16013e3657bSJohn Baldwin int rval; 16113e3657bSJohn Baldwin 1622067d312SJohn Baldwin ppb_lock(ppbus); 1632067d312SJohn Baldwin rval = ((ppb_rstr(ppbus) & SDA_in) == SDA_in); 1642067d312SJohn Baldwin ppb_unlock(ppbus); 16513e3657bSJohn Baldwin return (rval); 166c17d4340SNicolas Souchu } 167c17d4340SNicolas Souchu 16813e3657bSJohn Baldwin static void 16934a53f3fSWarner Losh lpbb_setsda(device_t dev, int val) 170c17d4340SNicolas Souchu { 171c17d4340SNicolas Souchu device_t ppbus = device_get_parent(dev); 172c17d4340SNicolas Souchu 1732067d312SJohn Baldwin ppb_lock(ppbus); 174f7ce69c9SNicolas Souchu if (val == 0) 1750f210c92SNicolas Souchu ppb_wdtr(ppbus, (u_char)SDA_out); 176f7ce69c9SNicolas Souchu else 1770f210c92SNicolas Souchu ppb_wdtr(ppbus, (u_char)~SDA_out); 1782067d312SJohn Baldwin ppb_unlock(ppbus); 179f7ce69c9SNicolas Souchu } 180f7ce69c9SNicolas Souchu 18113e3657bSJohn Baldwin static void 18234a53f3fSWarner Losh lpbb_setscl(device_t dev, int val) 183f7ce69c9SNicolas Souchu { 184c17d4340SNicolas Souchu device_t ppbus = device_get_parent(dev); 185c17d4340SNicolas Souchu 1862067d312SJohn Baldwin ppb_lock(ppbus); 187f7ce69c9SNicolas Souchu if (val == 0) 1880f210c92SNicolas Souchu ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus) & ~SCL_out)); 189f7ce69c9SNicolas Souchu else 1900f210c92SNicolas Souchu ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus) | SCL_out)); 1912067d312SJohn Baldwin ppb_unlock(ppbus); 192f7ce69c9SNicolas Souchu } 193f7ce69c9SNicolas Souchu 19413e3657bSJohn Baldwin static int 19513e3657bSJohn Baldwin lpbb_detect(device_t dev) 196f7ce69c9SNicolas Souchu { 1970f210c92SNicolas Souchu device_t ppbus = device_get_parent(dev); 1980f210c92SNicolas Souchu 1992067d312SJohn Baldwin ppb_lock(ppbus); 2000f210c92SNicolas Souchu if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) { 2012067d312SJohn Baldwin ppb_unlock(ppbus); 2020f210c92SNicolas Souchu device_printf(dev, "can't allocate ppbus\n"); 203f7ce69c9SNicolas Souchu return (0); 204f7ce69c9SNicolas Souchu } 205f7ce69c9SNicolas Souchu 2062067d312SJohn Baldwin lpbb_reset_bus(dev); 207f7ce69c9SNicolas Souchu 2080f210c92SNicolas Souchu if ((ppb_rstr(ppbus) & I2CKEY) || 2090f210c92SNicolas Souchu ((ppb_rstr(ppbus) & ALIM) != ALIM)) { 2100f210c92SNicolas Souchu ppb_release_bus(ppbus, dev); 2112067d312SJohn Baldwin ppb_unlock(ppbus); 212f7ce69c9SNicolas Souchu return (0); 213a6530aceSNicolas Souchu } 214f7ce69c9SNicolas Souchu 2150f210c92SNicolas Souchu ppb_release_bus(ppbus, dev); 2162067d312SJohn Baldwin ppb_unlock(ppbus); 217f7ce69c9SNicolas Souchu 218f7ce69c9SNicolas Souchu return (1); 219f7ce69c9SNicolas Souchu } 220f7ce69c9SNicolas Souchu 221f7ce69c9SNicolas Souchu static int 222f7ce69c9SNicolas Souchu lpbb_reset(device_t dev, u_char speed, u_char addr, u_char * oldaddr) 223f7ce69c9SNicolas Souchu { 2240f210c92SNicolas Souchu device_t ppbus = device_get_parent(dev); 225f7ce69c9SNicolas Souchu 2262067d312SJohn Baldwin ppb_lock(ppbus); 227c17d4340SNicolas Souchu if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) { 2282067d312SJohn Baldwin ppb_unlock(ppbus); 229c17d4340SNicolas Souchu device_printf(dev, "can't allocate ppbus\n"); 230c17d4340SNicolas Souchu return (0); 231c17d4340SNicolas Souchu } 232c17d4340SNicolas Souchu 2332067d312SJohn Baldwin lpbb_reset_bus(dev); 234c17d4340SNicolas Souchu 235c17d4340SNicolas Souchu ppb_release_bus(ppbus, dev); 2362067d312SJohn Baldwin ppb_unlock(ppbus); 237f7ce69c9SNicolas Souchu 238f7ce69c9SNicolas Souchu return (IIC_ENOADDR); 239f7ce69c9SNicolas Souchu } 240f7ce69c9SNicolas Souchu 2410f063508SPeter Wemm static device_method_t lpbb_methods[] = { 2420f063508SPeter Wemm /* device interface */ 2430f063508SPeter Wemm DEVMETHOD(device_identify, lpbb_identify), 2440f063508SPeter Wemm DEVMETHOD(device_probe, lpbb_probe), 2450f063508SPeter Wemm DEVMETHOD(device_attach, lpbb_attach), 2460f063508SPeter Wemm 2470f063508SPeter Wemm /* iicbb interface */ 2480f063508SPeter Wemm DEVMETHOD(iicbb_callback, lpbb_callback), 249c17d4340SNicolas Souchu DEVMETHOD(iicbb_setsda, lpbb_setsda), 250c17d4340SNicolas Souchu DEVMETHOD(iicbb_setscl, lpbb_setscl), 251c17d4340SNicolas Souchu DEVMETHOD(iicbb_getsda, lpbb_getsda), 252c17d4340SNicolas Souchu DEVMETHOD(iicbb_getscl, lpbb_getscl), 2530f063508SPeter Wemm DEVMETHOD(iicbb_reset, lpbb_reset), 2540f063508SPeter Wemm 2554b7ec270SMarius Strobl DEVMETHOD_END 2560f063508SPeter Wemm }; 2570f063508SPeter Wemm 2580f063508SPeter Wemm static driver_t lpbb_driver = { 2590f063508SPeter Wemm "lpbb", 2600f063508SPeter Wemm lpbb_methods, 2610f063508SPeter Wemm 1, 2620f063508SPeter Wemm }; 2630f063508SPeter Wemm 264*6d588090SJohn Baldwin DRIVER_MODULE(lpbb, ppbus, lpbb_driver, 0, 0); 2658c13dd83SJohn Baldwin DRIVER_MODULE(iicbb, lpbb, iicbb_driver, 0, 0); 266f5fd5611SRuslan Ermilov MODULE_DEPEND(lpbb, ppbus, 1, 1, 1); 267c17d4340SNicolas Souchu MODULE_DEPEND(lpbb, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER); 268c17d4340SNicolas Souchu MODULE_VERSION(lpbb, 1); 269