1/* $NetBSD$ */ 2/* 3 * Copyright (c) 2011 KIYOHARA Takashi 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 19 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 23 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 24 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 */ 27#include <sys/cdefs.h> 28__KERNEL_RCSID(0, "$NetBSD$"); 29 30#include <sys/param.h> 31#include <sys/device.h> 32#include <sys/mutex.h> 33 34#include <machine/platid.h> 35#include <machine/platid_mask.h> 36 37#include <arm/xscale/pxa2x0var.h> 38#include <arm/xscale/pxa2x0_i2c.h> 39 40#include <hpcarm/dev/nbppconvar.h> 41 42#include <dev/i2c/i2cvar.h> 43 44#include "nbppcon.h" 45 46#define NBPIIC_SLAVE_ADDR 0x70 47 48struct nbpiic_softc { 49 struct pxa2x0_i2c_softc sc_pxa_i2c; 50 51 struct i2c_controller sc_i2c; 52 kmutex_t sc_lock; 53 54 device_t sc_pcon; 55 char sc_buf[16]; 56 int sc_idx; 57}; 58 59 60static int pxaiic_match(device_t, cfdata_t, void *); 61static void pxaiic_attach(device_t, device_t, void *); 62 63static int nbpiic_intr(void *); 64int nbpiic_poll(void *, int, char *); 65 66/* fuctions for i2c_controller */ 67static int nbpiic_acquire_bus(void *, int); 68static void nbpiic_release_bus(void *, int); 69static int nbpiic_exec(void *cookie, i2c_op_t, i2c_addr_t, const void *, size_t, 70 void *, size_t, int); 71 72CFATTACH_DECL_NEW(pxaiic, sizeof(struct nbpiic_softc), 73 pxaiic_match, pxaiic_attach, NULL, NULL); 74 75 76/* ARGSUSED */ 77static int 78pxaiic_match(device_t parent, cfdata_t match, void *aux) 79{ 80 struct pxaip_attach_args *pxa = aux; 81 82 if (strcmp(pxa->pxa_name, match->cf_name) != 0 || 83 !platid_match(&platid, &platid_mask_MACH_PSIONTEKLOGIX_NETBOOK_PRO)) 84 return 0; 85 86 pxa->pxa_size = PXA2X0_I2C_SIZE; 87 return 1; 88} 89 90/* ARGSUSED */ 91static void 92pxaiic_attach(device_t parent, device_t self, void *aux) 93{ 94 struct nbpiic_softc *sc = device_private(self); 95 struct pxaip_attach_args *pxa = aux; 96 struct i2cbus_attach_args iba; 97 void *ih; 98 99 aprint_normal("\n"); 100 aprint_naive("\n"); 101 102 sc->sc_pxa_i2c.sc_dev = self; 103 sc->sc_pxa_i2c.sc_iot = pxa->pxa_iot; 104 sc->sc_pxa_i2c.sc_addr = pxa->pxa_addr; 105 sc->sc_pxa_i2c.sc_size = pxa->pxa_size; 106 sc->sc_pxa_i2c.sc_isar = NBPIIC_SLAVE_ADDR; 107 sc->sc_pxa_i2c.sc_stat = PI2C_STAT_INIT; 108 if (pxa2x0_i2c_attach_sub(&sc->sc_pxa_i2c)) { 109 aprint_error_dev(self, "unable to attach PXA I2C\n"); 110 return; 111 } 112 113 /* Initialize mutex with IPL_HIGH. Keyboard was connected to us. */ 114 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_HIGH); 115 116 ih = pxa2x0_intr_establish(pxa->pxa_intr, IPL_HIGH, nbpiic_intr, sc); 117 if (ih == NULL) { 118 aprint_error_dev(self, "intr_establish failed\n"); 119 return; 120 } 121 122 /* Initialize i2c_controller */ 123 sc->sc_i2c.ic_cookie = sc; 124 sc->sc_i2c.ic_acquire_bus = nbpiic_acquire_bus; 125 sc->sc_i2c.ic_release_bus = nbpiic_release_bus; 126 sc->sc_i2c.ic_send_start = NULL; 127 sc->sc_i2c.ic_send_stop = NULL; 128 sc->sc_i2c.ic_initiate_xfer = NULL; 129 sc->sc_i2c.ic_read_byte = NULL; 130 sc->sc_i2c.ic_write_byte = NULL; 131 sc->sc_i2c.ic_exec = nbpiic_exec; 132 133 iba.iba_tag = &sc->sc_i2c; 134 pxa2x0_i2c_open(&sc->sc_pxa_i2c); 135 config_found_ia(self, "i2cbus", &iba, iicbus_print); 136 137 sc->sc_pcon = device_find_by_xname("nbppcon0"); 138 139 /* Don't close I2C-bus. We are slave device. */ 140} 141 142static int 143nbpiic_intr(void *arg) 144{ 145 struct nbpiic_softc *sc = arg; 146 struct pxa2x0_i2c_softc *pi2c = &sc->sc_pxa_i2c; 147 int handled, len; 148 149 handled = pxa2x0_i2c_intr_sub(pi2c, &len, &sc->sc_buf[sc->sc_idx]); 150 if (len != 0) 151 sc->sc_idx += len; 152 if (pi2c->sc_stat == PI2C_STAT_STOP) { 153#if NNBPPCON > 0 154 nbppcon_intr(sc->sc_pcon, sc->sc_idx, sc->sc_buf); 155#endif 156 sc->sc_idx = 0; 157 pi2c->sc_stat = PI2C_STAT_INIT; 158 } 159 160 return handled; 161} 162 163int 164nbpiic_poll(void *cookie, int buflen, char *buf) 165{ 166 struct nbpiic_softc *sc = cookie; 167 int rv; 168 169 if (sc->sc_idx > 0) { 170 if (buflen < sc->sc_idx) { 171 (void) pxa2x0_i2c_poll(&sc->sc_pxa_i2c, 172 sizeof(sc->sc_buf) - sc->sc_idx, buf + sc->sc_idx, 173 I2C_F_READ); 174 sc->sc_idx = 0; 175 } else 176 memcpy(buf, sc->sc_buf, sc->sc_idx); 177 } 178 rv = pxa2x0_i2c_poll(&sc->sc_pxa_i2c, 179 buflen - sc->sc_idx, buf + sc->sc_idx, I2C_F_READ); 180 sc->sc_idx = 0; 181 182 return rv; 183} 184 185static int 186nbpiic_acquire_bus(void *cookie, int flags) 187{ 188 struct nbpiic_softc *sc = cookie; 189 190 mutex_enter(&sc->sc_lock); 191 192 return 0; 193} 194 195static void 196nbpiic_release_bus(void *cookie, int flags) 197{ 198 struct nbpiic_softc *sc = cookie; 199 200 mutex_exit(&sc->sc_lock); 201 202 return; 203} 204 205static int 206nbpiic_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *vcmd, 207 size_t cmdlen, void *vbuf, size_t buflen, int flags) 208{ 209 struct nbpiic_softc *sc = cookie; 210 int rv = -1; 211 const u_char cmd = *(const u_char *)vcmd; 212 213 if (I2C_OP_READ_P(op) && (cmdlen == 0) && (buflen == 1)) 214 rv = pxa2x0_i2c_read(&sc->sc_pxa_i2c, addr, (u_char *)vbuf); 215 216 if ((I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 1)) { 217 rv = pxa2x0_i2c_write(&sc->sc_pxa_i2c, addr, cmd); 218 if (rv == 0) 219 rv = pxa2x0_i2c_read(&sc->sc_pxa_i2c, 220 addr, (u_char *)vbuf); 221 } 222 223 if ((I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 2)) { 224printf("%s: read cmdlen=1, buflen=2: Ooops, maybe error...\n", __func__); 225 rv = pxa2x0_i2c_write(&sc->sc_pxa_i2c, addr, cmd); 226 if (rv == 0) 227 rv = pxa2x0_i2c_read(&sc->sc_pxa_i2c, 228 addr, &((u_char *)vbuf)[0]); 229 if (rv == 0) 230 rv = pxa2x0_i2c_read(&sc->sc_pxa_i2c, 231 addr, &((u_char *)vbuf)[1]); 232 } 233 234 if ((I2C_OP_WRITE_P(op)) && (cmdlen == 0) && (buflen == 1)) 235 rv = pxa2x0_i2c_write(&sc->sc_pxa_i2c, addr, *(u_char *)vbuf); 236 237 if ((I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 1)) { 238 u_short v = (cmd << 8) | ((u_char *)vbuf)[0]; 239 240 rv = pxa2x0_i2c_write_2(&sc->sc_pxa_i2c, addr, v); 241 } 242 243 if ((I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 2)) { 244printf("%s: write cmdlen=1, buflen=2: Ooops, maybe error...\n", __func__); 245 rv = pxa2x0_i2c_write(&sc->sc_pxa_i2c, addr, cmd); 246 if (rv == 0) { 247 u_short v = 248 (((u_char *)vbuf)[0] << 8) | ((u_char *)vbuf)[1]; 249 250 rv = pxa2x0_i2c_write_2(&sc->sc_pxa_i2c, addr, v); 251 } 252 } 253 254 /* Handle quick_read/quick_write ops - XXX Untested XXX */ 255 if ((cmdlen == 0) && (buflen == 0)) 256 rv = pxa2x0_i2c_quick(&sc->sc_pxa_i2c, addr, 257 I2C_OP_READ_P(op) ? 1 : 0); 258 259 return rv; 260} 261