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