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 50Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 60Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 70Sstevel@tonic-gate * with the License. 80Sstevel@tonic-gate * 90Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 100Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 110Sstevel@tonic-gate * See the License for the specific language governing permissions 120Sstevel@tonic-gate * and limitations under the License. 130Sstevel@tonic-gate * 140Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 150Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 160Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 170Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 180Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 190Sstevel@tonic-gate * 200Sstevel@tonic-gate * CDDL HEADER END 210Sstevel@tonic-gate */ 220Sstevel@tonic-gate /* 230Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 240Sstevel@tonic-gate * Use is subject to license terms. 250Sstevel@tonic-gate */ 260Sstevel@tonic-gate 270Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 280Sstevel@tonic-gate 290Sstevel@tonic-gate #include <sys/conf.h> 300Sstevel@tonic-gate #include <sys/sunddi.h> 310Sstevel@tonic-gate #include <sys/ddi_impldefs.h> 320Sstevel@tonic-gate #include <sys/kmem.h> 330Sstevel@tonic-gate #include <sys/dma_i8237A.h> 340Sstevel@tonic-gate #include <sys/isadma.h> 350Sstevel@tonic-gate #include <sys/nexusdebug.h> 360Sstevel@tonic-gate 370Sstevel@tonic-gate /* Bitfield debugging definitions for this file */ 380Sstevel@tonic-gate #define ISADMA_MAP_DEBUG 0x1 390Sstevel@tonic-gate #define ISADMA_REGACCESS_DEBUG 0x2 400Sstevel@tonic-gate 410Sstevel@tonic-gate /* 420Sstevel@tonic-gate * The isadam nexus serves two functions. The first is to represent a 430Sstevel@tonic-gate * a placeholder in the device tree for a shared dma controller register 440Sstevel@tonic-gate * for the SuperIO floppy and parallel ports. 450Sstevel@tonic-gate * The second function is to virtualize the shared dma controller register 460Sstevel@tonic-gate * for those two drivers. Rather than creating new ddi routines to manage 470Sstevel@tonic-gate * the shared register, we will use the ddi register mapping functions to 480Sstevel@tonic-gate * do this. The two child devices will use ddi_regs_map_setup to map in 490Sstevel@tonic-gate * their device registers. The isadma nexus will have an aliased entry in 500Sstevel@tonic-gate * it's own registers property for the shared dma controller register. When 510Sstevel@tonic-gate * the isadma detects the fact that it's children are trying to map the shared 520Sstevel@tonic-gate * register, it will intercept this mapping and provide it's own register 530Sstevel@tonic-gate * access routine to be used to access the register when the child devices 540Sstevel@tonic-gate * use the ddi_{get,put} calls. 550Sstevel@tonic-gate * 560Sstevel@tonic-gate * Sigh, the 82C37 has a weird quirk (BUG?) where when DMA is active on the 570Sstevel@tonic-gate * the bus, PIO's cannot happen. If they do, they generate bus faults and 580Sstevel@tonic-gate * cause the system to panic. On PC's, the Intel processor has special 590Sstevel@tonic-gate * req/grnt lines that prevent PIO's from occuring while DMA is in flight, 600Sstevel@tonic-gate * unfortunately, hummingbird doesn't support this special req/grnt pair. 610Sstevel@tonic-gate * I'm going to try and work around this by implementing a cv to stop PIO's 620Sstevel@tonic-gate * from occuring while DMA is in flight. When each child wants to do DMA, 630Sstevel@tonic-gate * they need to mask out all other channels using the allmask register. 640Sstevel@tonic-gate * This nexus keys on this access and locks down the hardware using a cv. 650Sstevel@tonic-gate * Once the driver's interrupt handler is called it needs to clear 660Sstevel@tonic-gate * the allmask register. The nexus keys off of this an issues cv wakeups 670Sstevel@tonic-gate * if necessary. 680Sstevel@tonic-gate */ 690Sstevel@tonic-gate /* 700Sstevel@tonic-gate * Function prototypes for busops routines: 710Sstevel@tonic-gate */ 720Sstevel@tonic-gate static int isadma_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 730Sstevel@tonic-gate off_t off, off_t len, caddr_t *addrp); 740Sstevel@tonic-gate 750Sstevel@tonic-gate /* 760Sstevel@tonic-gate * function prototypes for dev ops routines: 770Sstevel@tonic-gate */ 780Sstevel@tonic-gate static int isadma_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 790Sstevel@tonic-gate static int isadma_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 800Sstevel@tonic-gate 810Sstevel@tonic-gate /* 820Sstevel@tonic-gate * general function prototypes: 830Sstevel@tonic-gate */ 840Sstevel@tonic-gate 850Sstevel@tonic-gate /* 860Sstevel@tonic-gate * bus ops and dev ops structures: 870Sstevel@tonic-gate */ 880Sstevel@tonic-gate static struct bus_ops isadma_bus_ops = { 890Sstevel@tonic-gate BUSO_REV, 900Sstevel@tonic-gate isadma_map, 910Sstevel@tonic-gate NULL, 920Sstevel@tonic-gate NULL, 930Sstevel@tonic-gate NULL, 940Sstevel@tonic-gate i_ddi_map_fault, 950Sstevel@tonic-gate ddi_dma_map, 960Sstevel@tonic-gate ddi_dma_allochdl, 970Sstevel@tonic-gate ddi_dma_freehdl, 980Sstevel@tonic-gate ddi_dma_bindhdl, 990Sstevel@tonic-gate ddi_dma_unbindhdl, 1000Sstevel@tonic-gate ddi_dma_flush, 1010Sstevel@tonic-gate ddi_dma_win, 1020Sstevel@tonic-gate ddi_dma_mctl, 1030Sstevel@tonic-gate ddi_ctlops, 1040Sstevel@tonic-gate ddi_bus_prop_op, 1050Sstevel@tonic-gate 0, /* (*bus_get_eventcookie)(); */ 1060Sstevel@tonic-gate 0, /* (*bus_add_eventcall)(); */ 1070Sstevel@tonic-gate 0, /* (*bus_remove_eventcall)(); */ 1080Sstevel@tonic-gate 0, /* (*bus_post_event)(); */ 1090Sstevel@tonic-gate 0, /* (*bus_intr_control)(); */ 1100Sstevel@tonic-gate 0, /* (*bus_config)(); */ 1110Sstevel@tonic-gate 0, /* (*bus_unconfig)(); */ 1120Sstevel@tonic-gate 0, /* (*bus_fm_init)(); */ 1130Sstevel@tonic-gate 0, /* (*bus_fm_fini)(); */ 1140Sstevel@tonic-gate 0, /* (*bus_fm_access_enter)(); */ 1150Sstevel@tonic-gate 0, /* (*bus_fm_access_exit)(); */ 1160Sstevel@tonic-gate 0, /* (*bus_power)(); */ 1170Sstevel@tonic-gate i_ddi_intr_ops /* (*bus_intr_op(); */ 1180Sstevel@tonic-gate }; 1190Sstevel@tonic-gate 1200Sstevel@tonic-gate static struct dev_ops isadma_ops = { 1210Sstevel@tonic-gate DEVO_REV, 1220Sstevel@tonic-gate 0, 1230Sstevel@tonic-gate ddi_no_info, 1240Sstevel@tonic-gate nulldev, 1250Sstevel@tonic-gate 0, 1260Sstevel@tonic-gate isadma_attach, 1270Sstevel@tonic-gate isadma_detach, 1280Sstevel@tonic-gate nodev, 1290Sstevel@tonic-gate (struct cb_ops *)0, 1300Sstevel@tonic-gate &isadma_bus_ops 1310Sstevel@tonic-gate }; 1320Sstevel@tonic-gate 1330Sstevel@tonic-gate /* 1340Sstevel@tonic-gate * module definitions: 1350Sstevel@tonic-gate */ 1360Sstevel@tonic-gate #include <sys/modctl.h> 1370Sstevel@tonic-gate 1380Sstevel@tonic-gate static struct modldrv modldrv = { 1390Sstevel@tonic-gate &mod_driverops, /* Type of module. This one is a driver */ 1400Sstevel@tonic-gate "isadma nexus driver", /* Name of module. */ 1410Sstevel@tonic-gate &isadma_ops, /* driver ops */ 1420Sstevel@tonic-gate }; 1430Sstevel@tonic-gate 1440Sstevel@tonic-gate static struct modlinkage modlinkage = { 1450Sstevel@tonic-gate MODREV_1, (void *)&modldrv, NULL 1460Sstevel@tonic-gate }; 1470Sstevel@tonic-gate 1480Sstevel@tonic-gate /* 1490Sstevel@tonic-gate * driver global data: 1500Sstevel@tonic-gate */ 1510Sstevel@tonic-gate static void *per_isadma_state; /* per-isadma soft state pointer */ 1520Sstevel@tonic-gate 1530Sstevel@tonic-gate /* Global debug data */ 1540Sstevel@tonic-gate uint64_t isadma_sleep_cnt = 0; 1550Sstevel@tonic-gate uint64_t isadma_wakeup_cnt = 0; 1560Sstevel@tonic-gate #ifdef DEBUG 1570Sstevel@tonic-gate int64_t isadma_max_waiter = 0; 1580Sstevel@tonic-gate int64_t isadma_min_waiter = 0xffffll; 1590Sstevel@tonic-gate uint64_t isadma_punt = 0; 1600Sstevel@tonic-gate uint64_t isadma_setting_wdip = 0; 1610Sstevel@tonic-gate uint64_t isadma_clearing_wdip = 0; 1620Sstevel@tonic-gate #endif 1630Sstevel@tonic-gate 1640Sstevel@tonic-gate int 1650Sstevel@tonic-gate _init(void) 1660Sstevel@tonic-gate { 1670Sstevel@tonic-gate int e; 1680Sstevel@tonic-gate 1690Sstevel@tonic-gate /* 1700Sstevel@tonic-gate * Initialize per-isadma soft state pointer. 1710Sstevel@tonic-gate */ 1720Sstevel@tonic-gate e = ddi_soft_state_init(&per_isadma_state, 1730Sstevel@tonic-gate sizeof (isadma_devstate_t), 1); 1740Sstevel@tonic-gate if (e != 0) 1750Sstevel@tonic-gate return (e); 1760Sstevel@tonic-gate 1770Sstevel@tonic-gate /* 1780Sstevel@tonic-gate * Install the module. 1790Sstevel@tonic-gate */ 1800Sstevel@tonic-gate e = mod_install(&modlinkage); 1810Sstevel@tonic-gate if (e != 0) 1820Sstevel@tonic-gate ddi_soft_state_fini(&per_isadma_state); 1830Sstevel@tonic-gate return (e); 1840Sstevel@tonic-gate } 1850Sstevel@tonic-gate 1860Sstevel@tonic-gate int 1870Sstevel@tonic-gate _fini(void) 1880Sstevel@tonic-gate { 1890Sstevel@tonic-gate int e; 1900Sstevel@tonic-gate 1910Sstevel@tonic-gate /* 1920Sstevel@tonic-gate * Remove the module. 1930Sstevel@tonic-gate */ 1940Sstevel@tonic-gate e = mod_remove(&modlinkage); 1950Sstevel@tonic-gate if (e != 0) 1960Sstevel@tonic-gate return (e); 1970Sstevel@tonic-gate 1980Sstevel@tonic-gate /* 1990Sstevel@tonic-gate * Free the soft state info. 2000Sstevel@tonic-gate */ 2010Sstevel@tonic-gate ddi_soft_state_fini(&per_isadma_state); 2020Sstevel@tonic-gate return (e); 2030Sstevel@tonic-gate } 2040Sstevel@tonic-gate 2050Sstevel@tonic-gate int 2060Sstevel@tonic-gate _info(struct modinfo *modinfop) 2070Sstevel@tonic-gate { 2080Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 2090Sstevel@tonic-gate } 2100Sstevel@tonic-gate 2110Sstevel@tonic-gate /* device driver entry points */ 2120Sstevel@tonic-gate 2130Sstevel@tonic-gate /* 2140Sstevel@tonic-gate * attach entry point: 2150Sstevel@tonic-gate */ 2160Sstevel@tonic-gate static int 2170Sstevel@tonic-gate isadma_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 2180Sstevel@tonic-gate { 2190Sstevel@tonic-gate isadma_devstate_t *isadmap; /* per isadma state pointer */ 2200Sstevel@tonic-gate int32_t instance; 2210Sstevel@tonic-gate int ret = DDI_SUCCESS; 2220Sstevel@tonic-gate 2230Sstevel@tonic-gate #ifdef DEBUG 2240Sstevel@tonic-gate debug_print_level = 0; 2250Sstevel@tonic-gate debug_info = 1; 2260Sstevel@tonic-gate #endif 2270Sstevel@tonic-gate switch (cmd) { 2280Sstevel@tonic-gate case DDI_ATTACH: { 2290Sstevel@tonic-gate /* 2300Sstevel@tonic-gate * Allocate soft state for this instance. 2310Sstevel@tonic-gate */ 2320Sstevel@tonic-gate instance = ddi_get_instance(dip); 2330Sstevel@tonic-gate if (ddi_soft_state_zalloc(per_isadma_state, instance) 2340Sstevel@tonic-gate != DDI_SUCCESS) { 2350Sstevel@tonic-gate ret = DDI_FAILURE; 2360Sstevel@tonic-gate goto exit; 2370Sstevel@tonic-gate } 2380Sstevel@tonic-gate isadmap = ddi_get_soft_state(per_isadma_state, instance); 2390Sstevel@tonic-gate isadmap->isadma_dip = dip; 2400Sstevel@tonic-gate 2410Sstevel@tonic-gate /* Cache our register property */ 242*506Scth if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 2430Sstevel@tonic-gate "reg", (caddr_t)&isadmap->isadma_regp, 2440Sstevel@tonic-gate &isadmap->isadma_reglen) != DDI_SUCCESS) { 2450Sstevel@tonic-gate ret = DDI_FAILURE; 2460Sstevel@tonic-gate goto fail_get_prop; 2470Sstevel@tonic-gate } 2480Sstevel@tonic-gate 2490Sstevel@tonic-gate /* Initialize our mutex */ 2500Sstevel@tonic-gate mutex_init(&isadmap->isadma_access_lock, NULL, MUTEX_DRIVER, 2510Sstevel@tonic-gate NULL); 2520Sstevel@tonic-gate 2530Sstevel@tonic-gate /* Initialize our condition variable */ 2540Sstevel@tonic-gate cv_init(&isadmap->isadma_access_cv, NULL, CV_DRIVER, NULL); 2550Sstevel@tonic-gate 2560Sstevel@tonic-gate ddi_report_dev(dip); 2570Sstevel@tonic-gate goto exit; 2580Sstevel@tonic-gate 2590Sstevel@tonic-gate } 2600Sstevel@tonic-gate case DDI_RESUME: 2610Sstevel@tonic-gate default: 2620Sstevel@tonic-gate goto exit; 2630Sstevel@tonic-gate } 2640Sstevel@tonic-gate 2650Sstevel@tonic-gate fail_get_prop: 2660Sstevel@tonic-gate ddi_soft_state_free(per_isadma_state, instance); 2670Sstevel@tonic-gate 2680Sstevel@tonic-gate exit: 2690Sstevel@tonic-gate return (ret); 2700Sstevel@tonic-gate } 2710Sstevel@tonic-gate 2720Sstevel@tonic-gate /* 2730Sstevel@tonic-gate * detach entry point: 2740Sstevel@tonic-gate */ 2750Sstevel@tonic-gate static int 2760Sstevel@tonic-gate isadma_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 2770Sstevel@tonic-gate { 2780Sstevel@tonic-gate int instance = ddi_get_instance(dip); 2790Sstevel@tonic-gate isadma_devstate_t *isadmap = 2800Sstevel@tonic-gate ddi_get_soft_state(per_isadma_state, instance); 2810Sstevel@tonic-gate 2820Sstevel@tonic-gate switch (cmd) { 2830Sstevel@tonic-gate case DDI_DETACH: 2840Sstevel@tonic-gate cv_destroy(&isadmap->isadma_access_cv); 2850Sstevel@tonic-gate 2860Sstevel@tonic-gate mutex_destroy(&isadmap->isadma_access_lock); 2870Sstevel@tonic-gate 2880Sstevel@tonic-gate /* free the cached register property */ 2890Sstevel@tonic-gate kmem_free(isadmap->isadma_regp, isadmap->isadma_reglen); 2900Sstevel@tonic-gate 2910Sstevel@tonic-gate ddi_soft_state_free(per_isadma_state, instance); 2920Sstevel@tonic-gate return (DDI_SUCCESS); 2930Sstevel@tonic-gate 2940Sstevel@tonic-gate case DDI_SUSPEND: 2950Sstevel@tonic-gate return (DDI_SUCCESS); 2960Sstevel@tonic-gate } 2970Sstevel@tonic-gate return (DDI_FAILURE); 2980Sstevel@tonic-gate } 2990Sstevel@tonic-gate 3000Sstevel@tonic-gate 3010Sstevel@tonic-gate #ifdef DEBUG 3020Sstevel@tonic-gate static void 3030Sstevel@tonic-gate isadma_check_waiters(isadma_devstate_t *isadmap) 3040Sstevel@tonic-gate { 3050Sstevel@tonic-gate if (isadmap->isadma_want > isadma_max_waiter) 3060Sstevel@tonic-gate isadma_max_waiter = isadmap->isadma_want; 3070Sstevel@tonic-gate 3080Sstevel@tonic-gate if (isadmap->isadma_want < isadma_min_waiter) 3090Sstevel@tonic-gate isadma_min_waiter = isadmap->isadma_want; 3100Sstevel@tonic-gate } 3110Sstevel@tonic-gate #endif 3120Sstevel@tonic-gate 3130Sstevel@tonic-gate static void 3140Sstevel@tonic-gate isadma_dmawait(isadma_devstate_t *isadmap) 3150Sstevel@tonic-gate { 3160Sstevel@tonic-gate 3170Sstevel@tonic-gate ASSERT(mutex_owned(&isadmap->isadma_access_lock)); 3180Sstevel@tonic-gate 3190Sstevel@tonic-gate /* Wait loop, if the locking dip is set, we wait. */ 3200Sstevel@tonic-gate while (isadmap->isadma_ldip != NULL) { 3210Sstevel@tonic-gate 3220Sstevel@tonic-gate isadmap->isadma_want++; 3230Sstevel@tonic-gate cv_wait(&isadmap->isadma_access_cv, 3240Sstevel@tonic-gate &isadmap->isadma_access_lock); 3250Sstevel@tonic-gate isadmap->isadma_want--; 3260Sstevel@tonic-gate isadma_sleep_cnt++; 3270Sstevel@tonic-gate } 3280Sstevel@tonic-gate } 3290Sstevel@tonic-gate 3300Sstevel@tonic-gate static void 3310Sstevel@tonic-gate isadma_wakeup(isadma_devstate_t *isadmap) 3320Sstevel@tonic-gate { 3330Sstevel@tonic-gate 3340Sstevel@tonic-gate ASSERT(mutex_owned(&isadmap->isadma_access_lock)); 3350Sstevel@tonic-gate 3360Sstevel@tonic-gate /* 3370Sstevel@tonic-gate * If somebody wants register access and the lock dip is not set 3380Sstevel@tonic-gate * signal the waiters. 3390Sstevel@tonic-gate */ 3400Sstevel@tonic-gate if (isadmap->isadma_want > 0 && isadmap->isadma_ldip == NULL) { 3410Sstevel@tonic-gate cv_signal(&isadmap->isadma_access_cv); 3420Sstevel@tonic-gate isadma_wakeup_cnt++; 3430Sstevel@tonic-gate } 3440Sstevel@tonic-gate 3450Sstevel@tonic-gate } 3460Sstevel@tonic-gate 3470Sstevel@tonic-gate /* 3480Sstevel@tonic-gate * Register access vectors 3490Sstevel@tonic-gate */ 3500Sstevel@tonic-gate 3510Sstevel@tonic-gate /*ARGSUSED*/ 3520Sstevel@tonic-gate void 3530Sstevel@tonic-gate isadma_norep_get8(ddi_acc_impl_t *handle, uint8_t *host_addr, 3540Sstevel@tonic-gate uint8_t *dev_addr, size_t repcount, uint_t flags) 3550Sstevel@tonic-gate { 3560Sstevel@tonic-gate } 3570Sstevel@tonic-gate 3580Sstevel@tonic-gate /*ARGSUSED*/ 3590Sstevel@tonic-gate void 3600Sstevel@tonic-gate isadma_norep_get16(ddi_acc_impl_t *handle, uint16_t *host_addr, 3610Sstevel@tonic-gate uint16_t *dev_addr, size_t repcount, uint_t flags) 3620Sstevel@tonic-gate { 3630Sstevel@tonic-gate } 3640Sstevel@tonic-gate 3650Sstevel@tonic-gate /*ARGSUSED*/ 3660Sstevel@tonic-gate void 3670Sstevel@tonic-gate isadma_norep_get32(ddi_acc_impl_t *handle, uint32_t *host_addr, 3680Sstevel@tonic-gate uint32_t *dev_addr, size_t repcount, uint_t flags) 3690Sstevel@tonic-gate { 3700Sstevel@tonic-gate } 3710Sstevel@tonic-gate 3720Sstevel@tonic-gate /*ARGSUSED*/ 3730Sstevel@tonic-gate void 3740Sstevel@tonic-gate isadma_norep_get64(ddi_acc_impl_t *handle, uint64_t *host_addr, 3750Sstevel@tonic-gate uint64_t *dev_addr, size_t repcount, uint_t flags) 3760Sstevel@tonic-gate { 3770Sstevel@tonic-gate } 3780Sstevel@tonic-gate 3790Sstevel@tonic-gate /*ARGSUSED*/ 3800Sstevel@tonic-gate void 3810Sstevel@tonic-gate isadma_norep_put8(ddi_acc_impl_t *handle, uint8_t *host_addr, 3820Sstevel@tonic-gate uint8_t *dev_addr, size_t repcount, uint_t flags) 3830Sstevel@tonic-gate { 3840Sstevel@tonic-gate } 3850Sstevel@tonic-gate 3860Sstevel@tonic-gate /*ARGSUSED*/ 3870Sstevel@tonic-gate void 3880Sstevel@tonic-gate isadma_norep_put16(ddi_acc_impl_t *handle, uint16_t *host_addr, 3890Sstevel@tonic-gate uint16_t *dev_addr, size_t repcount, uint_t flags) 3900Sstevel@tonic-gate { 3910Sstevel@tonic-gate } 3920Sstevel@tonic-gate 3930Sstevel@tonic-gate /*ARGSUSED*/ 3940Sstevel@tonic-gate void 3950Sstevel@tonic-gate isadma_norep_put32(ddi_acc_impl_t *handle, uint32_t *host_addr, 3960Sstevel@tonic-gate uint32_t *dev_addr, size_t repcount, uint_t flags) 3970Sstevel@tonic-gate { 3980Sstevel@tonic-gate } 3990Sstevel@tonic-gate 4000Sstevel@tonic-gate /*ARGSUSED*/ 4010Sstevel@tonic-gate void 4020Sstevel@tonic-gate isadma_norep_put64(ddi_acc_impl_t *handle, uint64_t *host_addr, 4030Sstevel@tonic-gate uint64_t *dev_addr, size_t repcount, uint_t flags) 4040Sstevel@tonic-gate { 4050Sstevel@tonic-gate } 4060Sstevel@tonic-gate 4070Sstevel@tonic-gate /*ARGSUSED*/ 4080Sstevel@tonic-gate uint8_t 4090Sstevel@tonic-gate isadma_get8(ddi_acc_impl_t *hdlp, uint8_t *addr) 4100Sstevel@tonic-gate { 4110Sstevel@tonic-gate ddi_acc_handle_t phdl = hdlp->ahi_common.ah_platform_private; 4120Sstevel@tonic-gate isadma_devstate_t *isadmap = hdlp->ahi_common.ah_bus_private; 4130Sstevel@tonic-gate off_t offset = (caddr_t)addr - hdlp->ahi_common.ah_addr; 4140Sstevel@tonic-gate uint8_t ret = 0xff; 4150Sstevel@tonic-gate 4160Sstevel@tonic-gate if (IN_CHILD_SPACE(offset)) { /* Pass to parent */ 4170Sstevel@tonic-gate #ifdef DEBUG 4180Sstevel@tonic-gate isadma_punt++; 4190Sstevel@tonic-gate #endif 4200Sstevel@tonic-gate return (ddi_get8(phdl, addr)); 4210Sstevel@tonic-gate } 4220Sstevel@tonic-gate #ifdef DEBUG 4230Sstevel@tonic-gate isadma_check_waiters(isadmap); 4240Sstevel@tonic-gate #endif 4250Sstevel@tonic-gate mutex_enter(&isadmap->isadma_access_lock); 4260Sstevel@tonic-gate isadma_dmawait(isadmap); /* wait until on-going dma completes */ 4270Sstevel@tonic-gate 4280Sstevel@tonic-gate /* No 8 bit access to 16 bit address or count registers */ 4290Sstevel@tonic-gate if (IN_16BIT_SPACE(offset)) 4300Sstevel@tonic-gate goto exit; 4310Sstevel@tonic-gate 4320Sstevel@tonic-gate /* No 8 bit access to first/last flip-flop registers */ 4330Sstevel@tonic-gate if (IS_SEQREG(offset)) 4340Sstevel@tonic-gate goto exit; 4350Sstevel@tonic-gate 4360Sstevel@tonic-gate ret = ddi_get8(phdl, addr); /* Pass to parent */ 4370Sstevel@tonic-gate exit: 4380Sstevel@tonic-gate isadma_wakeup(isadmap); 4390Sstevel@tonic-gate mutex_exit(&isadmap->isadma_access_lock); 4400Sstevel@tonic-gate return (ret); 4410Sstevel@tonic-gate } 4420Sstevel@tonic-gate 4430Sstevel@tonic-gate /* 4440Sstevel@tonic-gate * Allow child devices to access this shared register set as if it were 4450Sstevel@tonic-gate * a real 16 bit register. The ISA bridge defines the access to this 4460Sstevel@tonic-gate * 16 bit dma controller & count register by programming an 8 byte register. 4470Sstevel@tonic-gate */ 4480Sstevel@tonic-gate /*ARGSUSED*/ 4490Sstevel@tonic-gate uint16_t 4500Sstevel@tonic-gate isadma_get16(ddi_acc_impl_t *hdlp, uint16_t *addr) 4510Sstevel@tonic-gate { 4520Sstevel@tonic-gate ddi_acc_handle_t phdl = hdlp->ahi_common.ah_platform_private; 4530Sstevel@tonic-gate isadma_devstate_t *isadmap = hdlp->ahi_common.ah_bus_private; 4540Sstevel@tonic-gate off_t offset = (caddr_t)addr - hdlp->ahi_common.ah_addr; 4550Sstevel@tonic-gate uint16_t ret = 0xffff; 4560Sstevel@tonic-gate 4570Sstevel@tonic-gate if (IN_CHILD_SPACE(offset)) { /* Pass to parent */ 4580Sstevel@tonic-gate #ifdef DEBUG 4590Sstevel@tonic-gate isadma_punt++; 4600Sstevel@tonic-gate #endif 4610Sstevel@tonic-gate return (ddi_get16(phdl, addr)); 4620Sstevel@tonic-gate } 4630Sstevel@tonic-gate #ifdef DEBUG 4640Sstevel@tonic-gate isadma_check_waiters(isadmap); 4650Sstevel@tonic-gate #endif 4660Sstevel@tonic-gate mutex_enter(&isadmap->isadma_access_lock); 4670Sstevel@tonic-gate isadma_dmawait(isadmap); /* wait until on-going dma completes */ 4680Sstevel@tonic-gate 4690Sstevel@tonic-gate /* Only Allow access to the 16 bit count and address registers */ 4700Sstevel@tonic-gate if (!IN_16BIT_SPACE(offset)) 4710Sstevel@tonic-gate goto exit; 4720Sstevel@tonic-gate 4730Sstevel@tonic-gate /* Set the sequencing register to the low byte */ 4740Sstevel@tonic-gate ddi_put8(phdl, (uint8_t *)HDL_TO_SEQREG_ADDR(hdlp, offset), 0); 4750Sstevel@tonic-gate 4760Sstevel@tonic-gate /* Read the low byte, then high byte */ 4770Sstevel@tonic-gate ret = ddi_get8(phdl, (uint8_t *)addr); 4780Sstevel@tonic-gate ret = (ddi_get8(phdl, (uint8_t *)addr) << 8) | ret; 4790Sstevel@tonic-gate exit: 4800Sstevel@tonic-gate isadma_wakeup(isadmap); 4810Sstevel@tonic-gate mutex_exit(&isadmap->isadma_access_lock); 4820Sstevel@tonic-gate return (ret); 4830Sstevel@tonic-gate } 4840Sstevel@tonic-gate 4850Sstevel@tonic-gate /*ARGSUSED*/ 4860Sstevel@tonic-gate uint32_t 4870Sstevel@tonic-gate isadma_noget32(ddi_acc_impl_t *hdlp, uint32_t *addr) 4880Sstevel@tonic-gate { 4890Sstevel@tonic-gate return (UINT32_MAX); 4900Sstevel@tonic-gate } 4910Sstevel@tonic-gate 4920Sstevel@tonic-gate /*ARGSUSED*/ 4930Sstevel@tonic-gate uint64_t 4940Sstevel@tonic-gate isadma_noget64(ddi_acc_impl_t *hdlp, uint64_t *addr) 4950Sstevel@tonic-gate { 4960Sstevel@tonic-gate return (UINT64_MAX); 4970Sstevel@tonic-gate } 4980Sstevel@tonic-gate 4990Sstevel@tonic-gate /* 5000Sstevel@tonic-gate * Here's where we do our locking magic. The dma all mask register is an 8 5010Sstevel@tonic-gate * bit register in the dma space, so we look for the access to the 5020Sstevel@tonic-gate * DMAC1_ALLMASK register. When somebody is masking out the dma channels 5030Sstevel@tonic-gate * we lock down the dma engine from further PIO accesses. When the driver 5040Sstevel@tonic-gate * calls back into this routine to clear the allmask register, we wakeup 5050Sstevel@tonic-gate * any blocked threads. 5060Sstevel@tonic-gate */ 5070Sstevel@tonic-gate /*ARGSUSED*/ 5080Sstevel@tonic-gate void 5090Sstevel@tonic-gate isadma_put8(ddi_acc_impl_t *hdlp, uint8_t *addr, uint8_t value) 5100Sstevel@tonic-gate { 5110Sstevel@tonic-gate ddi_acc_handle_t phdl = hdlp->ahi_common.ah_platform_private; 5120Sstevel@tonic-gate isadma_devstate_t *isadmap = hdlp->ahi_common.ah_bus_private; 5130Sstevel@tonic-gate off_t offset = (caddr_t)addr - hdlp->ahi_common.ah_addr; 5140Sstevel@tonic-gate 5150Sstevel@tonic-gate if (IN_CHILD_SPACE(offset)) { /* Pass to parent */ 5160Sstevel@tonic-gate #ifdef DEBUG 5170Sstevel@tonic-gate isadma_punt++; 5180Sstevel@tonic-gate #endif 5190Sstevel@tonic-gate ddi_put8(phdl, addr, value); 5200Sstevel@tonic-gate return; 5210Sstevel@tonic-gate } 5220Sstevel@tonic-gate #ifdef DEBUG 5230Sstevel@tonic-gate isadma_check_waiters(isadmap); 5240Sstevel@tonic-gate #endif 5250Sstevel@tonic-gate mutex_enter(&isadmap->isadma_access_lock); 5260Sstevel@tonic-gate 5270Sstevel@tonic-gate if (isadmap->isadma_ldip == hdlp->ahi_common.ah_dip) { /* owned lock? */ 5280Sstevel@tonic-gate if (END_ISADMA(offset, value)) { 5290Sstevel@tonic-gate isadmap->isadma_ldip = NULL; /* reset lock owner */ 5300Sstevel@tonic-gate #ifdef DEBUG 5310Sstevel@tonic-gate isadma_clearing_wdip++; 5320Sstevel@tonic-gate #endif 5330Sstevel@tonic-gate } 5340Sstevel@tonic-gate } else { /* we don't own the lock */ 5350Sstevel@tonic-gate /* wait until on-going dma completes */ 5360Sstevel@tonic-gate isadma_dmawait(isadmap); 5370Sstevel@tonic-gate 5380Sstevel@tonic-gate if (BEGIN_ISADMA(offset, value)) { 5390Sstevel@tonic-gate isadmap->isadma_ldip = hdlp->ahi_common.ah_dip; 5400Sstevel@tonic-gate #ifdef DEBUG 5410Sstevel@tonic-gate isadma_setting_wdip++; 5420Sstevel@tonic-gate #endif 5430Sstevel@tonic-gate } 5440Sstevel@tonic-gate } 5450Sstevel@tonic-gate 5460Sstevel@tonic-gate /* No 8 bit access to 16 bit address or count registers */ 5470Sstevel@tonic-gate if (IN_16BIT_SPACE(offset)) 5480Sstevel@tonic-gate goto exit; 5490Sstevel@tonic-gate 5500Sstevel@tonic-gate /* No 8 bit access to first/last flip-flop registers */ 5510Sstevel@tonic-gate if (IS_SEQREG(offset)) 5520Sstevel@tonic-gate goto exit; 5530Sstevel@tonic-gate 5540Sstevel@tonic-gate ddi_put8(phdl, addr, value); /* Pass to parent */ 5550Sstevel@tonic-gate exit: 5560Sstevel@tonic-gate isadma_wakeup(isadmap); 5570Sstevel@tonic-gate mutex_exit(&isadmap->isadma_access_lock); 5580Sstevel@tonic-gate } 5590Sstevel@tonic-gate 5600Sstevel@tonic-gate /* 5610Sstevel@tonic-gate * Allow child devices to access this shared register set as if it were 5620Sstevel@tonic-gate * a real 16 bit register. The ISA bridge defines the access to this 5630Sstevel@tonic-gate * 16 bit dma controller & count register by programming an 8 byte register. 5640Sstevel@tonic-gate */ 5650Sstevel@tonic-gate /*ARGSUSED*/ 5660Sstevel@tonic-gate void 5670Sstevel@tonic-gate isadma_put16(ddi_acc_impl_t *hdlp, uint16_t *addr, uint16_t value) 5680Sstevel@tonic-gate { 5690Sstevel@tonic-gate ddi_acc_handle_t phdl = hdlp->ahi_common.ah_platform_private; 5700Sstevel@tonic-gate isadma_devstate_t *isadmap = hdlp->ahi_common.ah_bus_private; 5710Sstevel@tonic-gate off_t offset = (caddr_t)addr - hdlp->ahi_common.ah_addr; 5720Sstevel@tonic-gate 5730Sstevel@tonic-gate if (IN_CHILD_SPACE(offset)) { /* Pass to parent */ 5740Sstevel@tonic-gate #ifdef DEBUG 5750Sstevel@tonic-gate isadma_punt++; 5760Sstevel@tonic-gate #endif 5770Sstevel@tonic-gate ddi_put16(phdl, addr, value); 5780Sstevel@tonic-gate return; 5790Sstevel@tonic-gate } 5800Sstevel@tonic-gate #ifdef DEBUG 5810Sstevel@tonic-gate isadma_check_waiters(isadmap); 5820Sstevel@tonic-gate #endif 5830Sstevel@tonic-gate mutex_enter(&isadmap->isadma_access_lock); 5840Sstevel@tonic-gate isadma_dmawait(isadmap); /* wait until on-going dma completes */ 5850Sstevel@tonic-gate 5860Sstevel@tonic-gate /* Only Allow access to the 16 bit count and address registers */ 5870Sstevel@tonic-gate if (!IN_16BIT_SPACE(offset)) 5880Sstevel@tonic-gate goto exit; 5890Sstevel@tonic-gate 5900Sstevel@tonic-gate /* Set the sequencing register to the low byte */ 5910Sstevel@tonic-gate ddi_put8(phdl, (uint8_t *)HDL_TO_SEQREG_ADDR(hdlp, offset), 0); 5920Sstevel@tonic-gate 5930Sstevel@tonic-gate /* Write the low byte, then the high byte */ 5940Sstevel@tonic-gate ddi_put8(phdl, (uint8_t *)addr, value & 0xff); 5950Sstevel@tonic-gate ddi_put8(phdl, (uint8_t *)addr, (value >> 8) & 0xff); 5960Sstevel@tonic-gate exit: 5970Sstevel@tonic-gate isadma_wakeup(isadmap); 5980Sstevel@tonic-gate mutex_exit(&isadmap->isadma_access_lock); 5990Sstevel@tonic-gate } 6000Sstevel@tonic-gate 6010Sstevel@tonic-gate /*ARGSUSED*/ 6020Sstevel@tonic-gate void 6030Sstevel@tonic-gate isadma_noput32(ddi_acc_impl_t *hdlp, uint32_t *addr, uint32_t value) {} 6040Sstevel@tonic-gate 6050Sstevel@tonic-gate /*ARGSUSED*/ 6060Sstevel@tonic-gate void 6070Sstevel@tonic-gate isadma_noput64(ddi_acc_impl_t *hdlp, uint64_t *addr, uint64_t value) {} 6080Sstevel@tonic-gate 6090Sstevel@tonic-gate #define IS_SAME_REG(r1, r2) (((r1)->ebus_addr_hi == (r2)->ebus_addr_hi) && \ 6100Sstevel@tonic-gate ((r1)->ebus_addr_low == (r2)->ebus_addr_low)) 6110Sstevel@tonic-gate 6120Sstevel@tonic-gate /* 6130Sstevel@tonic-gate * The isadma_map routine determines if it's child is attempting to map a 6140Sstevel@tonic-gate * shared reg. If it is, it installs it's own vectors and bus private pointer 6150Sstevel@tonic-gate * and stacks those ops that were already defined. 6160Sstevel@tonic-gate */ 6170Sstevel@tonic-gate static int 6180Sstevel@tonic-gate isadma_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 6190Sstevel@tonic-gate off_t off, off_t len, caddr_t *addrp) 6200Sstevel@tonic-gate { 6210Sstevel@tonic-gate isadma_devstate_t *isadmap = ddi_get_soft_state(per_isadma_state, 6220Sstevel@tonic-gate ddi_get_instance(dip)); 6230Sstevel@tonic-gate dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent; 6240Sstevel@tonic-gate ebus_regspec_t *child_regp, *regp; 6250Sstevel@tonic-gate int32_t rnumber = mp->map_obj.rnumber; 6260Sstevel@tonic-gate int32_t reglen; 6270Sstevel@tonic-gate int ret; 6280Sstevel@tonic-gate ddi_acc_impl_t *hp; 6290Sstevel@tonic-gate 6300Sstevel@tonic-gate /* 6310Sstevel@tonic-gate * Get child regspec since the mapping struct may not have it yet 6320Sstevel@tonic-gate */ 633*506Scth if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS, 6340Sstevel@tonic-gate "reg", (caddr_t)®p, ®len) != DDI_SUCCESS) { 6350Sstevel@tonic-gate return (DDI_FAILURE); 6360Sstevel@tonic-gate } 6370Sstevel@tonic-gate 6380Sstevel@tonic-gate child_regp = regp + rnumber; 6390Sstevel@tonic-gate 6400Sstevel@tonic-gate DPRINTF(ISADMA_MAP_DEBUG, ("isadma_map: child regp %p " 6410Sstevel@tonic-gate "parent regp %p Child reg array %p\n", child_regp, 6420Sstevel@tonic-gate isadmap->isadma_regp, regp)); 6430Sstevel@tonic-gate 6440Sstevel@tonic-gate /* Figure out if we're mapping or unmapping */ 6450Sstevel@tonic-gate switch (mp->map_op) { 6460Sstevel@tonic-gate case DDI_MO_MAP_LOCKED: 6470Sstevel@tonic-gate /* Call up device tree to establish mapping */ 6480Sstevel@tonic-gate ret = (DEVI(pdip)->devi_ops->devo_bus_ops->bus_map) 6490Sstevel@tonic-gate (pdip, rdip, mp, off, len, addrp); 6500Sstevel@tonic-gate 6510Sstevel@tonic-gate if ((ret != DDI_SUCCESS) || 6520Sstevel@tonic-gate !IS_SAME_REG(child_regp, isadmap->isadma_regp)) 6530Sstevel@tonic-gate break; 6540Sstevel@tonic-gate 6550Sstevel@tonic-gate /* Post-process the mapping request. */ 6560Sstevel@tonic-gate hp = kmem_alloc(sizeof (ddi_acc_impl_t), KM_SLEEP); 6570Sstevel@tonic-gate *hp = *(ddi_acc_impl_t *)mp->map_handlep; 6580Sstevel@tonic-gate impl_acc_hdl_get((ddi_acc_handle_t)mp->map_handlep)-> 6590Sstevel@tonic-gate ah_platform_private = hp; 6600Sstevel@tonic-gate hp = (ddi_acc_impl_t *)mp->map_handlep; 6610Sstevel@tonic-gate hp->ahi_common.ah_bus_private = isadmap; 6620Sstevel@tonic-gate hp->ahi_get8 = isadma_get8; 6630Sstevel@tonic-gate hp->ahi_get16 = isadma_get16; 6640Sstevel@tonic-gate hp->ahi_get32 = isadma_noget32; 6650Sstevel@tonic-gate hp->ahi_get64 = isadma_noget64; 6660Sstevel@tonic-gate hp->ahi_put8 = isadma_put8; 6670Sstevel@tonic-gate hp->ahi_put16 = isadma_put16; 6680Sstevel@tonic-gate hp->ahi_put32 = isadma_noput32; 6690Sstevel@tonic-gate hp->ahi_put64 = isadma_noput64; 6700Sstevel@tonic-gate hp->ahi_rep_get8 = isadma_norep_get8; 6710Sstevel@tonic-gate hp->ahi_rep_get16 = isadma_norep_get16; 6720Sstevel@tonic-gate hp->ahi_rep_get32 = isadma_norep_get32; 6730Sstevel@tonic-gate hp->ahi_rep_get64 = isadma_norep_get64; 6740Sstevel@tonic-gate hp->ahi_rep_put8 = isadma_norep_put8; 6750Sstevel@tonic-gate hp->ahi_rep_put16 = isadma_norep_put16; 6760Sstevel@tonic-gate hp->ahi_rep_put32 = isadma_norep_put32; 6770Sstevel@tonic-gate hp->ahi_rep_put64 = isadma_norep_put64; 6780Sstevel@tonic-gate break; 6790Sstevel@tonic-gate 6800Sstevel@tonic-gate case DDI_MO_UNMAP: 6810Sstevel@tonic-gate if (IS_SAME_REG(child_regp, isadmap->isadma_regp)) { 6820Sstevel@tonic-gate hp = impl_acc_hdl_get( 6830Sstevel@tonic-gate (ddi_acc_handle_t)mp->map_handlep)-> 6840Sstevel@tonic-gate ah_platform_private; 6850Sstevel@tonic-gate *(ddi_acc_impl_t *)mp->map_handlep = *hp; 6860Sstevel@tonic-gate kmem_free(hp, sizeof (ddi_acc_impl_t)); 6870Sstevel@tonic-gate } 6880Sstevel@tonic-gate 6890Sstevel@tonic-gate /* Call up tree to tear down mapping */ 6900Sstevel@tonic-gate ret = (DEVI(pdip)->devi_ops->devo_bus_ops->bus_map) 6910Sstevel@tonic-gate (pdip, rdip, mp, off, len, addrp); 6920Sstevel@tonic-gate break; 6930Sstevel@tonic-gate 6940Sstevel@tonic-gate default: 6950Sstevel@tonic-gate ret = DDI_FAILURE; 6960Sstevel@tonic-gate break; 6970Sstevel@tonic-gate } 6980Sstevel@tonic-gate 6990Sstevel@tonic-gate kmem_free(regp, reglen); 7000Sstevel@tonic-gate return (ret); 7010Sstevel@tonic-gate } 702