pcf8584.c revision 1.6
1/* $NetBSD: pcf8584.c,v 1.6 2010/02/28 11:47:28 martin Exp $ */ 2/* $OpenBSD: pcf8584.c,v 1.9 2007/10/20 18:46:21 kettenis Exp $ */ 3 4/* 5 * Copyright (c) 2006 David Gwynne <dlg@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20#include <sys/param.h> 21#include <sys/systm.h> 22#include <sys/device.h> 23#include <sys/malloc.h> 24#include <sys/kernel.h> 25#include <sys/rwlock.h> 26#include <sys/proc.h> 27 28#include <machine/bus.h> 29 30#include <dev/i2c/i2cvar.h> 31 32#include <dev/ic/pcf8584var.h> 33 34#define PCF_S0 0x00 35#define PCF_S1 0x01 36#define PCF_S2 0x02 37#define PCF_S3 0x03 38 39#define PCF_CTRL_ACK (1<<0) 40#define PCF_CTRL_STO (1<<1) 41#define PCF_CTRL_STA (1<<2) 42#define PCF_CTRL_ENI (1<<3) 43#define PCF_CTRL_ES2 (1<<4) 44#define PCF_CTRL_ES1 (1<<5) 45#define PCF_CTRL_ESO (1<<6) 46#define PCF_CTRL_PIN (1<<7) 47 48#define PCF_CTRL_START (PCF_CTRL_PIN | PCF_CTRL_ESO | \ 49 PCF_CTRL_STA | PCF_CTRL_ACK) 50#define PCF_CTRL_STOP (PCF_CTRL_PIN | PCF_CTRL_ESO | \ 51 PCF_CTRL_STO | PCF_CTRL_ACK) 52#define PCF_CTRL_REPSTART (PCF_CTRL_ESO | PCF_CTRL_STA | PCF_CTRL_ACK) 53#define PCF_CTRL_IDLE (PCF_CTRL_PIN | PCF_CTRL_ESO | PCF_CTRL_ACK) 54 55#define PCF_STAT_nBB (1<<0) 56#define PCF_STAT_LAB (1<<1) 57#define PCF_STAT_AAS (1<<2) 58#define PCF_STAT_AD0 (1<<3) 59#define PCF_STAT_LRB (1<<3) 60#define PCF_STAT_BER (1<<4) 61#define PCF_STAT_STS (1<<5) 62#define PCF_STAT_PIN (1<<7) 63 64void pcfiic_init(struct pcfiic_softc *); 65int pcfiic_i2c_acquire_bus(void *, int); 66void pcfiic_i2c_release_bus(void *, int); 67int pcfiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, 68 size_t, void *, size_t, int); 69 70int pcfiic_xmit(struct pcfiic_softc *, u_int8_t, const u_int8_t *, 71 size_t); 72int pcfiic_recv(struct pcfiic_softc *, u_int8_t, u_int8_t *, 73 size_t); 74 75u_int8_t pcfiic_read(struct pcfiic_softc *, bus_size_t); 76void pcfiic_write(struct pcfiic_softc *, bus_size_t, u_int8_t); 77void pcfiic_choose_bus(struct pcfiic_softc *, u_int8_t); 78int pcfiic_wait_nBB(struct pcfiic_softc *); 79int pcfiic_wait_pin(struct pcfiic_softc *, volatile u_int8_t *); 80 81void 82pcfiic_init(struct pcfiic_softc *sc) 83{ 84 /* init S1 */ 85 pcfiic_write(sc, PCF_S1, PCF_CTRL_PIN); 86 /* own address */ 87 pcfiic_write(sc, PCF_S0, sc->sc_addr); 88 89 /* select clock reg */ 90 pcfiic_write(sc, PCF_S1, PCF_CTRL_PIN|PCF_CTRL_ES1); 91 pcfiic_write(sc, PCF_S0, sc->sc_clock); 92 93 pcfiic_write(sc, PCF_S1, PCF_CTRL_IDLE); 94 95 delay(200000); /* Multi-Master mode, wait for longest i2c message */ 96} 97 98void 99pcfiic_attach(struct pcfiic_softc *sc, i2c_addr_t addr, u_int8_t clock, 100 int swapregs) 101{ 102 struct i2cbus_attach_args iba; 103 104 if (swapregs) { 105 sc->sc_regmap[PCF_S1] = PCF_S0; 106 sc->sc_regmap[PCF_S0] = PCF_S1; 107 } else { 108 sc->sc_regmap[PCF_S0] = PCF_S0; 109 sc->sc_regmap[PCF_S1] = PCF_S1; 110 } 111 sc->sc_clock = clock; 112 sc->sc_addr = addr; 113 114 pcfiic_init(sc); 115 116 printf("\n"); 117 118 if (sc->sc_master) 119 pcfiic_choose_bus(sc, 0); 120 121 rw_init(&sc->sc_lock); 122 sc->sc_i2c.ic_cookie = sc; 123 sc->sc_i2c.ic_acquire_bus = pcfiic_i2c_acquire_bus; 124 sc->sc_i2c.ic_release_bus = pcfiic_i2c_release_bus; 125 sc->sc_i2c.ic_exec = pcfiic_i2c_exec; 126 127 bzero(&iba, sizeof(iba)); 128 iba.iba_tag = &sc->sc_i2c; 129 config_found(sc->sc_dev, &iba, iicbus_print); 130} 131 132int 133pcfiic_intr(void *arg) 134{ 135 return (0); 136} 137 138int 139pcfiic_i2c_acquire_bus(void *arg, int flags) 140{ 141 struct pcfiic_softc *sc = arg; 142 143 if (cold || sc->sc_poll || (flags & I2C_F_POLL)) 144 return (0); 145 146 rw_enter(&sc->sc_lock, RW_WRITER); 147 return 0; 148} 149 150void 151pcfiic_i2c_release_bus(void *arg, int flags) 152{ 153 struct pcfiic_softc *sc = arg; 154 155 if (cold || sc->sc_poll || (flags & I2C_F_POLL)) 156 return; 157 158 rw_exit(&sc->sc_lock); 159} 160 161int 162pcfiic_i2c_exec(void *arg, i2c_op_t op, i2c_addr_t addr, 163 const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags) 164{ 165 struct pcfiic_softc *sc = arg; 166 int ret = 0; 167 168#if 0 169 printf("%s: exec op: %d addr: 0x%x cmdlen: %d len: %d flags 0x%x\n", 170 sc->sc_dev.dv_xname, op, addr, cmdlen, len, flags); 171#endif 172 173 if (cold || sc->sc_poll) 174 flags |= I2C_F_POLL; 175 176 if (sc->sc_master) 177 pcfiic_choose_bus(sc, addr >> 7); 178 179 if (cmdlen > 0) 180 if (pcfiic_xmit(sc, addr & 0x7f, cmdbuf, cmdlen) != 0) 181 return (1); 182 183 if (len > 0) { 184 if (I2C_OP_WRITE_P(op)) 185 ret = pcfiic_xmit(sc, addr & 0x7f, buf, len); 186 else 187 ret = pcfiic_recv(sc, addr & 0x7f, buf, len); 188 } 189 return (ret); 190} 191 192int 193pcfiic_xmit(struct pcfiic_softc *sc, u_int8_t addr, const u_int8_t *buf, 194 size_t len) 195{ 196 int i, err = 0; 197 volatile u_int8_t r; 198 199 if (pcfiic_wait_nBB(sc) != 0) 200 return (1); 201 202 pcfiic_write(sc, PCF_S0, addr << 1); 203 pcfiic_write(sc, PCF_S1, PCF_CTRL_START); 204 205 for (i = 0; i <= len; i++) { 206 if (pcfiic_wait_pin(sc, &r) != 0) { 207 pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP); 208 return (1); 209 } 210 211 if (r & PCF_STAT_LRB) { 212 err = 1; 213 break; 214 } 215 216 if (i < len) 217 pcfiic_write(sc, PCF_S0, buf[i]); 218 } 219 pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP); 220 return (err); 221} 222 223int 224pcfiic_recv(struct pcfiic_softc *sc, u_int8_t addr, u_int8_t *buf, size_t len) 225{ 226 int i = 0, err = 0; 227 volatile u_int8_t r; 228 229 if (pcfiic_wait_nBB(sc) != 0) 230 return (1); 231 232 pcfiic_write(sc, PCF_S0, (addr << 1) | 0x01); 233 pcfiic_write(sc, PCF_S1, PCF_CTRL_START); 234 235 for (i = 0; i <= len; i++) { 236 if (pcfiic_wait_pin(sc, &r) != 0) { 237 pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP); 238 return (1); 239 } 240 241 if ((i != len) && (r & PCF_STAT_LRB)) { 242 pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP); 243 return (1); 244 } 245 246 if (i == len - 1) { 247 pcfiic_write(sc, PCF_S1, PCF_CTRL_ESO); 248 } else if (i == len) { 249 pcfiic_write(sc, PCF_S1, PCF_CTRL_STOP); 250 } 251 252 r = pcfiic_read(sc, PCF_S0); 253 if (i > 0) 254 buf[i - 1] = r; 255 } 256 return (err); 257} 258 259u_int8_t 260pcfiic_read(struct pcfiic_softc *sc, bus_size_t r) 261{ 262 bus_space_barrier(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], 1, 263 BUS_SPACE_BARRIER_READ); 264 return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r])); 265} 266 267void 268pcfiic_write(struct pcfiic_softc *sc, bus_size_t r, u_int8_t v) 269{ 270 bus_space_write_1(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], v); 271 bus_space_barrier(sc->sc_iot, sc->sc_ioh, sc->sc_regmap[r], 1, 272 BUS_SPACE_BARRIER_WRITE); 273} 274 275void 276pcfiic_choose_bus(struct pcfiic_softc *sc, u_int8_t bus) 277{ 278 bus_space_write_1(sc->sc_iot, sc->sc_ioh2, 0, bus); 279 bus_space_barrier(sc->sc_iot, sc->sc_ioh2, 0, 1, 280 BUS_SPACE_BARRIER_WRITE); 281} 282 283int 284pcfiic_wait_nBB(struct pcfiic_softc *sc) 285{ 286 int i; 287 288 for (i = 0; i < 1000; i++) { 289 if (pcfiic_read(sc, PCF_S1) & PCF_STAT_nBB) 290 return (0); 291 delay(1000); 292 } 293 return (1); 294} 295 296int 297pcfiic_wait_pin(struct pcfiic_softc *sc, volatile u_int8_t *r) 298{ 299 int i; 300 301 for (i = 0; i < 1000; i++) { 302 *r = pcfiic_read(sc, PCF_S1); 303 if ((*r & PCF_STAT_PIN) == 0) 304 return (0); 305 delay(1000); 306 } 307 return (1); 308} 309