17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5ecd343b6Sarutz * Common Development and Distribution License (the "License"). 6ecd343b6Sarutz * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22*19397407SSherry Moore * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate 277c478bd9Sstevel@tonic-gate /* 287c478bd9Sstevel@tonic-gate * pcf8584.c is the nexus driver for all pcf8584 controller 297c478bd9Sstevel@tonic-gate * implementations. It supports both interrupt and polled 307c478bd9Sstevel@tonic-gate * mode operation, but defaults to interrupt. 317c478bd9Sstevel@tonic-gate */ 327c478bd9Sstevel@tonic-gate 337c478bd9Sstevel@tonic-gate #include <sys/types.h> 347c478bd9Sstevel@tonic-gate #include <sys/conf.h> 357c478bd9Sstevel@tonic-gate #include <sys/file.h> 367c478bd9Sstevel@tonic-gate #include <sys/open.h> 377c478bd9Sstevel@tonic-gate #include <sys/ddi.h> 387c478bd9Sstevel@tonic-gate #include <sys/sunddi.h> 397c478bd9Sstevel@tonic-gate #include <sys/sunndi.h> 407c478bd9Sstevel@tonic-gate #include <sys/modctl.h> 417c478bd9Sstevel@tonic-gate #include <sys/stat.h> 427c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 437c478bd9Sstevel@tonic-gate #include <sys/archsystm.h> 447c478bd9Sstevel@tonic-gate #include <sys/platform_module.h> 457c478bd9Sstevel@tonic-gate 467c478bd9Sstevel@tonic-gate #include <sys/i2c/clients/i2c_client.h> 477c478bd9Sstevel@tonic-gate #include <sys/i2c/misc/i2c_svc.h> 487c478bd9Sstevel@tonic-gate #include <sys/i2c/misc/i2c_svc_impl.h> 497c478bd9Sstevel@tonic-gate #include <sys/i2c/nexus/pcf8584.h> 507c478bd9Sstevel@tonic-gate 517c478bd9Sstevel@tonic-gate #include <sys/note.h> 527c478bd9Sstevel@tonic-gate 537c478bd9Sstevel@tonic-gate /* 547c478bd9Sstevel@tonic-gate * static function declarations 557c478bd9Sstevel@tonic-gate */ 567c478bd9Sstevel@tonic-gate static void pcf8584_resume(dev_info_t *dip); 577c478bd9Sstevel@tonic-gate static void pcf8584_suspend(dev_info_t *dip); 587c478bd9Sstevel@tonic-gate static int pcf8584_bus_ctl(dev_info_t *dip, dev_info_t *rdip, 597c478bd9Sstevel@tonic-gate ddi_ctl_enum_t op, void *arg, void *result); 607c478bd9Sstevel@tonic-gate static void pcf8584_acquire(pcf8584_t *, dev_info_t *dip, 617c478bd9Sstevel@tonic-gate i2c_transfer_t *tp, boolean_t force); 627c478bd9Sstevel@tonic-gate static void pcf8584_release(pcf8584_t *, boolean_t force); 637c478bd9Sstevel@tonic-gate static int pcf8584_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 647c478bd9Sstevel@tonic-gate static int pcf8584_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 657c478bd9Sstevel@tonic-gate static int pcf8584_open(dev_t *devp, int flag, int otyp, 667c478bd9Sstevel@tonic-gate cred_t *cred_p); 677c478bd9Sstevel@tonic-gate static int pcf8584_close(dev_t dev, int flag, int otyp, 687c478bd9Sstevel@tonic-gate cred_t *cred_p); 697c478bd9Sstevel@tonic-gate static int pcf8584_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 707c478bd9Sstevel@tonic-gate static void pcf8584_select_bus(pcf8584_t *i2c); 717c478bd9Sstevel@tonic-gate static enum tran_state pcf8584_type_to_state(int i2c_flags); 727c478bd9Sstevel@tonic-gate static void pcf8584_put_s1(pcf8584_t *i2c, char cmd); 737c478bd9Sstevel@tonic-gate static void pcf8584_put_s0(pcf8584_t *i2c, char data); 747c478bd9Sstevel@tonic-gate static uint8_t pcf8584_get_s0(pcf8584_t *i2c); 757c478bd9Sstevel@tonic-gate static uint8_t pcf8584_get_s1(pcf8584_t *i2c); 767c478bd9Sstevel@tonic-gate static int pcf8584_bbn_ready(pcf8584_t *i2c); 777c478bd9Sstevel@tonic-gate static int pcf8584_error(int status, uint8_t rdwr, pcf8584_t *i2c); 787c478bd9Sstevel@tonic-gate static void pcf8584_monitor_mode(pcf8584_t *i2c); 797c478bd9Sstevel@tonic-gate static int pcf8584_initchild(dev_info_t *cdip); 807c478bd9Sstevel@tonic-gate static void pcf8584_uninitchild(dev_info_t *cdip); 817c478bd9Sstevel@tonic-gate static void pcf8584_init(pcf8584_t *i2c); 827c478bd9Sstevel@tonic-gate static int pcf8584_setup_regs(dev_info_t *dip, pcf8584_t *i2c); 837c478bd9Sstevel@tonic-gate static void pcf8584_free_regs(pcf8584_t *i2c); 847c478bd9Sstevel@tonic-gate static void pcf8584_reportdev(dev_info_t *dip, dev_info_t *rdip); 857c478bd9Sstevel@tonic-gate static int pcf8584_dip_to_addr(dev_info_t *dip); 867c478bd9Sstevel@tonic-gate static uint_t pcf8584_intr(caddr_t arg); 877c478bd9Sstevel@tonic-gate static int pcf8584_process(pcf8584_t *i2c, uint8_t s1); 887c478bd9Sstevel@tonic-gate int pcf8584_transfer(dev_info_t *dip, i2c_transfer_t *tp); 897c478bd9Sstevel@tonic-gate 907c478bd9Sstevel@tonic-gate static void pcf8584_do_polled_io(pcf8584_t *i2c); 917c478bd9Sstevel@tonic-gate static void pcf8584_take_over(pcf8584_t *i2c, dev_info_t *dip, 927c478bd9Sstevel@tonic-gate i2c_transfer_t *tp, kcondvar_t **waiter, int *saved_mode); 937c478bd9Sstevel@tonic-gate static void pcf8584_give_up(pcf8584_t *i2c, kcondvar_t *waiter, int saved_mode); 947c478bd9Sstevel@tonic-gate 957c478bd9Sstevel@tonic-gate static struct bus_ops pcf8584_busops = { 967c478bd9Sstevel@tonic-gate BUSO_REV, 977c478bd9Sstevel@tonic-gate nullbusmap, /* bus_map */ 987c478bd9Sstevel@tonic-gate NULL, /* bus_get_intrspec */ 997c478bd9Sstevel@tonic-gate NULL, /* bus_add_intrspec */ 1007c478bd9Sstevel@tonic-gate NULL, /* bus_remove_intrspec */ 1017c478bd9Sstevel@tonic-gate NULL, /* bus_map_fault */ 1027c478bd9Sstevel@tonic-gate ddi_no_dma_map, /* bus_dma_map */ 1037c478bd9Sstevel@tonic-gate ddi_no_dma_allochdl, /* bus_dma_allochdl */ 1047c478bd9Sstevel@tonic-gate ddi_no_dma_freehdl, /* bus_dma_freehdl */ 1057c478bd9Sstevel@tonic-gate ddi_no_dma_bindhdl, /* bus_dma_bindhdl */ 1067c478bd9Sstevel@tonic-gate ddi_no_dma_unbindhdl, /* bus_unbindhdl */ 1077c478bd9Sstevel@tonic-gate ddi_no_dma_flush, /* bus_dma_flush */ 1087c478bd9Sstevel@tonic-gate ddi_no_dma_win, /* bus_dma_win */ 1097c478bd9Sstevel@tonic-gate ddi_no_dma_mctl, /* bus_dma_ctl */ 1107c478bd9Sstevel@tonic-gate pcf8584_bus_ctl, /* bus_ctl */ 1117c478bd9Sstevel@tonic-gate ddi_bus_prop_op, /* bus_prop_op */ 1127c478bd9Sstevel@tonic-gate NULL, /* bus_get_eventcookie */ 1137c478bd9Sstevel@tonic-gate NULL, /* bus_add_eventcall */ 1147c478bd9Sstevel@tonic-gate NULL, /* bus_remove_eventcall */ 1157c478bd9Sstevel@tonic-gate NULL, /* bus_post_event */ 1167c478bd9Sstevel@tonic-gate 0, /* bus_intr_ctl */ 1177c478bd9Sstevel@tonic-gate 0, /* bus_config */ 1187c478bd9Sstevel@tonic-gate 0, /* bus_unconfig */ 1197c478bd9Sstevel@tonic-gate 0, /* bus_fm_init */ 1207c478bd9Sstevel@tonic-gate 0, /* bus_fm_fini */ 1217c478bd9Sstevel@tonic-gate 0, /* bus_fm_access_enter */ 1227c478bd9Sstevel@tonic-gate 0, /* bus_fm_access_exit */ 1237c478bd9Sstevel@tonic-gate 0, /* bus_power */ 1247c478bd9Sstevel@tonic-gate i_ddi_intr_ops /* bus_intr_op */ 1257c478bd9Sstevel@tonic-gate }; 1267c478bd9Sstevel@tonic-gate 1277c478bd9Sstevel@tonic-gate struct cb_ops pcf8584_cb_ops = { 1287c478bd9Sstevel@tonic-gate pcf8584_open, /* open */ 1297c478bd9Sstevel@tonic-gate pcf8584_close, /* close */ 1307c478bd9Sstevel@tonic-gate nodev, /* strategy */ 1317c478bd9Sstevel@tonic-gate nodev, /* print */ 1327c478bd9Sstevel@tonic-gate nodev, /* dump */ 1337c478bd9Sstevel@tonic-gate nodev, /* read */ 1347c478bd9Sstevel@tonic-gate nodev, /* write */ 1357c478bd9Sstevel@tonic-gate pcf8584_ioctl, /* ioctl */ 1367c478bd9Sstevel@tonic-gate nodev, /* devmap */ 1377c478bd9Sstevel@tonic-gate nodev, /* mmap */ 1387c478bd9Sstevel@tonic-gate nodev, /* segmap */ 1397c478bd9Sstevel@tonic-gate nochpoll, /* poll */ 1407c478bd9Sstevel@tonic-gate ddi_prop_op, /* cb_prop_op */ 1417c478bd9Sstevel@tonic-gate 0, /* streamtab */ 1427c478bd9Sstevel@tonic-gate D_MP | D_NEW /* Driver compatibility flag */ 1437c478bd9Sstevel@tonic-gate }; 1447c478bd9Sstevel@tonic-gate 1457c478bd9Sstevel@tonic-gate static struct dev_ops pcf8584_ops = { 1467c478bd9Sstevel@tonic-gate DEVO_REV, 1477c478bd9Sstevel@tonic-gate 0, 1487c478bd9Sstevel@tonic-gate ddi_getinfo_1to1, 1497c478bd9Sstevel@tonic-gate nulldev, 1507c478bd9Sstevel@tonic-gate nulldev, 1517c478bd9Sstevel@tonic-gate pcf8584_attach, 1527c478bd9Sstevel@tonic-gate pcf8584_detach, 1537c478bd9Sstevel@tonic-gate nodev, 1547c478bd9Sstevel@tonic-gate &pcf8584_cb_ops, 155*19397407SSherry Moore &pcf8584_busops, 156*19397407SSherry Moore NULL, 157*19397407SSherry Moore ddi_quiesce_not_supported, /* devo_quiesce */ 1587c478bd9Sstevel@tonic-gate }; 1597c478bd9Sstevel@tonic-gate 1607c478bd9Sstevel@tonic-gate static struct modldrv modldrv = { 1617c478bd9Sstevel@tonic-gate &mod_driverops, /* Type of module. This one is a driver */ 162*19397407SSherry Moore "I2C Nexus Driver", /* Name of the module. */ 1637c478bd9Sstevel@tonic-gate &pcf8584_ops, /* driver ops */ 1647c478bd9Sstevel@tonic-gate }; 1657c478bd9Sstevel@tonic-gate 1667c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = { 1677c478bd9Sstevel@tonic-gate MODREV_1, 1687c478bd9Sstevel@tonic-gate &modldrv, 1697c478bd9Sstevel@tonic-gate NULL 1707c478bd9Sstevel@tonic-gate }; 1717c478bd9Sstevel@tonic-gate 1727c478bd9Sstevel@tonic-gate /* 1737c478bd9Sstevel@tonic-gate * pcf8584 soft state 1747c478bd9Sstevel@tonic-gate */ 1757c478bd9Sstevel@tonic-gate static void *pcf8584_state; 1767c478bd9Sstevel@tonic-gate 1777c478bd9Sstevel@tonic-gate i2c_nexus_reg_t pcf8584_regvec = { 1787c478bd9Sstevel@tonic-gate I2C_NEXUS_REV, 1797c478bd9Sstevel@tonic-gate pcf8584_transfer, 1807c478bd9Sstevel@tonic-gate }; 1817c478bd9Sstevel@tonic-gate 1827c478bd9Sstevel@tonic-gate /* 1837c478bd9Sstevel@tonic-gate * The "interrupt_priorities" property is how a driver can specify a SPARC 1847c478bd9Sstevel@tonic-gate * PIL level to associate with each of its interrupt properties. Most 1857c478bd9Sstevel@tonic-gate * self-identifying busses have a better mechanism for managing this, but I2C 1867c478bd9Sstevel@tonic-gate * doesn't. 1877c478bd9Sstevel@tonic-gate */ 1887c478bd9Sstevel@tonic-gate int pcf8584_pil = PCF8584_PIL; 1897c478bd9Sstevel@tonic-gate 1907c478bd9Sstevel@tonic-gate #ifdef DEBUG 1917c478bd9Sstevel@tonic-gate int pcf8584_print_lvl = 0; 1927c478bd9Sstevel@tonic-gate static kmutex_t msg_buf_lock; 1937c478bd9Sstevel@tonic-gate static char msg_buff[1024]; 1947c478bd9Sstevel@tonic-gate #define PCF8584_DDB(command) \ 1957c478bd9Sstevel@tonic-gate do { \ 1967c478bd9Sstevel@tonic-gate { command; } \ 1977c478bd9Sstevel@tonic-gate _NOTE(CONSTANTCONDITION) \ 1987c478bd9Sstevel@tonic-gate } while (0) 1997c478bd9Sstevel@tonic-gate 2007c478bd9Sstevel@tonic-gate static void 2017c478bd9Sstevel@tonic-gate pcf8584_print(int flags, const char *fmt, ...) 2027c478bd9Sstevel@tonic-gate { 2037c478bd9Sstevel@tonic-gate if (flags & pcf8584_print_lvl) { 2047c478bd9Sstevel@tonic-gate va_list ap; 2057c478bd9Sstevel@tonic-gate 2067c478bd9Sstevel@tonic-gate va_start(ap, fmt); 2077c478bd9Sstevel@tonic-gate 2087c478bd9Sstevel@tonic-gate if (pcf8584_print_lvl & PRT_PROM) { 2097c478bd9Sstevel@tonic-gate prom_vprintf(fmt, ap); 2107c478bd9Sstevel@tonic-gate } else { 2117c478bd9Sstevel@tonic-gate mutex_enter(&msg_buf_lock); 2127c478bd9Sstevel@tonic-gate (void) vsprintf(msg_buff, fmt, ap); 2137c478bd9Sstevel@tonic-gate if (pcf8584_print_lvl & PRT_BUFFONLY) { 2147c478bd9Sstevel@tonic-gate cmn_err(CE_CONT, "?%s", msg_buff); 2157c478bd9Sstevel@tonic-gate } else { 2167c478bd9Sstevel@tonic-gate cmn_err(CE_CONT, "%s", msg_buff); 2177c478bd9Sstevel@tonic-gate } 2187c478bd9Sstevel@tonic-gate mutex_exit(&msg_buf_lock); 2197c478bd9Sstevel@tonic-gate } 2207c478bd9Sstevel@tonic-gate va_end(ap); 2217c478bd9Sstevel@tonic-gate } 2227c478bd9Sstevel@tonic-gate } 2237c478bd9Sstevel@tonic-gate #else 2247c478bd9Sstevel@tonic-gate #define PCF8584_DDB(command) \ 2257c478bd9Sstevel@tonic-gate do { \ 2267c478bd9Sstevel@tonic-gate { _NOTE(EMPTY); } \ 2277c478bd9Sstevel@tonic-gate _NOTE(CONSTANTCONDITION) \ 2287c478bd9Sstevel@tonic-gate } while (0) 2297c478bd9Sstevel@tonic-gate #endif 2307c478bd9Sstevel@tonic-gate 231afd7fd7bSosaeed #define PCF8584_IMPL_DELAY(type, delay) \ 232afd7fd7bSosaeed if (type == PIC16F747) { \ 233afd7fd7bSosaeed drv_usecwait(delay); \ 234afd7fd7bSosaeed } 235afd7fd7bSosaeed 2367c478bd9Sstevel@tonic-gate int 2377c478bd9Sstevel@tonic-gate _init(void) 2387c478bd9Sstevel@tonic-gate { 2397c478bd9Sstevel@tonic-gate int status; 2407c478bd9Sstevel@tonic-gate 2417c478bd9Sstevel@tonic-gate status = ddi_soft_state_init(&pcf8584_state, sizeof (pcf8584_t), 2427c478bd9Sstevel@tonic-gate PCF8584_INITIAL_SOFT_SPACE); 2437c478bd9Sstevel@tonic-gate if (status != 0) { 2447c478bd9Sstevel@tonic-gate 2457c478bd9Sstevel@tonic-gate return (status); 2467c478bd9Sstevel@tonic-gate } 2477c478bd9Sstevel@tonic-gate 2487c478bd9Sstevel@tonic-gate if ((status = mod_install(&modlinkage)) != 0) { 2497c478bd9Sstevel@tonic-gate ddi_soft_state_fini(&pcf8584_state); 2507c478bd9Sstevel@tonic-gate } 2517c478bd9Sstevel@tonic-gate 2527c478bd9Sstevel@tonic-gate return (status); 2537c478bd9Sstevel@tonic-gate } 2547c478bd9Sstevel@tonic-gate 2557c478bd9Sstevel@tonic-gate int 2567c478bd9Sstevel@tonic-gate _fini(void) 2577c478bd9Sstevel@tonic-gate { 2587c478bd9Sstevel@tonic-gate int status; 2597c478bd9Sstevel@tonic-gate 2607c478bd9Sstevel@tonic-gate if ((status = mod_remove(&modlinkage)) == 0) { 2617c478bd9Sstevel@tonic-gate ddi_soft_state_fini(&pcf8584_state); 2627c478bd9Sstevel@tonic-gate } 2637c478bd9Sstevel@tonic-gate 2647c478bd9Sstevel@tonic-gate return (status); 2657c478bd9Sstevel@tonic-gate } 2667c478bd9Sstevel@tonic-gate 2677c478bd9Sstevel@tonic-gate /* 2687c478bd9Sstevel@tonic-gate * The loadable-module _info(9E) entry point 2697c478bd9Sstevel@tonic-gate */ 2707c478bd9Sstevel@tonic-gate int 2717c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop) 2727c478bd9Sstevel@tonic-gate { 2737c478bd9Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 2747c478bd9Sstevel@tonic-gate } 2757c478bd9Sstevel@tonic-gate 2767c478bd9Sstevel@tonic-gate static void 2777c478bd9Sstevel@tonic-gate pcf8584_dodetach(dev_info_t *dip) 2787c478bd9Sstevel@tonic-gate { 2797c478bd9Sstevel@tonic-gate pcf8584_t *i2c; 2807c478bd9Sstevel@tonic-gate int instance = ddi_get_instance(dip); 2817c478bd9Sstevel@tonic-gate 2827c478bd9Sstevel@tonic-gate i2c = (pcf8584_t *)ddi_get_soft_state(pcf8584_state, instance); 2837c478bd9Sstevel@tonic-gate 2847c478bd9Sstevel@tonic-gate if ((i2c->pcf8584_attachflags & ADD_INTR) != 0) { 2857c478bd9Sstevel@tonic-gate ddi_remove_intr(dip, 0, i2c->pcf8584_icookie); 2867c478bd9Sstevel@tonic-gate } 2877c478bd9Sstevel@tonic-gate 2887c478bd9Sstevel@tonic-gate cv_destroy(&i2c->pcf8584_cv); 2897c478bd9Sstevel@tonic-gate 2907c478bd9Sstevel@tonic-gate if ((i2c->pcf8584_attachflags & IMUTEX) != 0) { 2917c478bd9Sstevel@tonic-gate mutex_destroy(&i2c->pcf8584_imutex); 2927c478bd9Sstevel@tonic-gate cv_destroy(&i2c->pcf8584_icv); 2937c478bd9Sstevel@tonic-gate } 2947c478bd9Sstevel@tonic-gate if ((i2c->pcf8584_attachflags & SETUP_REGS) != 0) { 2957c478bd9Sstevel@tonic-gate pcf8584_free_regs(i2c); 2967c478bd9Sstevel@tonic-gate } 2977c478bd9Sstevel@tonic-gate if ((i2c->pcf8584_attachflags & NEXUS_REGISTER) != 0) { 2987c478bd9Sstevel@tonic-gate i2c_nexus_unregister(dip); 2997c478bd9Sstevel@tonic-gate } 3007c478bd9Sstevel@tonic-gate if ((i2c->pcf8584_attachflags & PROP_CREATE) != 0) { 3017c478bd9Sstevel@tonic-gate (void) ddi_prop_remove(DDI_DEV_T_NONE, dip, 3027c478bd9Sstevel@tonic-gate "interrupt-priorities"); 3037c478bd9Sstevel@tonic-gate } 3047c478bd9Sstevel@tonic-gate if ((i2c->pcf8584_attachflags & MINOR_NODE) != 0) { 3057c478bd9Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL); 3067c478bd9Sstevel@tonic-gate } 3077c478bd9Sstevel@tonic-gate 3087c478bd9Sstevel@tonic-gate ddi_soft_state_free(pcf8584_state, instance); 3097c478bd9Sstevel@tonic-gate } 3107c478bd9Sstevel@tonic-gate 3117c478bd9Sstevel@tonic-gate static int 3127c478bd9Sstevel@tonic-gate pcf8584_doattach(dev_info_t *dip) 3137c478bd9Sstevel@tonic-gate { 3147c478bd9Sstevel@tonic-gate pcf8584_t *i2c; 3157c478bd9Sstevel@tonic-gate int instance = ddi_get_instance(dip); 3167c478bd9Sstevel@tonic-gate 3177c478bd9Sstevel@tonic-gate /* 3187c478bd9Sstevel@tonic-gate * Allocate soft state structure. 3197c478bd9Sstevel@tonic-gate */ 3207c478bd9Sstevel@tonic-gate if (ddi_soft_state_zalloc(pcf8584_state, instance) != DDI_SUCCESS) { 3217c478bd9Sstevel@tonic-gate 3227c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 3237c478bd9Sstevel@tonic-gate } 3247c478bd9Sstevel@tonic-gate 3257c478bd9Sstevel@tonic-gate i2c = (pcf8584_t *)ddi_get_soft_state(pcf8584_state, instance); 3267c478bd9Sstevel@tonic-gate 3277c478bd9Sstevel@tonic-gate i2c->pcf8584_dip = dip; 3287c478bd9Sstevel@tonic-gate 3297c478bd9Sstevel@tonic-gate (void) snprintf(i2c->pcf8584_name, sizeof (i2c->pcf8584_name), 3307c478bd9Sstevel@tonic-gate "%s_%d", ddi_node_name(dip), instance); 3317c478bd9Sstevel@tonic-gate 332afd7fd7bSosaeed /* 333afd7fd7bSosaeed * Identify which pcf8584 implementation is being attached to. 334afd7fd7bSosaeed */ 335afd7fd7bSosaeed if (strcmp(ddi_binding_name(i2c->pcf8584_dip), "SUNW,bbc-i2c") == 0) { 336afd7fd7bSosaeed i2c->pcf8584_impl_type = BBC; 337afd7fd7bSosaeed i2c->pcf8584_impl_delay = PCF8584_GENERIC_DELAY; 338afd7fd7bSosaeed } else if (strcmp(ddi_binding_name(i2c->pcf8584_dip), 339afd7fd7bSosaeed "SUNW,i2c-pic16f747") == 0) { 340afd7fd7bSosaeed i2c->pcf8584_impl_type = PIC16F747; 341afd7fd7bSosaeed i2c->pcf8584_impl_delay = PCF8584_PIC16F747_DELAY; 342afd7fd7bSosaeed } else { 343afd7fd7bSosaeed i2c->pcf8584_impl_type = GENERIC; 344afd7fd7bSosaeed i2c->pcf8584_impl_delay = PCF8584_GENERIC_DELAY; 345afd7fd7bSosaeed } 346afd7fd7bSosaeed 3477c478bd9Sstevel@tonic-gate if (ddi_prop_exists(DDI_DEV_T_ANY, dip, 3487c478bd9Sstevel@tonic-gate DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, 3497c478bd9Sstevel@tonic-gate "interrupt-priorities") != 1) { 3507c478bd9Sstevel@tonic-gate (void) ddi_prop_create(DDI_DEV_T_NONE, dip, 3517c478bd9Sstevel@tonic-gate DDI_PROP_CANSLEEP, "interrupt-priorities", 3527c478bd9Sstevel@tonic-gate (caddr_t)&pcf8584_pil, 3537c478bd9Sstevel@tonic-gate sizeof (pcf8584_pil)); 3547c478bd9Sstevel@tonic-gate i2c->pcf8584_attachflags |= PROP_CREATE; 3557c478bd9Sstevel@tonic-gate } 3567c478bd9Sstevel@tonic-gate 3577c478bd9Sstevel@tonic-gate cv_init(&i2c->pcf8584_cv, NULL, CV_DRIVER, NULL); 3587c478bd9Sstevel@tonic-gate 3597c478bd9Sstevel@tonic-gate if (pcf8584_setup_regs(dip, i2c) != DDI_SUCCESS) { 3607c478bd9Sstevel@tonic-gate goto bad; 3617c478bd9Sstevel@tonic-gate } 3627c478bd9Sstevel@tonic-gate 3637c478bd9Sstevel@tonic-gate i2c->pcf8584_attachflags |= SETUP_REGS; 3647c478bd9Sstevel@tonic-gate 3657c478bd9Sstevel@tonic-gate if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS | 3667c478bd9Sstevel@tonic-gate DDI_PROP_CANSLEEP, "poll-mode") == 1) { 3677c478bd9Sstevel@tonic-gate i2c->pcf8584_mode = PCF8584_POLL_MODE; 3687c478bd9Sstevel@tonic-gate } else { 3697c478bd9Sstevel@tonic-gate 3707c478bd9Sstevel@tonic-gate if (ddi_get_iblock_cookie(dip, 0, 3717c478bd9Sstevel@tonic-gate &i2c->pcf8584_icookie) == DDI_SUCCESS) { 3727c478bd9Sstevel@tonic-gate mutex_init(&i2c->pcf8584_imutex, NULL, MUTEX_DRIVER, 3737c478bd9Sstevel@tonic-gate (void *)i2c->pcf8584_icookie); 3747c478bd9Sstevel@tonic-gate cv_init(&i2c->pcf8584_icv, NULL, CV_DRIVER, NULL); 3757c478bd9Sstevel@tonic-gate i2c->pcf8584_attachflags |= IMUTEX; 3767c478bd9Sstevel@tonic-gate 3777c478bd9Sstevel@tonic-gate if (ddi_add_intr(dip, 0, NULL, NULL, pcf8584_intr, 3787c478bd9Sstevel@tonic-gate (caddr_t)i2c) == DDI_SUCCESS) { 3797c478bd9Sstevel@tonic-gate i2c->pcf8584_attachflags |= ADD_INTR; 3807c478bd9Sstevel@tonic-gate i2c->pcf8584_mode = PCF8584_INTR_MODE; 3817c478bd9Sstevel@tonic-gate } else { 3827c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s failed to add interrupt", 3837c478bd9Sstevel@tonic-gate i2c->pcf8584_name); 3847c478bd9Sstevel@tonic-gate i2c->pcf8584_mode = PCF8584_POLL_MODE; 3857c478bd9Sstevel@tonic-gate } 3867c478bd9Sstevel@tonic-gate } else { 3877c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s failed to retrieve iblock cookie. " 3887c478bd9Sstevel@tonic-gate "Operating in POLL MODE only", i2c->pcf8584_name); 3897c478bd9Sstevel@tonic-gate i2c->pcf8584_mode = PCF8584_POLL_MODE; 3907c478bd9Sstevel@tonic-gate } 3917c478bd9Sstevel@tonic-gate } 3927c478bd9Sstevel@tonic-gate 3937c478bd9Sstevel@tonic-gate /* 3947c478bd9Sstevel@tonic-gate * For polled mode, still initialize a cv and mutex 3957c478bd9Sstevel@tonic-gate */ 3967c478bd9Sstevel@tonic-gate if ((i2c->pcf8584_attachflags & IMUTEX) == 0) { 3977c478bd9Sstevel@tonic-gate cv_init(&i2c->pcf8584_icv, NULL, CV_DRIVER, NULL); 3987c478bd9Sstevel@tonic-gate mutex_init(&i2c->pcf8584_imutex, NULL, MUTEX_DRIVER, NULL); 3997c478bd9Sstevel@tonic-gate i2c->pcf8584_attachflags |= IMUTEX; 4007c478bd9Sstevel@tonic-gate } 4017c478bd9Sstevel@tonic-gate 4027c478bd9Sstevel@tonic-gate i2c_nexus_register(dip, &pcf8584_regvec); 4037c478bd9Sstevel@tonic-gate i2c->pcf8584_attachflags |= NEXUS_REGISTER; 4047c478bd9Sstevel@tonic-gate 4057c478bd9Sstevel@tonic-gate if (ddi_create_minor_node(dip, "devctl", S_IFCHR, instance, 4067c478bd9Sstevel@tonic-gate DDI_NT_NEXUS, 0) == DDI_FAILURE) { 4077c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s ddi_create_minor_node failed", 4087c478bd9Sstevel@tonic-gate i2c->pcf8584_name); 4097c478bd9Sstevel@tonic-gate goto bad; 4107c478bd9Sstevel@tonic-gate } 4117c478bd9Sstevel@tonic-gate 4127c478bd9Sstevel@tonic-gate i2c->pcf8584_attachflags |= MINOR_NODE; 4137c478bd9Sstevel@tonic-gate 4147c478bd9Sstevel@tonic-gate pcf8584_init(i2c); 4157c478bd9Sstevel@tonic-gate 4167c478bd9Sstevel@tonic-gate i2c->pcf8584_nexus_dip = dip; 4177c478bd9Sstevel@tonic-gate 4187c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 4197c478bd9Sstevel@tonic-gate 4207c478bd9Sstevel@tonic-gate bad: 4217c478bd9Sstevel@tonic-gate pcf8584_dodetach(dip); 4227c478bd9Sstevel@tonic-gate 4237c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 4247c478bd9Sstevel@tonic-gate } 4257c478bd9Sstevel@tonic-gate 4267c478bd9Sstevel@tonic-gate static int 4277c478bd9Sstevel@tonic-gate pcf8584_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 4287c478bd9Sstevel@tonic-gate { 4297c478bd9Sstevel@tonic-gate switch (cmd) { 4307c478bd9Sstevel@tonic-gate case DDI_ATTACH: 4317c478bd9Sstevel@tonic-gate 4327c478bd9Sstevel@tonic-gate return (pcf8584_doattach(dip)); 4337c478bd9Sstevel@tonic-gate case DDI_RESUME: 4347c478bd9Sstevel@tonic-gate pcf8584_resume(dip); 4357c478bd9Sstevel@tonic-gate 4367c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 4377c478bd9Sstevel@tonic-gate default: 4387c478bd9Sstevel@tonic-gate 4397c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 4407c478bd9Sstevel@tonic-gate } 4417c478bd9Sstevel@tonic-gate } 4427c478bd9Sstevel@tonic-gate 4437c478bd9Sstevel@tonic-gate static int 4447c478bd9Sstevel@tonic-gate pcf8584_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 4457c478bd9Sstevel@tonic-gate { 4467c478bd9Sstevel@tonic-gate switch (cmd) { 4477c478bd9Sstevel@tonic-gate case DDI_DETACH: 4487c478bd9Sstevel@tonic-gate pcf8584_dodetach(dip); 4497c478bd9Sstevel@tonic-gate 4507c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 4517c478bd9Sstevel@tonic-gate case DDI_SUSPEND: 4527c478bd9Sstevel@tonic-gate pcf8584_suspend(dip); 4537c478bd9Sstevel@tonic-gate 4547c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 4557c478bd9Sstevel@tonic-gate default: 4567c478bd9Sstevel@tonic-gate 4577c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 4587c478bd9Sstevel@tonic-gate } 4597c478bd9Sstevel@tonic-gate } 4607c478bd9Sstevel@tonic-gate 4617c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 4627c478bd9Sstevel@tonic-gate static int 4637c478bd9Sstevel@tonic-gate pcf8584_open(dev_t *devp, int flag, int otyp, cred_t *cred_p) 4647c478bd9Sstevel@tonic-gate { 4657c478bd9Sstevel@tonic-gate int instance; 4667c478bd9Sstevel@tonic-gate pcf8584_t *i2c; 4677c478bd9Sstevel@tonic-gate 4687c478bd9Sstevel@tonic-gate /* 4697c478bd9Sstevel@tonic-gate * Make sure the open is for the right file type 4707c478bd9Sstevel@tonic-gate */ 4717c478bd9Sstevel@tonic-gate if (otyp != OTYP_CHR) 4727c478bd9Sstevel@tonic-gate return (EINVAL); 4737c478bd9Sstevel@tonic-gate 4747c478bd9Sstevel@tonic-gate instance = getminor(*devp); 4757c478bd9Sstevel@tonic-gate i2c = (pcf8584_t *)ddi_get_soft_state(pcf8584_state, instance); 4767c478bd9Sstevel@tonic-gate if (i2c == NULL) 4777c478bd9Sstevel@tonic-gate return (ENXIO); 4787c478bd9Sstevel@tonic-gate 4797c478bd9Sstevel@tonic-gate /* 4807c478bd9Sstevel@tonic-gate * Enforce exclusive access 4817c478bd9Sstevel@tonic-gate */ 4827c478bd9Sstevel@tonic-gate mutex_enter(&i2c->pcf8584_imutex); 4837c478bd9Sstevel@tonic-gate if (i2c->pcf8584_open) { 4847c478bd9Sstevel@tonic-gate mutex_exit(&i2c->pcf8584_imutex); 4857c478bd9Sstevel@tonic-gate 4867c478bd9Sstevel@tonic-gate return (EBUSY); 4877c478bd9Sstevel@tonic-gate } else 4887c478bd9Sstevel@tonic-gate i2c->pcf8584_open = 1; 4897c478bd9Sstevel@tonic-gate mutex_exit(&i2c->pcf8584_imutex); 4907c478bd9Sstevel@tonic-gate 4917c478bd9Sstevel@tonic-gate return (0); 4927c478bd9Sstevel@tonic-gate } 4937c478bd9Sstevel@tonic-gate 4947c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 4957c478bd9Sstevel@tonic-gate static int 4967c478bd9Sstevel@tonic-gate pcf8584_close(dev_t dev, int flag, int otyp, cred_t *cred_p) 4977c478bd9Sstevel@tonic-gate { 4987c478bd9Sstevel@tonic-gate int instance; 4997c478bd9Sstevel@tonic-gate pcf8584_t *i2c; 5007c478bd9Sstevel@tonic-gate 5017c478bd9Sstevel@tonic-gate /* 5027c478bd9Sstevel@tonic-gate * Make sure the close is for the right file type 5037c478bd9Sstevel@tonic-gate */ 5047c478bd9Sstevel@tonic-gate if (otyp != OTYP_CHR) 5057c478bd9Sstevel@tonic-gate return (EINVAL); 5067c478bd9Sstevel@tonic-gate 5077c478bd9Sstevel@tonic-gate instance = getminor(dev); 5087c478bd9Sstevel@tonic-gate i2c = (pcf8584_t *)ddi_get_soft_state(pcf8584_state, instance); 5097c478bd9Sstevel@tonic-gate if (i2c == NULL) 5107c478bd9Sstevel@tonic-gate return (ENXIO); 5117c478bd9Sstevel@tonic-gate 5127c478bd9Sstevel@tonic-gate mutex_enter(&i2c->pcf8584_imutex); 5137c478bd9Sstevel@tonic-gate i2c->pcf8584_open = 0; 5147c478bd9Sstevel@tonic-gate mutex_exit(&i2c->pcf8584_imutex); 5157c478bd9Sstevel@tonic-gate 5167c478bd9Sstevel@tonic-gate return (0); 5177c478bd9Sstevel@tonic-gate } 5187c478bd9Sstevel@tonic-gate 5197c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 5207c478bd9Sstevel@tonic-gate static int 5217c478bd9Sstevel@tonic-gate pcf8584_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 5227c478bd9Sstevel@tonic-gate int *rvalp) 5237c478bd9Sstevel@tonic-gate { 5247c478bd9Sstevel@tonic-gate pcf8584_t *i2c; 5257c478bd9Sstevel@tonic-gate dev_info_t *self; 5267c478bd9Sstevel@tonic-gate struct devctl_iocdata *dcp; 5277c478bd9Sstevel@tonic-gate int rv; 5287c478bd9Sstevel@tonic-gate 5297c478bd9Sstevel@tonic-gate i2c = (pcf8584_t *)ddi_get_soft_state(pcf8584_state, getminor(dev)); 5307c478bd9Sstevel@tonic-gate if (i2c == NULL) 5317c478bd9Sstevel@tonic-gate return (ENXIO); 5327c478bd9Sstevel@tonic-gate 5337c478bd9Sstevel@tonic-gate self = (dev_info_t *)i2c->pcf8584_nexus_dip; 5347c478bd9Sstevel@tonic-gate 5357c478bd9Sstevel@tonic-gate /* 5367c478bd9Sstevel@tonic-gate * read devctl ioctl data 5377c478bd9Sstevel@tonic-gate */ 5387c478bd9Sstevel@tonic-gate if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS) { 5397c478bd9Sstevel@tonic-gate 5407c478bd9Sstevel@tonic-gate return (EFAULT); 5417c478bd9Sstevel@tonic-gate } 5427c478bd9Sstevel@tonic-gate 5437c478bd9Sstevel@tonic-gate switch (cmd) { 5447c478bd9Sstevel@tonic-gate case DEVCTL_BUS_DEV_CREATE: 5457c478bd9Sstevel@tonic-gate rv = ndi_dc_devi_create(dcp, self, 0, NULL); 5467c478bd9Sstevel@tonic-gate break; 5477c478bd9Sstevel@tonic-gate case DEVCTL_DEVICE_REMOVE: 5487c478bd9Sstevel@tonic-gate rv = ndi_devctl_device_remove(self, dcp, 0); 5497c478bd9Sstevel@tonic-gate break; 5507c478bd9Sstevel@tonic-gate default: 5517c478bd9Sstevel@tonic-gate rv = ENOTSUP; 5527c478bd9Sstevel@tonic-gate } 5537c478bd9Sstevel@tonic-gate 5547c478bd9Sstevel@tonic-gate ndi_dc_freehdl(dcp); 5557c478bd9Sstevel@tonic-gate 5567c478bd9Sstevel@tonic-gate return (rv); 5577c478bd9Sstevel@tonic-gate } 5587c478bd9Sstevel@tonic-gate 5597c478bd9Sstevel@tonic-gate static int 5607c478bd9Sstevel@tonic-gate pcf8584_bus_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op, 5617c478bd9Sstevel@tonic-gate void *arg, void *result) 5627c478bd9Sstevel@tonic-gate { 5637c478bd9Sstevel@tonic-gate switch (op) { 5647c478bd9Sstevel@tonic-gate case DDI_CTLOPS_INITCHILD: 5657c478bd9Sstevel@tonic-gate 5667c478bd9Sstevel@tonic-gate return (pcf8584_initchild((dev_info_t *)arg)); 5677c478bd9Sstevel@tonic-gate case DDI_CTLOPS_UNINITCHILD: 5687c478bd9Sstevel@tonic-gate pcf8584_uninitchild((dev_info_t *)arg); 5697c478bd9Sstevel@tonic-gate 5707c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 571ecd343b6Sarutz case DDI_CTLOPS_REPORTDEV: 5727c478bd9Sstevel@tonic-gate pcf8584_reportdev(dip, rdip); 5737c478bd9Sstevel@tonic-gate 5747c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 5757c478bd9Sstevel@tonic-gate case DDI_CTLOPS_DMAPMAPC: 5767c478bd9Sstevel@tonic-gate case DDI_CTLOPS_POKE: 5777c478bd9Sstevel@tonic-gate case DDI_CTLOPS_PEEK: 5787c478bd9Sstevel@tonic-gate case DDI_CTLOPS_IOMIN: 5797c478bd9Sstevel@tonic-gate case DDI_CTLOPS_REPORTINT: 5807c478bd9Sstevel@tonic-gate case DDI_CTLOPS_SIDDEV: 5817c478bd9Sstevel@tonic-gate case DDI_CTLOPS_SLAVEONLY: 5827c478bd9Sstevel@tonic-gate case DDI_CTLOPS_AFFINITY: 5837c478bd9Sstevel@tonic-gate case DDI_CTLOPS_PTOB: 5847c478bd9Sstevel@tonic-gate case DDI_CTLOPS_BTOP: 5857c478bd9Sstevel@tonic-gate case DDI_CTLOPS_BTOPR: 5867c478bd9Sstevel@tonic-gate case DDI_CTLOPS_DVMAPAGESIZE: 5877c478bd9Sstevel@tonic-gate 5887c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 5897c478bd9Sstevel@tonic-gate default: 5907c478bd9Sstevel@tonic-gate 5917c478bd9Sstevel@tonic-gate return (ddi_ctlops(dip, rdip, op, arg, result)); 5927c478bd9Sstevel@tonic-gate } 5937c478bd9Sstevel@tonic-gate } 5947c478bd9Sstevel@tonic-gate 5957c478bd9Sstevel@tonic-gate /* 5967c478bd9Sstevel@tonic-gate * pcf8584_suspend() is called before the system suspends. Existing 5977c478bd9Sstevel@tonic-gate * transfer in progress or waiting will complete, but new transfers are 5987c478bd9Sstevel@tonic-gate * effectively blocked by "acquiring" the bus. 5997c478bd9Sstevel@tonic-gate */ 6007c478bd9Sstevel@tonic-gate static void 6017c478bd9Sstevel@tonic-gate pcf8584_suspend(dev_info_t *dip) 6027c478bd9Sstevel@tonic-gate { 6037c478bd9Sstevel@tonic-gate pcf8584_t *i2c; 6047c478bd9Sstevel@tonic-gate int instance; 6057c478bd9Sstevel@tonic-gate 6067c478bd9Sstevel@tonic-gate instance = ddi_get_instance(dip); 6077c478bd9Sstevel@tonic-gate i2c = (pcf8584_t *)ddi_get_soft_state(pcf8584_state, instance); 6087c478bd9Sstevel@tonic-gate 6097c478bd9Sstevel@tonic-gate pcf8584_acquire(i2c, NULL, NULL, B_FALSE); 6107c478bd9Sstevel@tonic-gate } 6117c478bd9Sstevel@tonic-gate 6127c478bd9Sstevel@tonic-gate /* 6137c478bd9Sstevel@tonic-gate * pcf8584_resume() is called when the system resumes from CPR. It releases 6147c478bd9Sstevel@tonic-gate * the hold that was placed on the i2c bus, which allows any real 6157c478bd9Sstevel@tonic-gate * transfers to continue. 6167c478bd9Sstevel@tonic-gate */ 6177c478bd9Sstevel@tonic-gate static void 6187c478bd9Sstevel@tonic-gate pcf8584_resume(dev_info_t *dip) 6197c478bd9Sstevel@tonic-gate { 6207c478bd9Sstevel@tonic-gate pcf8584_t *i2c; 6217c478bd9Sstevel@tonic-gate int instance; 6227c478bd9Sstevel@tonic-gate 6237c478bd9Sstevel@tonic-gate instance = ddi_get_instance(dip); 6247c478bd9Sstevel@tonic-gate i2c = (pcf8584_t *)ddi_get_soft_state(pcf8584_state, instance); 6257c478bd9Sstevel@tonic-gate 6267c478bd9Sstevel@tonic-gate pcf8584_release(i2c, B_FALSE); 6277c478bd9Sstevel@tonic-gate 6287c478bd9Sstevel@tonic-gate pcf8584_init(i2c); 6297c478bd9Sstevel@tonic-gate } 6307c478bd9Sstevel@tonic-gate 6317c478bd9Sstevel@tonic-gate /* 6327c478bd9Sstevel@tonic-gate * pcf8584_acquire() is called by a thread wishing to "own" the I2C bus. 6337c478bd9Sstevel@tonic-gate * It should not be held across multiple transfers. If the 'force' flag 6347c478bd9Sstevel@tonic-gate * is set, do not try to acquire mutex or do cv_wait. 6357c478bd9Sstevel@tonic-gate */ 6367c478bd9Sstevel@tonic-gate static void 6377c478bd9Sstevel@tonic-gate pcf8584_acquire(pcf8584_t *i2c, dev_info_t *dip, i2c_transfer_t *tp, 6387c478bd9Sstevel@tonic-gate boolean_t force) 6397c478bd9Sstevel@tonic-gate { 6407c478bd9Sstevel@tonic-gate if (force) { 6417c478bd9Sstevel@tonic-gate i2c->pcf8584_busy = 1; 6427c478bd9Sstevel@tonic-gate i2c->pcf8584_cur_tran = tp; 6437c478bd9Sstevel@tonic-gate i2c->pcf8584_cur_dip = dip; 6447c478bd9Sstevel@tonic-gate i2c->pcf8584_cur_status = PCF8584_TRANSFER_NEW; 6457c478bd9Sstevel@tonic-gate return; 6467c478bd9Sstevel@tonic-gate } 6477c478bd9Sstevel@tonic-gate 6487c478bd9Sstevel@tonic-gate mutex_enter(&i2c->pcf8584_imutex); 6497c478bd9Sstevel@tonic-gate while (i2c->pcf8584_busy) { 6507c478bd9Sstevel@tonic-gate cv_wait(&i2c->pcf8584_cv, &i2c->pcf8584_imutex); 6517c478bd9Sstevel@tonic-gate } 6527c478bd9Sstevel@tonic-gate i2c->pcf8584_busy = 1; 6537c478bd9Sstevel@tonic-gate mutex_exit(&i2c->pcf8584_imutex); 6547c478bd9Sstevel@tonic-gate /* 6557c478bd9Sstevel@tonic-gate * On systems where OBP shares a pcf8584 controller with the 6567c478bd9Sstevel@tonic-gate * OS, plat_shared_i2c_enter will serialize access to the 6577c478bd9Sstevel@tonic-gate * pcf8584 controller. Do not grab this lock during CPR 6587c478bd9Sstevel@tonic-gate * suspend as the CPR thread also acquires this muxex 6597c478bd9Sstevel@tonic-gate * through through prom_setprop which causes recursive 6607c478bd9Sstevel@tonic-gate * mutex enter. 6617c478bd9Sstevel@tonic-gate * 6627c478bd9Sstevel@tonic-gate * dip == NULL during CPR. 6637c478bd9Sstevel@tonic-gate */ 6647c478bd9Sstevel@tonic-gate if ((&plat_shared_i2c_enter != NULL) && (dip != NULL)) { 6657c478bd9Sstevel@tonic-gate plat_shared_i2c_enter(i2c->pcf8584_dip); 6667c478bd9Sstevel@tonic-gate } 6677c478bd9Sstevel@tonic-gate 6687c478bd9Sstevel@tonic-gate mutex_enter(&i2c->pcf8584_imutex); 6697c478bd9Sstevel@tonic-gate i2c->pcf8584_cur_tran = tp; 6707c478bd9Sstevel@tonic-gate i2c->pcf8584_cur_dip = dip; 6717c478bd9Sstevel@tonic-gate mutex_exit(&i2c->pcf8584_imutex); 6727c478bd9Sstevel@tonic-gate } 6737c478bd9Sstevel@tonic-gate 6747c478bd9Sstevel@tonic-gate /* 6757c478bd9Sstevel@tonic-gate * pcf8584_release() is called to release a hold made by pcf8584_acquire(). 6767c478bd9Sstevel@tonic-gate */ 6777c478bd9Sstevel@tonic-gate static void 6787c478bd9Sstevel@tonic-gate pcf8584_release(pcf8584_t *i2c, boolean_t force) 6797c478bd9Sstevel@tonic-gate { 6807c478bd9Sstevel@tonic-gate if (force) { 6817c478bd9Sstevel@tonic-gate i2c->pcf8584_busy = 0; 6827c478bd9Sstevel@tonic-gate i2c->pcf8584_cur_tran = NULL; 6837c478bd9Sstevel@tonic-gate i2c->pcf8584_cur_dip = NULL; 6847c478bd9Sstevel@tonic-gate i2c->pcf8584_cur_status = PCF8584_TRANSFER_OVER; 6857c478bd9Sstevel@tonic-gate cv_signal(&i2c->pcf8584_cv); 6867c478bd9Sstevel@tonic-gate return; 6877c478bd9Sstevel@tonic-gate } 6887c478bd9Sstevel@tonic-gate 6897c478bd9Sstevel@tonic-gate mutex_enter(&i2c->pcf8584_imutex); 6907c478bd9Sstevel@tonic-gate i2c->pcf8584_busy = 0; 6917c478bd9Sstevel@tonic-gate i2c->pcf8584_cur_tran = NULL; 6927c478bd9Sstevel@tonic-gate cv_signal(&i2c->pcf8584_cv); 6937c478bd9Sstevel@tonic-gate mutex_exit(&i2c->pcf8584_imutex); 6947c478bd9Sstevel@tonic-gate 6957c478bd9Sstevel@tonic-gate if ((&plat_shared_i2c_exit != NULL) && (i2c->pcf8584_cur_dip != NULL)) { 6967c478bd9Sstevel@tonic-gate plat_shared_i2c_exit(i2c->pcf8584_dip); 6977c478bd9Sstevel@tonic-gate } 6987c478bd9Sstevel@tonic-gate } 6997c478bd9Sstevel@tonic-gate 7007c478bd9Sstevel@tonic-gate /* 7017c478bd9Sstevel@tonic-gate * if pcf8584_b_reg exists, it means the current bus controller signals 7027c478bd9Sstevel@tonic-gate * are multiplexed into more than a single bus. Select the bus needed 7037c478bd9Sstevel@tonic-gate * by writing to the mux register. 7047c478bd9Sstevel@tonic-gate */ 7057c478bd9Sstevel@tonic-gate static void 7067c478bd9Sstevel@tonic-gate pcf8584_select_bus(pcf8584_t *i2c) 7077c478bd9Sstevel@tonic-gate { 7087c478bd9Sstevel@tonic-gate int bus; 7097c478bd9Sstevel@tonic-gate pcf8584_ppvt_t *ppvt; 7107c478bd9Sstevel@tonic-gate 7117c478bd9Sstevel@tonic-gate /* 7127c478bd9Sstevel@tonic-gate * The existence of pcf8584_b_reg means the bus registers 7137c478bd9Sstevel@tonic-gate * are multiplexed. 7147c478bd9Sstevel@tonic-gate */ 7157c478bd9Sstevel@tonic-gate 7167c478bd9Sstevel@tonic-gate PCF8584_DDB(pcf8584_print(PRT_SELECT, "bus multiplex: %X\n", 7177c478bd9Sstevel@tonic-gate i2c->pcf8584_b_reg)); 7187c478bd9Sstevel@tonic-gate if (i2c->pcf8584_b_reg != NULL) { 7197c478bd9Sstevel@tonic-gate ppvt = ddi_get_parent_data(i2c->pcf8584_cur_dip); 7207c478bd9Sstevel@tonic-gate 7217c478bd9Sstevel@tonic-gate bus = ppvt->pcf8584_ppvt_bus; 7227c478bd9Sstevel@tonic-gate 7237c478bd9Sstevel@tonic-gate PCF8584_DDB(pcf8584_print(PRT_SELECT, 7247c478bd9Sstevel@tonic-gate "transmitting bus number %d\n", bus)); 7257c478bd9Sstevel@tonic-gate 7267c478bd9Sstevel@tonic-gate ddi_put8(i2c->pcf8584_b_rhandle, i2c->pcf8584_b_reg, bus); 7277c478bd9Sstevel@tonic-gate } 7287c478bd9Sstevel@tonic-gate } 7297c478bd9Sstevel@tonic-gate 7307c478bd9Sstevel@tonic-gate /* 7317c478bd9Sstevel@tonic-gate * pcf8584_type_to_state() converts a transfer type to the 7327c478bd9Sstevel@tonic-gate * next state of the I2C state machine based on the requested 7337c478bd9Sstevel@tonic-gate * transfer type. 7347c478bd9Sstevel@tonic-gate */ 7357c478bd9Sstevel@tonic-gate static enum tran_state 7367c478bd9Sstevel@tonic-gate pcf8584_type_to_state(int i2c_flags) 7377c478bd9Sstevel@tonic-gate { 7387c478bd9Sstevel@tonic-gate switch (i2c_flags) { 7397c478bd9Sstevel@tonic-gate case I2C_WR: 7407c478bd9Sstevel@tonic-gate 7417c478bd9Sstevel@tonic-gate return (TRAN_STATE_WR); 7427c478bd9Sstevel@tonic-gate case I2C_RD: 7437c478bd9Sstevel@tonic-gate 7447c478bd9Sstevel@tonic-gate return (TRAN_STATE_DUMMY_RD); 7457c478bd9Sstevel@tonic-gate case I2C_WR_RD: 7467c478bd9Sstevel@tonic-gate 7477c478bd9Sstevel@tonic-gate return (TRAN_STATE_WR_RD); 7487c478bd9Sstevel@tonic-gate } 7497c478bd9Sstevel@tonic-gate /*NOTREACHED*/ 750f47a9c50Smathue return (TRAN_STATE_NULL); 7517c478bd9Sstevel@tonic-gate } 7527c478bd9Sstevel@tonic-gate 7537c478bd9Sstevel@tonic-gate /* 7547c478bd9Sstevel@tonic-gate * pcf8584_put_s1() writes out cmd to register S1. 7557c478bd9Sstevel@tonic-gate */ 7567c478bd9Sstevel@tonic-gate static void 7577c478bd9Sstevel@tonic-gate pcf8584_put_s1(pcf8584_t *i2c, char cmd) 7587c478bd9Sstevel@tonic-gate { 7597c478bd9Sstevel@tonic-gate ddi_acc_handle_t hp = i2c->pcf8584_rhandle; 7607c478bd9Sstevel@tonic-gate pcf8584_regs_t *rp = &i2c->pcf8584_regs; 7617c478bd9Sstevel@tonic-gate 7627c478bd9Sstevel@tonic-gate ddi_put8(hp, rp->pcf8584_regs_s1, cmd); 763afd7fd7bSosaeed PCF8584_IMPL_DELAY(i2c->pcf8584_impl_type, 764afd7fd7bSosaeed i2c->pcf8584_impl_delay); 7657c478bd9Sstevel@tonic-gate /* 7667c478bd9Sstevel@tonic-gate * read status to make sure write is flushed 7677c478bd9Sstevel@tonic-gate */ 7687c478bd9Sstevel@tonic-gate (void) ddi_get8(hp, rp->pcf8584_regs_s1); 769afd7fd7bSosaeed PCF8584_IMPL_DELAY(i2c->pcf8584_impl_type, 770afd7fd7bSosaeed i2c->pcf8584_impl_delay); 7717c478bd9Sstevel@tonic-gate } 7727c478bd9Sstevel@tonic-gate 7737c478bd9Sstevel@tonic-gate /* 7747c478bd9Sstevel@tonic-gate * pcf8584_put_s0() writes out data to register S0. 7757c478bd9Sstevel@tonic-gate */ 7767c478bd9Sstevel@tonic-gate static void 7777c478bd9Sstevel@tonic-gate pcf8584_put_s0(pcf8584_t *i2c, char data) 7787c478bd9Sstevel@tonic-gate { 7797c478bd9Sstevel@tonic-gate ddi_acc_handle_t hp = i2c->pcf8584_rhandle; 7807c478bd9Sstevel@tonic-gate pcf8584_regs_t *rp = &i2c->pcf8584_regs; 7817c478bd9Sstevel@tonic-gate 7827c478bd9Sstevel@tonic-gate ddi_put8(hp, rp->pcf8584_regs_s0, data); 783afd7fd7bSosaeed PCF8584_IMPL_DELAY(i2c->pcf8584_impl_type, 784afd7fd7bSosaeed i2c->pcf8584_impl_delay); 7857c478bd9Sstevel@tonic-gate /* 7867c478bd9Sstevel@tonic-gate * read status to make sure write is flushed 7877c478bd9Sstevel@tonic-gate */ 7887c478bd9Sstevel@tonic-gate (void) ddi_get8(hp, rp->pcf8584_regs_s1); 789afd7fd7bSosaeed PCF8584_IMPL_DELAY(i2c->pcf8584_impl_type, 790afd7fd7bSosaeed i2c->pcf8584_impl_delay); 7917c478bd9Sstevel@tonic-gate } 7927c478bd9Sstevel@tonic-gate 7937c478bd9Sstevel@tonic-gate /* 7947c478bd9Sstevel@tonic-gate * pcf8584_get_s0() reads from register S0. 7957c478bd9Sstevel@tonic-gate */ 7967c478bd9Sstevel@tonic-gate static uint8_t 7977c478bd9Sstevel@tonic-gate pcf8584_get_s0(pcf8584_t *i2c) 7987c478bd9Sstevel@tonic-gate { 7997c478bd9Sstevel@tonic-gate ddi_acc_handle_t hp = i2c->pcf8584_rhandle; 8007c478bd9Sstevel@tonic-gate pcf8584_regs_t *rp = &i2c->pcf8584_regs; 801afd7fd7bSosaeed uint8_t s0; 8027c478bd9Sstevel@tonic-gate 803afd7fd7bSosaeed s0 = ddi_get8(hp, rp->pcf8584_regs_s0); 804afd7fd7bSosaeed PCF8584_IMPL_DELAY(i2c->pcf8584_impl_type, 805afd7fd7bSosaeed i2c->pcf8584_impl_delay); 806afd7fd7bSosaeed 807afd7fd7bSosaeed return (s0); 8087c478bd9Sstevel@tonic-gate } 8097c478bd9Sstevel@tonic-gate 8107c478bd9Sstevel@tonic-gate /* 8117c478bd9Sstevel@tonic-gate * pcf8584_get_s1() reads from register S1. 8127c478bd9Sstevel@tonic-gate */ 8137c478bd9Sstevel@tonic-gate static uint8_t 8147c478bd9Sstevel@tonic-gate pcf8584_get_s1(pcf8584_t *i2c) 8157c478bd9Sstevel@tonic-gate { 8167c478bd9Sstevel@tonic-gate ddi_acc_handle_t hp = i2c->pcf8584_rhandle; 8177c478bd9Sstevel@tonic-gate pcf8584_regs_t *rp = &i2c->pcf8584_regs; 818afd7fd7bSosaeed uint8_t s1; 8197c478bd9Sstevel@tonic-gate 820afd7fd7bSosaeed s1 = ddi_get8(hp, rp->pcf8584_regs_s1); 821afd7fd7bSosaeed PCF8584_IMPL_DELAY(i2c->pcf8584_impl_type, 822afd7fd7bSosaeed i2c->pcf8584_impl_delay); 823afd7fd7bSosaeed 824afd7fd7bSosaeed return (s1); 8257c478bd9Sstevel@tonic-gate } 8267c478bd9Sstevel@tonic-gate 8277c478bd9Sstevel@tonic-gate /* 8287c478bd9Sstevel@tonic-gate * If the previous transaction was a write, the stop 8297c478bd9Sstevel@tonic-gate * bit may not make it out on the wire before 8307c478bd9Sstevel@tonic-gate * the next transaction startes. And unfortunately, there 8317c478bd9Sstevel@tonic-gate * is no interrupt after the stop bit is written, so this 8327c478bd9Sstevel@tonic-gate * function will poll to make sure the BBC is ready. 8337c478bd9Sstevel@tonic-gate */ 8347c478bd9Sstevel@tonic-gate static int 8357c478bd9Sstevel@tonic-gate pcf8584_bbn_ready(pcf8584_t *i2c) 8367c478bd9Sstevel@tonic-gate { 8377c478bd9Sstevel@tonic-gate uint8_t s1; 8387c478bd9Sstevel@tonic-gate int usecwaits = 0; 8397c478bd9Sstevel@tonic-gate 8407c478bd9Sstevel@tonic-gate s1 = pcf8584_get_s1(i2c); 8417c478bd9Sstevel@tonic-gate 8427c478bd9Sstevel@tonic-gate while ((s1 & S1_BBN) == 0) { 8437c478bd9Sstevel@tonic-gate 8447c478bd9Sstevel@tonic-gate if (usecwaits++ == 100) { 8457c478bd9Sstevel@tonic-gate /* Try initializing the bus */ 8467c478bd9Sstevel@tonic-gate pcf8584_monitor_mode(i2c); 8477c478bd9Sstevel@tonic-gate pcf8584_put_s1(i2c, S1_STOP); 8487c478bd9Sstevel@tonic-gate delay(1); 8497c478bd9Sstevel@tonic-gate pcf8584_init(i2c); 8507c478bd9Sstevel@tonic-gate (void) pcf8584_get_s0(i2c); 8517c478bd9Sstevel@tonic-gate s1 = pcf8584_get_s1(i2c); 8527c478bd9Sstevel@tonic-gate if (s1 & S1_BBN) { 8537c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, 8547c478bd9Sstevel@tonic-gate "!%s: cleared bus busy. addr=0x%x", 8557c478bd9Sstevel@tonic-gate i2c->pcf8584_name, 8567c478bd9Sstevel@tonic-gate pcf8584_dip_to_addr(i2c->pcf8584_cur_dip)); 8577c478bd9Sstevel@tonic-gate 8587c478bd9Sstevel@tonic-gate return (I2C_SUCCESS); 8597c478bd9Sstevel@tonic-gate } else { 8607c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, 8617c478bd9Sstevel@tonic-gate "!%s bus busy after init addr=0x%x", 8627c478bd9Sstevel@tonic-gate i2c->pcf8584_name, 8637c478bd9Sstevel@tonic-gate pcf8584_dip_to_addr(i2c->pcf8584_cur_dip)); 8647c478bd9Sstevel@tonic-gate 8657c478bd9Sstevel@tonic-gate return (I2C_FAILURE); 8667c478bd9Sstevel@tonic-gate } 8677c478bd9Sstevel@tonic-gate } 8687c478bd9Sstevel@tonic-gate drv_usecwait(1); 8697c478bd9Sstevel@tonic-gate s1 = pcf8584_get_s1(i2c); 8707c478bd9Sstevel@tonic-gate } 8717c478bd9Sstevel@tonic-gate 8727c478bd9Sstevel@tonic-gate return (I2C_SUCCESS); 8737c478bd9Sstevel@tonic-gate } 8747c478bd9Sstevel@tonic-gate 8757c478bd9Sstevel@tonic-gate static int 8767c478bd9Sstevel@tonic-gate pcf8584_error(int status, uint8_t rdwr, pcf8584_t *i2c) 8777c478bd9Sstevel@tonic-gate { 8787c478bd9Sstevel@tonic-gate int addr = pcf8584_dip_to_addr(i2c->pcf8584_cur_dip); 8797c478bd9Sstevel@tonic-gate pcf8584_regs_t *rp = &i2c->pcf8584_regs; 8807c478bd9Sstevel@tonic-gate 8817c478bd9Sstevel@tonic-gate if (status & S1_BER) { 8827c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, 883f47a9c50Smathue "!%s bus error; Controller = 0x%p " 8847c478bd9Sstevel@tonic-gate " addr = 0x%x", i2c->pcf8584_name, 885f47a9c50Smathue (void *)rp->pcf8584_regs_s1, addr); 8867c478bd9Sstevel@tonic-gate pcf8584_init(i2c); 8877c478bd9Sstevel@tonic-gate 8887c478bd9Sstevel@tonic-gate return (I2C_FAILURE); 8897c478bd9Sstevel@tonic-gate } else if (status & S1_LAB) { 8907c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "!%s lost arbitration; Controller =" 891f47a9c50Smathue " 0x%p addr = 0x%x", i2c->pcf8584_name, 892f47a9c50Smathue (void *)rp->pcf8584_regs_s1, addr); 8937c478bd9Sstevel@tonic-gate pcf8584_init(i2c); 8947c478bd9Sstevel@tonic-gate 8957c478bd9Sstevel@tonic-gate return (I2C_FAILURE); 8967c478bd9Sstevel@tonic-gate } else if ((status & S1_LRB) && (rdwr == I2C_WR)) { 8977c478bd9Sstevel@tonic-gate /* 8987c478bd9Sstevel@tonic-gate * No error logged here, because this may be benign. 8997c478bd9Sstevel@tonic-gate * Cf. the "Alert Response Address" feature of SMBUS. 9007c478bd9Sstevel@tonic-gate */ 9017c478bd9Sstevel@tonic-gate pcf8584_put_s1(i2c, S1_STOP); 9027c478bd9Sstevel@tonic-gate 9037c478bd9Sstevel@tonic-gate return (I2C_FAILURE); 9047c478bd9Sstevel@tonic-gate } 9057c478bd9Sstevel@tonic-gate 9067c478bd9Sstevel@tonic-gate return (I2C_SUCCESS); 9077c478bd9Sstevel@tonic-gate } 9087c478bd9Sstevel@tonic-gate 9097c478bd9Sstevel@tonic-gate static void 9107c478bd9Sstevel@tonic-gate pcf8584_monitor_mode(pcf8584_t *i2c) 9117c478bd9Sstevel@tonic-gate { 9127c478bd9Sstevel@tonic-gate pcf8584_put_s1(i2c, S1_PIN); 9137c478bd9Sstevel@tonic-gate 9147c478bd9Sstevel@tonic-gate pcf8584_put_s0(i2c, MONITOR_ADDRESS); 9157c478bd9Sstevel@tonic-gate } 9167c478bd9Sstevel@tonic-gate 9177c478bd9Sstevel@tonic-gate static int 9187c478bd9Sstevel@tonic-gate pcf8584_initchild(dev_info_t *cdip) 9197c478bd9Sstevel@tonic-gate { 9207c478bd9Sstevel@tonic-gate int32_t cell_size; 9217c478bd9Sstevel@tonic-gate int len; 9227c478bd9Sstevel@tonic-gate int32_t regs[2]; 9237c478bd9Sstevel@tonic-gate int err; 9247c478bd9Sstevel@tonic-gate pcf8584_ppvt_t *ppvt; 9257c478bd9Sstevel@tonic-gate char name[30]; 9267c478bd9Sstevel@tonic-gate 9277c478bd9Sstevel@tonic-gate PCF8584_DDB(pcf8584_print(PRT_INIT, "pcf8584_initchild enter: %s\n", 9287c478bd9Sstevel@tonic-gate ddi_node_name(cdip))); 9297c478bd9Sstevel@tonic-gate 9307c478bd9Sstevel@tonic-gate ppvt = kmem_alloc(sizeof (pcf8584_ppvt_t), KM_SLEEP); 9317c478bd9Sstevel@tonic-gate 9327c478bd9Sstevel@tonic-gate len = sizeof (cell_size); 9337c478bd9Sstevel@tonic-gate err = ddi_getlongprop_buf(DDI_DEV_T_ANY, cdip, 9347c478bd9Sstevel@tonic-gate DDI_PROP_CANSLEEP, "#address-cells", 9357c478bd9Sstevel@tonic-gate (caddr_t)&cell_size, &len); 9367c478bd9Sstevel@tonic-gate if (err != DDI_PROP_SUCCESS || len != sizeof (cell_size)) { 9377c478bd9Sstevel@tonic-gate 9387c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 9397c478bd9Sstevel@tonic-gate } 9407c478bd9Sstevel@tonic-gate 9417c478bd9Sstevel@tonic-gate len = sizeof (regs); 9427c478bd9Sstevel@tonic-gate err = ddi_getlongprop_buf(DDI_DEV_T_ANY, cdip, 9437c478bd9Sstevel@tonic-gate DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, 9447c478bd9Sstevel@tonic-gate "reg", (caddr_t)regs, &len); 9457c478bd9Sstevel@tonic-gate if (err != DDI_PROP_SUCCESS || 9467c478bd9Sstevel@tonic-gate len != (cell_size * sizeof (int32_t))) { 9477c478bd9Sstevel@tonic-gate 9487c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 9497c478bd9Sstevel@tonic-gate } 9507c478bd9Sstevel@tonic-gate 9517c478bd9Sstevel@tonic-gate if (cell_size == 1) { 9527c478bd9Sstevel@tonic-gate ppvt->pcf8584_ppvt_addr = regs[0]; 9537c478bd9Sstevel@tonic-gate (void) sprintf(name, "%x", regs[0]); 9547c478bd9Sstevel@tonic-gate } else if (cell_size == 2) { 9557c478bd9Sstevel@tonic-gate ppvt->pcf8584_ppvt_bus = regs[0]; 9567c478bd9Sstevel@tonic-gate ppvt->pcf8584_ppvt_addr = regs[1]; 9577c478bd9Sstevel@tonic-gate (void) sprintf(name, "%x,%x", regs[0], regs[1]); 9587c478bd9Sstevel@tonic-gate } else { 9597c478bd9Sstevel@tonic-gate 9607c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 9617c478bd9Sstevel@tonic-gate } 9627c478bd9Sstevel@tonic-gate 9637c478bd9Sstevel@tonic-gate ddi_set_parent_data(cdip, ppvt); 9647c478bd9Sstevel@tonic-gate 9657c478bd9Sstevel@tonic-gate ddi_set_name_addr(cdip, name); 9667c478bd9Sstevel@tonic-gate 9677c478bd9Sstevel@tonic-gate PCF8584_DDB(pcf8584_print(PRT_INIT, 9687c478bd9Sstevel@tonic-gate "pcf8584_initchild SUCCESS: %s\n", ddi_node_name(cdip))); 9697c478bd9Sstevel@tonic-gate 9707c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 9717c478bd9Sstevel@tonic-gate } 9727c478bd9Sstevel@tonic-gate 9737c478bd9Sstevel@tonic-gate static void 9747c478bd9Sstevel@tonic-gate pcf8584_uninitchild(dev_info_t *cdip) 9757c478bd9Sstevel@tonic-gate { 9767c478bd9Sstevel@tonic-gate pcf8584_ppvt_t *ppvt; 9777c478bd9Sstevel@tonic-gate 9787c478bd9Sstevel@tonic-gate ppvt = ddi_get_parent_data(cdip); 9797c478bd9Sstevel@tonic-gate kmem_free(ppvt, sizeof (pcf8584_ppvt_t)); 9807c478bd9Sstevel@tonic-gate 9817c478bd9Sstevel@tonic-gate ddi_set_parent_data(cdip, NULL); 9827c478bd9Sstevel@tonic-gate ddi_set_name_addr(cdip, NULL); 9837c478bd9Sstevel@tonic-gate 9847c478bd9Sstevel@tonic-gate PCF8584_DDB(pcf8584_print(PRT_INIT, "i2c_uninitchild: %s\n", 9857c478bd9Sstevel@tonic-gate ddi_node_name(cdip))); 9867c478bd9Sstevel@tonic-gate } 9877c478bd9Sstevel@tonic-gate 9887c478bd9Sstevel@tonic-gate static void 9897c478bd9Sstevel@tonic-gate pcf8584_init(pcf8584_t *i2c) 9907c478bd9Sstevel@tonic-gate { 9917c478bd9Sstevel@tonic-gate uint8_t clk_div = 0x1C; 9927c478bd9Sstevel@tonic-gate 9937c478bd9Sstevel@tonic-gate pcf8584_put_s1(i2c, S1_PIN); 9947c478bd9Sstevel@tonic-gate 9957c478bd9Sstevel@tonic-gate pcf8584_put_s0(i2c, S0_OWN); 9967c478bd9Sstevel@tonic-gate 9977c478bd9Sstevel@tonic-gate pcf8584_put_s1(i2c, S1_PIN | S1_ES1); 9987c478bd9Sstevel@tonic-gate 9997c478bd9Sstevel@tonic-gate /* 10007c478bd9Sstevel@tonic-gate * The default case is to set the clock divisor to the least common 10017c478bd9Sstevel@tonic-gate * denominator to avoid over clocking the I2C bus. Assume that 10027c478bd9Sstevel@tonic-gate * BBC based systems are using the Safari clock as input, so select 10037c478bd9Sstevel@tonic-gate * the clk divisor based on it. 10047c478bd9Sstevel@tonic-gate */ 1005afd7fd7bSosaeed if (i2c->pcf8584_impl_type == BBC) { 10067c478bd9Sstevel@tonic-gate dev_info_t *root_node; 10077c478bd9Sstevel@tonic-gate int clock_freq; 10087c478bd9Sstevel@tonic-gate root_node = ddi_root_node(); 10097c478bd9Sstevel@tonic-gate clock_freq = ddi_prop_get_int(DDI_DEV_T_ANY, root_node, 10107c478bd9Sstevel@tonic-gate DDI_PROP_DONTPASS, "clock-frequency", 0); 10117c478bd9Sstevel@tonic-gate 10127c478bd9Sstevel@tonic-gate if (clock_freq < 105000000) { 10137c478bd9Sstevel@tonic-gate clk_div = 0x00; 10147c478bd9Sstevel@tonic-gate } else if (clock_freq < 160000000) { 10157c478bd9Sstevel@tonic-gate clk_div = 0x10; 10167c478bd9Sstevel@tonic-gate } else { 10177c478bd9Sstevel@tonic-gate clk_div = 0x1C; 10187c478bd9Sstevel@tonic-gate } 10197c478bd9Sstevel@tonic-gate } 10207c478bd9Sstevel@tonic-gate 10217c478bd9Sstevel@tonic-gate /* set I2C clock speed */ 10227c478bd9Sstevel@tonic-gate pcf8584_put_s0(i2c, clk_div); 10237c478bd9Sstevel@tonic-gate 10247c478bd9Sstevel@tonic-gate pcf8584_put_s1(i2c, S1_PIN | S1_ESO | S1_ACK); 10257c478bd9Sstevel@tonic-gate 10267c478bd9Sstevel@tonic-gate /* 10277c478bd9Sstevel@tonic-gate * Multi-Master: Wait for a period of time equal to the 10287c478bd9Sstevel@tonic-gate * longest I2C message. This accounts for the case 10297c478bd9Sstevel@tonic-gate * where multiple controllers and, if this particular one 10307c478bd9Sstevel@tonic-gate * is "lagging", misses the BB(bus busy) condition. 10317c478bd9Sstevel@tonic-gate * We wait 200 ms since the longest transaction at this time 10327c478bd9Sstevel@tonic-gate * on the i2c bus is a 256 byte read from the seprom which takes 10337c478bd9Sstevel@tonic-gate * about 75 ms. Some additional buffer does no harm to the driver. 10347c478bd9Sstevel@tonic-gate */ 10357c478bd9Sstevel@tonic-gate 10367c478bd9Sstevel@tonic-gate delay(drv_usectohz(PCF8584_INIT_WAIT)); 10377c478bd9Sstevel@tonic-gate } 10387c478bd9Sstevel@tonic-gate 10397c478bd9Sstevel@tonic-gate /* 10407c478bd9Sstevel@tonic-gate * pcf8584_setup_regs() is called to map in registers specific to 10417c478bd9Sstevel@tonic-gate * the pcf8584. 10427c478bd9Sstevel@tonic-gate */ 10437c478bd9Sstevel@tonic-gate static int 10447c478bd9Sstevel@tonic-gate pcf8584_setup_regs(dev_info_t *dip, pcf8584_t *i2c) 10457c478bd9Sstevel@tonic-gate { 10467c478bd9Sstevel@tonic-gate int nregs; 10477c478bd9Sstevel@tonic-gate ddi_device_acc_attr_t attr; 10487c478bd9Sstevel@tonic-gate caddr_t reg_base; 10497c478bd9Sstevel@tonic-gate 10507c478bd9Sstevel@tonic-gate attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 10517c478bd9Sstevel@tonic-gate attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; 10527c478bd9Sstevel@tonic-gate attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 10537c478bd9Sstevel@tonic-gate 10547c478bd9Sstevel@tonic-gate if (ddi_dev_nregs(dip, &nregs) != DDI_SUCCESS) { 10557c478bd9Sstevel@tonic-gate 10567c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 10577c478bd9Sstevel@tonic-gate } 10587c478bd9Sstevel@tonic-gate 10597c478bd9Sstevel@tonic-gate if (ddi_regs_map_setup(dip, 0, 10607c478bd9Sstevel@tonic-gate (caddr_t *)®_base, 0, 0, &attr, 10617c478bd9Sstevel@tonic-gate &i2c->pcf8584_rhandle) != DDI_SUCCESS) { 10627c478bd9Sstevel@tonic-gate 10637c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 10647c478bd9Sstevel@tonic-gate } 10657c478bd9Sstevel@tonic-gate 10667c478bd9Sstevel@tonic-gate /* 10677c478bd9Sstevel@tonic-gate * If i2c controller is on BBC, then s1 comes before s0. 10687c478bd9Sstevel@tonic-gate */ 1069afd7fd7bSosaeed if (i2c->pcf8584_impl_type == BBC) { 10707c478bd9Sstevel@tonic-gate i2c->pcf8584_regs.pcf8584_regs_s0 = 10717c478bd9Sstevel@tonic-gate (uint8_t *)®_base[1]; 10727c478bd9Sstevel@tonic-gate i2c->pcf8584_regs.pcf8584_regs_s1 = 10737c478bd9Sstevel@tonic-gate (uint8_t *)®_base[0]; 10747c478bd9Sstevel@tonic-gate } else { 10757c478bd9Sstevel@tonic-gate i2c->pcf8584_regs.pcf8584_regs_s0 = 10767c478bd9Sstevel@tonic-gate (uint8_t *)®_base[0]; 10777c478bd9Sstevel@tonic-gate i2c->pcf8584_regs.pcf8584_regs_s1 = 10787c478bd9Sstevel@tonic-gate (uint8_t *)®_base[1]; 10797c478bd9Sstevel@tonic-gate } 10807c478bd9Sstevel@tonic-gate 10817c478bd9Sstevel@tonic-gate if (nregs > 1) { 10827c478bd9Sstevel@tonic-gate if (ddi_regs_map_setup(dip, 10837c478bd9Sstevel@tonic-gate 1, (caddr_t *)&i2c->pcf8584_b_reg, 10847c478bd9Sstevel@tonic-gate 0, 0, &attr, &i2c->pcf8584_b_rhandle) != 10857c478bd9Sstevel@tonic-gate DDI_SUCCESS) { 10867c478bd9Sstevel@tonic-gate 10877c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 10887c478bd9Sstevel@tonic-gate } 10897c478bd9Sstevel@tonic-gate } 10907c478bd9Sstevel@tonic-gate 10917c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 10927c478bd9Sstevel@tonic-gate } 10937c478bd9Sstevel@tonic-gate 10947c478bd9Sstevel@tonic-gate /* 10957c478bd9Sstevel@tonic-gate * pcf8584_free_regs() frees any registers previously 10967c478bd9Sstevel@tonic-gate * allocated. 10977c478bd9Sstevel@tonic-gate */ 10987c478bd9Sstevel@tonic-gate static void 10997c478bd9Sstevel@tonic-gate pcf8584_free_regs(pcf8584_t *i2c) 11007c478bd9Sstevel@tonic-gate { 11017c478bd9Sstevel@tonic-gate if (i2c->pcf8584_regs.pcf8584_regs_s0 != NULL) { 11027c478bd9Sstevel@tonic-gate ddi_regs_map_free(&i2c->pcf8584_rhandle); 11037c478bd9Sstevel@tonic-gate } 11047c478bd9Sstevel@tonic-gate if (i2c->pcf8584_b_reg != NULL) { 11057c478bd9Sstevel@tonic-gate ddi_regs_map_free(&i2c->pcf8584_b_rhandle); 11067c478bd9Sstevel@tonic-gate } 11077c478bd9Sstevel@tonic-gate } 11087c478bd9Sstevel@tonic-gate 11097c478bd9Sstevel@tonic-gate static void 11107c478bd9Sstevel@tonic-gate pcf8584_reportdev(dev_info_t *dip, dev_info_t *rdip) 11117c478bd9Sstevel@tonic-gate { 11127c478bd9Sstevel@tonic-gate pcf8584_ppvt_t *ppvt; 11137c478bd9Sstevel@tonic-gate 11147c478bd9Sstevel@tonic-gate ppvt = ddi_get_parent_data(rdip); 11157c478bd9Sstevel@tonic-gate 11167c478bd9Sstevel@tonic-gate cmn_err(CE_CONT, "?%s%d at %s%d: addr 0x%x", 11177c478bd9Sstevel@tonic-gate ddi_driver_name(rdip), ddi_get_instance(rdip), 11187c478bd9Sstevel@tonic-gate ddi_driver_name(dip), ddi_get_instance(dip), 11197c478bd9Sstevel@tonic-gate ppvt->pcf8584_ppvt_addr); 11207c478bd9Sstevel@tonic-gate } 11217c478bd9Sstevel@tonic-gate 11227c478bd9Sstevel@tonic-gate /* 11237c478bd9Sstevel@tonic-gate * i2_nexus_dip_to_addr() takes a dip and returns an I2C address. 11247c478bd9Sstevel@tonic-gate */ 11257c478bd9Sstevel@tonic-gate static int 11267c478bd9Sstevel@tonic-gate pcf8584_dip_to_addr(dev_info_t *dip) 11277c478bd9Sstevel@tonic-gate { 11287c478bd9Sstevel@tonic-gate pcf8584_ppvt_t *ppvt; 11297c478bd9Sstevel@tonic-gate 11307c478bd9Sstevel@tonic-gate ppvt = ddi_get_parent_data(dip); 11317c478bd9Sstevel@tonic-gate 11327c478bd9Sstevel@tonic-gate return (ppvt->pcf8584_ppvt_addr); 11337c478bd9Sstevel@tonic-gate } 11347c478bd9Sstevel@tonic-gate 11357c478bd9Sstevel@tonic-gate /* 11367c478bd9Sstevel@tonic-gate * pcf8584_intr() is the interrupt service routine registered during 11377c478bd9Sstevel@tonic-gate * attach, and remains registered even if the driver is in POLLED mode. So if 11387c478bd9Sstevel@tonic-gate * this is called from POLLED mode, it needs to return without doing 11397c478bd9Sstevel@tonic-gate * any work to prevent the I2C bus from entering an unknown state. 11407c478bd9Sstevel@tonic-gate */ 11417c478bd9Sstevel@tonic-gate static uint_t 11427c478bd9Sstevel@tonic-gate pcf8584_intr(caddr_t arg) 11437c478bd9Sstevel@tonic-gate { 11447c478bd9Sstevel@tonic-gate pcf8584_t *i2c = (pcf8584_t *)arg; 11457c478bd9Sstevel@tonic-gate uint8_t s1; 11467c478bd9Sstevel@tonic-gate 1147afd7fd7bSosaeed ASSERT(i2c->pcf8584_mode != PCF8584_POLL_MODE); 11487c478bd9Sstevel@tonic-gate PCF8584_DDB(pcf8584_print(PRT_INTR, "pcf8584_intr: enter\n")); 11497c478bd9Sstevel@tonic-gate 11507c478bd9Sstevel@tonic-gate mutex_enter(&i2c->pcf8584_imutex); 11517c478bd9Sstevel@tonic-gate 11527c478bd9Sstevel@tonic-gate /* 11537c478bd9Sstevel@tonic-gate * It is necessary to check both whether the hardware is interrupting 11547c478bd9Sstevel@tonic-gate * and that there is a current transaction for the bus in progress. 11557c478bd9Sstevel@tonic-gate * Checking just one but not the other will lead to a panic on xcal 11567c478bd9Sstevel@tonic-gate * since both controllers share the same ino, and also because OBP 11577c478bd9Sstevel@tonic-gate * shares a controller with the kernel even while the kernel is running. 11587c478bd9Sstevel@tonic-gate */ 11597c478bd9Sstevel@tonic-gate 11607c478bd9Sstevel@tonic-gate if (i2c->pcf8584_cur_tran == NULL) { 11617c478bd9Sstevel@tonic-gate mutex_exit(&i2c->pcf8584_imutex); 11627c478bd9Sstevel@tonic-gate 11637c478bd9Sstevel@tonic-gate return (DDI_INTR_UNCLAIMED); 11647c478bd9Sstevel@tonic-gate } 11657c478bd9Sstevel@tonic-gate 11667c478bd9Sstevel@tonic-gate 11677c478bd9Sstevel@tonic-gate s1 = pcf8584_get_s1(i2c); 11687c478bd9Sstevel@tonic-gate if (s1 & S1_PIN) { 11697c478bd9Sstevel@tonic-gate mutex_exit(&i2c->pcf8584_imutex); 11707c478bd9Sstevel@tonic-gate 11717c478bd9Sstevel@tonic-gate return (DDI_INTR_UNCLAIMED); 11727c478bd9Sstevel@tonic-gate } 11737c478bd9Sstevel@tonic-gate 11747c478bd9Sstevel@tonic-gate if (pcf8584_process(i2c, s1) == I2C_COMPLETE) { 11757c478bd9Sstevel@tonic-gate i2c->pcf8584_tran_state = TRAN_STATE_NULL; 11767c478bd9Sstevel@tonic-gate i2c->pcf8584_cur_status = PCF8584_TRANSFER_OVER; 11777c478bd9Sstevel@tonic-gate cv_signal(&i2c->pcf8584_icv); 11787c478bd9Sstevel@tonic-gate } else 11797c478bd9Sstevel@tonic-gate i2c->pcf8584_cur_status = PCF8584_TRANSFER_ON; 11807c478bd9Sstevel@tonic-gate 11817c478bd9Sstevel@tonic-gate mutex_exit(&i2c->pcf8584_imutex); 11827c478bd9Sstevel@tonic-gate 11837c478bd9Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 11847c478bd9Sstevel@tonic-gate } 11857c478bd9Sstevel@tonic-gate 11867c478bd9Sstevel@tonic-gate /* 11877c478bd9Sstevel@tonic-gate * Interrupt occurs after a byte is transmitted or received, indicating 11887c478bd9Sstevel@tonic-gate * the device is ready to be serviced. 11897c478bd9Sstevel@tonic-gate */ 11907c478bd9Sstevel@tonic-gate static int 11917c478bd9Sstevel@tonic-gate pcf8584_process(pcf8584_t *i2c, uint8_t s1) 11927c478bd9Sstevel@tonic-gate { 11937c478bd9Sstevel@tonic-gate i2c_transfer_t *tp = i2c->pcf8584_cur_tran; 11947c478bd9Sstevel@tonic-gate int addr = pcf8584_dip_to_addr(i2c->pcf8584_cur_dip); 11957c478bd9Sstevel@tonic-gate int dummy_read; 11967c478bd9Sstevel@tonic-gate 11977c478bd9Sstevel@tonic-gate ASSERT(i2c->pcf8584_tran_state != TRAN_STATE_NULL); 11987c478bd9Sstevel@tonic-gate 11997c478bd9Sstevel@tonic-gate switch (i2c->pcf8584_tran_state) { 12007c478bd9Sstevel@tonic-gate case TRAN_STATE_DUMMY_DATA: 12017c478bd9Sstevel@tonic-gate PCF8584_DDB(pcf8584_print(PRT_TRAN, 12027c478bd9Sstevel@tonic-gate "TRAN_STATE_DUMMY DATA: write dummy %x\n", DUMMY_DATA)); 12037c478bd9Sstevel@tonic-gate if (pcf8584_error(s1, I2C_RD, i2c) != I2C_SUCCESS) { 12047c478bd9Sstevel@tonic-gate tp->i2c_result = I2C_FAILURE; 12057c478bd9Sstevel@tonic-gate 12067c478bd9Sstevel@tonic-gate return (I2C_COMPLETE); 12077c478bd9Sstevel@tonic-gate } 12087c478bd9Sstevel@tonic-gate i2c->pcf8584_tran_state = TRAN_STATE_START; 12097c478bd9Sstevel@tonic-gate pcf8584_put_s0(i2c, DUMMY_DATA); 12107c478bd9Sstevel@tonic-gate 12117c478bd9Sstevel@tonic-gate return (I2C_PENDING); 12127c478bd9Sstevel@tonic-gate case TRAN_STATE_START: 12137c478bd9Sstevel@tonic-gate if (pcf8584_error(s1, I2C_RD, i2c) != I2C_SUCCESS) { 12147c478bd9Sstevel@tonic-gate PCF8584_DDB(pcf8584_print(PRT_TRAN, 12157c478bd9Sstevel@tonic-gate "TRAN_STATE_START failure\n")); 12167c478bd9Sstevel@tonic-gate tp->i2c_result = I2C_FAILURE; 12177c478bd9Sstevel@tonic-gate 12187c478bd9Sstevel@tonic-gate return (I2C_COMPLETE); 12197c478bd9Sstevel@tonic-gate } 12207c478bd9Sstevel@tonic-gate i2c->pcf8584_tran_state = 12217c478bd9Sstevel@tonic-gate pcf8584_type_to_state(tp->i2c_flags); 12227c478bd9Sstevel@tonic-gate 12237c478bd9Sstevel@tonic-gate /* Set read bit if this is a read transaction */ 12247c478bd9Sstevel@tonic-gate if (tp->i2c_flags == I2C_RD) { 12257c478bd9Sstevel@tonic-gate addr |= I2C_READ; 12267c478bd9Sstevel@tonic-gate } 1227afd7fd7bSosaeed if (i2c->pcf8584_mode == PCF8584_POLL_MODE) 1228afd7fd7bSosaeed pcf8584_put_s1(i2c, S1_START2); 1229afd7fd7bSosaeed else 12307c478bd9Sstevel@tonic-gate pcf8584_put_s1(i2c, S1_START2 | S1_ENI); 12317c478bd9Sstevel@tonic-gate pcf8584_put_s0(i2c, addr); 12327c478bd9Sstevel@tonic-gate PCF8584_DDB(pcf8584_print(PRT_TRAN, 12337c478bd9Sstevel@tonic-gate "TRAN_STATE_START: write addr: %x\n", addr)); 12347c478bd9Sstevel@tonic-gate 12357c478bd9Sstevel@tonic-gate return (I2C_PENDING); 12367c478bd9Sstevel@tonic-gate case TRAN_STATE_WR: 12377c478bd9Sstevel@tonic-gate 12387c478bd9Sstevel@tonic-gate if (pcf8584_error(s1, I2C_WR, i2c) != I2C_SUCCESS) { 12397c478bd9Sstevel@tonic-gate PCF8584_DDB(pcf8584_print(PRT_TRAN, 12407c478bd9Sstevel@tonic-gate "TRAN_STATE_WR failure\n")); 12417c478bd9Sstevel@tonic-gate tp->i2c_result = I2C_FAILURE; 12427c478bd9Sstevel@tonic-gate 12437c478bd9Sstevel@tonic-gate return (I2C_COMPLETE); 12447c478bd9Sstevel@tonic-gate } 12457c478bd9Sstevel@tonic-gate /* check to see if at end of buffer */ 12467c478bd9Sstevel@tonic-gate if (tp->i2c_w_resid == 0) { 12477c478bd9Sstevel@tonic-gate pcf8584_put_s1(i2c, S1_STOP); 12487c478bd9Sstevel@tonic-gate PCF8584_DDB(pcf8584_print(PRT_TRAN, 12497c478bd9Sstevel@tonic-gate "TRAN_STATE_WR: write STOP\n")); 12507c478bd9Sstevel@tonic-gate 12517c478bd9Sstevel@tonic-gate return (I2C_COMPLETE); 12527c478bd9Sstevel@tonic-gate } 12537c478bd9Sstevel@tonic-gate 12547c478bd9Sstevel@tonic-gate pcf8584_put_s0(i2c, tp->i2c_wbuf[tp->i2c_wlen - 12557c478bd9Sstevel@tonic-gate tp->i2c_w_resid--]); 12567c478bd9Sstevel@tonic-gate PCF8584_DDB(pcf8584_print(PRT_TRAN, 12577c478bd9Sstevel@tonic-gate "TRAN_STATE_WR: write data %x\n", 12587c478bd9Sstevel@tonic-gate tp->i2c_wbuf[tp->i2c_wlen - (tp->i2c_w_resid + 1)])); 12597c478bd9Sstevel@tonic-gate 12607c478bd9Sstevel@tonic-gate return (I2C_PENDING); 12617c478bd9Sstevel@tonic-gate case TRAN_STATE_DUMMY_RD: 12627c478bd9Sstevel@tonic-gate 12637c478bd9Sstevel@tonic-gate if (pcf8584_error(s1, I2C_WR, i2c) != I2C_SUCCESS) { 12647c478bd9Sstevel@tonic-gate tp->i2c_result = I2C_FAILURE; 12657c478bd9Sstevel@tonic-gate 12667c478bd9Sstevel@tonic-gate return (I2C_COMPLETE); 12677c478bd9Sstevel@tonic-gate } 12687c478bd9Sstevel@tonic-gate /* 12697c478bd9Sstevel@tonic-gate * The first read is always a dummy read, because reading S0 12707c478bd9Sstevel@tonic-gate * is what starts bit shifting and ACK on the I2c bus. 12717c478bd9Sstevel@tonic-gate * This byte is accessed during the next read, which starts 12727c478bd9Sstevel@tonic-gate * another 8 bit bus shift. 12737c478bd9Sstevel@tonic-gate * 12747c478bd9Sstevel@tonic-gate * special case for 1 byte reads: Clear the ACK bit 12757c478bd9Sstevel@tonic-gate * here since this read causes the last and only byte 12767c478bd9Sstevel@tonic-gate * to be sent on the I2C bus. 12777c478bd9Sstevel@tonic-gate */ 12787c478bd9Sstevel@tonic-gate if (tp->i2c_r_resid == 1) { 1279afd7fd7bSosaeed if (i2c->pcf8584_mode == PCF8584_POLL_MODE) 1280afd7fd7bSosaeed pcf8584_put_s1(i2c, S1_ESO); 1281afd7fd7bSosaeed else 12827c478bd9Sstevel@tonic-gate pcf8584_put_s1(i2c, S1_ESO | S1_ENI); 12837c478bd9Sstevel@tonic-gate } 12847c478bd9Sstevel@tonic-gate 12857c478bd9Sstevel@tonic-gate /* 12867c478bd9Sstevel@tonic-gate * dummy read 12877c478bd9Sstevel@tonic-gate */ 12887c478bd9Sstevel@tonic-gate dummy_read = pcf8584_get_s0(i2c); 12897c478bd9Sstevel@tonic-gate 12907c478bd9Sstevel@tonic-gate i2c->pcf8584_tran_state = TRAN_STATE_RD; 12917c478bd9Sstevel@tonic-gate PCF8584_DDB(pcf8584_print(PRT_TRAN, 12927c478bd9Sstevel@tonic-gate "TRAN_STATE_DUMMY_RD: read dummy %d\n", dummy_read)); 12937c478bd9Sstevel@tonic-gate 12947c478bd9Sstevel@tonic-gate return (I2C_PENDING); 12957c478bd9Sstevel@tonic-gate case TRAN_STATE_RD: 12967c478bd9Sstevel@tonic-gate if (pcf8584_error(s1, I2C_RD, i2c) != I2C_SUCCESS) { 12977c478bd9Sstevel@tonic-gate tp->i2c_result = I2C_FAILURE; 12987c478bd9Sstevel@tonic-gate PCF8584_DDB(pcf8584_print(PRT_TRAN, 12997c478bd9Sstevel@tonic-gate "TRAN_STATE_RD failure\n")); 13007c478bd9Sstevel@tonic-gate 13017c478bd9Sstevel@tonic-gate return (I2C_COMPLETE); 13027c478bd9Sstevel@tonic-gate } 13037c478bd9Sstevel@tonic-gate 13047c478bd9Sstevel@tonic-gate /* 13057c478bd9Sstevel@tonic-gate * If resid == 1, the last byte has already been shifted into 13067c478bd9Sstevel@tonic-gate * the accumulator. Send the stop bit. This also prevents the 13077c478bd9Sstevel@tonic-gate * last S0 read from shifting in another byte from the I2C bus. 13087c478bd9Sstevel@tonic-gate */ 13097c478bd9Sstevel@tonic-gate if (tp->i2c_r_resid == 1) { 13107c478bd9Sstevel@tonic-gate pcf8584_put_s1(i2c, S1_STOP); 13117c478bd9Sstevel@tonic-gate } 13127c478bd9Sstevel@tonic-gate 13137c478bd9Sstevel@tonic-gate /* 13147c478bd9Sstevel@tonic-gate * If resid == 2, then the next read will cause the I2C bus to 13157c478bd9Sstevel@tonic-gate * start shifting in the last byte on the I2C bus, which we 13167c478bd9Sstevel@tonic-gate * don't want to be ACK'd, so clear the ACK bit. 13177c478bd9Sstevel@tonic-gate */ 13187c478bd9Sstevel@tonic-gate if (tp->i2c_r_resid == 2) { 1319afd7fd7bSosaeed if (i2c->pcf8584_mode == PCF8584_POLL_MODE) 1320afd7fd7bSosaeed pcf8584_put_s1(i2c, S1_ESO); 1321afd7fd7bSosaeed else 13227c478bd9Sstevel@tonic-gate pcf8584_put_s1(i2c, S1_ESO | S1_ENI); 13237c478bd9Sstevel@tonic-gate } 13247c478bd9Sstevel@tonic-gate 13257c478bd9Sstevel@tonic-gate tp->i2c_rbuf[tp->i2c_rlen - tp->i2c_r_resid] = 13267c478bd9Sstevel@tonic-gate pcf8584_get_s0(i2c); 13277c478bd9Sstevel@tonic-gate 13287c478bd9Sstevel@tonic-gate PCF8584_DDB(pcf8584_print(PRT_TRAN, 13297c478bd9Sstevel@tonic-gate "TRAN_STATE_RD: returning. i2c_rlen = %d " 13307c478bd9Sstevel@tonic-gate "i2c_r_resid = %d, data =%x\n", tp->i2c_rlen, 13317c478bd9Sstevel@tonic-gate tp->i2c_r_resid, tp->i2c_rbuf[tp->i2c_rlen - 13327c478bd9Sstevel@tonic-gate tp->i2c_r_resid])); 13337c478bd9Sstevel@tonic-gate 13347c478bd9Sstevel@tonic-gate if (--tp->i2c_r_resid == 0) { 13357c478bd9Sstevel@tonic-gate 13367c478bd9Sstevel@tonic-gate return (I2C_COMPLETE); 13377c478bd9Sstevel@tonic-gate } 13387c478bd9Sstevel@tonic-gate 13397c478bd9Sstevel@tonic-gate return (I2C_PENDING); 13407c478bd9Sstevel@tonic-gate case TRAN_STATE_WR_RD: 13417c478bd9Sstevel@tonic-gate 13427c478bd9Sstevel@tonic-gate if (pcf8584_error(s1, I2C_WR, i2c) != I2C_SUCCESS) { 13437c478bd9Sstevel@tonic-gate tp->i2c_result = I2C_FAILURE; 13447c478bd9Sstevel@tonic-gate 13457c478bd9Sstevel@tonic-gate return (I2C_COMPLETE); 13467c478bd9Sstevel@tonic-gate } 13477c478bd9Sstevel@tonic-gate if ((s1 & S1_LRB)) { 13487c478bd9Sstevel@tonic-gate pcf8584_put_s1(i2c, S1_STOP); 13497c478bd9Sstevel@tonic-gate PCF8584_DDB(pcf8584_print(PRT_TRAN, 13507c478bd9Sstevel@tonic-gate "TRAN_STATE_WR_RD sending STOP\n")); 13517c478bd9Sstevel@tonic-gate 13527c478bd9Sstevel@tonic-gate return (I2C_COMPLETE); 13537c478bd9Sstevel@tonic-gate } 13547c478bd9Sstevel@tonic-gate if (tp->i2c_w_resid != 0) { 13557c478bd9Sstevel@tonic-gate pcf8584_put_s0(i2c, tp->i2c_wbuf[tp->i2c_wlen - 13567c478bd9Sstevel@tonic-gate tp->i2c_w_resid--]); 13577c478bd9Sstevel@tonic-gate PCF8584_DDB(pcf8584_print(PRT_TRAN, 13587c478bd9Sstevel@tonic-gate "TRAN_STATE_WR_RD: write data %x\n", 13597c478bd9Sstevel@tonic-gate tp->i2c_wbuf[tp->i2c_wlen - 13607c478bd9Sstevel@tonic-gate (tp->i2c_w_resid + 1)])); 13617c478bd9Sstevel@tonic-gate } else { 1362afd7fd7bSosaeed if (i2c->pcf8584_mode == PCF8584_POLL_MODE) 1363afd7fd7bSosaeed pcf8584_put_s1(i2c, S1_START2); 1364afd7fd7bSosaeed else 13657c478bd9Sstevel@tonic-gate pcf8584_put_s1(i2c, S1_START2 | S1_ENI); 13667c478bd9Sstevel@tonic-gate pcf8584_put_s0(i2c, addr | I2C_READ); 13677c478bd9Sstevel@tonic-gate i2c->pcf8584_tran_state = 13687c478bd9Sstevel@tonic-gate TRAN_STATE_DUMMY_RD; 13697c478bd9Sstevel@tonic-gate PCF8584_DDB(pcf8584_print(PRT_TRAN, 13707c478bd9Sstevel@tonic-gate "TRAN_STATE_WR_RD: write addr " 13717c478bd9Sstevel@tonic-gate "%x\n", addr | I2C_READ)); 13727c478bd9Sstevel@tonic-gate } 13737c478bd9Sstevel@tonic-gate 13747c478bd9Sstevel@tonic-gate return (I2C_PENDING); 13757c478bd9Sstevel@tonic-gate default: 13767c478bd9Sstevel@tonic-gate 13777c478bd9Sstevel@tonic-gate return (I2C_COMPLETE); 13787c478bd9Sstevel@tonic-gate } 13797c478bd9Sstevel@tonic-gate } 13807c478bd9Sstevel@tonic-gate 13817c478bd9Sstevel@tonic-gate /* 13827c478bd9Sstevel@tonic-gate * pcf8584_transfer() is the function that is registered with 13837c478bd9Sstevel@tonic-gate * I2C services to be called from pcf8584_transfer() for each transfer. 13847c478bd9Sstevel@tonic-gate * 13857c478bd9Sstevel@tonic-gate * This function starts the transfer, and then waits for the 13867c478bd9Sstevel@tonic-gate * interrupt or polled thread to signal that the transfer has 13877c478bd9Sstevel@tonic-gate * completed. 13887c478bd9Sstevel@tonic-gate */ 13897c478bd9Sstevel@tonic-gate int 13907c478bd9Sstevel@tonic-gate pcf8584_transfer(dev_info_t *dip, i2c_transfer_t *tp) 13917c478bd9Sstevel@tonic-gate { 13927c478bd9Sstevel@tonic-gate pcf8584_t *i2c; 13937c478bd9Sstevel@tonic-gate int saved_mode, took_over = 0; 13947c478bd9Sstevel@tonic-gate kcondvar_t *waiter = NULL; 13957c478bd9Sstevel@tonic-gate extern int do_polled_io; 13967c478bd9Sstevel@tonic-gate 13977c478bd9Sstevel@tonic-gate i2c = (pcf8584_t *)ddi_get_soft_state(pcf8584_state, 13987c478bd9Sstevel@tonic-gate ddi_get_instance(ddi_get_parent(dip))); 13997c478bd9Sstevel@tonic-gate 14007c478bd9Sstevel@tonic-gate tp->i2c_r_resid = tp->i2c_rlen; 14017c478bd9Sstevel@tonic-gate tp->i2c_w_resid = tp->i2c_wlen; 14027c478bd9Sstevel@tonic-gate tp->i2c_result = I2C_SUCCESS; 14037c478bd9Sstevel@tonic-gate 14047c478bd9Sstevel@tonic-gate begin: 14057c478bd9Sstevel@tonic-gate /* 14067c478bd9Sstevel@tonic-gate * If we're explicitly asked to do polled io (or if we are panic'ing), 14077c478bd9Sstevel@tonic-gate * we need to usurp ownership of the I2C bus, bypassing any other 14087c478bd9Sstevel@tonic-gate * waiters. 14097c478bd9Sstevel@tonic-gate */ 1410afd7fd7bSosaeed if (do_polled_io || ddi_in_panic()) { 14117c478bd9Sstevel@tonic-gate pcf8584_take_over(i2c, dip, tp, &waiter, &saved_mode); 14127c478bd9Sstevel@tonic-gate took_over = 1; 14137c478bd9Sstevel@tonic-gate } else { 14147c478bd9Sstevel@tonic-gate pcf8584_acquire(i2c, dip, tp, B_FALSE); 14157c478bd9Sstevel@tonic-gate mutex_enter(&i2c->pcf8584_imutex); 14167c478bd9Sstevel@tonic-gate 14177c478bd9Sstevel@tonic-gate /* 14187c478bd9Sstevel@tonic-gate * See if someone else had intruded and taken over the bus 14197c478bd9Sstevel@tonic-gate * between the 'pcf8584_acquire' and 'mutex_enter' above. 14207c478bd9Sstevel@tonic-gate * If so, we'll have to start all over again. 14217c478bd9Sstevel@tonic-gate */ 14227c478bd9Sstevel@tonic-gate if (i2c->pcf8584_cur_tran != tp) { 14237c478bd9Sstevel@tonic-gate mutex_exit(&i2c->pcf8584_imutex); 14247c478bd9Sstevel@tonic-gate goto begin; 14257c478bd9Sstevel@tonic-gate } 14267c478bd9Sstevel@tonic-gate } 14277c478bd9Sstevel@tonic-gate 14287c478bd9Sstevel@tonic-gate if (pcf8584_bbn_ready(i2c) != I2C_SUCCESS) { 14297c478bd9Sstevel@tonic-gate if (took_over) 14307c478bd9Sstevel@tonic-gate pcf8584_give_up(i2c, waiter, saved_mode); 14317c478bd9Sstevel@tonic-gate else { 14327c478bd9Sstevel@tonic-gate mutex_exit(&i2c->pcf8584_imutex); 14337c478bd9Sstevel@tonic-gate pcf8584_release(i2c, B_FALSE); 14347c478bd9Sstevel@tonic-gate } 14357c478bd9Sstevel@tonic-gate 14367c478bd9Sstevel@tonic-gate return (tp->i2c_result = I2C_FAILURE); 14377c478bd9Sstevel@tonic-gate } 14387c478bd9Sstevel@tonic-gate 14397c478bd9Sstevel@tonic-gate /* 14407c478bd9Sstevel@tonic-gate * Bus selection must be followed by pcf8584_bbn_ready(), 14417c478bd9Sstevel@tonic-gate * otherwise the bus can be switched before the stop 14427c478bd9Sstevel@tonic-gate * bit is written out, causing the stop bit to get 14437c478bd9Sstevel@tonic-gate * sent to the wrong (new) bus. This causes the 14447c478bd9Sstevel@tonic-gate * previous bus to permanently hang waiting for the 14457c478bd9Sstevel@tonic-gate * stop bit. 14467c478bd9Sstevel@tonic-gate */ 14477c478bd9Sstevel@tonic-gate pcf8584_select_bus(i2c); 14487c478bd9Sstevel@tonic-gate 14497c478bd9Sstevel@tonic-gate i2c->pcf8584_tran_state = TRAN_STATE_DUMMY_DATA; 14507c478bd9Sstevel@tonic-gate pcf8584_put_s0(i2c, DUMMY_ADDR); 14517c478bd9Sstevel@tonic-gate PCF8584_DDB(pcf8584_print(PRT_TRAN, 14527c478bd9Sstevel@tonic-gate "FIRST WRITE DUMMY ADDR: write %x\n", DUMMY_ADDR)); 1453afd7fd7bSosaeed if (i2c->pcf8584_mode == PCF8584_POLL_MODE) 1454afd7fd7bSosaeed pcf8584_put_s1(i2c, S1_START); 1455afd7fd7bSosaeed else 14567c478bd9Sstevel@tonic-gate pcf8584_put_s1(i2c, S1_START | S1_ENI); 14577c478bd9Sstevel@tonic-gate 14587c478bd9Sstevel@tonic-gate /* 14597c478bd9Sstevel@tonic-gate * Update transfer status so any polled i/o request coming in 14607c478bd9Sstevel@tonic-gate * after this will complete this transfer for us, before issuing 14617c478bd9Sstevel@tonic-gate * its own. 14627c478bd9Sstevel@tonic-gate */ 14637c478bd9Sstevel@tonic-gate i2c->pcf8584_cur_status = PCF8584_TRANSFER_ON; 14647c478bd9Sstevel@tonic-gate 14657c478bd9Sstevel@tonic-gate if (i2c->pcf8584_mode == PCF8584_POLL_MODE) 14667c478bd9Sstevel@tonic-gate pcf8584_do_polled_io(i2c); 14677c478bd9Sstevel@tonic-gate 14687c478bd9Sstevel@tonic-gate if (took_over) 14697c478bd9Sstevel@tonic-gate pcf8584_give_up(i2c, waiter, saved_mode); 14707c478bd9Sstevel@tonic-gate else { 14717c478bd9Sstevel@tonic-gate if (i2c->pcf8584_mode != PCF8584_POLL_MODE) 14727c478bd9Sstevel@tonic-gate cv_wait(&i2c->pcf8584_icv, &i2c->pcf8584_imutex); 14737c478bd9Sstevel@tonic-gate mutex_exit(&i2c->pcf8584_imutex); 14747c478bd9Sstevel@tonic-gate 14757c478bd9Sstevel@tonic-gate /* 14767c478bd9Sstevel@tonic-gate * Release the I2C bus only if we still own it. If we don't 14777c478bd9Sstevel@tonic-gate * own it (someone usurped it from us while we were waiting), 14787c478bd9Sstevel@tonic-gate * we still need to drop the lock that serializes access to 14797c478bd9Sstevel@tonic-gate * the pcf8584 controller on systems where OBP shares the 14807c478bd9Sstevel@tonic-gate * controller with the OS. 14817c478bd9Sstevel@tonic-gate */ 14827c478bd9Sstevel@tonic-gate if (i2c->pcf8584_cur_tran == tp) 14837c478bd9Sstevel@tonic-gate pcf8584_release(i2c, B_FALSE); 14847c478bd9Sstevel@tonic-gate else if (&plat_shared_i2c_exit && dip) 14857c478bd9Sstevel@tonic-gate plat_shared_i2c_exit(i2c->pcf8584_dip); 14867c478bd9Sstevel@tonic-gate } 14877c478bd9Sstevel@tonic-gate 14887c478bd9Sstevel@tonic-gate return (tp->i2c_result); 14897c478bd9Sstevel@tonic-gate } 14907c478bd9Sstevel@tonic-gate 14917c478bd9Sstevel@tonic-gate static void 14927c478bd9Sstevel@tonic-gate pcf8584_do_polled_io(pcf8584_t *i2c) 14937c478bd9Sstevel@tonic-gate { 14947c478bd9Sstevel@tonic-gate int completed = I2C_PENDING; 14957c478bd9Sstevel@tonic-gate uint8_t s1; 14967c478bd9Sstevel@tonic-gate 14977c478bd9Sstevel@tonic-gate while (completed != I2C_COMPLETE) { 14987c478bd9Sstevel@tonic-gate s1 = pcf8584_get_s1(i2c); 14997c478bd9Sstevel@tonic-gate if (!(s1 & S1_PIN)) { 15007c478bd9Sstevel@tonic-gate ASSERT(i2c->pcf8584_cur_tran); 15017c478bd9Sstevel@tonic-gate completed = pcf8584_process(i2c, s1); 15027c478bd9Sstevel@tonic-gate } 15037c478bd9Sstevel@tonic-gate drv_usecwait(1); 15047c478bd9Sstevel@tonic-gate } 15057c478bd9Sstevel@tonic-gate 15067c478bd9Sstevel@tonic-gate i2c->pcf8584_cur_status = PCF8584_TRANSFER_OVER; 15077c478bd9Sstevel@tonic-gate } 15087c478bd9Sstevel@tonic-gate 15097c478bd9Sstevel@tonic-gate /* 15107c478bd9Sstevel@tonic-gate * pcf8584_take_over() grabs the I2C bus and other resources by force and 15117c478bd9Sstevel@tonic-gate * flushes any pending transaction. This is called if a polled i/o 15127c478bd9Sstevel@tonic-gate * request comes in. 15137c478bd9Sstevel@tonic-gate */ 15147c478bd9Sstevel@tonic-gate static void 15157c478bd9Sstevel@tonic-gate pcf8584_take_over(pcf8584_t *i2c, dev_info_t *dip, i2c_transfer_t *tp, 15167c478bd9Sstevel@tonic-gate kcondvar_t **waiter, int *saved_mode) 15177c478bd9Sstevel@tonic-gate { 15187c478bd9Sstevel@tonic-gate mutex_enter(&i2c->pcf8584_imutex); 1519afd7fd7bSosaeed *saved_mode = i2c->pcf8584_mode; 1520afd7fd7bSosaeed i2c->pcf8584_mode = PCF8584_POLL_MODE; 15217c478bd9Sstevel@tonic-gate 15227c478bd9Sstevel@tonic-gate /* 15237c478bd9Sstevel@tonic-gate * We need to flush out any currently pending transaction before 15247c478bd9Sstevel@tonic-gate * issuing ours. 15257c478bd9Sstevel@tonic-gate */ 15267c478bd9Sstevel@tonic-gate if (i2c->pcf8584_busy) { 15277c478bd9Sstevel@tonic-gate if (i2c->pcf8584_cur_tran && 15287c478bd9Sstevel@tonic-gate i2c->pcf8584_cur_status == PCF8584_TRANSFER_ON) { 15297c478bd9Sstevel@tonic-gate pcf8584_do_polled_io(i2c); 15307c478bd9Sstevel@tonic-gate *waiter = &i2c->pcf8584_icv; 15317c478bd9Sstevel@tonic-gate } 15327c478bd9Sstevel@tonic-gate } 15337c478bd9Sstevel@tonic-gate 15347c478bd9Sstevel@tonic-gate /* 15357c478bd9Sstevel@tonic-gate * Since pcf8584_acquire() is by default a good citizen that 15367c478bd9Sstevel@tonic-gate * will wait its turn to acquire the I2C bus, we need to set 15377c478bd9Sstevel@tonic-gate * the 'force' flag on. 15387c478bd9Sstevel@tonic-gate */ 15397c478bd9Sstevel@tonic-gate pcf8584_acquire(i2c, dip, tp, B_TRUE); 15407c478bd9Sstevel@tonic-gate } 15417c478bd9Sstevel@tonic-gate 15427c478bd9Sstevel@tonic-gate /* 15437c478bd9Sstevel@tonic-gate * pcf8584_give_up() returns all resources that were taken over forcefully 15447c478bd9Sstevel@tonic-gate */ 15457c478bd9Sstevel@tonic-gate static void 15467c478bd9Sstevel@tonic-gate pcf8584_give_up(pcf8584_t *i2c, kcondvar_t *waiter, int saved_mode) 15477c478bd9Sstevel@tonic-gate { 15487c478bd9Sstevel@tonic-gate i2c->pcf8584_mode = saved_mode; 15497c478bd9Sstevel@tonic-gate 15507c478bd9Sstevel@tonic-gate /* 15517c478bd9Sstevel@tonic-gate * Note that pcf8584_release only wakes up threads waiting to acquire 15527c478bd9Sstevel@tonic-gate * the I2C bus. We still need to wake up the waiter from whom we 15537c478bd9Sstevel@tonic-gate * usurped the bus. 15547c478bd9Sstevel@tonic-gate */ 15557c478bd9Sstevel@tonic-gate pcf8584_release(i2c, B_TRUE); 15567c478bd9Sstevel@tonic-gate if (waiter) 15577c478bd9Sstevel@tonic-gate cv_signal(waiter); 15587c478bd9Sstevel@tonic-gate 15597c478bd9Sstevel@tonic-gate mutex_exit(&i2c->pcf8584_imutex); 15607c478bd9Sstevel@tonic-gate } 1561