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