1/*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 1998, 2001 Nicolas Souchu, Marc Bouget 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * 29 */ 30 31#include <sys/cdefs.h> 32/* 33 * I2C Bit-Banging over parallel port 34 * 35 * See the Official Philips interface description in lpbb(4) 36 */ 37 38#include <sys/param.h> 39#include <sys/bus.h> 40#include <sys/lock.h> 41#include <sys/kernel.h> 42#include <sys/module.h> 43#include <sys/mutex.h> 44#include <sys/systm.h> 45#include <sys/uio.h> 46 47#include <dev/ppbus/ppbconf.h> 48#include "ppbus_if.h" 49#include <dev/ppbus/ppbio.h> 50 51#include <dev/iicbus/iiconf.h> 52#include <dev/iicbus/iicbus.h> 53 54#include "iicbb_if.h" 55 56static int lpbb_detect(device_t dev); 57 58static void 59lpbb_identify(driver_t *driver, device_t parent) 60{ 61 62 device_t dev; 63 64 dev = device_find_child(parent, "lpbb", -1); 65 if (!dev) 66 BUS_ADD_CHILD(parent, 0, "lpbb", -1); 67} 68 69static int 70lpbb_probe(device_t dev) 71{ 72 73 /* Perhaps call this during identify instead? */ 74 if (!lpbb_detect(dev)) 75 return (ENXIO); 76 77 device_set_desc(dev, "Parallel I2C bit-banging interface"); 78 79 return (0); 80} 81 82static int 83lpbb_attach(device_t dev) 84{ 85 device_t bitbang; 86 87 /* add generic bit-banging code */ 88 bitbang = device_add_child(dev, "iicbb", -1); 89 device_probe_and_attach(bitbang); 90 91 return (0); 92} 93 94static int 95lpbb_callback(device_t dev, int index, caddr_t data) 96{ 97 device_t ppbus = device_get_parent(dev); 98 int error = 0; 99 int how; 100 101 switch (index) { 102 case IIC_REQUEST_BUS: 103 /* request the ppbus */ 104 how = *(int *)data; 105 ppb_lock(ppbus); 106 error = ppb_request_bus(ppbus, dev, how); 107 ppb_unlock(ppbus); 108 break; 109 110 case IIC_RELEASE_BUS: 111 /* release the ppbus */ 112 ppb_lock(ppbus); 113 error = ppb_release_bus(ppbus, dev); 114 ppb_unlock(ppbus); 115 break; 116 117 default: 118 error = EINVAL; 119 } 120 121 return (error); 122} 123 124#define SDA_out 0x80 125#define SCL_out 0x08 126#define SDA_in 0x80 127#define SCL_in 0x08 128#define ALIM 0x20 129#define I2CKEY 0x50 130 131/* Reset bus by setting SDA first and then SCL. */ 132static void 133lpbb_reset_bus(device_t dev) 134{ 135 device_t ppbus = device_get_parent(dev); 136 137 ppb_assert_locked(ppbus); 138 ppb_wdtr(ppbus, (u_char)~SDA_out); 139 ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus) | SCL_out)); 140} 141 142static int 143lpbb_getscl(device_t dev) 144{ 145 device_t ppbus = device_get_parent(dev); 146 int rval; 147 148 ppb_lock(ppbus); 149 rval = ((ppb_rstr(ppbus) & SCL_in) == SCL_in); 150 ppb_unlock(ppbus); 151 return (rval); 152} 153 154static int 155lpbb_getsda(device_t dev) 156{ 157 device_t ppbus = device_get_parent(dev); 158 int rval; 159 160 ppb_lock(ppbus); 161 rval = ((ppb_rstr(ppbus) & SDA_in) == SDA_in); 162 ppb_unlock(ppbus); 163 return (rval); 164} 165 166static void 167lpbb_setsda(device_t dev, int val) 168{ 169 device_t ppbus = device_get_parent(dev); 170 171 ppb_lock(ppbus); 172 if (val == 0) 173 ppb_wdtr(ppbus, (u_char)SDA_out); 174 else 175 ppb_wdtr(ppbus, (u_char)~SDA_out); 176 ppb_unlock(ppbus); 177} 178 179static void 180lpbb_setscl(device_t dev, int val) 181{ 182 device_t ppbus = device_get_parent(dev); 183 184 ppb_lock(ppbus); 185 if (val == 0) 186 ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus) & ~SCL_out)); 187 else 188 ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus) | SCL_out)); 189 ppb_unlock(ppbus); 190} 191 192static int 193lpbb_detect(device_t dev) 194{ 195 device_t ppbus = device_get_parent(dev); 196 197 ppb_lock(ppbus); 198 if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) { 199 ppb_unlock(ppbus); 200 device_printf(dev, "can't allocate ppbus\n"); 201 return (0); 202 } 203 204 lpbb_reset_bus(dev); 205 206 if ((ppb_rstr(ppbus) & I2CKEY) || 207 ((ppb_rstr(ppbus) & ALIM) != ALIM)) { 208 ppb_release_bus(ppbus, dev); 209 ppb_unlock(ppbus); 210 return (0); 211 } 212 213 ppb_release_bus(ppbus, dev); 214 ppb_unlock(ppbus); 215 216 return (1); 217} 218 219static int 220lpbb_reset(device_t dev, u_char speed, u_char addr, u_char * oldaddr) 221{ 222 device_t ppbus = device_get_parent(dev); 223 224 ppb_lock(ppbus); 225 if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) { 226 ppb_unlock(ppbus); 227 device_printf(dev, "can't allocate ppbus\n"); 228 return (0); 229 } 230 231 lpbb_reset_bus(dev); 232 233 ppb_release_bus(ppbus, dev); 234 ppb_unlock(ppbus); 235 236 return (IIC_ENOADDR); 237} 238 239static device_method_t lpbb_methods[] = { 240 /* device interface */ 241 DEVMETHOD(device_identify, lpbb_identify), 242 DEVMETHOD(device_probe, lpbb_probe), 243 DEVMETHOD(device_attach, lpbb_attach), 244 245 /* iicbb interface */ 246 DEVMETHOD(iicbb_callback, lpbb_callback), 247 DEVMETHOD(iicbb_setsda, lpbb_setsda), 248 DEVMETHOD(iicbb_setscl, lpbb_setscl), 249 DEVMETHOD(iicbb_getsda, lpbb_getsda), 250 DEVMETHOD(iicbb_getscl, lpbb_getscl), 251 DEVMETHOD(iicbb_reset, lpbb_reset), 252 253 DEVMETHOD_END 254}; 255 256static driver_t lpbb_driver = { 257 "lpbb", 258 lpbb_methods, 259 1, 260}; 261 262DRIVER_MODULE(lpbb, ppbus, lpbb_driver, 0, 0); 263DRIVER_MODULE(iicbb, lpbb, iicbb_driver, 0, 0); 264MODULE_DEPEND(lpbb, ppbus, 1, 1, 1); 265MODULE_DEPEND(lpbb, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER); 266MODULE_VERSION(lpbb, 1); 267