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/errno.h> 33 34#include <machine/platid.h> 35#include <machine/platid_mask.h> 36 37#include <arm/xscale/pxa2x0_i2c.h> 38 39#include <hpcarm/dev/nbppconvar.h> 40 41#include <dev/i2c/i2cvar.h> 42#include <dev/i2c/at24cxxvar.h> 43 44#include "locators.h" 45 46#define NBPPCON_ADDR 0x13 47 48 49struct nbppcon_softc { 50 device_t sc_dev; 51 i2c_tag_t sc_tag; 52 int sc_address; 53 54 struct callback { 55 int cb_tag; 56 int (*cb_func)(void *, int, char *); 57 void *cb_arg; 58 } sc_cbs[2]; 59}; 60 61static int nbppcon_match(device_t, cfdata_t, void *); 62static void nbppcon_attach(device_t, device_t, void *); 63 64static int nbppcon_search(device_t, cfdata_t, const int *, void *); 65static int nbppcon_print(void *aux, const char *pnp); 66 67CFATTACH_DECL_NEW(nbppcon, sizeof(struct nbppcon_softc), 68 nbppcon_match, nbppcon_attach, NULL, NULL); 69 70 71/* ARGSUSED */ 72static int 73nbppcon_match(device_t parent, cfdata_t match, void *aux) 74{ 75 struct i2c_attach_args *ia = aux; 76 77 if (ia->ia_addr != NBPPCON_ADDR || 78 !platid_match(&platid, &platid_mask_MACH_PSIONTEKLOGIX_NETBOOK_PRO)) 79 return 0; 80 81 return 1; 82} 83 84/* ARGSUSED */ 85static void 86nbppcon_attach(device_t parent, device_t self, void *aux) 87{ 88 struct nbppcon_softc *sc = device_private(self); 89 struct i2c_attach_args *ia = aux; 90 int rv; 91 char buf[128]; 92 93 sc->sc_dev = self; 94 sc->sc_tag = ia->ia_tag; 95 sc->sc_address = ia->ia_addr; 96 97 aprint_naive("\n"); 98 aprint_normal(": NETBOOK PRO PCon\n"); 99 100#define NVRAM_ADDR 0x53 101#define NVRAM_DATA_REV 0x14 102 rv = seeprom_bootstrap_read(sc->sc_tag, NVRAM_ADDR, 0x00, 128, 103 buf, sizeof(buf)); 104 if (rv == 0) 105 aprint_normal_dev(self, "NETBOOK PRO Rev.%c", 106 buf[NVRAM_DATA_REV] - '0' + '@'); 107 else 108 aprint_error_dev(self, "NVRAM read failed\n"); 109 110 config_search_ia(nbppcon_search, self, "nbppcon", NULL); 111} 112 113/* ARGSUSED */ 114static int 115nbppcon_search(device_t self, cfdata_t cf, const int *ldesc, void *aux) 116{ 117 struct nbppcon_attach_args pcon; 118 119 pcon.aa_name = cf->cf_name; 120 pcon.aa_tag = cf->cf_loc[NBPPCONCF_TAG]; 121 122 if (config_match(self, cf, &pcon)) 123 config_attach(self, cf, &pcon, nbppcon_print); 124 125 return 0; 126} 127 128/* ARGSUSED */ 129static int 130nbppcon_print(void *aux, const char *pnp) 131{ 132 133 if (pnp != NULL) 134 aprint_normal("%s", pnp); 135 return UNCONF; 136} 137 138 139void * 140nbppcon_regist_callback(device_t self, 141 int tag, int (*func)(void *, int, char *), void *arg) 142{ 143 struct nbppcon_softc *sc = device_private(self); 144 int i; 145 146 for (i = 0; i < __arraycount(sc->sc_cbs); i++) 147 if (sc->sc_cbs[i].cb_func == NULL) { 148 sc->sc_cbs[i].cb_tag = tag; 149 sc->sc_cbs[i].cb_func = func; 150 sc->sc_cbs[i].cb_arg = arg; 151 return func; 152 } 153 154 return NULL; 155} 156 157int 158nbppcon_intr(device_t self, int buflen, char *buf) 159{ 160 struct nbppcon_softc *sc = device_private(self); 161 struct callback *cb; 162 int i; 163 164 for (i = 0; i < __arraycount(sc->sc_cbs); i++) { 165 cb = &sc->sc_cbs[i]; 166 if (cb->cb_func != NULL && 167 cb->cb_tag == buf[0]) 168 return cb->cb_func(cb->cb_arg, buflen, buf); 169 } 170 aprint_error_dev(sc->sc_dev, "unknown tag received: 0x%02x\n", buf[0]); 171 return 0; 172} 173 174int 175nbppcon_poll(device_t self, int tag, int buflen, char *buf) 176{ 177 struct nbppcon_softc *sc = device_private(self); 178 int rv; 179 180 if (iic_acquire_bus(sc->sc_tag, I2C_F_POLL)) { 181 aprint_error_dev(sc->sc_dev, 182 "%s: acquire bus failed\n", __func__); 183 return -1; 184 } 185 186 do { 187 extern int nbpiic_poll(void *, int, char *); 188 189 rv = nbpiic_poll(sc->sc_tag->ic_cookie, buflen, buf); 190 if (rv > 0 && buf[0] == tag) 191 break; 192 } while (rv == 0 || buf[0] != tag); 193 194 iic_release_bus(sc->sc_tag, I2C_F_POLL); 195 196 return rv; 197} 198 199int 200nbppcon_kbd_ready(device_t self) 201{ 202 struct nbppcon_softc *sc = device_private(self); 203 int rv; 204 char cmd[1], data[1]; 205 206 if (iic_acquire_bus(sc->sc_tag, I2C_F_POLL)) { 207 aprint_error_dev(sc->sc_dev, 208 "%s: acquire bus failed\n", __func__); 209 return -1; 210 } 211 212 cmd[0] = 0x00; /* Keyboard */ 213 data[0] = 0x00; /* 0x00:Ready, 0x01:Busy? */ 214 rv = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_address, 215 cmd, sizeof(cmd), data, sizeof(data), I2C_F_POLL); 216 217 cpu_dcache_wbinv_all(); /* XXXX: Why? */ 218 219 iic_release_bus(sc->sc_tag, I2C_F_POLL); 220 221 return rv; 222} 223 224int 225nbppcon_pwr_hwreset(device_t self) 226{ 227 struct nbppcon_softc *sc = device_private(self); 228 int rv; 229 char cmd[1], data[1]; 230 231 if (iic_acquire_bus(sc->sc_tag, I2C_F_POLL)) { 232 aprint_error_dev(sc->sc_dev, 233 "%s: acquire bus failed\n", __func__); 234 return -1; 235 } 236 237 cmd[0] = 0x05; /* POWER */ 238 data[0] = 0x00; /* HWReset */ 239#if 0 240 rv = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_address, 241 cmd, sizeof(cmd), data, sizeof(data), I2C_F_POLL); 242#else /* XXXX: Oops, ensure HWReset, ignore cmd and data. */ 243 rv = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_address + 1, 244 cmd, sizeof(cmd), data, sizeof(data), I2C_F_POLL); 245#endif 246 247 iic_release_bus(sc->sc_tag, I2C_F_POLL); 248 249 return rv; 250} 251