10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
53923Sarutz * Common Development and Distribution License (the "License").
63923Sarutz * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
210Sstevel@tonic-gate /*
22*7656SSherry.Moore@Sun.COM * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
230Sstevel@tonic-gate * Use is subject to license terms.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
260Sstevel@tonic-gate
270Sstevel@tonic-gate /*
280Sstevel@tonic-gate * This is the nexus driver for SMBUS devices. It mostly does not use
290Sstevel@tonic-gate * the SMBUS protocol so that it fits better into the solaris i2c
300Sstevel@tonic-gate * framework.
310Sstevel@tonic-gate */
320Sstevel@tonic-gate
330Sstevel@tonic-gate #include <sys/types.h>
340Sstevel@tonic-gate #include <sys/conf.h>
350Sstevel@tonic-gate #include <sys/file.h>
360Sstevel@tonic-gate #include <sys/open.h>
370Sstevel@tonic-gate #include <sys/ddi.h>
380Sstevel@tonic-gate #include <sys/sunddi.h>
390Sstevel@tonic-gate #include <sys/modctl.h>
400Sstevel@tonic-gate #include <sys/stat.h>
410Sstevel@tonic-gate #include <sys/kmem.h>
420Sstevel@tonic-gate #include <sys/archsystm.h>
430Sstevel@tonic-gate #include <sys/platform_module.h>
440Sstevel@tonic-gate
450Sstevel@tonic-gate #include <sys/i2c/clients/i2c_client.h>
460Sstevel@tonic-gate #include <sys/i2c/misc/i2c_svc.h>
470Sstevel@tonic-gate #include <sys/i2c/misc/i2c_svc_impl.h>
480Sstevel@tonic-gate #include <sys/i2c/nexus/smbus.h>
490Sstevel@tonic-gate
500Sstevel@tonic-gate /*
510Sstevel@tonic-gate * static function declarations
520Sstevel@tonic-gate */
530Sstevel@tonic-gate static uint_t smbus_intr_cmn(smbus_t *smbus, char *src);
540Sstevel@tonic-gate static void smbus_intr_timeout(void *arg);
550Sstevel@tonic-gate static void smbus_resume(dev_info_t *dip);
560Sstevel@tonic-gate static void smbus_suspend(dev_info_t *dip);
570Sstevel@tonic-gate static int smbus_bus_ctl(dev_info_t *dip, dev_info_t *rdip,
580Sstevel@tonic-gate ddi_ctl_enum_t op, void *arg, void *result);
590Sstevel@tonic-gate static int smbus_acquire(smbus_t *, dev_info_t *dip,
600Sstevel@tonic-gate i2c_transfer_t *tp);
610Sstevel@tonic-gate static void smbus_release(smbus_t *);
620Sstevel@tonic-gate static int smbus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
630Sstevel@tonic-gate static int smbus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
640Sstevel@tonic-gate static void smbus_free_regs(smbus_t *smbus);
650Sstevel@tonic-gate static int smbus_setup_regs(dev_info_t *dip, smbus_t *smbus);
660Sstevel@tonic-gate static void smbus_reportdev(dev_info_t *dip, dev_info_t *rdip);
670Sstevel@tonic-gate static void smbus_uninitchild(dev_info_t *cdip);
680Sstevel@tonic-gate static int smbus_initchild(dev_info_t *cdip);
690Sstevel@tonic-gate static int smbus_rd(smbus_t *smbus);
700Sstevel@tonic-gate static int smbus_wr(smbus_t *smbus);
710Sstevel@tonic-gate static void smbus_put(smbus_t *smbus, uint8_t reg, uint8_t data, uint8_t flags);
720Sstevel@tonic-gate static uint8_t smbus_get(smbus_t *smbus, uint8_t reg);
730Sstevel@tonic-gate static int smbus_dip_to_addr(dev_info_t *dip);
740Sstevel@tonic-gate static uint_t smbus_intr(caddr_t arg);
750Sstevel@tonic-gate static int smbus_switch(smbus_t *smbus);
760Sstevel@tonic-gate
770Sstevel@tonic-gate static struct bus_ops smbus_busops = {
780Sstevel@tonic-gate BUSO_REV,
790Sstevel@tonic-gate nullbusmap, /* bus_map */
800Sstevel@tonic-gate NULL, /* bus_get_intrspec */
810Sstevel@tonic-gate NULL, /* bus_add_intrspec */
820Sstevel@tonic-gate NULL, /* bus_remove_intrspec */
830Sstevel@tonic-gate NULL, /* bus_map_fault */
840Sstevel@tonic-gate ddi_no_dma_map, /* bus_dma_map */
850Sstevel@tonic-gate ddi_no_dma_allochdl, /* bus_dma_allochdl */
860Sstevel@tonic-gate ddi_no_dma_freehdl, /* bus_dma_freehdl */
870Sstevel@tonic-gate ddi_no_dma_bindhdl, /* bus_dma_bindhdl */
880Sstevel@tonic-gate ddi_no_dma_unbindhdl, /* bus_unbindhdl */
890Sstevel@tonic-gate ddi_no_dma_flush, /* bus_dma_flush */
900Sstevel@tonic-gate ddi_no_dma_win, /* bus_dma_win */
910Sstevel@tonic-gate ddi_no_dma_mctl, /* bus_dma_ctl */
920Sstevel@tonic-gate smbus_bus_ctl, /* bus_ctl */
930Sstevel@tonic-gate ddi_bus_prop_op, /* bus_prop_op */
940Sstevel@tonic-gate NULL, /* bus_get_eventcookie */
950Sstevel@tonic-gate NULL, /* bus_add_eventcall */
960Sstevel@tonic-gate NULL, /* bus_remove_eventcall */
970Sstevel@tonic-gate NULL, /* bus_post_event */
980Sstevel@tonic-gate 0, /* bus_intr_ctl */
990Sstevel@tonic-gate 0, /* bus_config */
1000Sstevel@tonic-gate 0, /* bus_unconfig */
1010Sstevel@tonic-gate 0, /* bus_fm_init */
1020Sstevel@tonic-gate 0, /* bus_fm_fini */
1030Sstevel@tonic-gate 0, /* bus_fm_access_enter */
1040Sstevel@tonic-gate 0, /* bus_fm_access_exit */
1050Sstevel@tonic-gate 0, /* bus_power */
1060Sstevel@tonic-gate i_ddi_intr_ops /* bus_intr_op */
1070Sstevel@tonic-gate };
1080Sstevel@tonic-gate
1090Sstevel@tonic-gate struct cb_ops smbus_cb_ops = {
1100Sstevel@tonic-gate nodev, /* open */
1110Sstevel@tonic-gate nodev, /* close */
1120Sstevel@tonic-gate nodev, /* strategy */
1130Sstevel@tonic-gate nodev, /* print */
1140Sstevel@tonic-gate nodev, /* dump */
1150Sstevel@tonic-gate nodev, /* read */
1160Sstevel@tonic-gate nodev, /* write */
1170Sstevel@tonic-gate nodev, /* ioctl */
1180Sstevel@tonic-gate nodev, /* devmap */
1190Sstevel@tonic-gate nodev, /* mmap */
1200Sstevel@tonic-gate nodev, /* segmap */
1210Sstevel@tonic-gate nochpoll, /* poll */
1220Sstevel@tonic-gate ddi_prop_op, /* cb_prop_op */
1230Sstevel@tonic-gate 0, /* streamtab */
1240Sstevel@tonic-gate D_MP | D_NEW /* Driver compatibility flag */
1250Sstevel@tonic-gate };
1260Sstevel@tonic-gate
1270Sstevel@tonic-gate static struct dev_ops smbus_ops = {
1280Sstevel@tonic-gate DEVO_REV,
1290Sstevel@tonic-gate 0,
1300Sstevel@tonic-gate ddi_no_info,
1310Sstevel@tonic-gate nulldev,
1320Sstevel@tonic-gate nulldev,
1330Sstevel@tonic-gate smbus_attach,
1340Sstevel@tonic-gate smbus_detach,
1350Sstevel@tonic-gate nodev,
1360Sstevel@tonic-gate &smbus_cb_ops,
137*7656SSherry.Moore@Sun.COM &smbus_busops,
138*7656SSherry.Moore@Sun.COM NULL,
139*7656SSherry.Moore@Sun.COM ddi_quiesce_not_supported, /* devo_quiesce */
1400Sstevel@tonic-gate };
1410Sstevel@tonic-gate
1420Sstevel@tonic-gate static struct modldrv modldrv = {
1430Sstevel@tonic-gate &mod_driverops, /* Type of module. This one is a driver */
144*7656SSherry.Moore@Sun.COM "SMBUS nexus Driver", /* Name of the module. */
1450Sstevel@tonic-gate &smbus_ops, /* driver ops */
1460Sstevel@tonic-gate };
1470Sstevel@tonic-gate
1480Sstevel@tonic-gate static struct modlinkage modlinkage = {
1490Sstevel@tonic-gate MODREV_1,
1500Sstevel@tonic-gate &modldrv,
1510Sstevel@tonic-gate NULL
1520Sstevel@tonic-gate };
1530Sstevel@tonic-gate
1540Sstevel@tonic-gate /*
1550Sstevel@tonic-gate * Globals
1560Sstevel@tonic-gate */
1570Sstevel@tonic-gate static void *smbus_state;
1580Sstevel@tonic-gate
1590Sstevel@tonic-gate static int intr_timeout = INTR_TIMEOUT;
1600Sstevel@tonic-gate
1610Sstevel@tonic-gate /*
1620Sstevel@tonic-gate * The "interrupt-priorities" property is how a driver can specify a SPARC
1630Sstevel@tonic-gate * PIL level to associate with each of its interrupt properties. Most
1640Sstevel@tonic-gate * self-identifying busses have a better mechanism for managing this, but I2C
1650Sstevel@tonic-gate * doesn't.
1660Sstevel@tonic-gate */
1670Sstevel@tonic-gate int smbus_pil = SMBUS_PIL;
1680Sstevel@tonic-gate
1690Sstevel@tonic-gate i2c_nexus_reg_t smbus_regvec = {
1700Sstevel@tonic-gate I2C_NEXUS_REV,
1710Sstevel@tonic-gate smbus_transfer,
1720Sstevel@tonic-gate };
1730Sstevel@tonic-gate
1740Sstevel@tonic-gate #ifdef DEBUG
1750Sstevel@tonic-gate
1760Sstevel@tonic-gate static int smbus_print_lvl = 0;
1770Sstevel@tonic-gate static char msg_buff[1024];
1780Sstevel@tonic-gate static kmutex_t msg_buf_lock;
1790Sstevel@tonic-gate
1800Sstevel@tonic-gate void
smbus_print(int flags,const char * fmt,...)1810Sstevel@tonic-gate smbus_print(int flags, const char *fmt, ...)
1820Sstevel@tonic-gate {
1830Sstevel@tonic-gate if (flags & smbus_print_lvl) {
1840Sstevel@tonic-gate va_list ap;
1850Sstevel@tonic-gate
1860Sstevel@tonic-gate va_start(ap, fmt);
1870Sstevel@tonic-gate
1880Sstevel@tonic-gate if (smbus_print_lvl & PRT_PROM) {
1890Sstevel@tonic-gate prom_vprintf(fmt, ap);
1900Sstevel@tonic-gate } else {
1910Sstevel@tonic-gate
1920Sstevel@tonic-gate mutex_enter(&msg_buf_lock);
1930Sstevel@tonic-gate (void) vsprintf(msg_buff, fmt, ap);
1940Sstevel@tonic-gate if (smbus_print_lvl & PRT_BUFFONLY) {
1950Sstevel@tonic-gate cmn_err(CE_CONT, "?%s", msg_buff);
1960Sstevel@tonic-gate } else {
1970Sstevel@tonic-gate cmn_err(CE_CONT, "%s", msg_buff);
1980Sstevel@tonic-gate }
1990Sstevel@tonic-gate mutex_exit(&msg_buf_lock);
2000Sstevel@tonic-gate }
2010Sstevel@tonic-gate va_end(ap);
2020Sstevel@tonic-gate }
2030Sstevel@tonic-gate }
2040Sstevel@tonic-gate #endif /* DEBUG */
2050Sstevel@tonic-gate
2060Sstevel@tonic-gate int
_init(void)2070Sstevel@tonic-gate _init(void)
2080Sstevel@tonic-gate {
2090Sstevel@tonic-gate int status;
2100Sstevel@tonic-gate
2110Sstevel@tonic-gate status = ddi_soft_state_init(&smbus_state, sizeof (smbus_t),
212*7656SSherry.Moore@Sun.COM 1);
2130Sstevel@tonic-gate if (status != 0) {
2140Sstevel@tonic-gate
2150Sstevel@tonic-gate return (status);
2160Sstevel@tonic-gate }
2170Sstevel@tonic-gate
2180Sstevel@tonic-gate if ((status = mod_install(&modlinkage)) != 0) {
2190Sstevel@tonic-gate ddi_soft_state_fini(&smbus_state);
2200Sstevel@tonic-gate } else {
2210Sstevel@tonic-gate #ifdef DEBUG
2220Sstevel@tonic-gate mutex_init(&msg_buf_lock, NULL, MUTEX_DRIVER, NULL);
2230Sstevel@tonic-gate #endif
2240Sstevel@tonic-gate }
2250Sstevel@tonic-gate return (status);
2260Sstevel@tonic-gate }
2270Sstevel@tonic-gate
2280Sstevel@tonic-gate int
_fini(void)2290Sstevel@tonic-gate _fini(void)
2300Sstevel@tonic-gate {
2310Sstevel@tonic-gate int status;
2320Sstevel@tonic-gate
2330Sstevel@tonic-gate if ((status = mod_remove(&modlinkage)) == 0) {
2340Sstevel@tonic-gate ddi_soft_state_fini(&smbus_state);
2350Sstevel@tonic-gate #ifdef DEBUG
2360Sstevel@tonic-gate mutex_destroy(&msg_buf_lock);
2370Sstevel@tonic-gate #endif
2380Sstevel@tonic-gate }
2390Sstevel@tonic-gate
2400Sstevel@tonic-gate return (status);
2410Sstevel@tonic-gate }
2420Sstevel@tonic-gate
2430Sstevel@tonic-gate /*
2440Sstevel@tonic-gate * The loadable-module _info(9E) entry point
2450Sstevel@tonic-gate */
2460Sstevel@tonic-gate int
_info(struct modinfo * modinfop)2470Sstevel@tonic-gate _info(struct modinfo *modinfop)
2480Sstevel@tonic-gate {
2490Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop));
2500Sstevel@tonic-gate }
2510Sstevel@tonic-gate
2520Sstevel@tonic-gate static void
smbus_interrupts_on(smbus_t * smbus)2530Sstevel@tonic-gate smbus_interrupts_on(smbus_t *smbus)
2540Sstevel@tonic-gate {
2550Sstevel@tonic-gate int src_enable;
2560Sstevel@tonic-gate
2570Sstevel@tonic-gate src_enable = ddi_get32(smbus->smbus_confighandle,
2580Sstevel@tonic-gate (uint32_t *)&smbus->smbus_configregaddr[SMBUS_SRC_ENA]);
2590Sstevel@tonic-gate src_enable |= SMBUS_SMI;
2600Sstevel@tonic-gate ddi_put32(smbus->smbus_confighandle,
2610Sstevel@tonic-gate (uint32_t *)&smbus->smbus_configregaddr[SMBUS_SRC_ENA],
2620Sstevel@tonic-gate src_enable);
2630Sstevel@tonic-gate (void) ddi_get32(smbus->smbus_confighandle,
2640Sstevel@tonic-gate (uint32_t *)&smbus->smbus_configregaddr[SMBUS_SRC_ENA]);
2650Sstevel@tonic-gate }
2660Sstevel@tonic-gate
2670Sstevel@tonic-gate static void
smbus_interrupts_off(smbus_t * smbus)2680Sstevel@tonic-gate smbus_interrupts_off(smbus_t *smbus)
2690Sstevel@tonic-gate {
2700Sstevel@tonic-gate int src_enable;
2710Sstevel@tonic-gate
2720Sstevel@tonic-gate src_enable = ddi_get32(smbus->smbus_confighandle,
2730Sstevel@tonic-gate (uint32_t *)&smbus->smbus_configregaddr[SMBUS_SRC_ENA]);
2740Sstevel@tonic-gate src_enable &= ~SMBUS_SMI;
2750Sstevel@tonic-gate ddi_put32(smbus->smbus_confighandle,
2760Sstevel@tonic-gate (uint32_t *)&smbus->smbus_configregaddr[SMBUS_SRC_ENA],
2770Sstevel@tonic-gate src_enable);
2780Sstevel@tonic-gate (void) ddi_get32(smbus->smbus_confighandle,
2790Sstevel@tonic-gate (uint32_t *)&smbus->smbus_configregaddr[SMBUS_SRC_ENA]);
2800Sstevel@tonic-gate }
2810Sstevel@tonic-gate
2820Sstevel@tonic-gate static void
smbus_dodetach(dev_info_t * dip)2830Sstevel@tonic-gate smbus_dodetach(dev_info_t *dip)
2840Sstevel@tonic-gate {
2850Sstevel@tonic-gate smbus_t *smbus;
2860Sstevel@tonic-gate int instance = ddi_get_instance(dip);
2870Sstevel@tonic-gate
2880Sstevel@tonic-gate smbus = ddi_get_soft_state(smbus_state, instance);
2890Sstevel@tonic-gate
2900Sstevel@tonic-gate if (smbus == NULL) {
2910Sstevel@tonic-gate
2920Sstevel@tonic-gate return;
2930Sstevel@tonic-gate }
2940Sstevel@tonic-gate
2950Sstevel@tonic-gate cv_destroy(&smbus->smbus_cv);
2960Sstevel@tonic-gate mutex_destroy(&smbus->smbus_mutex);
2970Sstevel@tonic-gate
2980Sstevel@tonic-gate if ((smbus->smbus_attachflags & INTERRUPT_PRI) != 0) {
2990Sstevel@tonic-gate (void) ddi_prop_remove(DDI_DEV_T_NONE, dip,
3000Sstevel@tonic-gate "interrupt-priorities");
3010Sstevel@tonic-gate }
3020Sstevel@tonic-gate
3030Sstevel@tonic-gate smbus_free_regs(smbus);
3040Sstevel@tonic-gate
3050Sstevel@tonic-gate if ((smbus->smbus_attachflags & NEXUS_REGISTER) != 0) {
3060Sstevel@tonic-gate i2c_nexus_unregister(dip);
3070Sstevel@tonic-gate }
3080Sstevel@tonic-gate if ((smbus->smbus_attachflags & IMUTEX) != 0) {
3090Sstevel@tonic-gate mutex_destroy(&smbus->smbus_imutex);
3100Sstevel@tonic-gate cv_destroy(&smbus->smbus_icv);
3110Sstevel@tonic-gate }
3120Sstevel@tonic-gate
3130Sstevel@tonic-gate if (smbus->smbus_timeout != 0) {
3140Sstevel@tonic-gate (void) untimeout(smbus->smbus_timeout);
3150Sstevel@tonic-gate }
3160Sstevel@tonic-gate
3170Sstevel@tonic-gate if ((smbus->smbus_attachflags & ADD_INTR) != 0) {
3180Sstevel@tonic-gate ddi_remove_intr(dip, 0, smbus->smbus_icookie);
3190Sstevel@tonic-gate }
3200Sstevel@tonic-gate
3210Sstevel@tonic-gate ddi_soft_state_free(smbus_state, instance);
3220Sstevel@tonic-gate }
3230Sstevel@tonic-gate
3240Sstevel@tonic-gate static int
smbus_doattach(dev_info_t * dip)3250Sstevel@tonic-gate smbus_doattach(dev_info_t *dip)
3260Sstevel@tonic-gate {
3270Sstevel@tonic-gate smbus_t *smbus;
3280Sstevel@tonic-gate int instance = ddi_get_instance(dip);
3290Sstevel@tonic-gate
3300Sstevel@tonic-gate /*
3310Sstevel@tonic-gate * Allocate soft state structure.
3320Sstevel@tonic-gate */
3330Sstevel@tonic-gate if (ddi_soft_state_zalloc(smbus_state, instance) != DDI_SUCCESS) {
3340Sstevel@tonic-gate
3350Sstevel@tonic-gate goto bad;
3360Sstevel@tonic-gate }
3370Sstevel@tonic-gate
3380Sstevel@tonic-gate smbus = ddi_get_soft_state(smbus_state, instance);
3390Sstevel@tonic-gate
3400Sstevel@tonic-gate (void) snprintf(smbus->smbus_name, sizeof (smbus->smbus_name),
3410Sstevel@tonic-gate "%s%d", ddi_node_name(dip), instance);
3420Sstevel@tonic-gate
3430Sstevel@tonic-gate smbus->smbus_dip = dip;
3440Sstevel@tonic-gate
3450Sstevel@tonic-gate mutex_init(&smbus->smbus_mutex, NULL, MUTEX_DRIVER, NULL);
3460Sstevel@tonic-gate mutex_init(&smbus->smbus_imutex, NULL, MUTEX_DRIVER, NULL);
3470Sstevel@tonic-gate cv_init(&smbus->smbus_cv, NULL, CV_DRIVER, NULL);
3480Sstevel@tonic-gate cv_init(&smbus->smbus_intr_cv, NULL, CV_DRIVER, NULL);
3490Sstevel@tonic-gate
3500Sstevel@tonic-gate if (smbus_setup_regs(dip, smbus) != DDI_SUCCESS) {
3510Sstevel@tonic-gate goto bad;
3520Sstevel@tonic-gate }
3530Sstevel@tonic-gate
3540Sstevel@tonic-gate if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
3550Sstevel@tonic-gate "interrupts") == 1) {
3560Sstevel@tonic-gate smbus->smbus_polling = 0;
3570Sstevel@tonic-gate /*
3580Sstevel@tonic-gate * The "interrupt-priorities" property is how a driver can
3590Sstevel@tonic-gate * specify a SPARC PIL level to associate with each of its
3600Sstevel@tonic-gate * interrupt properties. Most self-identifying busses have
3610Sstevel@tonic-gate * a better mechanism for managing this, but I2C doesn't.
3620Sstevel@tonic-gate */
3630Sstevel@tonic-gate if (ddi_prop_exists(DDI_DEV_T_ANY, dip,
3640Sstevel@tonic-gate DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
3650Sstevel@tonic-gate "interrupt-priorities") != 1) {
3660Sstevel@tonic-gate (void) ddi_prop_create(DDI_DEV_T_NONE, dip,
3670Sstevel@tonic-gate DDI_PROP_CANSLEEP, "interrupt-priorities",
3680Sstevel@tonic-gate (caddr_t)&smbus_pil,
3690Sstevel@tonic-gate sizeof (smbus_pil));
3700Sstevel@tonic-gate smbus->smbus_attachflags |= INTERRUPT_PRI;
3710Sstevel@tonic-gate }
3720Sstevel@tonic-gate
3730Sstevel@tonic-gate /*
3740Sstevel@tonic-gate * Clear status to clear any possible interrupt
3750Sstevel@tonic-gate */
3760Sstevel@tonic-gate smbus_put(smbus, SMB_STS, 0xff, SMBUS_FLUSH);
3770Sstevel@tonic-gate
3780Sstevel@tonic-gate if (ddi_get_iblock_cookie(dip, 0, &smbus->smbus_icookie) !=
3790Sstevel@tonic-gate DDI_SUCCESS) {
3800Sstevel@tonic-gate goto bad;
3810Sstevel@tonic-gate }
3820Sstevel@tonic-gate
3830Sstevel@tonic-gate if (ddi_add_intr(dip, 0, NULL, NULL, smbus_intr,
3840Sstevel@tonic-gate (caddr_t)smbus) != DDI_SUCCESS) {
3850Sstevel@tonic-gate cmn_err(CE_WARN, "%s failed to add interrupt",
3860Sstevel@tonic-gate smbus->smbus_name);
3870Sstevel@tonic-gate goto bad;
3880Sstevel@tonic-gate }
3890Sstevel@tonic-gate smbus->smbus_attachflags |= ADD_INTR;
3900Sstevel@tonic-gate } else {
3910Sstevel@tonic-gate smbus->smbus_polling = 1;
3920Sstevel@tonic-gate /* Clear status */
3930Sstevel@tonic-gate smbus_put(smbus, SMB_STS, 0xff, SMBUS_FLUSH);
3940Sstevel@tonic-gate }
3950Sstevel@tonic-gate
3960Sstevel@tonic-gate /*
3970Sstevel@tonic-gate * initialize a cv and mutex
3980Sstevel@tonic-gate */
3990Sstevel@tonic-gate cv_init(&smbus->smbus_icv, NULL, CV_DRIVER, NULL);
4000Sstevel@tonic-gate mutex_init(&smbus->smbus_imutex, NULL, MUTEX_DRIVER,
4010Sstevel@tonic-gate (void *)smbus->smbus_icookie);
4020Sstevel@tonic-gate smbus->smbus_attachflags |= IMUTEX;
4030Sstevel@tonic-gate
4040Sstevel@tonic-gate /*
4050Sstevel@tonic-gate * Register with the i2c framework
4060Sstevel@tonic-gate */
4070Sstevel@tonic-gate i2c_nexus_register(dip, &smbus_regvec);
4080Sstevel@tonic-gate smbus->smbus_attachflags |= NEXUS_REGISTER;
4090Sstevel@tonic-gate
4100Sstevel@tonic-gate return (DDI_SUCCESS);
4110Sstevel@tonic-gate
4120Sstevel@tonic-gate bad:
4130Sstevel@tonic-gate smbus_dodetach(dip);
4140Sstevel@tonic-gate
4150Sstevel@tonic-gate return (DDI_FAILURE);
4160Sstevel@tonic-gate }
4170Sstevel@tonic-gate
4180Sstevel@tonic-gate static int
smbus_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)4190Sstevel@tonic-gate smbus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
4200Sstevel@tonic-gate {
4210Sstevel@tonic-gate switch (cmd) {
4220Sstevel@tonic-gate case DDI_ATTACH:
4230Sstevel@tonic-gate
4240Sstevel@tonic-gate return (smbus_doattach(dip));
4250Sstevel@tonic-gate case DDI_RESUME:
4260Sstevel@tonic-gate smbus_resume(dip);
4270Sstevel@tonic-gate
4280Sstevel@tonic-gate return (DDI_SUCCESS);
4290Sstevel@tonic-gate default:
4300Sstevel@tonic-gate
4310Sstevel@tonic-gate return (DDI_FAILURE);
4320Sstevel@tonic-gate }
4330Sstevel@tonic-gate }
4340Sstevel@tonic-gate
4350Sstevel@tonic-gate static int
smbus_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)4360Sstevel@tonic-gate smbus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
4370Sstevel@tonic-gate {
4380Sstevel@tonic-gate switch (cmd) {
4390Sstevel@tonic-gate case DDI_DETACH:
4400Sstevel@tonic-gate smbus_dodetach(dip);
4410Sstevel@tonic-gate
4420Sstevel@tonic-gate return (DDI_SUCCESS);
4430Sstevel@tonic-gate case DDI_SUSPEND:
4440Sstevel@tonic-gate smbus_suspend(dip);
4450Sstevel@tonic-gate
4460Sstevel@tonic-gate return (DDI_SUCCESS);
4470Sstevel@tonic-gate default:
4480Sstevel@tonic-gate
4490Sstevel@tonic-gate return (DDI_FAILURE);
4500Sstevel@tonic-gate }
4510Sstevel@tonic-gate }
4520Sstevel@tonic-gate
4530Sstevel@tonic-gate static int
smbus_bus_ctl(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t op,void * arg,void * result)4540Sstevel@tonic-gate smbus_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op,
4550Sstevel@tonic-gate void *arg, void *result)
4560Sstevel@tonic-gate {
4570Sstevel@tonic-gate switch (op) {
4580Sstevel@tonic-gate case DDI_CTLOPS_INITCHILD:
4590Sstevel@tonic-gate
4600Sstevel@tonic-gate return (smbus_initchild((dev_info_t *)arg));
4610Sstevel@tonic-gate case DDI_CTLOPS_UNINITCHILD:
4620Sstevel@tonic-gate smbus_uninitchild((dev_info_t *)arg);
4630Sstevel@tonic-gate
4640Sstevel@tonic-gate return (DDI_SUCCESS);
4653923Sarutz case DDI_CTLOPS_REPORTDEV:
4660Sstevel@tonic-gate smbus_reportdev(dip, rdip);
4670Sstevel@tonic-gate
4680Sstevel@tonic-gate return (DDI_SUCCESS);
4690Sstevel@tonic-gate case DDI_CTLOPS_DMAPMAPC:
4700Sstevel@tonic-gate case DDI_CTLOPS_POKE:
4710Sstevel@tonic-gate case DDI_CTLOPS_PEEK:
4720Sstevel@tonic-gate case DDI_CTLOPS_IOMIN:
4730Sstevel@tonic-gate case DDI_CTLOPS_REPORTINT:
4740Sstevel@tonic-gate case DDI_CTLOPS_SIDDEV:
4750Sstevel@tonic-gate case DDI_CTLOPS_SLAVEONLY:
4760Sstevel@tonic-gate case DDI_CTLOPS_AFFINITY:
4770Sstevel@tonic-gate case DDI_CTLOPS_PTOB:
4780Sstevel@tonic-gate case DDI_CTLOPS_BTOP:
4790Sstevel@tonic-gate case DDI_CTLOPS_BTOPR:
4800Sstevel@tonic-gate case DDI_CTLOPS_DVMAPAGESIZE:
4810Sstevel@tonic-gate
4820Sstevel@tonic-gate return (DDI_FAILURE);
4830Sstevel@tonic-gate default:
4840Sstevel@tonic-gate
4850Sstevel@tonic-gate return (ddi_ctlops(dip, rdip, op, arg, result));
4860Sstevel@tonic-gate }
4870Sstevel@tonic-gate }
4880Sstevel@tonic-gate
4890Sstevel@tonic-gate static int
smbus_initchild(dev_info_t * cdip)4900Sstevel@tonic-gate smbus_initchild(dev_info_t *cdip)
4910Sstevel@tonic-gate {
4920Sstevel@tonic-gate int32_t cell_size;
4930Sstevel@tonic-gate int len;
4940Sstevel@tonic-gate int32_t regs[2];
4950Sstevel@tonic-gate int err;
4960Sstevel@tonic-gate smbus_ppvt_t *ppvt;
4970Sstevel@tonic-gate char name[30];
4980Sstevel@tonic-gate
4990Sstevel@tonic-gate SMBUS_PRINT((PRT_INIT, "smbus_initchild ENTER: %s\n",
5000Sstevel@tonic-gate ddi_node_name(cdip)));
5010Sstevel@tonic-gate
5020Sstevel@tonic-gate len = sizeof (cell_size);
5030Sstevel@tonic-gate err = ddi_getlongprop_buf(DDI_DEV_T_ANY, cdip,
504*7656SSherry.Moore@Sun.COM DDI_PROP_CANSLEEP, "#address-cells",
505*7656SSherry.Moore@Sun.COM (caddr_t)&cell_size, &len);
5060Sstevel@tonic-gate if (err != DDI_PROP_SUCCESS || len != sizeof (cell_size)) {
5070Sstevel@tonic-gate cmn_err(CE_WARN, "cannot find address-cells");
5080Sstevel@tonic-gate
5090Sstevel@tonic-gate return (DDI_FAILURE);
5100Sstevel@tonic-gate }
5110Sstevel@tonic-gate
5120Sstevel@tonic-gate len = sizeof (regs);
5130Sstevel@tonic-gate err = ddi_getlongprop_buf(DDI_DEV_T_ANY, cdip,
5140Sstevel@tonic-gate DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP,
5150Sstevel@tonic-gate "reg", (caddr_t)regs, &len);
5160Sstevel@tonic-gate
5170Sstevel@tonic-gate if (err != DDI_PROP_SUCCESS) {
5180Sstevel@tonic-gate cmn_err(CE_WARN, "cannot get reg property");
5190Sstevel@tonic-gate
5200Sstevel@tonic-gate return (DDI_FAILURE);
5210Sstevel@tonic-gate }
5220Sstevel@tonic-gate
5230Sstevel@tonic-gate ppvt = kmem_zalloc(sizeof (smbus_ppvt_t), KM_SLEEP);
5240Sstevel@tonic-gate ddi_set_parent_data(cdip, ppvt);
5250Sstevel@tonic-gate
5260Sstevel@tonic-gate /*
5270Sstevel@tonic-gate * The reg property contains an unused first element (which is
5280Sstevel@tonic-gate * the mux addr on xcal), and the second element is the i2c bus
5290Sstevel@tonic-gate * address of the device.
5300Sstevel@tonic-gate */
5310Sstevel@tonic-gate ppvt->smbus_ppvt_addr = regs[1];
5320Sstevel@tonic-gate (void) sprintf(name, "%x", regs[1]);
5330Sstevel@tonic-gate
5340Sstevel@tonic-gate ddi_set_name_addr(cdip, name);
5350Sstevel@tonic-gate
5360Sstevel@tonic-gate SMBUS_PRINT((PRT_INIT, "smbus_initchild SUCCESS: %s\n",
5370Sstevel@tonic-gate ddi_node_name(cdip)));
5380Sstevel@tonic-gate
5390Sstevel@tonic-gate return (DDI_SUCCESS);
5400Sstevel@tonic-gate }
5410Sstevel@tonic-gate
5420Sstevel@tonic-gate static void
smbus_uninitchild(dev_info_t * cdip)5430Sstevel@tonic-gate smbus_uninitchild(dev_info_t *cdip)
5440Sstevel@tonic-gate {
5450Sstevel@tonic-gate smbus_ppvt_t *ppvt;
5460Sstevel@tonic-gate
5470Sstevel@tonic-gate ppvt = ddi_get_parent_data(cdip);
5480Sstevel@tonic-gate ddi_set_parent_data(cdip, NULL);
5490Sstevel@tonic-gate
5500Sstevel@tonic-gate ddi_set_name_addr(cdip, NULL);
5510Sstevel@tonic-gate
5520Sstevel@tonic-gate kmem_free(ppvt, sizeof (smbus_ppvt_t));
5530Sstevel@tonic-gate
5540Sstevel@tonic-gate SMBUS_PRINT((PRT_INIT, "smbus_uninitchild: %s\n", ddi_node_name(cdip)));
5550Sstevel@tonic-gate }
5560Sstevel@tonic-gate
5570Sstevel@tonic-gate static void
smbus_reportdev(dev_info_t * dip,dev_info_t * rdip)5580Sstevel@tonic-gate smbus_reportdev(dev_info_t *dip, dev_info_t *rdip)
5590Sstevel@tonic-gate {
5600Sstevel@tonic-gate smbus_ppvt_t *ppvt;
5610Sstevel@tonic-gate
5620Sstevel@tonic-gate ppvt = ddi_get_parent_data(rdip);
5630Sstevel@tonic-gate
5640Sstevel@tonic-gate cmn_err(CE_CONT, "?%s%d at %s%d: addr 0x%x",
5650Sstevel@tonic-gate ddi_driver_name(rdip), ddi_get_instance(rdip),
5660Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip),
5670Sstevel@tonic-gate ppvt->smbus_ppvt_addr);
5680Sstevel@tonic-gate }
5690Sstevel@tonic-gate
5700Sstevel@tonic-gate /*
5710Sstevel@tonic-gate * smbus_setup_regs() is called to map in the registers
5720Sstevel@tonic-gate * specific to the smbus.
5730Sstevel@tonic-gate */
5740Sstevel@tonic-gate static int
smbus_setup_regs(dev_info_t * dip,smbus_t * smbus)5750Sstevel@tonic-gate smbus_setup_regs(dev_info_t *dip, smbus_t *smbus)
5760Sstevel@tonic-gate {
5770Sstevel@tonic-gate ddi_device_acc_attr_t attr;
5780Sstevel@tonic-gate int ret;
5790Sstevel@tonic-gate
5800Sstevel@tonic-gate attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
5810Sstevel@tonic-gate attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
5820Sstevel@tonic-gate attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
5830Sstevel@tonic-gate
5840Sstevel@tonic-gate ret = ddi_regs_map_setup(dip, 1, (caddr_t *)&smbus->smbus_regaddr,
5850Sstevel@tonic-gate 0, 0, &attr, &smbus->smbus_rhandle);
5860Sstevel@tonic-gate
5870Sstevel@tonic-gate if (ret == DDI_FAILURE) {
5880Sstevel@tonic-gate cmn_err(CE_WARN, "%s unable to map regs", smbus->smbus_name);
5890Sstevel@tonic-gate
5900Sstevel@tonic-gate } else if (ret == DDI_REGS_ACC_CONFLICT) {
5910Sstevel@tonic-gate cmn_err(CE_WARN,
5920Sstevel@tonic-gate "%s unable to map regs because of conflict",
5930Sstevel@tonic-gate smbus->smbus_name);
5940Sstevel@tonic-gate ret = DDI_FAILURE;
5950Sstevel@tonic-gate }
5960Sstevel@tonic-gate
5970Sstevel@tonic-gate if (ret == DDI_FAILURE) {
5980Sstevel@tonic-gate
5990Sstevel@tonic-gate return (ret);
6000Sstevel@tonic-gate }
6010Sstevel@tonic-gate
6020Sstevel@tonic-gate ret = ddi_regs_map_setup(dip, 0, (caddr_t *)&smbus->smbus_configregaddr,
6030Sstevel@tonic-gate 0, 0, &attr, &smbus->smbus_confighandle);
6040Sstevel@tonic-gate
6050Sstevel@tonic-gate if (ret == DDI_FAILURE) {
6060Sstevel@tonic-gate cmn_err(CE_WARN, "%s unable to map config regs",
6070Sstevel@tonic-gate smbus->smbus_name);
6080Sstevel@tonic-gate
6090Sstevel@tonic-gate } else if (ret == DDI_REGS_ACC_CONFLICT) {
6100Sstevel@tonic-gate cmn_err(CE_WARN,
6110Sstevel@tonic-gate "%s unable to map config regs because of conflict",
6120Sstevel@tonic-gate smbus->smbus_name);
6130Sstevel@tonic-gate ret = DDI_FAILURE;
6140Sstevel@tonic-gate }
6150Sstevel@tonic-gate
6160Sstevel@tonic-gate return (ret);
6170Sstevel@tonic-gate }
6180Sstevel@tonic-gate
6190Sstevel@tonic-gate /*
6200Sstevel@tonic-gate * smbus_free_regs() frees any registers previously allocated.
6210Sstevel@tonic-gate */
6220Sstevel@tonic-gate static void
smbus_free_regs(smbus_t * smbus)6230Sstevel@tonic-gate smbus_free_regs(smbus_t *smbus)
6240Sstevel@tonic-gate {
6250Sstevel@tonic-gate if (smbus->smbus_regaddr != NULL) {
6260Sstevel@tonic-gate ddi_regs_map_free(&smbus->smbus_rhandle);
6270Sstevel@tonic-gate }
6280Sstevel@tonic-gate
6290Sstevel@tonic-gate if (smbus->smbus_configregaddr != NULL) {
6300Sstevel@tonic-gate ddi_regs_map_free(&smbus->smbus_confighandle);
6310Sstevel@tonic-gate }
6320Sstevel@tonic-gate }
6330Sstevel@tonic-gate
6340Sstevel@tonic-gate /*
6350Sstevel@tonic-gate * smbus_dip_to_addr() takes a dip and returns an I2C address.
6360Sstevel@tonic-gate */
6370Sstevel@tonic-gate static int
smbus_dip_to_addr(dev_info_t * cdip)6380Sstevel@tonic-gate smbus_dip_to_addr(dev_info_t *cdip)
6390Sstevel@tonic-gate {
6400Sstevel@tonic-gate smbus_ppvt_t *ppvt;
6410Sstevel@tonic-gate
6420Sstevel@tonic-gate ppvt = ddi_get_parent_data(cdip);
6430Sstevel@tonic-gate
6440Sstevel@tonic-gate return (ppvt->smbus_ppvt_addr);
6450Sstevel@tonic-gate }
6460Sstevel@tonic-gate
6470Sstevel@tonic-gate /*
6480Sstevel@tonic-gate * smbus_suspend() is called before the system suspends. Existing
6490Sstevel@tonic-gate * transfer in progress or waiting will complete, but new transfers are
6500Sstevel@tonic-gate * effectively blocked by "acquiring" the bus.
6510Sstevel@tonic-gate */
6520Sstevel@tonic-gate static void
smbus_suspend(dev_info_t * dip)6530Sstevel@tonic-gate smbus_suspend(dev_info_t *dip)
6540Sstevel@tonic-gate {
6550Sstevel@tonic-gate smbus_t *smbus;
6560Sstevel@tonic-gate int instance;
6570Sstevel@tonic-gate
6580Sstevel@tonic-gate instance = ddi_get_instance(dip);
6590Sstevel@tonic-gate smbus = ddi_get_soft_state(smbus_state, instance);
6600Sstevel@tonic-gate
6610Sstevel@tonic-gate (void) smbus_acquire(smbus, NULL, NULL);
6620Sstevel@tonic-gate }
6630Sstevel@tonic-gate
6640Sstevel@tonic-gate /*
6650Sstevel@tonic-gate * smbus_resume() is called when the system resumes from CPR. It releases
6660Sstevel@tonic-gate * the hold that was placed on the i2c bus, which allows any real
6670Sstevel@tonic-gate * transfers to continue.
6680Sstevel@tonic-gate */
6690Sstevel@tonic-gate static void
smbus_resume(dev_info_t * dip)6700Sstevel@tonic-gate smbus_resume(dev_info_t *dip)
6710Sstevel@tonic-gate {
6720Sstevel@tonic-gate smbus_t *smbus;
6730Sstevel@tonic-gate int instance;
6740Sstevel@tonic-gate
6750Sstevel@tonic-gate instance = ddi_get_instance(dip);
6760Sstevel@tonic-gate smbus = ddi_get_soft_state(smbus_state, instance);
6770Sstevel@tonic-gate
6780Sstevel@tonic-gate smbus_release(smbus);
6790Sstevel@tonic-gate }
6800Sstevel@tonic-gate
6810Sstevel@tonic-gate /*
6820Sstevel@tonic-gate * smbus_acquire() is called by a thread wishing to "own" the SMbus.
6830Sstevel@tonic-gate * It should not be held across multiple transfers.
6840Sstevel@tonic-gate */
6850Sstevel@tonic-gate static int
smbus_acquire(smbus_t * smbus,dev_info_t * dip,i2c_transfer_t * tp)6860Sstevel@tonic-gate smbus_acquire(smbus_t *smbus, dev_info_t *dip, i2c_transfer_t *tp)
6870Sstevel@tonic-gate {
6880Sstevel@tonic-gate mutex_enter(&smbus->smbus_mutex);
6890Sstevel@tonic-gate while (smbus->smbus_busy) {
6900Sstevel@tonic-gate cv_wait(&smbus->smbus_cv, &smbus->smbus_mutex);
6910Sstevel@tonic-gate }
6920Sstevel@tonic-gate smbus->smbus_busy = 1;
6930Sstevel@tonic-gate mutex_exit(&smbus->smbus_mutex);
6940Sstevel@tonic-gate
6950Sstevel@tonic-gate /*
6960Sstevel@tonic-gate * On systems where OBP shares a smbus controller with the
6970Sstevel@tonic-gate * OS, plat_shared_i2c_enter will serialize access to the
6980Sstevel@tonic-gate * smbus controller. Do not grab this lock during CPR
6990Sstevel@tonic-gate * suspend as the CPR thread also acquires this muxex
7000Sstevel@tonic-gate * through through prom_setprop which causes recursive
7010Sstevel@tonic-gate * mutex enter.
7020Sstevel@tonic-gate *
7030Sstevel@tonic-gate * dip == NULL during CPR.
7040Sstevel@tonic-gate */
7050Sstevel@tonic-gate if ((&plat_shared_i2c_enter != NULL) && (dip != NULL)) {
7060Sstevel@tonic-gate plat_shared_i2c_enter(smbus->smbus_dip);
7070Sstevel@tonic-gate }
7080Sstevel@tonic-gate
7090Sstevel@tonic-gate smbus->smbus_cur_tran = tp;
7100Sstevel@tonic-gate smbus->smbus_cur_dip = dip;
7110Sstevel@tonic-gate
7120Sstevel@tonic-gate return (SMBUS_SUCCESS);
7130Sstevel@tonic-gate }
7140Sstevel@tonic-gate
7150Sstevel@tonic-gate /*
7160Sstevel@tonic-gate * smbus_release() is called to release a hold made by smbus_acquire().
7170Sstevel@tonic-gate */
7180Sstevel@tonic-gate static void
smbus_release(smbus_t * smbus)7190Sstevel@tonic-gate smbus_release(smbus_t *smbus)
7200Sstevel@tonic-gate {
7210Sstevel@tonic-gate mutex_enter(&smbus->smbus_mutex);
7220Sstevel@tonic-gate smbus->smbus_busy = 0;
7230Sstevel@tonic-gate cv_signal(&smbus->smbus_cv);
7240Sstevel@tonic-gate smbus->smbus_cur_tran = NULL;
7250Sstevel@tonic-gate smbus->smbus_cur_dip = NULL;
7260Sstevel@tonic-gate mutex_exit(&smbus->smbus_mutex);
7270Sstevel@tonic-gate
7280Sstevel@tonic-gate if ((&plat_shared_i2c_exit != NULL) && (smbus->smbus_cur_dip != NULL)) {
7290Sstevel@tonic-gate plat_shared_i2c_exit(smbus->smbus_dip);
7300Sstevel@tonic-gate }
7310Sstevel@tonic-gate }
7320Sstevel@tonic-gate
7330Sstevel@tonic-gate static void
smbus_put(smbus_t * smbus,uint8_t reg,uint8_t data,uint8_t flags)7340Sstevel@tonic-gate smbus_put(smbus_t *smbus, uint8_t reg, uint8_t data, uint8_t flags)
7350Sstevel@tonic-gate {
7360Sstevel@tonic-gate ddi_acc_handle_t hp = smbus->smbus_rhandle;
7370Sstevel@tonic-gate uint8_t *reg_addr = smbus->smbus_regaddr;
7380Sstevel@tonic-gate uint8_t *config_addr = smbus->smbus_configregaddr;
7390Sstevel@tonic-gate ddi_acc_handle_t config_handle = smbus->smbus_confighandle;
7400Sstevel@tonic-gate
7410Sstevel@tonic-gate ddi_put8(hp, ®_addr[reg], data);
7420Sstevel@tonic-gate
7430Sstevel@tonic-gate SMBUS_PRINT((PRT_PUT, "smbus_put: addr = %p data = %x\n",
7440Sstevel@tonic-gate ®_addr[reg], data));
7450Sstevel@tonic-gate
7460Sstevel@tonic-gate /*
7470Sstevel@tonic-gate * if FLUSH flag is passed, read a config regs to make sure
7480Sstevel@tonic-gate * data written is flushed.
7490Sstevel@tonic-gate */
7500Sstevel@tonic-gate if (flags & SMBUS_FLUSH) {
7510Sstevel@tonic-gate (void) ddi_get8(config_handle, &config_addr[0]);
7520Sstevel@tonic-gate }
7530Sstevel@tonic-gate }
7540Sstevel@tonic-gate
7550Sstevel@tonic-gate static uint8_t
smbus_get(smbus_t * smbus,uint8_t reg)7560Sstevel@tonic-gate smbus_get(smbus_t *smbus, uint8_t reg)
7570Sstevel@tonic-gate {
7580Sstevel@tonic-gate
7590Sstevel@tonic-gate ddi_acc_handle_t hp = smbus->smbus_rhandle;
7600Sstevel@tonic-gate uint8_t *regaddr = smbus->smbus_regaddr;
7610Sstevel@tonic-gate uint8_t data;
7620Sstevel@tonic-gate
7630Sstevel@tonic-gate data = ddi_get8(hp, ®addr[reg]);
7640Sstevel@tonic-gate
7650Sstevel@tonic-gate SMBUS_PRINT((PRT_GET, "smbus_get: data = %x\n", data));
7660Sstevel@tonic-gate
7670Sstevel@tonic-gate return (data);
7680Sstevel@tonic-gate }
7690Sstevel@tonic-gate
7700Sstevel@tonic-gate
7710Sstevel@tonic-gate /*
7720Sstevel@tonic-gate * The southbridge smbus device appears to have a feature where
7730Sstevel@tonic-gate * reads from the status register return 0 for a few microseconds
7740Sstevel@tonic-gate * after clearing the status.
7750Sstevel@tonic-gate *
7760Sstevel@tonic-gate * "status_wait_idle" allows for this by retrying until
7770Sstevel@tonic-gate * it gets the right answer or times out. The loop count
7780Sstevel@tonic-gate * and the delay are empirical. The routine uses up
7790Sstevel@tonic-gate * 400 us if it fails.
7800Sstevel@tonic-gate *
7810Sstevel@tonic-gate * The fact that this routine waits for 10 us before the
7820Sstevel@tonic-gate * first check is deliberate.
7830Sstevel@tonic-gate */
7840Sstevel@tonic-gate static int
smbus_wait_idle(smbus_t * smbus)7850Sstevel@tonic-gate smbus_wait_idle(smbus_t *smbus)
7860Sstevel@tonic-gate {
7870Sstevel@tonic-gate int retries = 40;
7880Sstevel@tonic-gate int status;
7890Sstevel@tonic-gate
7900Sstevel@tonic-gate smbus_put(smbus, SMB_STS, 0xff, SMBUS_FLUSH);
7910Sstevel@tonic-gate do {
7920Sstevel@tonic-gate drv_usecwait(10);
7930Sstevel@tonic-gate status = smbus_get(smbus, SMB_STS);
7940Sstevel@tonic-gate } while (status != IDLE && --retries > 0);
7950Sstevel@tonic-gate return (status);
7960Sstevel@tonic-gate }
7970Sstevel@tonic-gate /*
7980Sstevel@tonic-gate * smbus_transfer is the function that is registered with
7990Sstevel@tonic-gate * I2C services to be called for each i2c transaction.
8000Sstevel@tonic-gate */
8010Sstevel@tonic-gate int
smbus_transfer(dev_info_t * dip,i2c_transfer_t * tp)8020Sstevel@tonic-gate smbus_transfer(dev_info_t *dip, i2c_transfer_t *tp)
8030Sstevel@tonic-gate {
8040Sstevel@tonic-gate smbus_t *smbus;
8050Sstevel@tonic-gate uint8_t status;
8060Sstevel@tonic-gate clock_t ctime;
8070Sstevel@tonic-gate
8080Sstevel@tonic-gate smbus = ddi_get_soft_state(smbus_state,
8090Sstevel@tonic-gate ddi_get_instance(ddi_get_parent(dip)));
8100Sstevel@tonic-gate
8110Sstevel@tonic-gate if (smbus_acquire(smbus, dip, tp) == SMBUS_FAILURE) {
8120Sstevel@tonic-gate tp->i2c_result = I2C_FAILURE;
8130Sstevel@tonic-gate
8140Sstevel@tonic-gate return (I2C_FAILURE);
8150Sstevel@tonic-gate }
8160Sstevel@tonic-gate
8170Sstevel@tonic-gate tp->i2c_r_resid = tp->i2c_rlen;
8180Sstevel@tonic-gate tp->i2c_w_resid = tp->i2c_wlen;
8190Sstevel@tonic-gate tp->i2c_result = I2C_SUCCESS;
8200Sstevel@tonic-gate smbus->smbus_retries = 0;
8210Sstevel@tonic-gate smbus->smbus_bytes_to_read = 0;
8220Sstevel@tonic-gate
8230Sstevel@tonic-gate mutex_enter(&smbus->smbus_imutex);
8240Sstevel@tonic-gate
8250Sstevel@tonic-gate SMBUS_PRINT((PRT_TRANS, "smbus_transfer: rlen=%d wlen=%d flags=%d",
8260Sstevel@tonic-gate tp->i2c_r_resid, tp->i2c_w_resid, tp->i2c_flags));
8270Sstevel@tonic-gate
8280Sstevel@tonic-gate /*
8290Sstevel@tonic-gate * First clear the status bits, then read them back to determine
8300Sstevel@tonic-gate * the current state.
8310Sstevel@tonic-gate */
8320Sstevel@tonic-gate status = smbus_wait_idle(smbus);
8330Sstevel@tonic-gate
8340Sstevel@tonic-gate if (status != IDLE) {
8350Sstevel@tonic-gate /*
8360Sstevel@tonic-gate * Try to issue bus reset
8370Sstevel@tonic-gate * First reset the state machine.
8380Sstevel@tonic-gate */
8390Sstevel@tonic-gate smbus_put(smbus, SMB_TYP, KILL, SMBUS_FLUSH);
8400Sstevel@tonic-gate status = smbus_wait_idle(smbus);
8410Sstevel@tonic-gate
8420Sstevel@tonic-gate if (status != IDLE) {
8430Sstevel@tonic-gate
8440Sstevel@tonic-gate smbus_put(smbus, SMB_TYP, T_OUT, SMBUS_FLUSH);
8450Sstevel@tonic-gate status = smbus_wait_idle(smbus);
8460Sstevel@tonic-gate if (status != IDLE) {
8470Sstevel@tonic-gate cmn_err(CE_WARN,
8480Sstevel@tonic-gate "%s smbus not idle. Unable to reset %x",
8490Sstevel@tonic-gate smbus->smbus_name, status);
8500Sstevel@tonic-gate smbus->smbus_cur_tran->i2c_result = I2C_FAILURE;
8510Sstevel@tonic-gate mutex_exit(&smbus->smbus_imutex);
8520Sstevel@tonic-gate smbus_release(smbus);
8530Sstevel@tonic-gate
8540Sstevel@tonic-gate return (I2C_FAILURE);
8550Sstevel@tonic-gate } else {
8560Sstevel@tonic-gate cmn_err(CE_WARN, "%s T_OUT reset required",
8570Sstevel@tonic-gate smbus->smbus_name);
8580Sstevel@tonic-gate }
8590Sstevel@tonic-gate }
8600Sstevel@tonic-gate }
8610Sstevel@tonic-gate
8620Sstevel@tonic-gate if (smbus_switch(smbus) != SMBUS_COMPLETE) {
8630Sstevel@tonic-gate if (smbus->smbus_polling) {
8640Sstevel@tonic-gate smbus->smbus_poll_complete = 0;
8650Sstevel@tonic-gate smbus->smbus_poll_retries = 0;
8660Sstevel@tonic-gate do {
8670Sstevel@tonic-gate drv_usecwait(SMBUS_POLL_INTERVAL);
8680Sstevel@tonic-gate (void) smbus_intr_cmn(smbus, SMBUS_POLL);
8690Sstevel@tonic-gate } while (!smbus->smbus_poll_complete);
8700Sstevel@tonic-gate } else {
8710Sstevel@tonic-gate /*
8720Sstevel@tonic-gate * Start a timeout as there is a bug in southbridge
8730Sstevel@tonic-gate * smbus where sometimes a transaction never starts,
8740Sstevel@tonic-gate * and needs to be reinitiated.
8750Sstevel@tonic-gate */
8760Sstevel@tonic-gate
8770Sstevel@tonic-gate smbus->smbus_timeout = timeout(smbus_intr_timeout,
8780Sstevel@tonic-gate smbus, drv_usectohz(intr_timeout));
8790Sstevel@tonic-gate SMBUS_PRINT((PRT_TRANS,
8800Sstevel@tonic-gate "starting timeout in smbus_transfer %p",
8810Sstevel@tonic-gate smbus->smbus_timeout));
8820Sstevel@tonic-gate
8830Sstevel@tonic-gate ctime = ddi_get_lbolt();
8840Sstevel@tonic-gate ctime += drv_usectohz(SMBUS_TRANS_TIMEOUT);
8850Sstevel@tonic-gate
8860Sstevel@tonic-gate smbus_interrupts_on(smbus);
8870Sstevel@tonic-gate
8880Sstevel@tonic-gate
8890Sstevel@tonic-gate cv_wait(&smbus->smbus_icv, &smbus->smbus_imutex);
8900Sstevel@tonic-gate }
8910Sstevel@tonic-gate }
8920Sstevel@tonic-gate
8930Sstevel@tonic-gate
8940Sstevel@tonic-gate mutex_exit(&smbus->smbus_imutex);
8950Sstevel@tonic-gate smbus_release(smbus);
8960Sstevel@tonic-gate
8970Sstevel@tonic-gate return (tp->i2c_result);
8980Sstevel@tonic-gate }
8990Sstevel@tonic-gate
9000Sstevel@tonic-gate /*
9010Sstevel@tonic-gate * This is called by smbus_intr_cmn() to figure out whether to call
9020Sstevel@tonic-gate * smbus_wr or smbus_rd depending on the command and current state.
9030Sstevel@tonic-gate */
9040Sstevel@tonic-gate static int
smbus_switch(smbus_t * smbus)9050Sstevel@tonic-gate smbus_switch(smbus_t *smbus)
9060Sstevel@tonic-gate {
9070Sstevel@tonic-gate int ret;
9080Sstevel@tonic-gate i2c_transfer_t *tp = smbus->smbus_cur_tran;
9090Sstevel@tonic-gate
9100Sstevel@tonic-gate if (tp == NULL) {
9110Sstevel@tonic-gate cmn_err(CE_WARN,
9120Sstevel@tonic-gate "%s smbus_cur_tran is NULL. Transaction failed",
9130Sstevel@tonic-gate smbus->smbus_name);
9140Sstevel@tonic-gate
9150Sstevel@tonic-gate return (SMBUS_FAILURE);
9160Sstevel@tonic-gate }
9170Sstevel@tonic-gate
9180Sstevel@tonic-gate smbus->smbus_saved_w_resid = tp->i2c_w_resid;
9190Sstevel@tonic-gate
9200Sstevel@tonic-gate switch (tp->i2c_flags) {
9210Sstevel@tonic-gate case I2C_WR:
9220Sstevel@tonic-gate ret = smbus_wr(smbus);
9230Sstevel@tonic-gate break;
9240Sstevel@tonic-gate case I2C_RD:
9250Sstevel@tonic-gate ret = smbus_rd(smbus);
9260Sstevel@tonic-gate break;
9270Sstevel@tonic-gate case I2C_WR_RD:
9280Sstevel@tonic-gate /*
9290Sstevel@tonic-gate * We could do a bit more decoding here,
9300Sstevel@tonic-gate * to allow the transactions that would
9310Sstevel@tonic-gate * work as a single smbus command to
9320Sstevel@tonic-gate * be done as such. It's not really
9330Sstevel@tonic-gate * worth the trouble.
9340Sstevel@tonic-gate */
9350Sstevel@tonic-gate if (tp->i2c_w_resid > 0) {
9360Sstevel@tonic-gate ret = smbus_wr(smbus);
9370Sstevel@tonic-gate } else {
9380Sstevel@tonic-gate ret = smbus_rd(smbus);
9390Sstevel@tonic-gate }
9400Sstevel@tonic-gate break;
9410Sstevel@tonic-gate default:
9420Sstevel@tonic-gate tp->i2c_result = I2C_FAILURE;
9430Sstevel@tonic-gate ret = SMBUS_COMPLETE;
9440Sstevel@tonic-gate break;
9450Sstevel@tonic-gate }
9460Sstevel@tonic-gate
9470Sstevel@tonic-gate return (ret);
9480Sstevel@tonic-gate }
9490Sstevel@tonic-gate
9500Sstevel@tonic-gate /*
9510Sstevel@tonic-gate *
9520Sstevel@tonic-gate */
9530Sstevel@tonic-gate static void
smbus_intr_timeout(void * arg)9540Sstevel@tonic-gate smbus_intr_timeout(void *arg)
9550Sstevel@tonic-gate {
9560Sstevel@tonic-gate smbus_t *smbus = (smbus_t *)arg;
9570Sstevel@tonic-gate
9580Sstevel@tonic-gate mutex_enter(&smbus->smbus_imutex);
9590Sstevel@tonic-gate /*
9600Sstevel@tonic-gate * If timeout is already cleared, it means interrupt arrived
9610Sstevel@tonic-gate * while timeout fired. In this case, just return from here.
9620Sstevel@tonic-gate */
9630Sstevel@tonic-gate if (smbus->smbus_timeout == 0) {
9640Sstevel@tonic-gate
9650Sstevel@tonic-gate mutex_exit(&smbus->smbus_imutex);
9660Sstevel@tonic-gate
9670Sstevel@tonic-gate return;
9680Sstevel@tonic-gate }
9690Sstevel@tonic-gate
9700Sstevel@tonic-gate (void) smbus_intr_cmn(smbus, SMBUS_TIMEOUT);
9710Sstevel@tonic-gate mutex_exit(&smbus->smbus_imutex);
9720Sstevel@tonic-gate }
9730Sstevel@tonic-gate
9740Sstevel@tonic-gate /*
9750Sstevel@tonic-gate * smbus_intr() is the interrupt handler for smbus.
9760Sstevel@tonic-gate */
9770Sstevel@tonic-gate static uint_t
smbus_intr(caddr_t arg)9780Sstevel@tonic-gate smbus_intr(caddr_t arg)
9790Sstevel@tonic-gate {
9800Sstevel@tonic-gate smbus_t *smbus = (smbus_t *)arg;
9810Sstevel@tonic-gate uint32_t intr_status;
9820Sstevel@tonic-gate uint_t result;
9830Sstevel@tonic-gate
9840Sstevel@tonic-gate /*
9850Sstevel@tonic-gate * Check to see if intr is really from smbus
9860Sstevel@tonic-gate */
9870Sstevel@tonic-gate intr_status = ddi_get32(smbus->smbus_confighandle,
9880Sstevel@tonic-gate (uint32_t *)&smbus->smbus_configregaddr[SMBUS_SRC_STATUS]);
9890Sstevel@tonic-gate
9900Sstevel@tonic-gate
9910Sstevel@tonic-gate if ((intr_status & SMBUS_SMB_INTR_STATUS) == 0) {
9920Sstevel@tonic-gate SMBUS_PRINT((PRT_INTR, "smbus_intr: intr not from smbus\n"));
9930Sstevel@tonic-gate
9940Sstevel@tonic-gate return (DDI_INTR_UNCLAIMED);
9950Sstevel@tonic-gate }
9960Sstevel@tonic-gate
9970Sstevel@tonic-gate mutex_enter(&smbus->smbus_imutex);
9980Sstevel@tonic-gate
9990Sstevel@tonic-gate /*
10000Sstevel@tonic-gate * If timeout is already cleared, it means it arrived before the intr.
10010Sstevel@tonic-gate * In that case, just return from here.
10020Sstevel@tonic-gate */
10030Sstevel@tonic-gate if (smbus->smbus_timeout == 0) {
10040Sstevel@tonic-gate
10050Sstevel@tonic-gate mutex_exit(&smbus->smbus_imutex);
10060Sstevel@tonic-gate
10070Sstevel@tonic-gate return (DDI_INTR_CLAIMED);
10080Sstevel@tonic-gate }
10090Sstevel@tonic-gate
10100Sstevel@tonic-gate result = smbus_intr_cmn(smbus, SMBUS_INTR);
10110Sstevel@tonic-gate mutex_exit(&smbus->smbus_imutex);
10120Sstevel@tonic-gate return (result);
10130Sstevel@tonic-gate }
10140Sstevel@tonic-gate
10150Sstevel@tonic-gate /*
10160Sstevel@tonic-gate * smbus_intr() is the interrupt handler for smbus.
10170Sstevel@tonic-gate */
10180Sstevel@tonic-gate static uint_t
smbus_intr_cmn(smbus_t * smbus,char * src)10190Sstevel@tonic-gate smbus_intr_cmn(smbus_t *smbus, char *src)
10200Sstevel@tonic-gate {
10210Sstevel@tonic-gate i2c_transfer_t *tp;
10220Sstevel@tonic-gate char error_str[128];
10230Sstevel@tonic-gate uint8_t status;
10240Sstevel@tonic-gate int ret = SMBUS_SUCCESS;
10250Sstevel@tonic-gate timeout_id_t timer_id;
10260Sstevel@tonic-gate
10270Sstevel@tonic-gate ASSERT(mutex_owned(&smbus->smbus_imutex));
10280Sstevel@tonic-gate error_str[0] = '\0';
10290Sstevel@tonic-gate
10300Sstevel@tonic-gate smbus_interrupts_off(smbus);
10310Sstevel@tonic-gate
10320Sstevel@tonic-gate tp = smbus->smbus_cur_tran;
10330Sstevel@tonic-gate /*
10340Sstevel@tonic-gate * This only happens when top half is interrupted or
10350Sstevel@tonic-gate * times out, then the interrupt arrives. Interrupt
10360Sstevel@tonic-gate * was already disabled by top half, so just exit.
10370Sstevel@tonic-gate */
10380Sstevel@tonic-gate if (tp == NULL) {
10390Sstevel@tonic-gate return (DDI_INTR_CLAIMED);
10400Sstevel@tonic-gate }
10410Sstevel@tonic-gate
10420Sstevel@tonic-gate /*
10430Sstevel@tonic-gate * This wait is required before reading the status, otherwise
10440Sstevel@tonic-gate * a parity error can occur which causes a panic. A bug with
10450Sstevel@tonic-gate * southbridge SMBUS.
10460Sstevel@tonic-gate */
10470Sstevel@tonic-gate drv_usecwait(15);
10480Sstevel@tonic-gate status = smbus_get(smbus, SMB_STS);
10490Sstevel@tonic-gate if (smbus->smbus_polling) {
10500Sstevel@tonic-gate /*
10510Sstevel@tonic-gate * If we are polling, then we expect not to
10520Sstevel@tonic-gate * get the right answer for a while,
10530Sstevel@tonic-gate * so we don't go on to that error stuff
10540Sstevel@tonic-gate * until we've polled the status for a
10550Sstevel@tonic-gate * few times. We check for errors here to save time,
10560Sstevel@tonic-gate * otherwise we would have to wait for the full
10570Sstevel@tonic-gate * poll timeout before dealing with them.
10580Sstevel@tonic-gate */
10590Sstevel@tonic-gate if (status != (CMD_CMPL|IDLE) &&
10600Sstevel@tonic-gate (status & (FAILED|BUS_ERR|DRV_ERR)) == 0 &&
10610Sstevel@tonic-gate smbus->smbus_poll_retries++ < SMBUS_POLL_MAX_RETRIES) {
10620Sstevel@tonic-gate return (DDI_INTR_CLAIMED);
10630Sstevel@tonic-gate }
10640Sstevel@tonic-gate /*
10650Sstevel@tonic-gate * else either ...
10660Sstevel@tonic-gate * [] the command has completed, or;
10670Sstevel@tonic-gate * [] There has been an error, or;
10680Sstevel@tonic-gate * [] we timed out waiting for something useful
10690Sstevel@tonic-gate * to happen, so we go on to to the error handling bit that
10700Sstevel@tonic-gate * follows, * which will reset the controller then restart the
10710Sstevel@tonic-gate * whole transaction.
10720Sstevel@tonic-gate *
10730Sstevel@tonic-gate * In all cases, clear "poll_retries" for the next command or
10740Sstevel@tonic-gate * retry
10750Sstevel@tonic-gate */
10760Sstevel@tonic-gate smbus->smbus_poll_retries = 0;
10770Sstevel@tonic-gate }
10780Sstevel@tonic-gate
10790Sstevel@tonic-gate /*
10800Sstevel@tonic-gate * A bug in southbridge SMBUS sometimes requires a reset. Status
10810Sstevel@tonic-gate * should NOT be IDLE without any other bit set. If it is, the
10820Sstevel@tonic-gate * transaction should be restarted.
10830Sstevel@tonic-gate */
10840Sstevel@tonic-gate
10850Sstevel@tonic-gate if (status == IDLE) {
10860Sstevel@tonic-gate (void) sprintf(error_str, "%s bus is idle, ", error_str);
10870Sstevel@tonic-gate }
10880Sstevel@tonic-gate
10890Sstevel@tonic-gate if ((status & CMD_CMPL) == 0) {
10900Sstevel@tonic-gate (void) sprintf(error_str, "%s command failed to complete, ",
10910Sstevel@tonic-gate error_str);
10920Sstevel@tonic-gate }
10930Sstevel@tonic-gate if (status & BUS_ERR) {
10940Sstevel@tonic-gate (void) sprintf(error_str, "%s bus error, ", error_str);
10950Sstevel@tonic-gate }
10960Sstevel@tonic-gate if (status & FAILED) {
10970Sstevel@tonic-gate (void) sprintf(error_str, "%s failed transaction, ", error_str);
10980Sstevel@tonic-gate }
10990Sstevel@tonic-gate if (status & DRV_ERR) {
11000Sstevel@tonic-gate (void) sprintf(error_str, "%s timeout or bus reset", error_str);
11010Sstevel@tonic-gate }
11020Sstevel@tonic-gate
11030Sstevel@tonic-gate if (error_str[0] != '\0') {
11040Sstevel@tonic-gate (void) sprintf(error_str, "%s %s ", error_str, src);
11050Sstevel@tonic-gate }
11060Sstevel@tonic-gate
11070Sstevel@tonic-gate /*
11080Sstevel@tonic-gate * Clear status to clear the interrupt.
11090Sstevel@tonic-gate */
11100Sstevel@tonic-gate smbus_put(smbus, SMB_STS, 0xff, SMBUS_FLUSH);
11110Sstevel@tonic-gate if (error_str[0] != '\0') {
11120Sstevel@tonic-gate smbus_put(smbus, SMB_TYP, KILL, SMBUS_FLUSH);
11130Sstevel@tonic-gate if (smbus->smbus_retries++ < SMBUS_MAX_RETRIES) {
11140Sstevel@tonic-gate /*
11150Sstevel@tonic-gate * XXXX There was a panic here when the
11160Sstevel@tonic-gate * intr timeout was greater than the timeout
11170Sstevel@tonic-gate * for the entire transfer.
11180Sstevel@tonic-gate *
11190Sstevel@tonic-gate * Restore the value of w_resid before the
11200Sstevel@tonic-gate * last transaction. r_resid doesn't need to
11210Sstevel@tonic-gate * be restored because it is only decremented
11220Sstevel@tonic-gate * after a successful read. Need to do this
11230Sstevel@tonic-gate * here since smbus_switch() keys off of a
11240Sstevel@tonic-gate * resid to know whether to call smbus_rd() or
11250Sstevel@tonic-gate * smbus_wr().
11260Sstevel@tonic-gate */
11270Sstevel@tonic-gate tp->i2c_w_resid = smbus->smbus_saved_w_resid;
11280Sstevel@tonic-gate smbus->smbus_bytes_to_read = 0;
11290Sstevel@tonic-gate
11300Sstevel@tonic-gate SMBUS_PRINT((PRT_INTR_ERR,
11310Sstevel@tonic-gate "retrying: %s %s w_resid=%d\n", error_str,
11320Sstevel@tonic-gate src, tp->i2c_w_resid));
11330Sstevel@tonic-gate } else {
11340Sstevel@tonic-gate cmn_err(CE_WARN, "%s max retries exceeded: %s",
11350Sstevel@tonic-gate smbus->smbus_name, error_str);
11360Sstevel@tonic-gate /*
11370Sstevel@tonic-gate * bailing, but first will reset the bus.
11380Sstevel@tonic-gate */
11390Sstevel@tonic-gate smbus_put(smbus, SMB_TYP, KILL, SMBUS_FLUSH);
11400Sstevel@tonic-gate smbus_put(smbus, SMB_STS, 0xff, SMBUS_FLUSH);
11410Sstevel@tonic-gate smbus->smbus_cur_tran->i2c_result = I2C_FAILURE;
11420Sstevel@tonic-gate
11430Sstevel@tonic-gate ret = SMBUS_FAILURE;
11440Sstevel@tonic-gate }
11450Sstevel@tonic-gate } else {
11460Sstevel@tonic-gate smbus->smbus_retries = 0;
11470Sstevel@tonic-gate }
11480Sstevel@tonic-gate
11490Sstevel@tonic-gate if (tp != NULL) {
11500Sstevel@tonic-gate SMBUS_PRINT((PRT_INTR, "flags=%d wresid=%d r_resid=%d %s\n",
11510Sstevel@tonic-gate tp->i2c_flags, tp->i2c_w_resid, tp->i2c_r_resid, src));
11520Sstevel@tonic-gate }
11530Sstevel@tonic-gate
11540Sstevel@tonic-gate if (ret != SMBUS_FAILURE) {
11550Sstevel@tonic-gate ret = smbus_switch(smbus);
11560Sstevel@tonic-gate }
11570Sstevel@tonic-gate
11580Sstevel@tonic-gate if (smbus->smbus_polling) {
11590Sstevel@tonic-gate if (ret == SMBUS_COMPLETE || ret == SMBUS_FAILURE) {
11600Sstevel@tonic-gate smbus->smbus_poll_complete = 1;
11610Sstevel@tonic-gate }
11620Sstevel@tonic-gate } else {
11630Sstevel@tonic-gate /*
11640Sstevel@tonic-gate * Disable previous timeout. In case it was about to fire this
11650Sstevel@tonic-gate * will let it exit without doing anything.
11660Sstevel@tonic-gate */
11670Sstevel@tonic-gate timer_id = smbus->smbus_timeout;
11680Sstevel@tonic-gate smbus->smbus_timeout = 0;
11690Sstevel@tonic-gate mutex_exit(&smbus->smbus_imutex);
11700Sstevel@tonic-gate (void) untimeout(timer_id);
11710Sstevel@tonic-gate mutex_enter(&smbus->smbus_imutex);
11720Sstevel@tonic-gate if (ret == SMBUS_COMPLETE || ret == SMBUS_FAILURE) {
11730Sstevel@tonic-gate cv_signal(&smbus->smbus_icv);
11740Sstevel@tonic-gate } else {
11750Sstevel@tonic-gate smbus_interrupts_on(smbus);
11760Sstevel@tonic-gate smbus->smbus_timeout = timeout(smbus_intr_timeout,
11770Sstevel@tonic-gate smbus, drv_usectohz(intr_timeout));
11780Sstevel@tonic-gate SMBUS_PRINT((PRT_INTR, "smbus_intr starting timeout %p "
1179*7656SSherry.Moore@Sun.COM "%s", smbus->smbus_timeout, src));
11800Sstevel@tonic-gate }
11810Sstevel@tonic-gate }
11820Sstevel@tonic-gate
11830Sstevel@tonic-gate return (DDI_INTR_CLAIMED);
11840Sstevel@tonic-gate }
11850Sstevel@tonic-gate
11860Sstevel@tonic-gate /*
11870Sstevel@tonic-gate * smbus_wr handles writes to the smbus. Unlike true I2C busses
11880Sstevel@tonic-gate * such as provided by pcf8584, smbus attaches a start and stop bit for each
11890Sstevel@tonic-gate * transaction, so this limits writes to the maximum number of bytes
11900Sstevel@tonic-gate * in a single transaction, which is 33.
11910Sstevel@tonic-gate *
11920Sstevel@tonic-gate * If more than 33 bytes are contained in the transfer, a non-zero
11930Sstevel@tonic-gate * residual has to be returned, and the calling driver has to restart
11940Sstevel@tonic-gate * another transaction to complete writing out any remaining data. The
11950Sstevel@tonic-gate * reason for this is that most devices require a register/offset as the
11960Sstevel@tonic-gate * first byte to be written for each SMBUS transaction.
11970Sstevel@tonic-gate */
11980Sstevel@tonic-gate static int
smbus_wr(smbus_t * smbus)11990Sstevel@tonic-gate smbus_wr(smbus_t *smbus)
12000Sstevel@tonic-gate {
12010Sstevel@tonic-gate i2c_transfer_t *tp = smbus->smbus_cur_tran;
12020Sstevel@tonic-gate uint8_t addr = smbus_dip_to_addr(smbus->smbus_cur_dip);
12030Sstevel@tonic-gate int bytes_written = 0;
12040Sstevel@tonic-gate uint8_t a;
12050Sstevel@tonic-gate uint8_t b;
12060Sstevel@tonic-gate
12070Sstevel@tonic-gate if (tp->i2c_w_resid != tp->i2c_wlen) {
12080Sstevel@tonic-gate return (SMBUS_COMPLETE);
12090Sstevel@tonic-gate }
12100Sstevel@tonic-gate
12110Sstevel@tonic-gate SMBUS_PRINT((PRT_WR, "smbus_wr: addr = %x resid = %d\n",
12120Sstevel@tonic-gate addr, tp->i2c_w_resid));
12130Sstevel@tonic-gate
12140Sstevel@tonic-gate smbus_put(smbus, SMB_STS, 0xff, 0);
12150Sstevel@tonic-gate
12160Sstevel@tonic-gate /*
12170Sstevel@tonic-gate * Address must be re-written for each command and it has to
12180Sstevel@tonic-gate * be written before SMB_TYP.
12190Sstevel@tonic-gate */
12200Sstevel@tonic-gate smbus_put(smbus, DEV_ADDR, addr, 0);
12210Sstevel@tonic-gate
12220Sstevel@tonic-gate switch (tp->i2c_w_resid) {
12230Sstevel@tonic-gate
12240Sstevel@tonic-gate case 1:
12250Sstevel@tonic-gate a = tp->i2c_wbuf[tp->i2c_wlen - tp->i2c_w_resid--];
12260Sstevel@tonic-gate smbus_put(smbus, SMB_CMD, a, 0);
12270Sstevel@tonic-gate smbus_put(smbus, SMB_TYP, SEND_BYTE, 0);
12280Sstevel@tonic-gate SMBUS_PRINT((PRT_WR, "smbus_wr: send one byte:"
12290Sstevel@tonic-gate " %d\n", a));
12300Sstevel@tonic-gate break;
12310Sstevel@tonic-gate case 2:
12320Sstevel@tonic-gate a = tp->i2c_wbuf[tp->i2c_wlen - tp->i2c_w_resid--];
12330Sstevel@tonic-gate smbus_put(smbus, SMB_CMD, a, 0);
12340Sstevel@tonic-gate
12350Sstevel@tonic-gate b = tp->i2c_wbuf[tp->i2c_wlen - tp->i2c_w_resid--];
12360Sstevel@tonic-gate smbus_put(smbus, DEV_DATA0, b, 0);
12370Sstevel@tonic-gate smbus_put(smbus, SMB_TYP, WR_BYTE, 0);
12380Sstevel@tonic-gate SMBUS_PRINT((PRT_WR, "smbus_wr: send two bytes:"
12390Sstevel@tonic-gate " %d %d\n", a, b));
12400Sstevel@tonic-gate break;
12410Sstevel@tonic-gate
12420Sstevel@tonic-gate default:
12430Sstevel@tonic-gate /*
12440Sstevel@tonic-gate * Write out as many bytes as possible in a single command.
12450Sstevel@tonic-gate * Note that BLK_DATA just creats a byte stream. ie, the
12460Sstevel@tonic-gate * smbus protocol is not used or interpreted by this driver.
12470Sstevel@tonic-gate */
12480Sstevel@tonic-gate smbus_put(smbus, SMB_TYP, WR_BLK, 0);
12490Sstevel@tonic-gate a = tp->i2c_wbuf[tp->i2c_wlen - tp->i2c_w_resid--];
12500Sstevel@tonic-gate
12510Sstevel@tonic-gate smbus_put(smbus, SMB_CMD, a, 0);
12520Sstevel@tonic-gate
12530Sstevel@tonic-gate SMBUS_PRINT((PRT_WR, "smbus_wr: send multiple bytes: "));
12540Sstevel@tonic-gate SMBUS_PRINT((PRT_WR, "%x ", a));
12550Sstevel@tonic-gate
12560Sstevel@tonic-gate while (tp->i2c_w_resid != 0) {
12570Sstevel@tonic-gate a = tp->i2c_wbuf[tp->i2c_wlen - tp->i2c_w_resid--];
12580Sstevel@tonic-gate smbus_put(smbus, BLK_DATA, a, 0);
12590Sstevel@tonic-gate SMBUS_PRINT((PRT_WR, "%x ", a));
12600Sstevel@tonic-gate /*
12610Sstevel@tonic-gate * Note that MAX_BLK_SEND defines how many bytes may
12620Sstevel@tonic-gate * be sent to the BLK_DATA register. The leading byte
12630Sstevel@tonic-gate * already sent to the SMB_CMD register doesn't count
12640Sstevel@tonic-gate * But ALL the BLK_DATA bytes count so pre-increment
12650Sstevel@tonic-gate * bytes_written before testing.
12660Sstevel@tonic-gate */
12670Sstevel@tonic-gate if (++bytes_written == MAX_BLK_SEND) {
12680Sstevel@tonic-gate break;
12690Sstevel@tonic-gate }
12700Sstevel@tonic-gate }
12710Sstevel@tonic-gate SMBUS_PRINT((PRT_WR, "\n"));
12720Sstevel@tonic-gate smbus_put(smbus, DEV_DATA0, bytes_written, 0);
12730Sstevel@tonic-gate break;
12740Sstevel@tonic-gate }
12750Sstevel@tonic-gate
12760Sstevel@tonic-gate /*
12770Sstevel@tonic-gate * writing anything to port reg starts transfer
12780Sstevel@tonic-gate */
12790Sstevel@tonic-gate smbus_put(smbus, STR_PORT, 0, SMBUS_FLUSH);
12800Sstevel@tonic-gate
12810Sstevel@tonic-gate return (SMBUS_PENDING);
12820Sstevel@tonic-gate }
12830Sstevel@tonic-gate
12840Sstevel@tonic-gate /*
12850Sstevel@tonic-gate * smbus_rd handles reads to the smbus. Unlike a true I2C bus
12860Sstevel@tonic-gate * such as provided by pcf8584, smbus attaches a start and stop bit
12870Sstevel@tonic-gate * for each transaction, which limits reads to the maximum number of
12880Sstevel@tonic-gate * bytes in a single SMBUS transaction. (Block reads don't
12890Sstevel@tonic-gate * seem to work on smbus, and the southbridge documentation is poor).
12900Sstevel@tonic-gate *
12910Sstevel@tonic-gate * It doesn't appear that reads spanning multiple I2C transactions
12920Sstevel@tonic-gate * (ie each with a start-stop) affects the transfer when reading
12930Sstevel@tonic-gate * multiple bytes from devices with internal counters. The counter
12940Sstevel@tonic-gate * is correctly maintained.
12950Sstevel@tonic-gate *
12960Sstevel@tonic-gate * RD_WORD and RD_BYTE write out the byte in the SMB_CMD register
12970Sstevel@tonic-gate * before reading, so RCV_BYTE is used instead.
12980Sstevel@tonic-gate *
12990Sstevel@tonic-gate * Multi-byte reads iniatiate a SMBUS transaction for each byte to be
13000Sstevel@tonic-gate * received. Because register/offset information doesn't need to
13010Sstevel@tonic-gate * be resent for each I2C transaction (as opposed to when writing data),
13020Sstevel@tonic-gate * the driver can continue reading data in separate SMBUS transactions
13030Sstevel@tonic-gate * until the requested buffer is filled.
13040Sstevel@tonic-gate */
13050Sstevel@tonic-gate static int
smbus_rd(smbus_t * smbus)13060Sstevel@tonic-gate smbus_rd(smbus_t *smbus)
13070Sstevel@tonic-gate {
13080Sstevel@tonic-gate i2c_transfer_t *tp = smbus->smbus_cur_tran;
13090Sstevel@tonic-gate uint8_t addr = smbus_dip_to_addr(smbus->smbus_cur_dip);
13100Sstevel@tonic-gate
13110Sstevel@tonic-gate if (smbus->smbus_bytes_to_read == 1) {
13120Sstevel@tonic-gate tp->i2c_rbuf[tp->i2c_rlen - tp->i2c_r_resid] =
13130Sstevel@tonic-gate smbus_get(smbus, DEV_DATA0);
13140Sstevel@tonic-gate SMBUS_PRINT((PRT_RD, "smbus_rd: data in = %d\n",
13150Sstevel@tonic-gate tp->i2c_rbuf[tp->i2c_rlen - tp->i2c_r_resid]));
13160Sstevel@tonic-gate tp->i2c_r_resid--;
13170Sstevel@tonic-gate smbus->smbus_bytes_to_read = 0;
13180Sstevel@tonic-gate
13190Sstevel@tonic-gate if (tp->i2c_r_resid == 0) {
13200Sstevel@tonic-gate return (SMBUS_COMPLETE);
13210Sstevel@tonic-gate }
13220Sstevel@tonic-gate }
13230Sstevel@tonic-gate
13240Sstevel@tonic-gate /*
13250Sstevel@tonic-gate * Address must be re-written for each command. It must
13260Sstevel@tonic-gate * be written before SMB_TYP.
13270Sstevel@tonic-gate */
13280Sstevel@tonic-gate smbus_put(smbus, DEV_ADDR, addr | I2C_READ, 0);
13290Sstevel@tonic-gate
13300Sstevel@tonic-gate if (tp->i2c_r_resid == 0) {
13310Sstevel@tonic-gate smbus->smbus_bytes_to_read = 0;
13320Sstevel@tonic-gate
13330Sstevel@tonic-gate return (SMBUS_COMPLETE);
13340Sstevel@tonic-gate }
13350Sstevel@tonic-gate
13360Sstevel@tonic-gate smbus->smbus_bytes_to_read = 1;
13370Sstevel@tonic-gate smbus_put(smbus, SMB_TYP, RCV_BYTE, 0);
13380Sstevel@tonic-gate
13390Sstevel@tonic-gate smbus_put(smbus, SMB_STS, 0xff, 0);
13400Sstevel@tonic-gate
13410Sstevel@tonic-gate SMBUS_PRINT((PRT_RD, "smbus_rd: starting a read addr = %x resid = %d "
13420Sstevel@tonic-gate "bytes_to_read=%d\n", addr, tp->i2c_r_resid,
13430Sstevel@tonic-gate smbus->smbus_bytes_to_read));
13440Sstevel@tonic-gate
13450Sstevel@tonic-gate smbus_put(smbus, STR_PORT, 0, SMBUS_FLUSH);
13460Sstevel@tonic-gate
13470Sstevel@tonic-gate return (SMBUS_PENDING);
13480Sstevel@tonic-gate }
1349