1f7ce69c9SNicolas Souchu /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 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> 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 #include <dev/ppbus/ppbconf.h> 480f210c92SNicolas Souchu #include "ppbus_if.h" 490f210c92SNicolas Souchu #include <dev/ppbus/ppbio.h> 50f7ce69c9SNicolas Souchu 51f7ce69c9SNicolas Souchu #include <dev/iicbus/iiconf.h> 52f7ce69c9SNicolas Souchu #include <dev/iicbus/iicbus.h> 53f7ce69c9SNicolas Souchu 54f7ce69c9SNicolas Souchu #include "iicbb_if.h" 55f7ce69c9SNicolas Souchu 560f210c92SNicolas Souchu static int lpbb_detect(device_t dev); 57f7ce69c9SNicolas Souchu 580f063508SPeter Wemm static void 590f063508SPeter Wemm lpbb_identify(driver_t *driver, device_t parent) 600f063508SPeter Wemm { 61f7ce69c9SNicolas Souchu 62a5c7e3bbSGuido van Rooij device_t dev; 63a5c7e3bbSGuido van Rooij 64ae6b868aSJohn Baldwin dev = device_find_child(parent, "lpbb", -1); 65a5c7e3bbSGuido van Rooij if (!dev) 66*a05a6804SWarner Losh BUS_ADD_CHILD(parent, 0, "lpbb", DEVICE_UNIT_ANY); 670f063508SPeter Wemm } 68f7ce69c9SNicolas Souchu 69f7ce69c9SNicolas Souchu static int 70f7ce69c9SNicolas Souchu lpbb_probe(device_t dev) 71f7ce69c9SNicolas Souchu { 72f7ce69c9SNicolas Souchu 730f063508SPeter Wemm /* Perhaps call this during identify instead? */ 740f210c92SNicolas Souchu if (!lpbb_detect(dev)) 750f210c92SNicolas Souchu return (ENXIO); 76f7ce69c9SNicolas Souchu 770f063508SPeter Wemm device_set_desc(dev, "Parallel I2C bit-banging interface"); 780f063508SPeter Wemm 79f7ce69c9SNicolas Souchu return (0); 80f7ce69c9SNicolas Souchu } 81f7ce69c9SNicolas Souchu 82f7ce69c9SNicolas Souchu static int 83f7ce69c9SNicolas Souchu lpbb_attach(device_t dev) 84f7ce69c9SNicolas Souchu { 85c17d4340SNicolas Souchu device_t bitbang; 86f7ce69c9SNicolas Souchu 87f7ce69c9SNicolas Souchu /* add generic bit-banging code */ 885b56413dSWarner Losh bitbang = device_add_child(dev, "iicbb", DEVICE_UNIT_ANY); 89f7ce69c9SNicolas Souchu device_probe_and_attach(bitbang); 90f7ce69c9SNicolas Souchu 91f7ce69c9SNicolas Souchu return (0); 92f7ce69c9SNicolas Souchu } 93f7ce69c9SNicolas Souchu 94f7ce69c9SNicolas Souchu static int 9534a53f3fSWarner Losh lpbb_callback(device_t dev, int index, caddr_t data) 96f7ce69c9SNicolas Souchu { 970f210c92SNicolas Souchu device_t ppbus = device_get_parent(dev); 98f7ce69c9SNicolas Souchu int error = 0; 99f7ce69c9SNicolas Souchu int how; 100f7ce69c9SNicolas Souchu 101f7ce69c9SNicolas Souchu switch (index) { 102f7ce69c9SNicolas Souchu case IIC_REQUEST_BUS: 103f7ce69c9SNicolas Souchu /* request the ppbus */ 104f7ce69c9SNicolas Souchu how = *(int *)data; 1052067d312SJohn Baldwin ppb_lock(ppbus); 1060f210c92SNicolas Souchu error = ppb_request_bus(ppbus, dev, how); 1072067d312SJohn Baldwin ppb_unlock(ppbus); 108f7ce69c9SNicolas Souchu break; 109f7ce69c9SNicolas Souchu 110f7ce69c9SNicolas Souchu case IIC_RELEASE_BUS: 111f7ce69c9SNicolas Souchu /* release the ppbus */ 1122067d312SJohn Baldwin ppb_lock(ppbus); 1130f210c92SNicolas Souchu error = ppb_release_bus(ppbus, dev); 1142067d312SJohn Baldwin ppb_unlock(ppbus); 115f7ce69c9SNicolas Souchu break; 116f7ce69c9SNicolas Souchu 117f7ce69c9SNicolas Souchu default: 118f7ce69c9SNicolas Souchu error = EINVAL; 119f7ce69c9SNicolas Souchu } 120f7ce69c9SNicolas Souchu 121f7ce69c9SNicolas Souchu return (error); 122f7ce69c9SNicolas Souchu } 123f7ce69c9SNicolas Souchu 124f7ce69c9SNicolas Souchu #define SDA_out 0x80 125f7ce69c9SNicolas Souchu #define SCL_out 0x08 126f7ce69c9SNicolas Souchu #define SDA_in 0x80 127f7ce69c9SNicolas Souchu #define SCL_in 0x08 128f7ce69c9SNicolas Souchu #define ALIM 0x20 129f7ce69c9SNicolas Souchu #define I2CKEY 0x50 130f7ce69c9SNicolas Souchu 1312067d312SJohn Baldwin /* Reset bus by setting SDA first and then SCL. */ 1322067d312SJohn Baldwin static void 1332067d312SJohn Baldwin lpbb_reset_bus(device_t dev) 1342067d312SJohn Baldwin { 1352067d312SJohn Baldwin device_t ppbus = device_get_parent(dev); 1362067d312SJohn Baldwin 1372067d312SJohn Baldwin ppb_assert_locked(ppbus); 1382067d312SJohn Baldwin ppb_wdtr(ppbus, (u_char)~SDA_out); 1392067d312SJohn Baldwin ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus) | SCL_out)); 1402067d312SJohn Baldwin } 1412067d312SJohn Baldwin 14213e3657bSJohn Baldwin static int 14313e3657bSJohn Baldwin lpbb_getscl(device_t dev) 144f7ce69c9SNicolas Souchu { 1452067d312SJohn Baldwin device_t ppbus = device_get_parent(dev); 14613e3657bSJohn Baldwin int rval; 14713e3657bSJohn Baldwin 1482067d312SJohn Baldwin ppb_lock(ppbus); 1492067d312SJohn Baldwin rval = ((ppb_rstr(ppbus) & SCL_in) == SCL_in); 1502067d312SJohn Baldwin ppb_unlock(ppbus); 15113e3657bSJohn Baldwin return (rval); 152f7ce69c9SNicolas Souchu } 153f7ce69c9SNicolas Souchu 15413e3657bSJohn Baldwin static int 15513e3657bSJohn Baldwin lpbb_getsda(device_t dev) 156f7ce69c9SNicolas Souchu { 1572067d312SJohn Baldwin device_t ppbus = device_get_parent(dev); 15813e3657bSJohn Baldwin int rval; 15913e3657bSJohn Baldwin 1602067d312SJohn Baldwin ppb_lock(ppbus); 1612067d312SJohn Baldwin rval = ((ppb_rstr(ppbus) & SDA_in) == SDA_in); 1622067d312SJohn Baldwin ppb_unlock(ppbus); 16313e3657bSJohn Baldwin return (rval); 164c17d4340SNicolas Souchu } 165c17d4340SNicolas Souchu 16613e3657bSJohn Baldwin static void 16734a53f3fSWarner Losh lpbb_setsda(device_t dev, int val) 168c17d4340SNicolas Souchu { 169c17d4340SNicolas Souchu device_t ppbus = device_get_parent(dev); 170c17d4340SNicolas Souchu 1712067d312SJohn Baldwin ppb_lock(ppbus); 172f7ce69c9SNicolas Souchu if (val == 0) 1730f210c92SNicolas Souchu ppb_wdtr(ppbus, (u_char)SDA_out); 174f7ce69c9SNicolas Souchu else 1750f210c92SNicolas Souchu ppb_wdtr(ppbus, (u_char)~SDA_out); 1762067d312SJohn Baldwin ppb_unlock(ppbus); 177f7ce69c9SNicolas Souchu } 178f7ce69c9SNicolas Souchu 17913e3657bSJohn Baldwin static void 18034a53f3fSWarner Losh lpbb_setscl(device_t dev, int val) 181f7ce69c9SNicolas Souchu { 182c17d4340SNicolas Souchu device_t ppbus = device_get_parent(dev); 183c17d4340SNicolas Souchu 1842067d312SJohn Baldwin ppb_lock(ppbus); 185f7ce69c9SNicolas Souchu if (val == 0) 1860f210c92SNicolas Souchu ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus) & ~SCL_out)); 187f7ce69c9SNicolas Souchu else 1880f210c92SNicolas Souchu ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus) | SCL_out)); 1892067d312SJohn Baldwin ppb_unlock(ppbus); 190f7ce69c9SNicolas Souchu } 191f7ce69c9SNicolas Souchu 19213e3657bSJohn Baldwin static int 19313e3657bSJohn Baldwin lpbb_detect(device_t dev) 194f7ce69c9SNicolas Souchu { 1950f210c92SNicolas Souchu device_t ppbus = device_get_parent(dev); 1960f210c92SNicolas Souchu 1972067d312SJohn Baldwin ppb_lock(ppbus); 1980f210c92SNicolas Souchu if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) { 1992067d312SJohn Baldwin ppb_unlock(ppbus); 2000f210c92SNicolas Souchu device_printf(dev, "can't allocate ppbus\n"); 201f7ce69c9SNicolas Souchu return (0); 202f7ce69c9SNicolas Souchu } 203f7ce69c9SNicolas Souchu 2042067d312SJohn Baldwin lpbb_reset_bus(dev); 205f7ce69c9SNicolas Souchu 2060f210c92SNicolas Souchu if ((ppb_rstr(ppbus) & I2CKEY) || 2070f210c92SNicolas Souchu ((ppb_rstr(ppbus) & ALIM) != ALIM)) { 2080f210c92SNicolas Souchu ppb_release_bus(ppbus, dev); 2092067d312SJohn Baldwin ppb_unlock(ppbus); 210f7ce69c9SNicolas Souchu return (0); 211a6530aceSNicolas Souchu } 212f7ce69c9SNicolas Souchu 2130f210c92SNicolas Souchu ppb_release_bus(ppbus, dev); 2142067d312SJohn Baldwin ppb_unlock(ppbus); 215f7ce69c9SNicolas Souchu 216f7ce69c9SNicolas Souchu return (1); 217f7ce69c9SNicolas Souchu } 218f7ce69c9SNicolas Souchu 219f7ce69c9SNicolas Souchu static int 220f7ce69c9SNicolas Souchu lpbb_reset(device_t dev, u_char speed, u_char addr, u_char * oldaddr) 221f7ce69c9SNicolas Souchu { 2220f210c92SNicolas Souchu device_t ppbus = device_get_parent(dev); 223f7ce69c9SNicolas Souchu 2242067d312SJohn Baldwin ppb_lock(ppbus); 225c17d4340SNicolas Souchu if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) { 2262067d312SJohn Baldwin ppb_unlock(ppbus); 227c17d4340SNicolas Souchu device_printf(dev, "can't allocate ppbus\n"); 228c17d4340SNicolas Souchu return (0); 229c17d4340SNicolas Souchu } 230c17d4340SNicolas Souchu 2312067d312SJohn Baldwin lpbb_reset_bus(dev); 232c17d4340SNicolas Souchu 233c17d4340SNicolas Souchu ppb_release_bus(ppbus, dev); 2342067d312SJohn Baldwin ppb_unlock(ppbus); 235f7ce69c9SNicolas Souchu 236f7ce69c9SNicolas Souchu return (IIC_ENOADDR); 237f7ce69c9SNicolas Souchu } 238f7ce69c9SNicolas Souchu 2390f063508SPeter Wemm static device_method_t lpbb_methods[] = { 2400f063508SPeter Wemm /* device interface */ 2410f063508SPeter Wemm DEVMETHOD(device_identify, lpbb_identify), 2420f063508SPeter Wemm DEVMETHOD(device_probe, lpbb_probe), 2430f063508SPeter Wemm DEVMETHOD(device_attach, lpbb_attach), 2440f063508SPeter Wemm 2450f063508SPeter Wemm /* iicbb interface */ 2460f063508SPeter Wemm DEVMETHOD(iicbb_callback, lpbb_callback), 247c17d4340SNicolas Souchu DEVMETHOD(iicbb_setsda, lpbb_setsda), 248c17d4340SNicolas Souchu DEVMETHOD(iicbb_setscl, lpbb_setscl), 249c17d4340SNicolas Souchu DEVMETHOD(iicbb_getsda, lpbb_getsda), 250c17d4340SNicolas Souchu DEVMETHOD(iicbb_getscl, lpbb_getscl), 2510f063508SPeter Wemm DEVMETHOD(iicbb_reset, lpbb_reset), 2520f063508SPeter Wemm 2534b7ec270SMarius Strobl DEVMETHOD_END 2540f063508SPeter Wemm }; 2550f063508SPeter Wemm 2560f063508SPeter Wemm static driver_t lpbb_driver = { 2570f063508SPeter Wemm "lpbb", 2580f063508SPeter Wemm lpbb_methods, 2590f063508SPeter Wemm 1, 2600f063508SPeter Wemm }; 2610f063508SPeter Wemm 2626d588090SJohn Baldwin DRIVER_MODULE(lpbb, ppbus, lpbb_driver, 0, 0); 2638c13dd83SJohn Baldwin DRIVER_MODULE(iicbb, lpbb, iicbb_driver, 0, 0); 264f5fd5611SRuslan Ermilov MODULE_DEPEND(lpbb, ppbus, 1, 1, 1); 265c17d4340SNicolas Souchu MODULE_DEPEND(lpbb, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER); 266c17d4340SNicolas Souchu MODULE_VERSION(lpbb, 1); 267