1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * CDDL HEADER START 3*7c478bd9Sstevel@tonic-gate * 4*7c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*7c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*7c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*7c478bd9Sstevel@tonic-gate * with the License. 8*7c478bd9Sstevel@tonic-gate * 9*7c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*7c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*7c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 12*7c478bd9Sstevel@tonic-gate * and limitations under the License. 13*7c478bd9Sstevel@tonic-gate * 14*7c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*7c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*7c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*7c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*7c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*7c478bd9Sstevel@tonic-gate * 20*7c478bd9Sstevel@tonic-gate * CDDL HEADER END 21*7c478bd9Sstevel@tonic-gate */ 22*7c478bd9Sstevel@tonic-gate /* 23*7c478bd9Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*7c478bd9Sstevel@tonic-gate * Use is subject to license terms. 25*7c478bd9Sstevel@tonic-gate */ 26*7c478bd9Sstevel@tonic-gate 27*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*7c478bd9Sstevel@tonic-gate 29*7c478bd9Sstevel@tonic-gate /* 30*7c478bd9Sstevel@tonic-gate * This is the nexus driver for SMBUS devices. It mostly does not use 31*7c478bd9Sstevel@tonic-gate * the SMBUS protocol so that it fits better into the solaris i2c 32*7c478bd9Sstevel@tonic-gate * framework. 33*7c478bd9Sstevel@tonic-gate */ 34*7c478bd9Sstevel@tonic-gate 35*7c478bd9Sstevel@tonic-gate #include <sys/types.h> 36*7c478bd9Sstevel@tonic-gate #include <sys/conf.h> 37*7c478bd9Sstevel@tonic-gate #include <sys/file.h> 38*7c478bd9Sstevel@tonic-gate #include <sys/open.h> 39*7c478bd9Sstevel@tonic-gate #include <sys/ddi.h> 40*7c478bd9Sstevel@tonic-gate #include <sys/sunddi.h> 41*7c478bd9Sstevel@tonic-gate #include <sys/modctl.h> 42*7c478bd9Sstevel@tonic-gate #include <sys/stat.h> 43*7c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 44*7c478bd9Sstevel@tonic-gate #include <sys/archsystm.h> 45*7c478bd9Sstevel@tonic-gate #include <sys/platform_module.h> 46*7c478bd9Sstevel@tonic-gate 47*7c478bd9Sstevel@tonic-gate #include <sys/i2c/clients/i2c_client.h> 48*7c478bd9Sstevel@tonic-gate #include <sys/i2c/misc/i2c_svc.h> 49*7c478bd9Sstevel@tonic-gate #include <sys/i2c/misc/i2c_svc_impl.h> 50*7c478bd9Sstevel@tonic-gate #include <sys/i2c/nexus/smbus.h> 51*7c478bd9Sstevel@tonic-gate 52*7c478bd9Sstevel@tonic-gate /* 53*7c478bd9Sstevel@tonic-gate * static function declarations 54*7c478bd9Sstevel@tonic-gate */ 55*7c478bd9Sstevel@tonic-gate static uint_t smbus_intr_cmn(smbus_t *smbus, char *src); 56*7c478bd9Sstevel@tonic-gate static void smbus_intr_timeout(void *arg); 57*7c478bd9Sstevel@tonic-gate static void smbus_resume(dev_info_t *dip); 58*7c478bd9Sstevel@tonic-gate static void smbus_suspend(dev_info_t *dip); 59*7c478bd9Sstevel@tonic-gate static int smbus_bus_ctl(dev_info_t *dip, dev_info_t *rdip, 60*7c478bd9Sstevel@tonic-gate ddi_ctl_enum_t op, void *arg, void *result); 61*7c478bd9Sstevel@tonic-gate static int smbus_acquire(smbus_t *, dev_info_t *dip, 62*7c478bd9Sstevel@tonic-gate i2c_transfer_t *tp); 63*7c478bd9Sstevel@tonic-gate static void smbus_release(smbus_t *); 64*7c478bd9Sstevel@tonic-gate static int smbus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 65*7c478bd9Sstevel@tonic-gate static int smbus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 66*7c478bd9Sstevel@tonic-gate static void smbus_free_regs(smbus_t *smbus); 67*7c478bd9Sstevel@tonic-gate static int smbus_setup_regs(dev_info_t *dip, smbus_t *smbus); 68*7c478bd9Sstevel@tonic-gate static void smbus_reportdev(dev_info_t *dip, dev_info_t *rdip); 69*7c478bd9Sstevel@tonic-gate static void smbus_uninitchild(dev_info_t *cdip); 70*7c478bd9Sstevel@tonic-gate static int smbus_initchild(dev_info_t *cdip); 71*7c478bd9Sstevel@tonic-gate static int smbus_rd(smbus_t *smbus); 72*7c478bd9Sstevel@tonic-gate static int smbus_wr(smbus_t *smbus); 73*7c478bd9Sstevel@tonic-gate static void smbus_put(smbus_t *smbus, uint8_t reg, uint8_t data, uint8_t flags); 74*7c478bd9Sstevel@tonic-gate static uint8_t smbus_get(smbus_t *smbus, uint8_t reg); 75*7c478bd9Sstevel@tonic-gate static int smbus_dip_to_addr(dev_info_t *dip); 76*7c478bd9Sstevel@tonic-gate static uint_t smbus_intr(caddr_t arg); 77*7c478bd9Sstevel@tonic-gate static int smbus_switch(smbus_t *smbus); 78*7c478bd9Sstevel@tonic-gate 79*7c478bd9Sstevel@tonic-gate static struct bus_ops smbus_busops = { 80*7c478bd9Sstevel@tonic-gate BUSO_REV, 81*7c478bd9Sstevel@tonic-gate nullbusmap, /* bus_map */ 82*7c478bd9Sstevel@tonic-gate NULL, /* bus_get_intrspec */ 83*7c478bd9Sstevel@tonic-gate NULL, /* bus_add_intrspec */ 84*7c478bd9Sstevel@tonic-gate NULL, /* bus_remove_intrspec */ 85*7c478bd9Sstevel@tonic-gate NULL, /* bus_map_fault */ 86*7c478bd9Sstevel@tonic-gate ddi_no_dma_map, /* bus_dma_map */ 87*7c478bd9Sstevel@tonic-gate ddi_no_dma_allochdl, /* bus_dma_allochdl */ 88*7c478bd9Sstevel@tonic-gate ddi_no_dma_freehdl, /* bus_dma_freehdl */ 89*7c478bd9Sstevel@tonic-gate ddi_no_dma_bindhdl, /* bus_dma_bindhdl */ 90*7c478bd9Sstevel@tonic-gate ddi_no_dma_unbindhdl, /* bus_unbindhdl */ 91*7c478bd9Sstevel@tonic-gate ddi_no_dma_flush, /* bus_dma_flush */ 92*7c478bd9Sstevel@tonic-gate ddi_no_dma_win, /* bus_dma_win */ 93*7c478bd9Sstevel@tonic-gate ddi_no_dma_mctl, /* bus_dma_ctl */ 94*7c478bd9Sstevel@tonic-gate smbus_bus_ctl, /* bus_ctl */ 95*7c478bd9Sstevel@tonic-gate ddi_bus_prop_op, /* bus_prop_op */ 96*7c478bd9Sstevel@tonic-gate NULL, /* bus_get_eventcookie */ 97*7c478bd9Sstevel@tonic-gate NULL, /* bus_add_eventcall */ 98*7c478bd9Sstevel@tonic-gate NULL, /* bus_remove_eventcall */ 99*7c478bd9Sstevel@tonic-gate NULL, /* bus_post_event */ 100*7c478bd9Sstevel@tonic-gate 0, /* bus_intr_ctl */ 101*7c478bd9Sstevel@tonic-gate 0, /* bus_config */ 102*7c478bd9Sstevel@tonic-gate 0, /* bus_unconfig */ 103*7c478bd9Sstevel@tonic-gate 0, /* bus_fm_init */ 104*7c478bd9Sstevel@tonic-gate 0, /* bus_fm_fini */ 105*7c478bd9Sstevel@tonic-gate 0, /* bus_fm_access_enter */ 106*7c478bd9Sstevel@tonic-gate 0, /* bus_fm_access_exit */ 107*7c478bd9Sstevel@tonic-gate 0, /* bus_power */ 108*7c478bd9Sstevel@tonic-gate i_ddi_intr_ops /* bus_intr_op */ 109*7c478bd9Sstevel@tonic-gate }; 110*7c478bd9Sstevel@tonic-gate 111*7c478bd9Sstevel@tonic-gate struct cb_ops smbus_cb_ops = { 112*7c478bd9Sstevel@tonic-gate nodev, /* open */ 113*7c478bd9Sstevel@tonic-gate nodev, /* close */ 114*7c478bd9Sstevel@tonic-gate nodev, /* strategy */ 115*7c478bd9Sstevel@tonic-gate nodev, /* print */ 116*7c478bd9Sstevel@tonic-gate nodev, /* dump */ 117*7c478bd9Sstevel@tonic-gate nodev, /* read */ 118*7c478bd9Sstevel@tonic-gate nodev, /* write */ 119*7c478bd9Sstevel@tonic-gate nodev, /* ioctl */ 120*7c478bd9Sstevel@tonic-gate nodev, /* devmap */ 121*7c478bd9Sstevel@tonic-gate nodev, /* mmap */ 122*7c478bd9Sstevel@tonic-gate nodev, /* segmap */ 123*7c478bd9Sstevel@tonic-gate nochpoll, /* poll */ 124*7c478bd9Sstevel@tonic-gate ddi_prop_op, /* cb_prop_op */ 125*7c478bd9Sstevel@tonic-gate 0, /* streamtab */ 126*7c478bd9Sstevel@tonic-gate D_MP | D_NEW /* Driver compatibility flag */ 127*7c478bd9Sstevel@tonic-gate }; 128*7c478bd9Sstevel@tonic-gate 129*7c478bd9Sstevel@tonic-gate static struct dev_ops smbus_ops = { 130*7c478bd9Sstevel@tonic-gate DEVO_REV, 131*7c478bd9Sstevel@tonic-gate 0, 132*7c478bd9Sstevel@tonic-gate ddi_no_info, 133*7c478bd9Sstevel@tonic-gate nulldev, 134*7c478bd9Sstevel@tonic-gate nulldev, 135*7c478bd9Sstevel@tonic-gate smbus_attach, 136*7c478bd9Sstevel@tonic-gate smbus_detach, 137*7c478bd9Sstevel@tonic-gate nodev, 138*7c478bd9Sstevel@tonic-gate &smbus_cb_ops, 139*7c478bd9Sstevel@tonic-gate &smbus_busops 140*7c478bd9Sstevel@tonic-gate }; 141*7c478bd9Sstevel@tonic-gate 142*7c478bd9Sstevel@tonic-gate static struct modldrv modldrv = { 143*7c478bd9Sstevel@tonic-gate &mod_driverops, /* Type of module. This one is a driver */ 144*7c478bd9Sstevel@tonic-gate "SMBUS nexus Driver %I%", /* Name of the module. */ 145*7c478bd9Sstevel@tonic-gate &smbus_ops, /* driver ops */ 146*7c478bd9Sstevel@tonic-gate }; 147*7c478bd9Sstevel@tonic-gate 148*7c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = { 149*7c478bd9Sstevel@tonic-gate MODREV_1, 150*7c478bd9Sstevel@tonic-gate &modldrv, 151*7c478bd9Sstevel@tonic-gate NULL 152*7c478bd9Sstevel@tonic-gate }; 153*7c478bd9Sstevel@tonic-gate 154*7c478bd9Sstevel@tonic-gate /* 155*7c478bd9Sstevel@tonic-gate * Globals 156*7c478bd9Sstevel@tonic-gate */ 157*7c478bd9Sstevel@tonic-gate static void *smbus_state; 158*7c478bd9Sstevel@tonic-gate 159*7c478bd9Sstevel@tonic-gate static int intr_timeout = INTR_TIMEOUT; 160*7c478bd9Sstevel@tonic-gate 161*7c478bd9Sstevel@tonic-gate /* 162*7c478bd9Sstevel@tonic-gate * The "interrupt-priorities" property is how a driver can specify a SPARC 163*7c478bd9Sstevel@tonic-gate * PIL level to associate with each of its interrupt properties. Most 164*7c478bd9Sstevel@tonic-gate * self-identifying busses have a better mechanism for managing this, but I2C 165*7c478bd9Sstevel@tonic-gate * doesn't. 166*7c478bd9Sstevel@tonic-gate */ 167*7c478bd9Sstevel@tonic-gate int smbus_pil = SMBUS_PIL; 168*7c478bd9Sstevel@tonic-gate 169*7c478bd9Sstevel@tonic-gate i2c_nexus_reg_t smbus_regvec = { 170*7c478bd9Sstevel@tonic-gate I2C_NEXUS_REV, 171*7c478bd9Sstevel@tonic-gate smbus_transfer, 172*7c478bd9Sstevel@tonic-gate }; 173*7c478bd9Sstevel@tonic-gate 174*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 175*7c478bd9Sstevel@tonic-gate 176*7c478bd9Sstevel@tonic-gate static int smbus_print_lvl = 0; 177*7c478bd9Sstevel@tonic-gate static char msg_buff[1024]; 178*7c478bd9Sstevel@tonic-gate static kmutex_t msg_buf_lock; 179*7c478bd9Sstevel@tonic-gate 180*7c478bd9Sstevel@tonic-gate void 181*7c478bd9Sstevel@tonic-gate smbus_print(int flags, const char *fmt, ...) 182*7c478bd9Sstevel@tonic-gate { 183*7c478bd9Sstevel@tonic-gate if (flags & smbus_print_lvl) { 184*7c478bd9Sstevel@tonic-gate va_list ap; 185*7c478bd9Sstevel@tonic-gate 186*7c478bd9Sstevel@tonic-gate va_start(ap, fmt); 187*7c478bd9Sstevel@tonic-gate 188*7c478bd9Sstevel@tonic-gate if (smbus_print_lvl & PRT_PROM) { 189*7c478bd9Sstevel@tonic-gate prom_vprintf(fmt, ap); 190*7c478bd9Sstevel@tonic-gate } else { 191*7c478bd9Sstevel@tonic-gate 192*7c478bd9Sstevel@tonic-gate mutex_enter(&msg_buf_lock); 193*7c478bd9Sstevel@tonic-gate (void) vsprintf(msg_buff, fmt, ap); 194*7c478bd9Sstevel@tonic-gate if (smbus_print_lvl & PRT_BUFFONLY) { 195*7c478bd9Sstevel@tonic-gate cmn_err(CE_CONT, "?%s", msg_buff); 196*7c478bd9Sstevel@tonic-gate } else { 197*7c478bd9Sstevel@tonic-gate cmn_err(CE_CONT, "%s", msg_buff); 198*7c478bd9Sstevel@tonic-gate } 199*7c478bd9Sstevel@tonic-gate mutex_exit(&msg_buf_lock); 200*7c478bd9Sstevel@tonic-gate } 201*7c478bd9Sstevel@tonic-gate va_end(ap); 202*7c478bd9Sstevel@tonic-gate } 203*7c478bd9Sstevel@tonic-gate } 204*7c478bd9Sstevel@tonic-gate #endif /* DEBUG */ 205*7c478bd9Sstevel@tonic-gate 206*7c478bd9Sstevel@tonic-gate int 207*7c478bd9Sstevel@tonic-gate _init(void) 208*7c478bd9Sstevel@tonic-gate { 209*7c478bd9Sstevel@tonic-gate int status; 210*7c478bd9Sstevel@tonic-gate 211*7c478bd9Sstevel@tonic-gate status = ddi_soft_state_init(&smbus_state, sizeof (smbus_t), 212*7c478bd9Sstevel@tonic-gate 1); 213*7c478bd9Sstevel@tonic-gate if (status != 0) { 214*7c478bd9Sstevel@tonic-gate 215*7c478bd9Sstevel@tonic-gate return (status); 216*7c478bd9Sstevel@tonic-gate } 217*7c478bd9Sstevel@tonic-gate 218*7c478bd9Sstevel@tonic-gate if ((status = mod_install(&modlinkage)) != 0) { 219*7c478bd9Sstevel@tonic-gate ddi_soft_state_fini(&smbus_state); 220*7c478bd9Sstevel@tonic-gate } else { 221*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 222*7c478bd9Sstevel@tonic-gate mutex_init(&msg_buf_lock, NULL, MUTEX_DRIVER, NULL); 223*7c478bd9Sstevel@tonic-gate #endif 224*7c478bd9Sstevel@tonic-gate } 225*7c478bd9Sstevel@tonic-gate return (status); 226*7c478bd9Sstevel@tonic-gate } 227*7c478bd9Sstevel@tonic-gate 228*7c478bd9Sstevel@tonic-gate int 229*7c478bd9Sstevel@tonic-gate _fini(void) 230*7c478bd9Sstevel@tonic-gate { 231*7c478bd9Sstevel@tonic-gate int status; 232*7c478bd9Sstevel@tonic-gate 233*7c478bd9Sstevel@tonic-gate if ((status = mod_remove(&modlinkage)) == 0) { 234*7c478bd9Sstevel@tonic-gate ddi_soft_state_fini(&smbus_state); 235*7c478bd9Sstevel@tonic-gate #ifdef DEBUG 236*7c478bd9Sstevel@tonic-gate mutex_destroy(&msg_buf_lock); 237*7c478bd9Sstevel@tonic-gate #endif 238*7c478bd9Sstevel@tonic-gate } 239*7c478bd9Sstevel@tonic-gate 240*7c478bd9Sstevel@tonic-gate return (status); 241*7c478bd9Sstevel@tonic-gate } 242*7c478bd9Sstevel@tonic-gate 243*7c478bd9Sstevel@tonic-gate /* 244*7c478bd9Sstevel@tonic-gate * The loadable-module _info(9E) entry point 245*7c478bd9Sstevel@tonic-gate */ 246*7c478bd9Sstevel@tonic-gate int 247*7c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop) 248*7c478bd9Sstevel@tonic-gate { 249*7c478bd9Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 250*7c478bd9Sstevel@tonic-gate } 251*7c478bd9Sstevel@tonic-gate 252*7c478bd9Sstevel@tonic-gate static void 253*7c478bd9Sstevel@tonic-gate smbus_interrupts_on(smbus_t *smbus) 254*7c478bd9Sstevel@tonic-gate { 255*7c478bd9Sstevel@tonic-gate int src_enable; 256*7c478bd9Sstevel@tonic-gate 257*7c478bd9Sstevel@tonic-gate src_enable = ddi_get32(smbus->smbus_confighandle, 258*7c478bd9Sstevel@tonic-gate (uint32_t *)&smbus->smbus_configregaddr[SMBUS_SRC_ENA]); 259*7c478bd9Sstevel@tonic-gate src_enable |= SMBUS_SMI; 260*7c478bd9Sstevel@tonic-gate ddi_put32(smbus->smbus_confighandle, 261*7c478bd9Sstevel@tonic-gate (uint32_t *)&smbus->smbus_configregaddr[SMBUS_SRC_ENA], 262*7c478bd9Sstevel@tonic-gate src_enable); 263*7c478bd9Sstevel@tonic-gate (void) ddi_get32(smbus->smbus_confighandle, 264*7c478bd9Sstevel@tonic-gate (uint32_t *)&smbus->smbus_configregaddr[SMBUS_SRC_ENA]); 265*7c478bd9Sstevel@tonic-gate } 266*7c478bd9Sstevel@tonic-gate 267*7c478bd9Sstevel@tonic-gate static void 268*7c478bd9Sstevel@tonic-gate smbus_interrupts_off(smbus_t *smbus) 269*7c478bd9Sstevel@tonic-gate { 270*7c478bd9Sstevel@tonic-gate int src_enable; 271*7c478bd9Sstevel@tonic-gate 272*7c478bd9Sstevel@tonic-gate src_enable = ddi_get32(smbus->smbus_confighandle, 273*7c478bd9Sstevel@tonic-gate (uint32_t *)&smbus->smbus_configregaddr[SMBUS_SRC_ENA]); 274*7c478bd9Sstevel@tonic-gate src_enable &= ~SMBUS_SMI; 275*7c478bd9Sstevel@tonic-gate ddi_put32(smbus->smbus_confighandle, 276*7c478bd9Sstevel@tonic-gate (uint32_t *)&smbus->smbus_configregaddr[SMBUS_SRC_ENA], 277*7c478bd9Sstevel@tonic-gate src_enable); 278*7c478bd9Sstevel@tonic-gate (void) ddi_get32(smbus->smbus_confighandle, 279*7c478bd9Sstevel@tonic-gate (uint32_t *)&smbus->smbus_configregaddr[SMBUS_SRC_ENA]); 280*7c478bd9Sstevel@tonic-gate } 281*7c478bd9Sstevel@tonic-gate 282*7c478bd9Sstevel@tonic-gate static void 283*7c478bd9Sstevel@tonic-gate smbus_dodetach(dev_info_t *dip) 284*7c478bd9Sstevel@tonic-gate { 285*7c478bd9Sstevel@tonic-gate smbus_t *smbus; 286*7c478bd9Sstevel@tonic-gate int instance = ddi_get_instance(dip); 287*7c478bd9Sstevel@tonic-gate 288*7c478bd9Sstevel@tonic-gate smbus = ddi_get_soft_state(smbus_state, instance); 289*7c478bd9Sstevel@tonic-gate 290*7c478bd9Sstevel@tonic-gate if (smbus == NULL) { 291*7c478bd9Sstevel@tonic-gate 292*7c478bd9Sstevel@tonic-gate return; 293*7c478bd9Sstevel@tonic-gate } 294*7c478bd9Sstevel@tonic-gate 295*7c478bd9Sstevel@tonic-gate cv_destroy(&smbus->smbus_cv); 296*7c478bd9Sstevel@tonic-gate mutex_destroy(&smbus->smbus_mutex); 297*7c478bd9Sstevel@tonic-gate 298*7c478bd9Sstevel@tonic-gate if ((smbus->smbus_attachflags & INTERRUPT_PRI) != 0) { 299*7c478bd9Sstevel@tonic-gate (void) ddi_prop_remove(DDI_DEV_T_NONE, dip, 300*7c478bd9Sstevel@tonic-gate "interrupt-priorities"); 301*7c478bd9Sstevel@tonic-gate } 302*7c478bd9Sstevel@tonic-gate 303*7c478bd9Sstevel@tonic-gate smbus_free_regs(smbus); 304*7c478bd9Sstevel@tonic-gate 305*7c478bd9Sstevel@tonic-gate if ((smbus->smbus_attachflags & NEXUS_REGISTER) != 0) { 306*7c478bd9Sstevel@tonic-gate i2c_nexus_unregister(dip); 307*7c478bd9Sstevel@tonic-gate } 308*7c478bd9Sstevel@tonic-gate if ((smbus->smbus_attachflags & IMUTEX) != 0) { 309*7c478bd9Sstevel@tonic-gate mutex_destroy(&smbus->smbus_imutex); 310*7c478bd9Sstevel@tonic-gate cv_destroy(&smbus->smbus_icv); 311*7c478bd9Sstevel@tonic-gate } 312*7c478bd9Sstevel@tonic-gate 313*7c478bd9Sstevel@tonic-gate if (smbus->smbus_timeout != 0) { 314*7c478bd9Sstevel@tonic-gate (void) untimeout(smbus->smbus_timeout); 315*7c478bd9Sstevel@tonic-gate } 316*7c478bd9Sstevel@tonic-gate 317*7c478bd9Sstevel@tonic-gate if ((smbus->smbus_attachflags & ADD_INTR) != 0) { 318*7c478bd9Sstevel@tonic-gate ddi_remove_intr(dip, 0, smbus->smbus_icookie); 319*7c478bd9Sstevel@tonic-gate } 320*7c478bd9Sstevel@tonic-gate 321*7c478bd9Sstevel@tonic-gate ddi_soft_state_free(smbus_state, instance); 322*7c478bd9Sstevel@tonic-gate } 323*7c478bd9Sstevel@tonic-gate 324*7c478bd9Sstevel@tonic-gate static int 325*7c478bd9Sstevel@tonic-gate smbus_doattach(dev_info_t *dip) 326*7c478bd9Sstevel@tonic-gate { 327*7c478bd9Sstevel@tonic-gate smbus_t *smbus; 328*7c478bd9Sstevel@tonic-gate int instance = ddi_get_instance(dip); 329*7c478bd9Sstevel@tonic-gate 330*7c478bd9Sstevel@tonic-gate /* 331*7c478bd9Sstevel@tonic-gate * Allocate soft state structure. 332*7c478bd9Sstevel@tonic-gate */ 333*7c478bd9Sstevel@tonic-gate if (ddi_soft_state_zalloc(smbus_state, instance) != DDI_SUCCESS) { 334*7c478bd9Sstevel@tonic-gate 335*7c478bd9Sstevel@tonic-gate goto bad; 336*7c478bd9Sstevel@tonic-gate } 337*7c478bd9Sstevel@tonic-gate 338*7c478bd9Sstevel@tonic-gate smbus = ddi_get_soft_state(smbus_state, instance); 339*7c478bd9Sstevel@tonic-gate 340*7c478bd9Sstevel@tonic-gate (void) snprintf(smbus->smbus_name, sizeof (smbus->smbus_name), 341*7c478bd9Sstevel@tonic-gate "%s%d", ddi_node_name(dip), instance); 342*7c478bd9Sstevel@tonic-gate 343*7c478bd9Sstevel@tonic-gate smbus->smbus_dip = dip; 344*7c478bd9Sstevel@tonic-gate 345*7c478bd9Sstevel@tonic-gate mutex_init(&smbus->smbus_mutex, NULL, MUTEX_DRIVER, NULL); 346*7c478bd9Sstevel@tonic-gate mutex_init(&smbus->smbus_imutex, NULL, MUTEX_DRIVER, NULL); 347*7c478bd9Sstevel@tonic-gate cv_init(&smbus->smbus_cv, NULL, CV_DRIVER, NULL); 348*7c478bd9Sstevel@tonic-gate cv_init(&smbus->smbus_intr_cv, NULL, CV_DRIVER, NULL); 349*7c478bd9Sstevel@tonic-gate 350*7c478bd9Sstevel@tonic-gate if (smbus_setup_regs(dip, smbus) != DDI_SUCCESS) { 351*7c478bd9Sstevel@tonic-gate goto bad; 352*7c478bd9Sstevel@tonic-gate } 353*7c478bd9Sstevel@tonic-gate 354*7c478bd9Sstevel@tonic-gate if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 355*7c478bd9Sstevel@tonic-gate "interrupts") == 1) { 356*7c478bd9Sstevel@tonic-gate smbus->smbus_polling = 0; 357*7c478bd9Sstevel@tonic-gate /* 358*7c478bd9Sstevel@tonic-gate * The "interrupt-priorities" property is how a driver can 359*7c478bd9Sstevel@tonic-gate * specify a SPARC PIL level to associate with each of its 360*7c478bd9Sstevel@tonic-gate * interrupt properties. Most self-identifying busses have 361*7c478bd9Sstevel@tonic-gate * a better mechanism for managing this, but I2C doesn't. 362*7c478bd9Sstevel@tonic-gate */ 363*7c478bd9Sstevel@tonic-gate if (ddi_prop_exists(DDI_DEV_T_ANY, dip, 364*7c478bd9Sstevel@tonic-gate DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, 365*7c478bd9Sstevel@tonic-gate "interrupt-priorities") != 1) { 366*7c478bd9Sstevel@tonic-gate (void) ddi_prop_create(DDI_DEV_T_NONE, dip, 367*7c478bd9Sstevel@tonic-gate DDI_PROP_CANSLEEP, "interrupt-priorities", 368*7c478bd9Sstevel@tonic-gate (caddr_t)&smbus_pil, 369*7c478bd9Sstevel@tonic-gate sizeof (smbus_pil)); 370*7c478bd9Sstevel@tonic-gate smbus->smbus_attachflags |= INTERRUPT_PRI; 371*7c478bd9Sstevel@tonic-gate } 372*7c478bd9Sstevel@tonic-gate 373*7c478bd9Sstevel@tonic-gate /* 374*7c478bd9Sstevel@tonic-gate * Clear status to clear any possible interrupt 375*7c478bd9Sstevel@tonic-gate */ 376*7c478bd9Sstevel@tonic-gate smbus_put(smbus, SMB_STS, 0xff, SMBUS_FLUSH); 377*7c478bd9Sstevel@tonic-gate 378*7c478bd9Sstevel@tonic-gate if (ddi_get_iblock_cookie(dip, 0, &smbus->smbus_icookie) != 379*7c478bd9Sstevel@tonic-gate DDI_SUCCESS) { 380*7c478bd9Sstevel@tonic-gate goto bad; 381*7c478bd9Sstevel@tonic-gate } 382*7c478bd9Sstevel@tonic-gate 383*7c478bd9Sstevel@tonic-gate if (ddi_add_intr(dip, 0, NULL, NULL, smbus_intr, 384*7c478bd9Sstevel@tonic-gate (caddr_t)smbus) != DDI_SUCCESS) { 385*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s failed to add interrupt", 386*7c478bd9Sstevel@tonic-gate smbus->smbus_name); 387*7c478bd9Sstevel@tonic-gate goto bad; 388*7c478bd9Sstevel@tonic-gate } 389*7c478bd9Sstevel@tonic-gate smbus->smbus_attachflags |= ADD_INTR; 390*7c478bd9Sstevel@tonic-gate } else { 391*7c478bd9Sstevel@tonic-gate smbus->smbus_polling = 1; 392*7c478bd9Sstevel@tonic-gate /* Clear status */ 393*7c478bd9Sstevel@tonic-gate smbus_put(smbus, SMB_STS, 0xff, SMBUS_FLUSH); 394*7c478bd9Sstevel@tonic-gate } 395*7c478bd9Sstevel@tonic-gate 396*7c478bd9Sstevel@tonic-gate /* 397*7c478bd9Sstevel@tonic-gate * initialize a cv and mutex 398*7c478bd9Sstevel@tonic-gate */ 399*7c478bd9Sstevel@tonic-gate cv_init(&smbus->smbus_icv, NULL, CV_DRIVER, NULL); 400*7c478bd9Sstevel@tonic-gate mutex_init(&smbus->smbus_imutex, NULL, MUTEX_DRIVER, 401*7c478bd9Sstevel@tonic-gate (void *)smbus->smbus_icookie); 402*7c478bd9Sstevel@tonic-gate smbus->smbus_attachflags |= IMUTEX; 403*7c478bd9Sstevel@tonic-gate 404*7c478bd9Sstevel@tonic-gate /* 405*7c478bd9Sstevel@tonic-gate * Register with the i2c framework 406*7c478bd9Sstevel@tonic-gate */ 407*7c478bd9Sstevel@tonic-gate i2c_nexus_register(dip, &smbus_regvec); 408*7c478bd9Sstevel@tonic-gate smbus->smbus_attachflags |= NEXUS_REGISTER; 409*7c478bd9Sstevel@tonic-gate 410*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 411*7c478bd9Sstevel@tonic-gate 412*7c478bd9Sstevel@tonic-gate bad: 413*7c478bd9Sstevel@tonic-gate smbus_dodetach(dip); 414*7c478bd9Sstevel@tonic-gate 415*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 416*7c478bd9Sstevel@tonic-gate } 417*7c478bd9Sstevel@tonic-gate 418*7c478bd9Sstevel@tonic-gate static int 419*7c478bd9Sstevel@tonic-gate smbus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 420*7c478bd9Sstevel@tonic-gate { 421*7c478bd9Sstevel@tonic-gate switch (cmd) { 422*7c478bd9Sstevel@tonic-gate case DDI_ATTACH: 423*7c478bd9Sstevel@tonic-gate 424*7c478bd9Sstevel@tonic-gate return (smbus_doattach(dip)); 425*7c478bd9Sstevel@tonic-gate case DDI_RESUME: 426*7c478bd9Sstevel@tonic-gate smbus_resume(dip); 427*7c478bd9Sstevel@tonic-gate 428*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 429*7c478bd9Sstevel@tonic-gate default: 430*7c478bd9Sstevel@tonic-gate 431*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 432*7c478bd9Sstevel@tonic-gate } 433*7c478bd9Sstevel@tonic-gate } 434*7c478bd9Sstevel@tonic-gate 435*7c478bd9Sstevel@tonic-gate static int 436*7c478bd9Sstevel@tonic-gate smbus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 437*7c478bd9Sstevel@tonic-gate { 438*7c478bd9Sstevel@tonic-gate switch (cmd) { 439*7c478bd9Sstevel@tonic-gate case DDI_DETACH: 440*7c478bd9Sstevel@tonic-gate smbus_dodetach(dip); 441*7c478bd9Sstevel@tonic-gate 442*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 443*7c478bd9Sstevel@tonic-gate case DDI_SUSPEND: 444*7c478bd9Sstevel@tonic-gate smbus_suspend(dip); 445*7c478bd9Sstevel@tonic-gate 446*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 447*7c478bd9Sstevel@tonic-gate default: 448*7c478bd9Sstevel@tonic-gate 449*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 450*7c478bd9Sstevel@tonic-gate } 451*7c478bd9Sstevel@tonic-gate } 452*7c478bd9Sstevel@tonic-gate 453*7c478bd9Sstevel@tonic-gate static int 454*7c478bd9Sstevel@tonic-gate smbus_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op, 455*7c478bd9Sstevel@tonic-gate void *arg, void *result) 456*7c478bd9Sstevel@tonic-gate { 457*7c478bd9Sstevel@tonic-gate switch (op) { 458*7c478bd9Sstevel@tonic-gate case DDI_CTLOPS_INITCHILD: 459*7c478bd9Sstevel@tonic-gate 460*7c478bd9Sstevel@tonic-gate return (smbus_initchild((dev_info_t *)arg)); 461*7c478bd9Sstevel@tonic-gate case DDI_CTLOPS_UNINITCHILD: 462*7c478bd9Sstevel@tonic-gate smbus_uninitchild((dev_info_t *)arg); 463*7c478bd9Sstevel@tonic-gate 464*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 465*7c478bd9Sstevel@tonic-gate CTLOPS_REPORTDEV: 466*7c478bd9Sstevel@tonic-gate smbus_reportdev(dip, rdip); 467*7c478bd9Sstevel@tonic-gate 468*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 469*7c478bd9Sstevel@tonic-gate case DDI_CTLOPS_DMAPMAPC: 470*7c478bd9Sstevel@tonic-gate case DDI_CTLOPS_POKE: 471*7c478bd9Sstevel@tonic-gate case DDI_CTLOPS_PEEK: 472*7c478bd9Sstevel@tonic-gate case DDI_CTLOPS_IOMIN: 473*7c478bd9Sstevel@tonic-gate case DDI_CTLOPS_REPORTINT: 474*7c478bd9Sstevel@tonic-gate case DDI_CTLOPS_SIDDEV: 475*7c478bd9Sstevel@tonic-gate case DDI_CTLOPS_SLAVEONLY: 476*7c478bd9Sstevel@tonic-gate case DDI_CTLOPS_AFFINITY: 477*7c478bd9Sstevel@tonic-gate case DDI_CTLOPS_PTOB: 478*7c478bd9Sstevel@tonic-gate case DDI_CTLOPS_BTOP: 479*7c478bd9Sstevel@tonic-gate case DDI_CTLOPS_BTOPR: 480*7c478bd9Sstevel@tonic-gate case DDI_CTLOPS_INTR_HILEVEL: 481*7c478bd9Sstevel@tonic-gate case DDI_CTLOPS_XLATE_INTRS: 482*7c478bd9Sstevel@tonic-gate case DDI_CTLOPS_DVMAPAGESIZE: 483*7c478bd9Sstevel@tonic-gate 484*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 485*7c478bd9Sstevel@tonic-gate default: 486*7c478bd9Sstevel@tonic-gate 487*7c478bd9Sstevel@tonic-gate return (ddi_ctlops(dip, rdip, op, arg, result)); 488*7c478bd9Sstevel@tonic-gate } 489*7c478bd9Sstevel@tonic-gate } 490*7c478bd9Sstevel@tonic-gate 491*7c478bd9Sstevel@tonic-gate static int 492*7c478bd9Sstevel@tonic-gate smbus_initchild(dev_info_t *cdip) 493*7c478bd9Sstevel@tonic-gate { 494*7c478bd9Sstevel@tonic-gate int32_t cell_size; 495*7c478bd9Sstevel@tonic-gate int len; 496*7c478bd9Sstevel@tonic-gate int32_t regs[2]; 497*7c478bd9Sstevel@tonic-gate int err; 498*7c478bd9Sstevel@tonic-gate smbus_ppvt_t *ppvt; 499*7c478bd9Sstevel@tonic-gate char name[30]; 500*7c478bd9Sstevel@tonic-gate 501*7c478bd9Sstevel@tonic-gate SMBUS_PRINT((PRT_INIT, "smbus_initchild ENTER: %s\n", 502*7c478bd9Sstevel@tonic-gate ddi_node_name(cdip))); 503*7c478bd9Sstevel@tonic-gate 504*7c478bd9Sstevel@tonic-gate len = sizeof (cell_size); 505*7c478bd9Sstevel@tonic-gate err = ddi_getlongprop_buf(DDI_DEV_T_ANY, cdip, 506*7c478bd9Sstevel@tonic-gate DDI_PROP_CANSLEEP, "#address-cells", 507*7c478bd9Sstevel@tonic-gate (caddr_t)&cell_size, &len); 508*7c478bd9Sstevel@tonic-gate if (err != DDI_PROP_SUCCESS || len != sizeof (cell_size)) { 509*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "cannot find address-cells"); 510*7c478bd9Sstevel@tonic-gate 511*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 512*7c478bd9Sstevel@tonic-gate } 513*7c478bd9Sstevel@tonic-gate 514*7c478bd9Sstevel@tonic-gate len = sizeof (regs); 515*7c478bd9Sstevel@tonic-gate err = ddi_getlongprop_buf(DDI_DEV_T_ANY, cdip, 516*7c478bd9Sstevel@tonic-gate DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, 517*7c478bd9Sstevel@tonic-gate "reg", (caddr_t)regs, &len); 518*7c478bd9Sstevel@tonic-gate 519*7c478bd9Sstevel@tonic-gate if (err != DDI_PROP_SUCCESS) { 520*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "cannot get reg property"); 521*7c478bd9Sstevel@tonic-gate 522*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 523*7c478bd9Sstevel@tonic-gate } 524*7c478bd9Sstevel@tonic-gate 525*7c478bd9Sstevel@tonic-gate ppvt = kmem_zalloc(sizeof (smbus_ppvt_t), KM_SLEEP); 526*7c478bd9Sstevel@tonic-gate ddi_set_parent_data(cdip, ppvt); 527*7c478bd9Sstevel@tonic-gate 528*7c478bd9Sstevel@tonic-gate /* 529*7c478bd9Sstevel@tonic-gate * The reg property contains an unused first element (which is 530*7c478bd9Sstevel@tonic-gate * the mux addr on xcal), and the second element is the i2c bus 531*7c478bd9Sstevel@tonic-gate * address of the device. 532*7c478bd9Sstevel@tonic-gate */ 533*7c478bd9Sstevel@tonic-gate ppvt->smbus_ppvt_addr = regs[1]; 534*7c478bd9Sstevel@tonic-gate (void) sprintf(name, "%x", regs[1]); 535*7c478bd9Sstevel@tonic-gate 536*7c478bd9Sstevel@tonic-gate ddi_set_name_addr(cdip, name); 537*7c478bd9Sstevel@tonic-gate 538*7c478bd9Sstevel@tonic-gate SMBUS_PRINT((PRT_INIT, "smbus_initchild SUCCESS: %s\n", 539*7c478bd9Sstevel@tonic-gate ddi_node_name(cdip))); 540*7c478bd9Sstevel@tonic-gate 541*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 542*7c478bd9Sstevel@tonic-gate } 543*7c478bd9Sstevel@tonic-gate 544*7c478bd9Sstevel@tonic-gate static void 545*7c478bd9Sstevel@tonic-gate smbus_uninitchild(dev_info_t *cdip) 546*7c478bd9Sstevel@tonic-gate { 547*7c478bd9Sstevel@tonic-gate smbus_ppvt_t *ppvt; 548*7c478bd9Sstevel@tonic-gate 549*7c478bd9Sstevel@tonic-gate ppvt = ddi_get_parent_data(cdip); 550*7c478bd9Sstevel@tonic-gate ddi_set_parent_data(cdip, NULL); 551*7c478bd9Sstevel@tonic-gate 552*7c478bd9Sstevel@tonic-gate ddi_set_name_addr(cdip, NULL); 553*7c478bd9Sstevel@tonic-gate 554*7c478bd9Sstevel@tonic-gate kmem_free(ppvt, sizeof (smbus_ppvt_t)); 555*7c478bd9Sstevel@tonic-gate 556*7c478bd9Sstevel@tonic-gate SMBUS_PRINT((PRT_INIT, "smbus_uninitchild: %s\n", ddi_node_name(cdip))); 557*7c478bd9Sstevel@tonic-gate } 558*7c478bd9Sstevel@tonic-gate 559*7c478bd9Sstevel@tonic-gate static void 560*7c478bd9Sstevel@tonic-gate smbus_reportdev(dev_info_t *dip, dev_info_t *rdip) 561*7c478bd9Sstevel@tonic-gate { 562*7c478bd9Sstevel@tonic-gate smbus_ppvt_t *ppvt; 563*7c478bd9Sstevel@tonic-gate 564*7c478bd9Sstevel@tonic-gate ppvt = ddi_get_parent_data(rdip); 565*7c478bd9Sstevel@tonic-gate 566*7c478bd9Sstevel@tonic-gate cmn_err(CE_CONT, "?%s%d at %s%d: addr 0x%x", 567*7c478bd9Sstevel@tonic-gate ddi_driver_name(rdip), ddi_get_instance(rdip), 568*7c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 569*7c478bd9Sstevel@tonic-gate ppvt->smbus_ppvt_addr); 570*7c478bd9Sstevel@tonic-gate } 571*7c478bd9Sstevel@tonic-gate 572*7c478bd9Sstevel@tonic-gate /* 573*7c478bd9Sstevel@tonic-gate * smbus_setup_regs() is called to map in the registers 574*7c478bd9Sstevel@tonic-gate * specific to the smbus. 575*7c478bd9Sstevel@tonic-gate */ 576*7c478bd9Sstevel@tonic-gate static int 577*7c478bd9Sstevel@tonic-gate smbus_setup_regs(dev_info_t *dip, smbus_t *smbus) 578*7c478bd9Sstevel@tonic-gate { 579*7c478bd9Sstevel@tonic-gate ddi_device_acc_attr_t attr; 580*7c478bd9Sstevel@tonic-gate int ret; 581*7c478bd9Sstevel@tonic-gate 582*7c478bd9Sstevel@tonic-gate attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 583*7c478bd9Sstevel@tonic-gate attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; 584*7c478bd9Sstevel@tonic-gate attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 585*7c478bd9Sstevel@tonic-gate 586*7c478bd9Sstevel@tonic-gate ret = ddi_regs_map_setup(dip, 1, (caddr_t *)&smbus->smbus_regaddr, 587*7c478bd9Sstevel@tonic-gate 0, 0, &attr, &smbus->smbus_rhandle); 588*7c478bd9Sstevel@tonic-gate 589*7c478bd9Sstevel@tonic-gate if (ret == DDI_FAILURE) { 590*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s unable to map regs", smbus->smbus_name); 591*7c478bd9Sstevel@tonic-gate 592*7c478bd9Sstevel@tonic-gate } else if (ret == DDI_REGS_ACC_CONFLICT) { 593*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, 594*7c478bd9Sstevel@tonic-gate "%s unable to map regs because of conflict", 595*7c478bd9Sstevel@tonic-gate smbus->smbus_name); 596*7c478bd9Sstevel@tonic-gate ret = DDI_FAILURE; 597*7c478bd9Sstevel@tonic-gate } 598*7c478bd9Sstevel@tonic-gate 599*7c478bd9Sstevel@tonic-gate if (ret == DDI_FAILURE) { 600*7c478bd9Sstevel@tonic-gate 601*7c478bd9Sstevel@tonic-gate return (ret); 602*7c478bd9Sstevel@tonic-gate } 603*7c478bd9Sstevel@tonic-gate 604*7c478bd9Sstevel@tonic-gate ret = ddi_regs_map_setup(dip, 0, (caddr_t *)&smbus->smbus_configregaddr, 605*7c478bd9Sstevel@tonic-gate 0, 0, &attr, &smbus->smbus_confighandle); 606*7c478bd9Sstevel@tonic-gate 607*7c478bd9Sstevel@tonic-gate if (ret == DDI_FAILURE) { 608*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s unable to map config regs", 609*7c478bd9Sstevel@tonic-gate smbus->smbus_name); 610*7c478bd9Sstevel@tonic-gate 611*7c478bd9Sstevel@tonic-gate } else if (ret == DDI_REGS_ACC_CONFLICT) { 612*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, 613*7c478bd9Sstevel@tonic-gate "%s unable to map config regs because of conflict", 614*7c478bd9Sstevel@tonic-gate smbus->smbus_name); 615*7c478bd9Sstevel@tonic-gate ret = DDI_FAILURE; 616*7c478bd9Sstevel@tonic-gate } 617*7c478bd9Sstevel@tonic-gate 618*7c478bd9Sstevel@tonic-gate return (ret); 619*7c478bd9Sstevel@tonic-gate } 620*7c478bd9Sstevel@tonic-gate 621*7c478bd9Sstevel@tonic-gate /* 622*7c478bd9Sstevel@tonic-gate * smbus_free_regs() frees any registers previously allocated. 623*7c478bd9Sstevel@tonic-gate */ 624*7c478bd9Sstevel@tonic-gate static void 625*7c478bd9Sstevel@tonic-gate smbus_free_regs(smbus_t *smbus) 626*7c478bd9Sstevel@tonic-gate { 627*7c478bd9Sstevel@tonic-gate if (smbus->smbus_regaddr != NULL) { 628*7c478bd9Sstevel@tonic-gate ddi_regs_map_free(&smbus->smbus_rhandle); 629*7c478bd9Sstevel@tonic-gate } 630*7c478bd9Sstevel@tonic-gate 631*7c478bd9Sstevel@tonic-gate if (smbus->smbus_configregaddr != NULL) { 632*7c478bd9Sstevel@tonic-gate ddi_regs_map_free(&smbus->smbus_confighandle); 633*7c478bd9Sstevel@tonic-gate } 634*7c478bd9Sstevel@tonic-gate } 635*7c478bd9Sstevel@tonic-gate 636*7c478bd9Sstevel@tonic-gate /* 637*7c478bd9Sstevel@tonic-gate * smbus_dip_to_addr() takes a dip and returns an I2C address. 638*7c478bd9Sstevel@tonic-gate */ 639*7c478bd9Sstevel@tonic-gate static int 640*7c478bd9Sstevel@tonic-gate smbus_dip_to_addr(dev_info_t *cdip) 641*7c478bd9Sstevel@tonic-gate { 642*7c478bd9Sstevel@tonic-gate smbus_ppvt_t *ppvt; 643*7c478bd9Sstevel@tonic-gate 644*7c478bd9Sstevel@tonic-gate ppvt = ddi_get_parent_data(cdip); 645*7c478bd9Sstevel@tonic-gate 646*7c478bd9Sstevel@tonic-gate return (ppvt->smbus_ppvt_addr); 647*7c478bd9Sstevel@tonic-gate } 648*7c478bd9Sstevel@tonic-gate 649*7c478bd9Sstevel@tonic-gate /* 650*7c478bd9Sstevel@tonic-gate * smbus_suspend() is called before the system suspends. Existing 651*7c478bd9Sstevel@tonic-gate * transfer in progress or waiting will complete, but new transfers are 652*7c478bd9Sstevel@tonic-gate * effectively blocked by "acquiring" the bus. 653*7c478bd9Sstevel@tonic-gate */ 654*7c478bd9Sstevel@tonic-gate static void 655*7c478bd9Sstevel@tonic-gate smbus_suspend(dev_info_t *dip) 656*7c478bd9Sstevel@tonic-gate { 657*7c478bd9Sstevel@tonic-gate smbus_t *smbus; 658*7c478bd9Sstevel@tonic-gate int instance; 659*7c478bd9Sstevel@tonic-gate 660*7c478bd9Sstevel@tonic-gate instance = ddi_get_instance(dip); 661*7c478bd9Sstevel@tonic-gate smbus = ddi_get_soft_state(smbus_state, instance); 662*7c478bd9Sstevel@tonic-gate 663*7c478bd9Sstevel@tonic-gate (void) smbus_acquire(smbus, NULL, NULL); 664*7c478bd9Sstevel@tonic-gate } 665*7c478bd9Sstevel@tonic-gate 666*7c478bd9Sstevel@tonic-gate /* 667*7c478bd9Sstevel@tonic-gate * smbus_resume() is called when the system resumes from CPR. It releases 668*7c478bd9Sstevel@tonic-gate * the hold that was placed on the i2c bus, which allows any real 669*7c478bd9Sstevel@tonic-gate * transfers to continue. 670*7c478bd9Sstevel@tonic-gate */ 671*7c478bd9Sstevel@tonic-gate static void 672*7c478bd9Sstevel@tonic-gate smbus_resume(dev_info_t *dip) 673*7c478bd9Sstevel@tonic-gate { 674*7c478bd9Sstevel@tonic-gate smbus_t *smbus; 675*7c478bd9Sstevel@tonic-gate int instance; 676*7c478bd9Sstevel@tonic-gate 677*7c478bd9Sstevel@tonic-gate instance = ddi_get_instance(dip); 678*7c478bd9Sstevel@tonic-gate smbus = ddi_get_soft_state(smbus_state, instance); 679*7c478bd9Sstevel@tonic-gate 680*7c478bd9Sstevel@tonic-gate smbus_release(smbus); 681*7c478bd9Sstevel@tonic-gate } 682*7c478bd9Sstevel@tonic-gate 683*7c478bd9Sstevel@tonic-gate /* 684*7c478bd9Sstevel@tonic-gate * smbus_acquire() is called by a thread wishing to "own" the SMbus. 685*7c478bd9Sstevel@tonic-gate * It should not be held across multiple transfers. 686*7c478bd9Sstevel@tonic-gate */ 687*7c478bd9Sstevel@tonic-gate static int 688*7c478bd9Sstevel@tonic-gate smbus_acquire(smbus_t *smbus, dev_info_t *dip, i2c_transfer_t *tp) 689*7c478bd9Sstevel@tonic-gate { 690*7c478bd9Sstevel@tonic-gate mutex_enter(&smbus->smbus_mutex); 691*7c478bd9Sstevel@tonic-gate while (smbus->smbus_busy) { 692*7c478bd9Sstevel@tonic-gate cv_wait(&smbus->smbus_cv, &smbus->smbus_mutex); 693*7c478bd9Sstevel@tonic-gate } 694*7c478bd9Sstevel@tonic-gate smbus->smbus_busy = 1; 695*7c478bd9Sstevel@tonic-gate mutex_exit(&smbus->smbus_mutex); 696*7c478bd9Sstevel@tonic-gate 697*7c478bd9Sstevel@tonic-gate /* 698*7c478bd9Sstevel@tonic-gate * On systems where OBP shares a smbus controller with the 699*7c478bd9Sstevel@tonic-gate * OS, plat_shared_i2c_enter will serialize access to the 700*7c478bd9Sstevel@tonic-gate * smbus controller. Do not grab this lock during CPR 701*7c478bd9Sstevel@tonic-gate * suspend as the CPR thread also acquires this muxex 702*7c478bd9Sstevel@tonic-gate * through through prom_setprop which causes recursive 703*7c478bd9Sstevel@tonic-gate * mutex enter. 704*7c478bd9Sstevel@tonic-gate * 705*7c478bd9Sstevel@tonic-gate * dip == NULL during CPR. 706*7c478bd9Sstevel@tonic-gate */ 707*7c478bd9Sstevel@tonic-gate if ((&plat_shared_i2c_enter != NULL) && (dip != NULL)) { 708*7c478bd9Sstevel@tonic-gate plat_shared_i2c_enter(smbus->smbus_dip); 709*7c478bd9Sstevel@tonic-gate } 710*7c478bd9Sstevel@tonic-gate 711*7c478bd9Sstevel@tonic-gate smbus->smbus_cur_tran = tp; 712*7c478bd9Sstevel@tonic-gate smbus->smbus_cur_dip = dip; 713*7c478bd9Sstevel@tonic-gate 714*7c478bd9Sstevel@tonic-gate return (SMBUS_SUCCESS); 715*7c478bd9Sstevel@tonic-gate } 716*7c478bd9Sstevel@tonic-gate 717*7c478bd9Sstevel@tonic-gate /* 718*7c478bd9Sstevel@tonic-gate * smbus_release() is called to release a hold made by smbus_acquire(). 719*7c478bd9Sstevel@tonic-gate */ 720*7c478bd9Sstevel@tonic-gate static void 721*7c478bd9Sstevel@tonic-gate smbus_release(smbus_t *smbus) 722*7c478bd9Sstevel@tonic-gate { 723*7c478bd9Sstevel@tonic-gate mutex_enter(&smbus->smbus_mutex); 724*7c478bd9Sstevel@tonic-gate smbus->smbus_busy = 0; 725*7c478bd9Sstevel@tonic-gate cv_signal(&smbus->smbus_cv); 726*7c478bd9Sstevel@tonic-gate smbus->smbus_cur_tran = NULL; 727*7c478bd9Sstevel@tonic-gate smbus->smbus_cur_dip = NULL; 728*7c478bd9Sstevel@tonic-gate mutex_exit(&smbus->smbus_mutex); 729*7c478bd9Sstevel@tonic-gate 730*7c478bd9Sstevel@tonic-gate if ((&plat_shared_i2c_exit != NULL) && (smbus->smbus_cur_dip != NULL)) { 731*7c478bd9Sstevel@tonic-gate plat_shared_i2c_exit(smbus->smbus_dip); 732*7c478bd9Sstevel@tonic-gate } 733*7c478bd9Sstevel@tonic-gate } 734*7c478bd9Sstevel@tonic-gate 735*7c478bd9Sstevel@tonic-gate static void 736*7c478bd9Sstevel@tonic-gate smbus_put(smbus_t *smbus, uint8_t reg, uint8_t data, uint8_t flags) 737*7c478bd9Sstevel@tonic-gate { 738*7c478bd9Sstevel@tonic-gate ddi_acc_handle_t hp = smbus->smbus_rhandle; 739*7c478bd9Sstevel@tonic-gate uint8_t *reg_addr = smbus->smbus_regaddr; 740*7c478bd9Sstevel@tonic-gate uint8_t *config_addr = smbus->smbus_configregaddr; 741*7c478bd9Sstevel@tonic-gate ddi_acc_handle_t config_handle = smbus->smbus_confighandle; 742*7c478bd9Sstevel@tonic-gate 743*7c478bd9Sstevel@tonic-gate ddi_put8(hp, ®_addr[reg], data); 744*7c478bd9Sstevel@tonic-gate 745*7c478bd9Sstevel@tonic-gate SMBUS_PRINT((PRT_PUT, "smbus_put: addr = %p data = %x\n", 746*7c478bd9Sstevel@tonic-gate ®_addr[reg], data)); 747*7c478bd9Sstevel@tonic-gate 748*7c478bd9Sstevel@tonic-gate /* 749*7c478bd9Sstevel@tonic-gate * if FLUSH flag is passed, read a config regs to make sure 750*7c478bd9Sstevel@tonic-gate * data written is flushed. 751*7c478bd9Sstevel@tonic-gate */ 752*7c478bd9Sstevel@tonic-gate if (flags & SMBUS_FLUSH) { 753*7c478bd9Sstevel@tonic-gate (void) ddi_get8(config_handle, &config_addr[0]); 754*7c478bd9Sstevel@tonic-gate } 755*7c478bd9Sstevel@tonic-gate } 756*7c478bd9Sstevel@tonic-gate 757*7c478bd9Sstevel@tonic-gate static uint8_t 758*7c478bd9Sstevel@tonic-gate smbus_get(smbus_t *smbus, uint8_t reg) 759*7c478bd9Sstevel@tonic-gate { 760*7c478bd9Sstevel@tonic-gate 761*7c478bd9Sstevel@tonic-gate ddi_acc_handle_t hp = smbus->smbus_rhandle; 762*7c478bd9Sstevel@tonic-gate uint8_t *regaddr = smbus->smbus_regaddr; 763*7c478bd9Sstevel@tonic-gate uint8_t data; 764*7c478bd9Sstevel@tonic-gate 765*7c478bd9Sstevel@tonic-gate data = ddi_get8(hp, ®addr[reg]); 766*7c478bd9Sstevel@tonic-gate 767*7c478bd9Sstevel@tonic-gate SMBUS_PRINT((PRT_GET, "smbus_get: data = %x\n", data)); 768*7c478bd9Sstevel@tonic-gate 769*7c478bd9Sstevel@tonic-gate return (data); 770*7c478bd9Sstevel@tonic-gate } 771*7c478bd9Sstevel@tonic-gate 772*7c478bd9Sstevel@tonic-gate 773*7c478bd9Sstevel@tonic-gate /* 774*7c478bd9Sstevel@tonic-gate * The southbridge smbus device appears to have a feature where 775*7c478bd9Sstevel@tonic-gate * reads from the status register return 0 for a few microseconds 776*7c478bd9Sstevel@tonic-gate * after clearing the status. 777*7c478bd9Sstevel@tonic-gate * 778*7c478bd9Sstevel@tonic-gate * "status_wait_idle" allows for this by retrying until 779*7c478bd9Sstevel@tonic-gate * it gets the right answer or times out. The loop count 780*7c478bd9Sstevel@tonic-gate * and the delay are empirical. The routine uses up 781*7c478bd9Sstevel@tonic-gate * 400 us if it fails. 782*7c478bd9Sstevel@tonic-gate * 783*7c478bd9Sstevel@tonic-gate * The fact that this routine waits for 10 us before the 784*7c478bd9Sstevel@tonic-gate * first check is deliberate. 785*7c478bd9Sstevel@tonic-gate */ 786*7c478bd9Sstevel@tonic-gate static int 787*7c478bd9Sstevel@tonic-gate smbus_wait_idle(smbus_t *smbus) 788*7c478bd9Sstevel@tonic-gate { 789*7c478bd9Sstevel@tonic-gate int retries = 40; 790*7c478bd9Sstevel@tonic-gate int status; 791*7c478bd9Sstevel@tonic-gate 792*7c478bd9Sstevel@tonic-gate smbus_put(smbus, SMB_STS, 0xff, SMBUS_FLUSH); 793*7c478bd9Sstevel@tonic-gate do { 794*7c478bd9Sstevel@tonic-gate drv_usecwait(10); 795*7c478bd9Sstevel@tonic-gate status = smbus_get(smbus, SMB_STS); 796*7c478bd9Sstevel@tonic-gate } while (status != IDLE && --retries > 0); 797*7c478bd9Sstevel@tonic-gate return (status); 798*7c478bd9Sstevel@tonic-gate } 799*7c478bd9Sstevel@tonic-gate /* 800*7c478bd9Sstevel@tonic-gate * smbus_transfer is the function that is registered with 801*7c478bd9Sstevel@tonic-gate * I2C services to be called for each i2c transaction. 802*7c478bd9Sstevel@tonic-gate */ 803*7c478bd9Sstevel@tonic-gate int 804*7c478bd9Sstevel@tonic-gate smbus_transfer(dev_info_t *dip, i2c_transfer_t *tp) 805*7c478bd9Sstevel@tonic-gate { 806*7c478bd9Sstevel@tonic-gate smbus_t *smbus; 807*7c478bd9Sstevel@tonic-gate uint8_t status; 808*7c478bd9Sstevel@tonic-gate clock_t ctime; 809*7c478bd9Sstevel@tonic-gate 810*7c478bd9Sstevel@tonic-gate smbus = ddi_get_soft_state(smbus_state, 811*7c478bd9Sstevel@tonic-gate ddi_get_instance(ddi_get_parent(dip))); 812*7c478bd9Sstevel@tonic-gate 813*7c478bd9Sstevel@tonic-gate if (smbus_acquire(smbus, dip, tp) == SMBUS_FAILURE) { 814*7c478bd9Sstevel@tonic-gate tp->i2c_result = I2C_FAILURE; 815*7c478bd9Sstevel@tonic-gate 816*7c478bd9Sstevel@tonic-gate return (I2C_FAILURE); 817*7c478bd9Sstevel@tonic-gate } 818*7c478bd9Sstevel@tonic-gate 819*7c478bd9Sstevel@tonic-gate tp->i2c_r_resid = tp->i2c_rlen; 820*7c478bd9Sstevel@tonic-gate tp->i2c_w_resid = tp->i2c_wlen; 821*7c478bd9Sstevel@tonic-gate tp->i2c_result = I2C_SUCCESS; 822*7c478bd9Sstevel@tonic-gate smbus->smbus_retries = 0; 823*7c478bd9Sstevel@tonic-gate smbus->smbus_bytes_to_read = 0; 824*7c478bd9Sstevel@tonic-gate 825*7c478bd9Sstevel@tonic-gate mutex_enter(&smbus->smbus_imutex); 826*7c478bd9Sstevel@tonic-gate 827*7c478bd9Sstevel@tonic-gate SMBUS_PRINT((PRT_TRANS, "smbus_transfer: rlen=%d wlen=%d flags=%d", 828*7c478bd9Sstevel@tonic-gate tp->i2c_r_resid, tp->i2c_w_resid, tp->i2c_flags)); 829*7c478bd9Sstevel@tonic-gate 830*7c478bd9Sstevel@tonic-gate /* 831*7c478bd9Sstevel@tonic-gate * First clear the status bits, then read them back to determine 832*7c478bd9Sstevel@tonic-gate * the current state. 833*7c478bd9Sstevel@tonic-gate */ 834*7c478bd9Sstevel@tonic-gate status = smbus_wait_idle(smbus); 835*7c478bd9Sstevel@tonic-gate 836*7c478bd9Sstevel@tonic-gate if (status != IDLE) { 837*7c478bd9Sstevel@tonic-gate /* 838*7c478bd9Sstevel@tonic-gate * Try to issue bus reset 839*7c478bd9Sstevel@tonic-gate * First reset the state machine. 840*7c478bd9Sstevel@tonic-gate */ 841*7c478bd9Sstevel@tonic-gate smbus_put(smbus, SMB_TYP, KILL, SMBUS_FLUSH); 842*7c478bd9Sstevel@tonic-gate status = smbus_wait_idle(smbus); 843*7c478bd9Sstevel@tonic-gate 844*7c478bd9Sstevel@tonic-gate if (status != IDLE) { 845*7c478bd9Sstevel@tonic-gate 846*7c478bd9Sstevel@tonic-gate smbus_put(smbus, SMB_TYP, T_OUT, SMBUS_FLUSH); 847*7c478bd9Sstevel@tonic-gate status = smbus_wait_idle(smbus); 848*7c478bd9Sstevel@tonic-gate if (status != IDLE) { 849*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, 850*7c478bd9Sstevel@tonic-gate "%s smbus not idle. Unable to reset %x", 851*7c478bd9Sstevel@tonic-gate smbus->smbus_name, status); 852*7c478bd9Sstevel@tonic-gate smbus->smbus_cur_tran->i2c_result = I2C_FAILURE; 853*7c478bd9Sstevel@tonic-gate mutex_exit(&smbus->smbus_imutex); 854*7c478bd9Sstevel@tonic-gate smbus_release(smbus); 855*7c478bd9Sstevel@tonic-gate 856*7c478bd9Sstevel@tonic-gate return (I2C_FAILURE); 857*7c478bd9Sstevel@tonic-gate } else { 858*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s T_OUT reset required", 859*7c478bd9Sstevel@tonic-gate smbus->smbus_name); 860*7c478bd9Sstevel@tonic-gate } 861*7c478bd9Sstevel@tonic-gate } 862*7c478bd9Sstevel@tonic-gate } 863*7c478bd9Sstevel@tonic-gate 864*7c478bd9Sstevel@tonic-gate if (smbus_switch(smbus) != SMBUS_COMPLETE) { 865*7c478bd9Sstevel@tonic-gate if (smbus->smbus_polling) { 866*7c478bd9Sstevel@tonic-gate smbus->smbus_poll_complete = 0; 867*7c478bd9Sstevel@tonic-gate smbus->smbus_poll_retries = 0; 868*7c478bd9Sstevel@tonic-gate do { 869*7c478bd9Sstevel@tonic-gate drv_usecwait(SMBUS_POLL_INTERVAL); 870*7c478bd9Sstevel@tonic-gate (void) smbus_intr_cmn(smbus, SMBUS_POLL); 871*7c478bd9Sstevel@tonic-gate } while (!smbus->smbus_poll_complete); 872*7c478bd9Sstevel@tonic-gate } else { 873*7c478bd9Sstevel@tonic-gate /* 874*7c478bd9Sstevel@tonic-gate * Start a timeout as there is a bug in southbridge 875*7c478bd9Sstevel@tonic-gate * smbus where sometimes a transaction never starts, 876*7c478bd9Sstevel@tonic-gate * and needs to be reinitiated. 877*7c478bd9Sstevel@tonic-gate */ 878*7c478bd9Sstevel@tonic-gate 879*7c478bd9Sstevel@tonic-gate smbus->smbus_timeout = timeout(smbus_intr_timeout, 880*7c478bd9Sstevel@tonic-gate smbus, drv_usectohz(intr_timeout)); 881*7c478bd9Sstevel@tonic-gate SMBUS_PRINT((PRT_TRANS, 882*7c478bd9Sstevel@tonic-gate "starting timeout in smbus_transfer %p", 883*7c478bd9Sstevel@tonic-gate smbus->smbus_timeout)); 884*7c478bd9Sstevel@tonic-gate 885*7c478bd9Sstevel@tonic-gate ctime = ddi_get_lbolt(); 886*7c478bd9Sstevel@tonic-gate ctime += drv_usectohz(SMBUS_TRANS_TIMEOUT); 887*7c478bd9Sstevel@tonic-gate 888*7c478bd9Sstevel@tonic-gate smbus_interrupts_on(smbus); 889*7c478bd9Sstevel@tonic-gate 890*7c478bd9Sstevel@tonic-gate 891*7c478bd9Sstevel@tonic-gate cv_wait(&smbus->smbus_icv, &smbus->smbus_imutex); 892*7c478bd9Sstevel@tonic-gate } 893*7c478bd9Sstevel@tonic-gate } 894*7c478bd9Sstevel@tonic-gate 895*7c478bd9Sstevel@tonic-gate 896*7c478bd9Sstevel@tonic-gate mutex_exit(&smbus->smbus_imutex); 897*7c478bd9Sstevel@tonic-gate smbus_release(smbus); 898*7c478bd9Sstevel@tonic-gate 899*7c478bd9Sstevel@tonic-gate return (tp->i2c_result); 900*7c478bd9Sstevel@tonic-gate } 901*7c478bd9Sstevel@tonic-gate 902*7c478bd9Sstevel@tonic-gate /* 903*7c478bd9Sstevel@tonic-gate * This is called by smbus_intr_cmn() to figure out whether to call 904*7c478bd9Sstevel@tonic-gate * smbus_wr or smbus_rd depending on the command and current state. 905*7c478bd9Sstevel@tonic-gate */ 906*7c478bd9Sstevel@tonic-gate static int 907*7c478bd9Sstevel@tonic-gate smbus_switch(smbus_t *smbus) 908*7c478bd9Sstevel@tonic-gate { 909*7c478bd9Sstevel@tonic-gate int ret; 910*7c478bd9Sstevel@tonic-gate i2c_transfer_t *tp = smbus->smbus_cur_tran; 911*7c478bd9Sstevel@tonic-gate 912*7c478bd9Sstevel@tonic-gate if (tp == NULL) { 913*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, 914*7c478bd9Sstevel@tonic-gate "%s smbus_cur_tran is NULL. Transaction failed", 915*7c478bd9Sstevel@tonic-gate smbus->smbus_name); 916*7c478bd9Sstevel@tonic-gate 917*7c478bd9Sstevel@tonic-gate return (SMBUS_FAILURE); 918*7c478bd9Sstevel@tonic-gate } 919*7c478bd9Sstevel@tonic-gate 920*7c478bd9Sstevel@tonic-gate smbus->smbus_saved_w_resid = tp->i2c_w_resid; 921*7c478bd9Sstevel@tonic-gate 922*7c478bd9Sstevel@tonic-gate switch (tp->i2c_flags) { 923*7c478bd9Sstevel@tonic-gate case I2C_WR: 924*7c478bd9Sstevel@tonic-gate ret = smbus_wr(smbus); 925*7c478bd9Sstevel@tonic-gate break; 926*7c478bd9Sstevel@tonic-gate case I2C_RD: 927*7c478bd9Sstevel@tonic-gate ret = smbus_rd(smbus); 928*7c478bd9Sstevel@tonic-gate break; 929*7c478bd9Sstevel@tonic-gate case I2C_WR_RD: 930*7c478bd9Sstevel@tonic-gate /* 931*7c478bd9Sstevel@tonic-gate * We could do a bit more decoding here, 932*7c478bd9Sstevel@tonic-gate * to allow the transactions that would 933*7c478bd9Sstevel@tonic-gate * work as a single smbus command to 934*7c478bd9Sstevel@tonic-gate * be done as such. It's not really 935*7c478bd9Sstevel@tonic-gate * worth the trouble. 936*7c478bd9Sstevel@tonic-gate */ 937*7c478bd9Sstevel@tonic-gate if (tp->i2c_w_resid > 0) { 938*7c478bd9Sstevel@tonic-gate ret = smbus_wr(smbus); 939*7c478bd9Sstevel@tonic-gate } else { 940*7c478bd9Sstevel@tonic-gate ret = smbus_rd(smbus); 941*7c478bd9Sstevel@tonic-gate } 942*7c478bd9Sstevel@tonic-gate break; 943*7c478bd9Sstevel@tonic-gate default: 944*7c478bd9Sstevel@tonic-gate tp->i2c_result = I2C_FAILURE; 945*7c478bd9Sstevel@tonic-gate ret = SMBUS_COMPLETE; 946*7c478bd9Sstevel@tonic-gate break; 947*7c478bd9Sstevel@tonic-gate } 948*7c478bd9Sstevel@tonic-gate 949*7c478bd9Sstevel@tonic-gate return (ret); 950*7c478bd9Sstevel@tonic-gate } 951*7c478bd9Sstevel@tonic-gate 952*7c478bd9Sstevel@tonic-gate /* 953*7c478bd9Sstevel@tonic-gate * 954*7c478bd9Sstevel@tonic-gate */ 955*7c478bd9Sstevel@tonic-gate static void 956*7c478bd9Sstevel@tonic-gate smbus_intr_timeout(void *arg) 957*7c478bd9Sstevel@tonic-gate { 958*7c478bd9Sstevel@tonic-gate smbus_t *smbus = (smbus_t *)arg; 959*7c478bd9Sstevel@tonic-gate 960*7c478bd9Sstevel@tonic-gate mutex_enter(&smbus->smbus_imutex); 961*7c478bd9Sstevel@tonic-gate /* 962*7c478bd9Sstevel@tonic-gate * If timeout is already cleared, it means interrupt arrived 963*7c478bd9Sstevel@tonic-gate * while timeout fired. In this case, just return from here. 964*7c478bd9Sstevel@tonic-gate */ 965*7c478bd9Sstevel@tonic-gate if (smbus->smbus_timeout == 0) { 966*7c478bd9Sstevel@tonic-gate 967*7c478bd9Sstevel@tonic-gate mutex_exit(&smbus->smbus_imutex); 968*7c478bd9Sstevel@tonic-gate 969*7c478bd9Sstevel@tonic-gate return; 970*7c478bd9Sstevel@tonic-gate } 971*7c478bd9Sstevel@tonic-gate 972*7c478bd9Sstevel@tonic-gate (void) smbus_intr_cmn(smbus, SMBUS_TIMEOUT); 973*7c478bd9Sstevel@tonic-gate mutex_exit(&smbus->smbus_imutex); 974*7c478bd9Sstevel@tonic-gate } 975*7c478bd9Sstevel@tonic-gate 976*7c478bd9Sstevel@tonic-gate /* 977*7c478bd9Sstevel@tonic-gate * smbus_intr() is the interrupt handler for smbus. 978*7c478bd9Sstevel@tonic-gate */ 979*7c478bd9Sstevel@tonic-gate static uint_t 980*7c478bd9Sstevel@tonic-gate smbus_intr(caddr_t arg) 981*7c478bd9Sstevel@tonic-gate { 982*7c478bd9Sstevel@tonic-gate smbus_t *smbus = (smbus_t *)arg; 983*7c478bd9Sstevel@tonic-gate uint32_t intr_status; 984*7c478bd9Sstevel@tonic-gate uint_t result; 985*7c478bd9Sstevel@tonic-gate 986*7c478bd9Sstevel@tonic-gate /* 987*7c478bd9Sstevel@tonic-gate * Check to see if intr is really from smbus 988*7c478bd9Sstevel@tonic-gate */ 989*7c478bd9Sstevel@tonic-gate intr_status = ddi_get32(smbus->smbus_confighandle, 990*7c478bd9Sstevel@tonic-gate (uint32_t *)&smbus->smbus_configregaddr[SMBUS_SRC_STATUS]); 991*7c478bd9Sstevel@tonic-gate 992*7c478bd9Sstevel@tonic-gate 993*7c478bd9Sstevel@tonic-gate if ((intr_status & SMBUS_SMB_INTR_STATUS) == 0) { 994*7c478bd9Sstevel@tonic-gate SMBUS_PRINT((PRT_INTR, "smbus_intr: intr not from smbus\n")); 995*7c478bd9Sstevel@tonic-gate 996*7c478bd9Sstevel@tonic-gate return (DDI_INTR_UNCLAIMED); 997*7c478bd9Sstevel@tonic-gate } 998*7c478bd9Sstevel@tonic-gate 999*7c478bd9Sstevel@tonic-gate mutex_enter(&smbus->smbus_imutex); 1000*7c478bd9Sstevel@tonic-gate 1001*7c478bd9Sstevel@tonic-gate /* 1002*7c478bd9Sstevel@tonic-gate * If timeout is already cleared, it means it arrived before the intr. 1003*7c478bd9Sstevel@tonic-gate * In that case, just return from here. 1004*7c478bd9Sstevel@tonic-gate */ 1005*7c478bd9Sstevel@tonic-gate if (smbus->smbus_timeout == 0) { 1006*7c478bd9Sstevel@tonic-gate 1007*7c478bd9Sstevel@tonic-gate mutex_exit(&smbus->smbus_imutex); 1008*7c478bd9Sstevel@tonic-gate 1009*7c478bd9Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 1010*7c478bd9Sstevel@tonic-gate } 1011*7c478bd9Sstevel@tonic-gate 1012*7c478bd9Sstevel@tonic-gate result = smbus_intr_cmn(smbus, SMBUS_INTR); 1013*7c478bd9Sstevel@tonic-gate mutex_exit(&smbus->smbus_imutex); 1014*7c478bd9Sstevel@tonic-gate return (result); 1015*7c478bd9Sstevel@tonic-gate } 1016*7c478bd9Sstevel@tonic-gate 1017*7c478bd9Sstevel@tonic-gate /* 1018*7c478bd9Sstevel@tonic-gate * smbus_intr() is the interrupt handler for smbus. 1019*7c478bd9Sstevel@tonic-gate */ 1020*7c478bd9Sstevel@tonic-gate static uint_t 1021*7c478bd9Sstevel@tonic-gate smbus_intr_cmn(smbus_t *smbus, char *src) 1022*7c478bd9Sstevel@tonic-gate { 1023*7c478bd9Sstevel@tonic-gate i2c_transfer_t *tp; 1024*7c478bd9Sstevel@tonic-gate char error_str[128]; 1025*7c478bd9Sstevel@tonic-gate uint8_t status; 1026*7c478bd9Sstevel@tonic-gate int ret = SMBUS_SUCCESS; 1027*7c478bd9Sstevel@tonic-gate timeout_id_t timer_id; 1028*7c478bd9Sstevel@tonic-gate 1029*7c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&smbus->smbus_imutex)); 1030*7c478bd9Sstevel@tonic-gate error_str[0] = '\0'; 1031*7c478bd9Sstevel@tonic-gate 1032*7c478bd9Sstevel@tonic-gate smbus_interrupts_off(smbus); 1033*7c478bd9Sstevel@tonic-gate 1034*7c478bd9Sstevel@tonic-gate tp = smbus->smbus_cur_tran; 1035*7c478bd9Sstevel@tonic-gate /* 1036*7c478bd9Sstevel@tonic-gate * This only happens when top half is interrupted or 1037*7c478bd9Sstevel@tonic-gate * times out, then the interrupt arrives. Interrupt 1038*7c478bd9Sstevel@tonic-gate * was already disabled by top half, so just exit. 1039*7c478bd9Sstevel@tonic-gate */ 1040*7c478bd9Sstevel@tonic-gate if (tp == NULL) { 1041*7c478bd9Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 1042*7c478bd9Sstevel@tonic-gate } 1043*7c478bd9Sstevel@tonic-gate 1044*7c478bd9Sstevel@tonic-gate /* 1045*7c478bd9Sstevel@tonic-gate * This wait is required before reading the status, otherwise 1046*7c478bd9Sstevel@tonic-gate * a parity error can occur which causes a panic. A bug with 1047*7c478bd9Sstevel@tonic-gate * southbridge SMBUS. 1048*7c478bd9Sstevel@tonic-gate */ 1049*7c478bd9Sstevel@tonic-gate drv_usecwait(15); 1050*7c478bd9Sstevel@tonic-gate status = smbus_get(smbus, SMB_STS); 1051*7c478bd9Sstevel@tonic-gate if (smbus->smbus_polling) { 1052*7c478bd9Sstevel@tonic-gate /* 1053*7c478bd9Sstevel@tonic-gate * If we are polling, then we expect not to 1054*7c478bd9Sstevel@tonic-gate * get the right answer for a while, 1055*7c478bd9Sstevel@tonic-gate * so we don't go on to that error stuff 1056*7c478bd9Sstevel@tonic-gate * until we've polled the status for a 1057*7c478bd9Sstevel@tonic-gate * few times. We check for errors here to save time, 1058*7c478bd9Sstevel@tonic-gate * otherwise we would have to wait for the full 1059*7c478bd9Sstevel@tonic-gate * poll timeout before dealing with them. 1060*7c478bd9Sstevel@tonic-gate */ 1061*7c478bd9Sstevel@tonic-gate if (status != (CMD_CMPL|IDLE) && 1062*7c478bd9Sstevel@tonic-gate (status & (FAILED|BUS_ERR|DRV_ERR)) == 0 && 1063*7c478bd9Sstevel@tonic-gate smbus->smbus_poll_retries++ < SMBUS_POLL_MAX_RETRIES) { 1064*7c478bd9Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 1065*7c478bd9Sstevel@tonic-gate } 1066*7c478bd9Sstevel@tonic-gate /* 1067*7c478bd9Sstevel@tonic-gate * else either ... 1068*7c478bd9Sstevel@tonic-gate * [] the command has completed, or; 1069*7c478bd9Sstevel@tonic-gate * [] There has been an error, or; 1070*7c478bd9Sstevel@tonic-gate * [] we timed out waiting for something useful 1071*7c478bd9Sstevel@tonic-gate * to happen, so we go on to to the error handling bit that 1072*7c478bd9Sstevel@tonic-gate * follows, * which will reset the controller then restart the 1073*7c478bd9Sstevel@tonic-gate * whole transaction. 1074*7c478bd9Sstevel@tonic-gate * 1075*7c478bd9Sstevel@tonic-gate * In all cases, clear "poll_retries" for the next command or 1076*7c478bd9Sstevel@tonic-gate * retry 1077*7c478bd9Sstevel@tonic-gate */ 1078*7c478bd9Sstevel@tonic-gate smbus->smbus_poll_retries = 0; 1079*7c478bd9Sstevel@tonic-gate } 1080*7c478bd9Sstevel@tonic-gate 1081*7c478bd9Sstevel@tonic-gate /* 1082*7c478bd9Sstevel@tonic-gate * A bug in southbridge SMBUS sometimes requires a reset. Status 1083*7c478bd9Sstevel@tonic-gate * should NOT be IDLE without any other bit set. If it is, the 1084*7c478bd9Sstevel@tonic-gate * transaction should be restarted. 1085*7c478bd9Sstevel@tonic-gate */ 1086*7c478bd9Sstevel@tonic-gate 1087*7c478bd9Sstevel@tonic-gate if (status == IDLE) { 1088*7c478bd9Sstevel@tonic-gate (void) sprintf(error_str, "%s bus is idle, ", error_str); 1089*7c478bd9Sstevel@tonic-gate } 1090*7c478bd9Sstevel@tonic-gate 1091*7c478bd9Sstevel@tonic-gate if ((status & CMD_CMPL) == 0) { 1092*7c478bd9Sstevel@tonic-gate (void) sprintf(error_str, "%s command failed to complete, ", 1093*7c478bd9Sstevel@tonic-gate error_str); 1094*7c478bd9Sstevel@tonic-gate } 1095*7c478bd9Sstevel@tonic-gate if (status & BUS_ERR) { 1096*7c478bd9Sstevel@tonic-gate (void) sprintf(error_str, "%s bus error, ", error_str); 1097*7c478bd9Sstevel@tonic-gate } 1098*7c478bd9Sstevel@tonic-gate if (status & FAILED) { 1099*7c478bd9Sstevel@tonic-gate (void) sprintf(error_str, "%s failed transaction, ", error_str); 1100*7c478bd9Sstevel@tonic-gate } 1101*7c478bd9Sstevel@tonic-gate if (status & DRV_ERR) { 1102*7c478bd9Sstevel@tonic-gate (void) sprintf(error_str, "%s timeout or bus reset", error_str); 1103*7c478bd9Sstevel@tonic-gate } 1104*7c478bd9Sstevel@tonic-gate 1105*7c478bd9Sstevel@tonic-gate if (error_str[0] != '\0') { 1106*7c478bd9Sstevel@tonic-gate (void) sprintf(error_str, "%s %s ", error_str, src); 1107*7c478bd9Sstevel@tonic-gate } 1108*7c478bd9Sstevel@tonic-gate 1109*7c478bd9Sstevel@tonic-gate /* 1110*7c478bd9Sstevel@tonic-gate * Clear status to clear the interrupt. 1111*7c478bd9Sstevel@tonic-gate */ 1112*7c478bd9Sstevel@tonic-gate smbus_put(smbus, SMB_STS, 0xff, SMBUS_FLUSH); 1113*7c478bd9Sstevel@tonic-gate if (error_str[0] != '\0') { 1114*7c478bd9Sstevel@tonic-gate smbus_put(smbus, SMB_TYP, KILL, SMBUS_FLUSH); 1115*7c478bd9Sstevel@tonic-gate if (smbus->smbus_retries++ < SMBUS_MAX_RETRIES) { 1116*7c478bd9Sstevel@tonic-gate /* 1117*7c478bd9Sstevel@tonic-gate * XXXX There was a panic here when the 1118*7c478bd9Sstevel@tonic-gate * intr timeout was greater than the timeout 1119*7c478bd9Sstevel@tonic-gate * for the entire transfer. 1120*7c478bd9Sstevel@tonic-gate * 1121*7c478bd9Sstevel@tonic-gate * Restore the value of w_resid before the 1122*7c478bd9Sstevel@tonic-gate * last transaction. r_resid doesn't need to 1123*7c478bd9Sstevel@tonic-gate * be restored because it is only decremented 1124*7c478bd9Sstevel@tonic-gate * after a successful read. Need to do this 1125*7c478bd9Sstevel@tonic-gate * here since smbus_switch() keys off of a 1126*7c478bd9Sstevel@tonic-gate * resid to know whether to call smbus_rd() or 1127*7c478bd9Sstevel@tonic-gate * smbus_wr(). 1128*7c478bd9Sstevel@tonic-gate */ 1129*7c478bd9Sstevel@tonic-gate tp->i2c_w_resid = smbus->smbus_saved_w_resid; 1130*7c478bd9Sstevel@tonic-gate smbus->smbus_bytes_to_read = 0; 1131*7c478bd9Sstevel@tonic-gate 1132*7c478bd9Sstevel@tonic-gate SMBUS_PRINT((PRT_INTR_ERR, 1133*7c478bd9Sstevel@tonic-gate "retrying: %s %s w_resid=%d\n", error_str, 1134*7c478bd9Sstevel@tonic-gate src, tp->i2c_w_resid)); 1135*7c478bd9Sstevel@tonic-gate } else { 1136*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s max retries exceeded: %s", 1137*7c478bd9Sstevel@tonic-gate smbus->smbus_name, error_str); 1138*7c478bd9Sstevel@tonic-gate /* 1139*7c478bd9Sstevel@tonic-gate * bailing, but first will reset the bus. 1140*7c478bd9Sstevel@tonic-gate */ 1141*7c478bd9Sstevel@tonic-gate smbus_put(smbus, SMB_TYP, KILL, SMBUS_FLUSH); 1142*7c478bd9Sstevel@tonic-gate smbus_put(smbus, SMB_STS, 0xff, SMBUS_FLUSH); 1143*7c478bd9Sstevel@tonic-gate smbus->smbus_cur_tran->i2c_result = I2C_FAILURE; 1144*7c478bd9Sstevel@tonic-gate 1145*7c478bd9Sstevel@tonic-gate ret = SMBUS_FAILURE; 1146*7c478bd9Sstevel@tonic-gate } 1147*7c478bd9Sstevel@tonic-gate } else { 1148*7c478bd9Sstevel@tonic-gate smbus->smbus_retries = 0; 1149*7c478bd9Sstevel@tonic-gate } 1150*7c478bd9Sstevel@tonic-gate 1151*7c478bd9Sstevel@tonic-gate if (tp != NULL) { 1152*7c478bd9Sstevel@tonic-gate SMBUS_PRINT((PRT_INTR, "flags=%d wresid=%d r_resid=%d %s\n", 1153*7c478bd9Sstevel@tonic-gate tp->i2c_flags, tp->i2c_w_resid, tp->i2c_r_resid, src)); 1154*7c478bd9Sstevel@tonic-gate } 1155*7c478bd9Sstevel@tonic-gate 1156*7c478bd9Sstevel@tonic-gate if (ret != SMBUS_FAILURE) { 1157*7c478bd9Sstevel@tonic-gate ret = smbus_switch(smbus); 1158*7c478bd9Sstevel@tonic-gate } 1159*7c478bd9Sstevel@tonic-gate 1160*7c478bd9Sstevel@tonic-gate if (smbus->smbus_polling) { 1161*7c478bd9Sstevel@tonic-gate if (ret == SMBUS_COMPLETE || ret == SMBUS_FAILURE) { 1162*7c478bd9Sstevel@tonic-gate smbus->smbus_poll_complete = 1; 1163*7c478bd9Sstevel@tonic-gate } 1164*7c478bd9Sstevel@tonic-gate } else { 1165*7c478bd9Sstevel@tonic-gate /* 1166*7c478bd9Sstevel@tonic-gate * Disable previous timeout. In case it was about to fire this 1167*7c478bd9Sstevel@tonic-gate * will let it exit without doing anything. 1168*7c478bd9Sstevel@tonic-gate */ 1169*7c478bd9Sstevel@tonic-gate timer_id = smbus->smbus_timeout; 1170*7c478bd9Sstevel@tonic-gate smbus->smbus_timeout = 0; 1171*7c478bd9Sstevel@tonic-gate mutex_exit(&smbus->smbus_imutex); 1172*7c478bd9Sstevel@tonic-gate (void) untimeout(timer_id); 1173*7c478bd9Sstevel@tonic-gate mutex_enter(&smbus->smbus_imutex); 1174*7c478bd9Sstevel@tonic-gate if (ret == SMBUS_COMPLETE || ret == SMBUS_FAILURE) { 1175*7c478bd9Sstevel@tonic-gate cv_signal(&smbus->smbus_icv); 1176*7c478bd9Sstevel@tonic-gate } else { 1177*7c478bd9Sstevel@tonic-gate smbus_interrupts_on(smbus); 1178*7c478bd9Sstevel@tonic-gate smbus->smbus_timeout = timeout(smbus_intr_timeout, 1179*7c478bd9Sstevel@tonic-gate smbus, drv_usectohz(intr_timeout)); 1180*7c478bd9Sstevel@tonic-gate SMBUS_PRINT((PRT_INTR, "smbus_intr starting timeout %p " 1181*7c478bd9Sstevel@tonic-gate "%s", smbus->smbus_timeout, src)); 1182*7c478bd9Sstevel@tonic-gate } 1183*7c478bd9Sstevel@tonic-gate } 1184*7c478bd9Sstevel@tonic-gate 1185*7c478bd9Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 1186*7c478bd9Sstevel@tonic-gate } 1187*7c478bd9Sstevel@tonic-gate 1188*7c478bd9Sstevel@tonic-gate /* 1189*7c478bd9Sstevel@tonic-gate * smbus_wr handles writes to the smbus. Unlike true I2C busses 1190*7c478bd9Sstevel@tonic-gate * such as provided by pcf8584, smbus attaches a start and stop bit for each 1191*7c478bd9Sstevel@tonic-gate * transaction, so this limits writes to the maximum number of bytes 1192*7c478bd9Sstevel@tonic-gate * in a single transaction, which is 33. 1193*7c478bd9Sstevel@tonic-gate * 1194*7c478bd9Sstevel@tonic-gate * If more than 33 bytes are contained in the transfer, a non-zero 1195*7c478bd9Sstevel@tonic-gate * residual has to be returned, and the calling driver has to restart 1196*7c478bd9Sstevel@tonic-gate * another transaction to complete writing out any remaining data. The 1197*7c478bd9Sstevel@tonic-gate * reason for this is that most devices require a register/offset as the 1198*7c478bd9Sstevel@tonic-gate * first byte to be written for each SMBUS transaction. 1199*7c478bd9Sstevel@tonic-gate */ 1200*7c478bd9Sstevel@tonic-gate static int 1201*7c478bd9Sstevel@tonic-gate smbus_wr(smbus_t *smbus) 1202*7c478bd9Sstevel@tonic-gate { 1203*7c478bd9Sstevel@tonic-gate i2c_transfer_t *tp = smbus->smbus_cur_tran; 1204*7c478bd9Sstevel@tonic-gate uint8_t addr = smbus_dip_to_addr(smbus->smbus_cur_dip); 1205*7c478bd9Sstevel@tonic-gate int bytes_written = 0; 1206*7c478bd9Sstevel@tonic-gate uint8_t a; 1207*7c478bd9Sstevel@tonic-gate uint8_t b; 1208*7c478bd9Sstevel@tonic-gate 1209*7c478bd9Sstevel@tonic-gate if (tp->i2c_w_resid != tp->i2c_wlen) { 1210*7c478bd9Sstevel@tonic-gate return (SMBUS_COMPLETE); 1211*7c478bd9Sstevel@tonic-gate } 1212*7c478bd9Sstevel@tonic-gate 1213*7c478bd9Sstevel@tonic-gate SMBUS_PRINT((PRT_WR, "smbus_wr: addr = %x resid = %d\n", 1214*7c478bd9Sstevel@tonic-gate addr, tp->i2c_w_resid)); 1215*7c478bd9Sstevel@tonic-gate 1216*7c478bd9Sstevel@tonic-gate smbus_put(smbus, SMB_STS, 0xff, 0); 1217*7c478bd9Sstevel@tonic-gate 1218*7c478bd9Sstevel@tonic-gate /* 1219*7c478bd9Sstevel@tonic-gate * Address must be re-written for each command and it has to 1220*7c478bd9Sstevel@tonic-gate * be written before SMB_TYP. 1221*7c478bd9Sstevel@tonic-gate */ 1222*7c478bd9Sstevel@tonic-gate smbus_put(smbus, DEV_ADDR, addr, 0); 1223*7c478bd9Sstevel@tonic-gate 1224*7c478bd9Sstevel@tonic-gate switch (tp->i2c_w_resid) { 1225*7c478bd9Sstevel@tonic-gate 1226*7c478bd9Sstevel@tonic-gate case 1: 1227*7c478bd9Sstevel@tonic-gate a = tp->i2c_wbuf[tp->i2c_wlen - tp->i2c_w_resid--]; 1228*7c478bd9Sstevel@tonic-gate smbus_put(smbus, SMB_CMD, a, 0); 1229*7c478bd9Sstevel@tonic-gate smbus_put(smbus, SMB_TYP, SEND_BYTE, 0); 1230*7c478bd9Sstevel@tonic-gate SMBUS_PRINT((PRT_WR, "smbus_wr: send one byte:" 1231*7c478bd9Sstevel@tonic-gate " %d\n", a)); 1232*7c478bd9Sstevel@tonic-gate break; 1233*7c478bd9Sstevel@tonic-gate case 2: 1234*7c478bd9Sstevel@tonic-gate a = tp->i2c_wbuf[tp->i2c_wlen - tp->i2c_w_resid--]; 1235*7c478bd9Sstevel@tonic-gate smbus_put(smbus, SMB_CMD, a, 0); 1236*7c478bd9Sstevel@tonic-gate 1237*7c478bd9Sstevel@tonic-gate b = tp->i2c_wbuf[tp->i2c_wlen - tp->i2c_w_resid--]; 1238*7c478bd9Sstevel@tonic-gate smbus_put(smbus, DEV_DATA0, b, 0); 1239*7c478bd9Sstevel@tonic-gate smbus_put(smbus, SMB_TYP, WR_BYTE, 0); 1240*7c478bd9Sstevel@tonic-gate SMBUS_PRINT((PRT_WR, "smbus_wr: send two bytes:" 1241*7c478bd9Sstevel@tonic-gate " %d %d\n", a, b)); 1242*7c478bd9Sstevel@tonic-gate break; 1243*7c478bd9Sstevel@tonic-gate 1244*7c478bd9Sstevel@tonic-gate default: 1245*7c478bd9Sstevel@tonic-gate /* 1246*7c478bd9Sstevel@tonic-gate * Write out as many bytes as possible in a single command. 1247*7c478bd9Sstevel@tonic-gate * Note that BLK_DATA just creats a byte stream. ie, the 1248*7c478bd9Sstevel@tonic-gate * smbus protocol is not used or interpreted by this driver. 1249*7c478bd9Sstevel@tonic-gate */ 1250*7c478bd9Sstevel@tonic-gate smbus_put(smbus, SMB_TYP, WR_BLK, 0); 1251*7c478bd9Sstevel@tonic-gate a = tp->i2c_wbuf[tp->i2c_wlen - tp->i2c_w_resid--]; 1252*7c478bd9Sstevel@tonic-gate 1253*7c478bd9Sstevel@tonic-gate smbus_put(smbus, SMB_CMD, a, 0); 1254*7c478bd9Sstevel@tonic-gate 1255*7c478bd9Sstevel@tonic-gate SMBUS_PRINT((PRT_WR, "smbus_wr: send multiple bytes: ")); 1256*7c478bd9Sstevel@tonic-gate SMBUS_PRINT((PRT_WR, "%x ", a)); 1257*7c478bd9Sstevel@tonic-gate 1258*7c478bd9Sstevel@tonic-gate while (tp->i2c_w_resid != 0) { 1259*7c478bd9Sstevel@tonic-gate a = tp->i2c_wbuf[tp->i2c_wlen - tp->i2c_w_resid--]; 1260*7c478bd9Sstevel@tonic-gate smbus_put(smbus, BLK_DATA, a, 0); 1261*7c478bd9Sstevel@tonic-gate SMBUS_PRINT((PRT_WR, "%x ", a)); 1262*7c478bd9Sstevel@tonic-gate /* 1263*7c478bd9Sstevel@tonic-gate * Note that MAX_BLK_SEND defines how many bytes may 1264*7c478bd9Sstevel@tonic-gate * be sent to the BLK_DATA register. The leading byte 1265*7c478bd9Sstevel@tonic-gate * already sent to the SMB_CMD register doesn't count 1266*7c478bd9Sstevel@tonic-gate * But ALL the BLK_DATA bytes count so pre-increment 1267*7c478bd9Sstevel@tonic-gate * bytes_written before testing. 1268*7c478bd9Sstevel@tonic-gate */ 1269*7c478bd9Sstevel@tonic-gate if (++bytes_written == MAX_BLK_SEND) { 1270*7c478bd9Sstevel@tonic-gate break; 1271*7c478bd9Sstevel@tonic-gate } 1272*7c478bd9Sstevel@tonic-gate } 1273*7c478bd9Sstevel@tonic-gate SMBUS_PRINT((PRT_WR, "\n")); 1274*7c478bd9Sstevel@tonic-gate smbus_put(smbus, DEV_DATA0, bytes_written, 0); 1275*7c478bd9Sstevel@tonic-gate break; 1276*7c478bd9Sstevel@tonic-gate } 1277*7c478bd9Sstevel@tonic-gate 1278*7c478bd9Sstevel@tonic-gate /* 1279*7c478bd9Sstevel@tonic-gate * writing anything to port reg starts transfer 1280*7c478bd9Sstevel@tonic-gate */ 1281*7c478bd9Sstevel@tonic-gate smbus_put(smbus, STR_PORT, 0, SMBUS_FLUSH); 1282*7c478bd9Sstevel@tonic-gate 1283*7c478bd9Sstevel@tonic-gate return (SMBUS_PENDING); 1284*7c478bd9Sstevel@tonic-gate } 1285*7c478bd9Sstevel@tonic-gate 1286*7c478bd9Sstevel@tonic-gate /* 1287*7c478bd9Sstevel@tonic-gate * smbus_rd handles reads to the smbus. Unlike a true I2C bus 1288*7c478bd9Sstevel@tonic-gate * such as provided by pcf8584, smbus attaches a start and stop bit 1289*7c478bd9Sstevel@tonic-gate * for each transaction, which limits reads to the maximum number of 1290*7c478bd9Sstevel@tonic-gate * bytes in a single SMBUS transaction. (Block reads don't 1291*7c478bd9Sstevel@tonic-gate * seem to work on smbus, and the southbridge documentation is poor). 1292*7c478bd9Sstevel@tonic-gate * 1293*7c478bd9Sstevel@tonic-gate * It doesn't appear that reads spanning multiple I2C transactions 1294*7c478bd9Sstevel@tonic-gate * (ie each with a start-stop) affects the transfer when reading 1295*7c478bd9Sstevel@tonic-gate * multiple bytes from devices with internal counters. The counter 1296*7c478bd9Sstevel@tonic-gate * is correctly maintained. 1297*7c478bd9Sstevel@tonic-gate * 1298*7c478bd9Sstevel@tonic-gate * RD_WORD and RD_BYTE write out the byte in the SMB_CMD register 1299*7c478bd9Sstevel@tonic-gate * before reading, so RCV_BYTE is used instead. 1300*7c478bd9Sstevel@tonic-gate * 1301*7c478bd9Sstevel@tonic-gate * Multi-byte reads iniatiate a SMBUS transaction for each byte to be 1302*7c478bd9Sstevel@tonic-gate * received. Because register/offset information doesn't need to 1303*7c478bd9Sstevel@tonic-gate * be resent for each I2C transaction (as opposed to when writing data), 1304*7c478bd9Sstevel@tonic-gate * the driver can continue reading data in separate SMBUS transactions 1305*7c478bd9Sstevel@tonic-gate * until the requested buffer is filled. 1306*7c478bd9Sstevel@tonic-gate */ 1307*7c478bd9Sstevel@tonic-gate static int 1308*7c478bd9Sstevel@tonic-gate smbus_rd(smbus_t *smbus) 1309*7c478bd9Sstevel@tonic-gate { 1310*7c478bd9Sstevel@tonic-gate i2c_transfer_t *tp = smbus->smbus_cur_tran; 1311*7c478bd9Sstevel@tonic-gate uint8_t addr = smbus_dip_to_addr(smbus->smbus_cur_dip); 1312*7c478bd9Sstevel@tonic-gate 1313*7c478bd9Sstevel@tonic-gate if (smbus->smbus_bytes_to_read == 1) { 1314*7c478bd9Sstevel@tonic-gate tp->i2c_rbuf[tp->i2c_rlen - tp->i2c_r_resid] = 1315*7c478bd9Sstevel@tonic-gate smbus_get(smbus, DEV_DATA0); 1316*7c478bd9Sstevel@tonic-gate SMBUS_PRINT((PRT_RD, "smbus_rd: data in = %d\n", 1317*7c478bd9Sstevel@tonic-gate tp->i2c_rbuf[tp->i2c_rlen - tp->i2c_r_resid])); 1318*7c478bd9Sstevel@tonic-gate tp->i2c_r_resid--; 1319*7c478bd9Sstevel@tonic-gate smbus->smbus_bytes_to_read = 0; 1320*7c478bd9Sstevel@tonic-gate 1321*7c478bd9Sstevel@tonic-gate if (tp->i2c_r_resid == 0) { 1322*7c478bd9Sstevel@tonic-gate return (SMBUS_COMPLETE); 1323*7c478bd9Sstevel@tonic-gate } 1324*7c478bd9Sstevel@tonic-gate } 1325*7c478bd9Sstevel@tonic-gate 1326*7c478bd9Sstevel@tonic-gate /* 1327*7c478bd9Sstevel@tonic-gate * Address must be re-written for each command. It must 1328*7c478bd9Sstevel@tonic-gate * be written before SMB_TYP. 1329*7c478bd9Sstevel@tonic-gate */ 1330*7c478bd9Sstevel@tonic-gate smbus_put(smbus, DEV_ADDR, addr | I2C_READ, 0); 1331*7c478bd9Sstevel@tonic-gate 1332*7c478bd9Sstevel@tonic-gate if (tp->i2c_r_resid == 0) { 1333*7c478bd9Sstevel@tonic-gate smbus->smbus_bytes_to_read = 0; 1334*7c478bd9Sstevel@tonic-gate 1335*7c478bd9Sstevel@tonic-gate return (SMBUS_COMPLETE); 1336*7c478bd9Sstevel@tonic-gate } 1337*7c478bd9Sstevel@tonic-gate 1338*7c478bd9Sstevel@tonic-gate smbus->smbus_bytes_to_read = 1; 1339*7c478bd9Sstevel@tonic-gate smbus_put(smbus, SMB_TYP, RCV_BYTE, 0); 1340*7c478bd9Sstevel@tonic-gate 1341*7c478bd9Sstevel@tonic-gate smbus_put(smbus, SMB_STS, 0xff, 0); 1342*7c478bd9Sstevel@tonic-gate 1343*7c478bd9Sstevel@tonic-gate SMBUS_PRINT((PRT_RD, "smbus_rd: starting a read addr = %x resid = %d " 1344*7c478bd9Sstevel@tonic-gate "bytes_to_read=%d\n", addr, tp->i2c_r_resid, 1345*7c478bd9Sstevel@tonic-gate smbus->smbus_bytes_to_read)); 1346*7c478bd9Sstevel@tonic-gate 1347*7c478bd9Sstevel@tonic-gate smbus_put(smbus, STR_PORT, 0, SMBUS_FLUSH); 1348*7c478bd9Sstevel@tonic-gate 1349*7c478bd9Sstevel@tonic-gate return (SMBUS_PENDING); 1350*7c478bd9Sstevel@tonic-gate } 1351