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 56 static int lpbb_detect(device_t dev); 57 58 static void 59 lpbb_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 69 static int 70 lpbb_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 82 static int 83 lpbb_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 94 static int 95 lpbb_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. */ 132 static void 133 lpbb_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 142 static int 143 lpbb_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 154 static int 155 lpbb_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 166 static void 167 lpbb_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 179 static void 180 lpbb_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 192 static int 193 lpbb_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 219 static int 220 lpbb_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 239 static 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 256 static driver_t lpbb_driver = { 257 "lpbb", 258 lpbb_methods, 259 1, 260 }; 261 262 DRIVER_MODULE(lpbb, ppbus, lpbb_driver, 0, 0); 263 DRIVER_MODULE(iicbb, lpbb, iicbb_driver, 0, 0); 264 MODULE_DEPEND(lpbb, ppbus, 1, 1, 1); 265 MODULE_DEPEND(lpbb, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER); 266 MODULE_VERSION(lpbb, 1); 267