1 /* $NetBSD: pcf8584.c,v 1.2 2007/10/19 11:59:58 ad Exp $ */ 2 3 /*- 4 * Copyright (c) 2007 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Tobias Nygren. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /* 40 * Philips PCF8584 I2C Bus Controller 41 * 42 * This driver does not yet support multi-master arbitration, concurrent access 43 * or interrupts, but it should be usable for single-master applications. 44 * It is currently used by the envctrl(4) driver on sparc64. 45 */ 46 47 #include <sys/cdefs.h> 48 __KERNEL_RCSID(0, "$NetBSD: pcf8584.c,v 1.2 2007/10/19 11:59:58 ad Exp $"); 49 50 #include <sys/param.h> 51 #include <sys/device.h> 52 #include <sys/kernel.h> 53 #include <sys/systm.h> 54 #include <sys/condvar.h> 55 #include <sys/mutex.h> 56 #include <sys/bus.h> 57 #include <machine/param.h> 58 #include <dev/i2c/i2cvar.h> 59 #include <dev/ic/pcf8584reg.h> 60 #include <dev/ic/pcf8584var.h> 61 62 static void pcf8584_bus_reset(struct pcf8584_handle *, int); 63 static int pcf8584_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t, 64 void *, size_t, int); 65 static int pcf8584_acquire_bus(void *, int); 66 static void pcf8584_release_bus(void *, int); 67 static void pcf8584_wait(struct pcf8584_handle *, int); 68 69 /* Must delay for 500 ns between bus accesses according to manual. */ 70 #define DATA_W(x) (DELAY(1), bus_space_write_1(ha->ha_iot, ha->ha_ioh, 0, x)) 71 #define DATA_R() (DELAY(1), bus_space_read_1(ha->ha_iot, ha->ha_ioh, 0)) 72 #define CSR_W(x) (DELAY(1), bus_space_write_1(ha->ha_iot, ha->ha_ioh, 1, x)) 73 #define STATUS_R() (DELAY(1), bus_space_read_1(ha->ha_iot, ha->ha_ioh, 1)) 74 #define BUSY() ((STATUS_R() & PCF8584_STATUS_BBN) == 0) 75 #define PENDING() ((STATUS_R() & PCF8584_STATUS_PIN) == 0) 76 #define NAK() ((STATUS_R() & PCF8584_STATUS_LRB) != 0) 77 78 /* 79 * Wait for an interrupt. 80 */ 81 static void 82 pcf8584_wait(struct pcf8584_handle *ha, int flags) 83 { 84 int timeo; 85 86 if (flags & I2C_F_POLL) { 87 timeo = 20; 88 while (timeo && !PENDING()) { 89 DELAY(1000); 90 timeo--; 91 } 92 } else { 93 mutex_enter(&ha->ha_intrmtx); 94 cv_timedwait(&ha->ha_intrcond, &ha->ha_intrmtx, mstohz(20)); 95 mutex_exit(&ha->ha_intrmtx); 96 } 97 } 98 99 #ifdef notyet 100 static void 101 pcf8584_intr(struct pcf8584_handle *ha) { 102 103 cv_wakeup(&ha->ha_intrcond); 104 } 105 #endif 106 107 int 108 pcf8584_init(struct pcf8584_handle *ha) 109 { 110 111 ha->ha_i2c.ic_cookie = ha; 112 ha->ha_i2c.ic_acquire_bus = pcf8584_acquire_bus; 113 ha->ha_i2c.ic_release_bus = pcf8584_release_bus; 114 ha->ha_i2c.ic_exec = pcf8584_exec; 115 116 mutex_init(&ha->ha_intrmtx, MUTEX_DEFAULT, IPL_NONE); 117 cv_init(&ha->ha_intrcond, "pcf8584"); 118 119 pcf8584_bus_reset(ha, I2C_F_POLL); 120 121 return 0; 122 } 123 124 /* 125 * Reset i2c bus. 126 */ 127 static void 128 pcf8584_bus_reset(struct pcf8584_handle *ha, int flags) 129 { 130 131 /* initialize PCF8584 */ 132 CSR_W(PCF8584_CTRL_PIN); 133 DATA_W(0x55); 134 CSR_W(PCF8584_CTRL_PIN | PCF8584_REG_S2); 135 DATA_W(PCF8584_CLK_12 | PCF8584_SCL_90); 136 CSR_W(PCF8584_CTRL_PIN | PCF8584_CTRL_ESO | PCF8584_CTRL_ACK); 137 138 /* XXX needs multi-master synchronization delay here */ 139 140 /* 141 * Blindly attempt a write at a nonexistent i2c address (0x7F). 142 * This allows hung i2c devices to pick up the stop condition. 143 */ 144 DATA_W(0x7F << 1); 145 CSR_W(PCF8584_CMD_START); 146 pcf8584_wait(ha, flags); 147 CSR_W(PCF8584_CMD_STOP); 148 pcf8584_wait(ha, flags); 149 } 150 151 static int 152 pcf8584_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, 153 const void *cmdbuf, size_t cmdlen, void *buf, 154 size_t len, int flags) 155 { 156 int i; 157 struct pcf8584_handle *ha = cookie; 158 uint8_t *p = buf; 159 160 KASSERT(cmdlen == 0); 161 KASSERT(op == I2C_OP_READ_WITH_STOP || op == I2C_OP_WRITE_WITH_STOP); 162 163 if (BUSY()) { 164 /* We're the only master on the bus, something is wrong. */ 165 printf("*%s: i2c bus busy!\n", ha->ha_parent->dv_xname); 166 pcf8584_bus_reset(ha, flags); 167 } 168 if (op == I2C_OP_READ_WITH_STOP) 169 DATA_W((addr << 1) | 1); 170 else 171 DATA_W(addr << 1); 172 173 CSR_W(PCF8584_CMD_START); 174 pcf8584_wait(ha, flags); 175 if (!PENDING()) { 176 printf("%s: no intr after i2c sla\n", ha->ha_parent->dv_xname); 177 } 178 if (NAK()) 179 goto fail; 180 181 if (op == I2C_OP_READ_WITH_STOP) { 182 (void) DATA_R();/* dummy read */ 183 for (i = 0; i < len; i++) { 184 /* wait for a byte to arrive */ 185 pcf8584_wait(ha, flags); 186 if (!PENDING()) { 187 printf("%s: lost intr during i2c read\n", 188 ha->ha_parent->dv_xname); 189 goto fail; 190 } 191 if (NAK()) 192 goto fail; 193 if (i == len - 1) { 194 /* 195 * we're about to read the final byte, so we 196 * set the controller to NAK the following 197 * byte, if any. 198 */ 199 CSR_W(PCF8584_CMD_NAK); 200 } 201 *p++ = DATA_R(); 202 } 203 pcf8584_wait(ha, flags); 204 if (!PENDING()) { 205 printf("%s: no intr on final i2c nak\n", 206 ha->ha_parent->dv_xname); 207 goto fail; 208 } 209 CSR_W(PCF8584_CMD_STOP); 210 (void) DATA_R();/* dummy read */ 211 } else { 212 for (i = 0; i < len; i++) { 213 DATA_W(*p++); 214 pcf8584_wait(ha, flags); 215 if (!PENDING()) { 216 printf("%s: no intr during i2c write\n", 217 ha->ha_parent->dv_xname); 218 goto fail; 219 } 220 if (NAK()) 221 goto fail; 222 } 223 CSR_W(PCF8584_CMD_STOP); 224 } 225 pcf8584_wait(ha, flags); 226 return 0; 227 fail: 228 CSR_W(PCF8584_CMD_STOP); 229 pcf8584_wait(ha, flags); 230 231 return 1; 232 } 233 234 static int 235 pcf8584_acquire_bus(void *cookie, int flags) 236 { 237 238 /* XXX concurrent access not yet implemented */ 239 return 0; 240 } 241 242 static void 243 pcf8584_release_bus(void *cookie, int flags) 244 { 245 246 } 247