1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 __FBSDID("$FreeBSD$"); 33 34 /* 35 * I2C Bit-Banging over parallel port 36 * 37 * See the Official Philips interface description in lpbb(4) 38 */ 39 40 #include <sys/param.h> 41 #include <sys/bus.h> 42 #include <sys/lock.h> 43 #include <sys/kernel.h> 44 #include <sys/module.h> 45 #include <sys/mutex.h> 46 #include <sys/systm.h> 47 #include <sys/uio.h> 48 49 50 #include <dev/ppbus/ppbconf.h> 51 #include "ppbus_if.h" 52 #include <dev/ppbus/ppbio.h> 53 54 #include <dev/iicbus/iiconf.h> 55 #include <dev/iicbus/iicbus.h> 56 57 #include "iicbb_if.h" 58 59 static int lpbb_detect(device_t dev); 60 61 static void 62 lpbb_identify(driver_t *driver, device_t parent) 63 { 64 65 device_t dev; 66 67 dev = device_find_child(parent, "lpbb", -1); 68 if (!dev) 69 BUS_ADD_CHILD(parent, 0, "lpbb", -1); 70 } 71 72 static int 73 lpbb_probe(device_t dev) 74 { 75 76 /* Perhaps call this during identify instead? */ 77 if (!lpbb_detect(dev)) 78 return (ENXIO); 79 80 device_set_desc(dev, "Parallel I2C bit-banging interface"); 81 82 return (0); 83 } 84 85 static int 86 lpbb_attach(device_t dev) 87 { 88 device_t bitbang; 89 90 /* add generic bit-banging code */ 91 bitbang = device_add_child(dev, "iicbb", -1); 92 device_probe_and_attach(bitbang); 93 94 return (0); 95 } 96 97 static int 98 lpbb_callback(device_t dev, int index, caddr_t data) 99 { 100 device_t ppbus = device_get_parent(dev); 101 int error = 0; 102 int how; 103 104 switch (index) { 105 case IIC_REQUEST_BUS: 106 /* request the ppbus */ 107 how = *(int *)data; 108 ppb_lock(ppbus); 109 error = ppb_request_bus(ppbus, dev, how); 110 ppb_unlock(ppbus); 111 break; 112 113 case IIC_RELEASE_BUS: 114 /* release the ppbus */ 115 ppb_lock(ppbus); 116 error = ppb_release_bus(ppbus, dev); 117 ppb_unlock(ppbus); 118 break; 119 120 default: 121 error = EINVAL; 122 } 123 124 return (error); 125 } 126 127 #define SDA_out 0x80 128 #define SCL_out 0x08 129 #define SDA_in 0x80 130 #define SCL_in 0x08 131 #define ALIM 0x20 132 #define I2CKEY 0x50 133 134 /* Reset bus by setting SDA first and then SCL. */ 135 static void 136 lpbb_reset_bus(device_t dev) 137 { 138 device_t ppbus = device_get_parent(dev); 139 140 ppb_assert_locked(ppbus); 141 ppb_wdtr(ppbus, (u_char)~SDA_out); 142 ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus) | SCL_out)); 143 } 144 145 static int 146 lpbb_getscl(device_t dev) 147 { 148 device_t ppbus = device_get_parent(dev); 149 int rval; 150 151 ppb_lock(ppbus); 152 rval = ((ppb_rstr(ppbus) & SCL_in) == SCL_in); 153 ppb_unlock(ppbus); 154 return (rval); 155 } 156 157 static int 158 lpbb_getsda(device_t dev) 159 { 160 device_t ppbus = device_get_parent(dev); 161 int rval; 162 163 ppb_lock(ppbus); 164 rval = ((ppb_rstr(ppbus) & SDA_in) == SDA_in); 165 ppb_unlock(ppbus); 166 return (rval); 167 } 168 169 static void 170 lpbb_setsda(device_t dev, int val) 171 { 172 device_t ppbus = device_get_parent(dev); 173 174 ppb_lock(ppbus); 175 if (val == 0) 176 ppb_wdtr(ppbus, (u_char)SDA_out); 177 else 178 ppb_wdtr(ppbus, (u_char)~SDA_out); 179 ppb_unlock(ppbus); 180 } 181 182 static void 183 lpbb_setscl(device_t dev, int val) 184 { 185 device_t ppbus = device_get_parent(dev); 186 187 ppb_lock(ppbus); 188 if (val == 0) 189 ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus) & ~SCL_out)); 190 else 191 ppb_wctr(ppbus, (u_char)(ppb_rctr(ppbus) | SCL_out)); 192 ppb_unlock(ppbus); 193 } 194 195 static int 196 lpbb_detect(device_t dev) 197 { 198 device_t ppbus = device_get_parent(dev); 199 200 ppb_lock(ppbus); 201 if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) { 202 ppb_unlock(ppbus); 203 device_printf(dev, "can't allocate ppbus\n"); 204 return (0); 205 } 206 207 lpbb_reset_bus(dev); 208 209 if ((ppb_rstr(ppbus) & I2CKEY) || 210 ((ppb_rstr(ppbus) & ALIM) != ALIM)) { 211 ppb_release_bus(ppbus, dev); 212 ppb_unlock(ppbus); 213 return (0); 214 } 215 216 ppb_release_bus(ppbus, dev); 217 ppb_unlock(ppbus); 218 219 return (1); 220 } 221 222 static int 223 lpbb_reset(device_t dev, u_char speed, u_char addr, u_char * oldaddr) 224 { 225 device_t ppbus = device_get_parent(dev); 226 227 ppb_lock(ppbus); 228 if (ppb_request_bus(ppbus, dev, PPB_DONTWAIT)) { 229 ppb_unlock(ppbus); 230 device_printf(dev, "can't allocate ppbus\n"); 231 return (0); 232 } 233 234 lpbb_reset_bus(dev); 235 236 ppb_release_bus(ppbus, dev); 237 ppb_unlock(ppbus); 238 239 return (IIC_ENOADDR); 240 } 241 242 static devclass_t lpbb_devclass; 243 244 static device_method_t lpbb_methods[] = { 245 /* device interface */ 246 DEVMETHOD(device_identify, lpbb_identify), 247 DEVMETHOD(device_probe, lpbb_probe), 248 DEVMETHOD(device_attach, lpbb_attach), 249 250 /* iicbb interface */ 251 DEVMETHOD(iicbb_callback, lpbb_callback), 252 DEVMETHOD(iicbb_setsda, lpbb_setsda), 253 DEVMETHOD(iicbb_setscl, lpbb_setscl), 254 DEVMETHOD(iicbb_getsda, lpbb_getsda), 255 DEVMETHOD(iicbb_getscl, lpbb_getscl), 256 DEVMETHOD(iicbb_reset, lpbb_reset), 257 258 DEVMETHOD_END 259 }; 260 261 static driver_t lpbb_driver = { 262 "lpbb", 263 lpbb_methods, 264 1, 265 }; 266 267 DRIVER_MODULE(lpbb, ppbus, lpbb_driver, lpbb_devclass, 0, 0); 268 DRIVER_MODULE(iicbb, lpbb, iicbb_driver, iicbb_devclass, 0, 0); 269 MODULE_DEPEND(lpbb, ppbus, 1, 1, 1); 270 MODULE_DEPEND(lpbb, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER); 271 MODULE_VERSION(lpbb, 1); 272