18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * HP i8042-based System Device Controller driver. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (c) 2001 Brian S. Julin 58c2ecf20Sopenharmony_ci * All rights reserved. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 88c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 98c2ecf20Sopenharmony_ci * are met: 108c2ecf20Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright 118c2ecf20Sopenharmony_ci * notice, this list of conditions, and the following disclaimer, 128c2ecf20Sopenharmony_ci * without modification. 138c2ecf20Sopenharmony_ci * 2. The name of the author may not be used to endorse or promote products 148c2ecf20Sopenharmony_ci * derived from this software without specific prior written permission. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Alternatively, this software may be distributed under the terms of the 178c2ecf20Sopenharmony_ci * GNU General Public License ("GPL"). 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 208c2ecf20Sopenharmony_ci * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 218c2ecf20Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 228c2ecf20Sopenharmony_ci * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 238c2ecf20Sopenharmony_ci * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 248c2ecf20Sopenharmony_ci * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 258c2ecf20Sopenharmony_ci * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 268c2ecf20Sopenharmony_ci * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 278c2ecf20Sopenharmony_ci * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * References: 308c2ecf20Sopenharmony_ci * System Device Controller Microprocessor Firmware Theory of Operation 318c2ecf20Sopenharmony_ci * for Part Number 1820-4784 Revision B. Dwg No. A-1820-4784-2 328c2ecf20Sopenharmony_ci * Helge Deller's original hilkbd.c port for PA-RISC. 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * Driver theory of operation: 368c2ecf20Sopenharmony_ci * 378c2ecf20Sopenharmony_ci * hp_sdc_put does all writing to the SDC. ISR can run on a different 388c2ecf20Sopenharmony_ci * CPU than hp_sdc_put, but only one CPU runs hp_sdc_put at a time 398c2ecf20Sopenharmony_ci * (it cannot really benefit from SMP anyway.) A tasket fit this perfectly. 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * All data coming back from the SDC is sent via interrupt and can be read 428c2ecf20Sopenharmony_ci * fully in the ISR, so there are no latency/throughput problems there. 438c2ecf20Sopenharmony_ci * The problem is with output, due to the slow clock speed of the SDC 448c2ecf20Sopenharmony_ci * compared to the CPU. This should not be too horrible most of the time, 458c2ecf20Sopenharmony_ci * but if used with HIL devices that support the multibyte transfer command, 468c2ecf20Sopenharmony_ci * keeping outbound throughput flowing at the 6500KBps that the HIL is 478c2ecf20Sopenharmony_ci * capable of is more than can be done at HZ=100. 488c2ecf20Sopenharmony_ci * 498c2ecf20Sopenharmony_ci * Busy polling for IBF clear wastes CPU cycles and bus cycles. hp_sdc.ibf 508c2ecf20Sopenharmony_ci * is set to 0 when the IBF flag in the status register has cleared. ISR 518c2ecf20Sopenharmony_ci * may do this, and may also access the parts of queued transactions related 528c2ecf20Sopenharmony_ci * to reading data back from the SDC, but otherwise will not touch the 538c2ecf20Sopenharmony_ci * hp_sdc state. Whenever a register is written hp_sdc.ibf is set to 1. 548c2ecf20Sopenharmony_ci * 558c2ecf20Sopenharmony_ci * The i8042 write index and the values in the 4-byte input buffer 568c2ecf20Sopenharmony_ci * starting at 0x70 are kept track of in hp_sdc.wi, and .r7[], respectively, 578c2ecf20Sopenharmony_ci * to minimize the amount of IO needed to the SDC. However these values 588c2ecf20Sopenharmony_ci * do not need to be locked since they are only ever accessed by hp_sdc_put. 598c2ecf20Sopenharmony_ci * 608c2ecf20Sopenharmony_ci * A timer task schedules the tasklet once per second just to make 618c2ecf20Sopenharmony_ci * sure it doesn't freeze up and to allow for bad reads to time out. 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#include <linux/hp_sdc.h> 658c2ecf20Sopenharmony_ci#include <linux/errno.h> 668c2ecf20Sopenharmony_ci#include <linux/init.h> 678c2ecf20Sopenharmony_ci#include <linux/module.h> 688c2ecf20Sopenharmony_ci#include <linux/ioport.h> 698c2ecf20Sopenharmony_ci#include <linux/time.h> 708c2ecf20Sopenharmony_ci#include <linux/semaphore.h> 718c2ecf20Sopenharmony_ci#include <linux/slab.h> 728c2ecf20Sopenharmony_ci#include <linux/hil.h> 738c2ecf20Sopenharmony_ci#include <asm/io.h> 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* Machine-specific abstraction */ 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#if defined(__hppa__) 788c2ecf20Sopenharmony_ci# include <asm/parisc-device.h> 798c2ecf20Sopenharmony_ci# define sdc_readb(p) gsc_readb(p) 808c2ecf20Sopenharmony_ci# define sdc_writeb(v,p) gsc_writeb((v),(p)) 818c2ecf20Sopenharmony_ci#elif defined(__mc68000__) 828c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 838c2ecf20Sopenharmony_ci# define sdc_readb(p) in_8(p) 848c2ecf20Sopenharmony_ci# define sdc_writeb(v,p) out_8((p),(v)) 858c2ecf20Sopenharmony_ci#else 868c2ecf20Sopenharmony_ci# error "HIL is not supported on this platform" 878c2ecf20Sopenharmony_ci#endif 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci#define PREFIX "HP SDC: " 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ciMODULE_AUTHOR("Brian S. Julin <bri@calyx.com>"); 928c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("HP i8042-based SDC Driver"); 938c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hp_sdc_request_timer_irq); 968c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hp_sdc_request_hil_irq); 978c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hp_sdc_request_cooked_irq); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hp_sdc_release_timer_irq); 1008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hp_sdc_release_hil_irq); 1018c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hp_sdc_release_cooked_irq); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__hp_sdc_enqueue_transaction); 1048c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hp_sdc_enqueue_transaction); 1058c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hp_sdc_dequeue_transaction); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic bool hp_sdc_disabled; 1088c2ecf20Sopenharmony_cimodule_param_named(no_hpsdc, hp_sdc_disabled, bool, 0); 1098c2ecf20Sopenharmony_ciMODULE_PARM_DESC(no_hpsdc, "Do not enable HP SDC driver."); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic hp_i8042_sdc hp_sdc; /* All driver state is kept in here. */ 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/*************** primitives for use in any context *********************/ 1148c2ecf20Sopenharmony_cistatic inline uint8_t hp_sdc_status_in8(void) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci uint8_t status; 1178c2ecf20Sopenharmony_ci unsigned long flags; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci write_lock_irqsave(&hp_sdc.ibf_lock, flags); 1208c2ecf20Sopenharmony_ci status = sdc_readb(hp_sdc.status_io); 1218c2ecf20Sopenharmony_ci if (!(status & HP_SDC_STATUS_IBF)) 1228c2ecf20Sopenharmony_ci hp_sdc.ibf = 0; 1238c2ecf20Sopenharmony_ci write_unlock_irqrestore(&hp_sdc.ibf_lock, flags); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return status; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic inline uint8_t hp_sdc_data_in8(void) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci return sdc_readb(hp_sdc.data_io); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic inline void hp_sdc_status_out8(uint8_t val) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci unsigned long flags; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci write_lock_irqsave(&hp_sdc.ibf_lock, flags); 1388c2ecf20Sopenharmony_ci hp_sdc.ibf = 1; 1398c2ecf20Sopenharmony_ci if ((val & 0xf0) == 0xe0) 1408c2ecf20Sopenharmony_ci hp_sdc.wi = 0xff; 1418c2ecf20Sopenharmony_ci sdc_writeb(val, hp_sdc.status_io); 1428c2ecf20Sopenharmony_ci write_unlock_irqrestore(&hp_sdc.ibf_lock, flags); 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic inline void hp_sdc_data_out8(uint8_t val) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci unsigned long flags; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci write_lock_irqsave(&hp_sdc.ibf_lock, flags); 1508c2ecf20Sopenharmony_ci hp_sdc.ibf = 1; 1518c2ecf20Sopenharmony_ci sdc_writeb(val, hp_sdc.data_io); 1528c2ecf20Sopenharmony_ci write_unlock_irqrestore(&hp_sdc.ibf_lock, flags); 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci/* Care must be taken to only invoke hp_sdc_spin_ibf when 1568c2ecf20Sopenharmony_ci * absolutely needed, or in rarely invoked subroutines. 1578c2ecf20Sopenharmony_ci * Not only does it waste CPU cycles, it also wastes bus cycles. 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_cistatic inline void hp_sdc_spin_ibf(void) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci unsigned long flags; 1628c2ecf20Sopenharmony_ci rwlock_t *lock; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci lock = &hp_sdc.ibf_lock; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci read_lock_irqsave(lock, flags); 1678c2ecf20Sopenharmony_ci if (!hp_sdc.ibf) { 1688c2ecf20Sopenharmony_ci read_unlock_irqrestore(lock, flags); 1698c2ecf20Sopenharmony_ci return; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci read_unlock(lock); 1728c2ecf20Sopenharmony_ci write_lock(lock); 1738c2ecf20Sopenharmony_ci while (sdc_readb(hp_sdc.status_io) & HP_SDC_STATUS_IBF) 1748c2ecf20Sopenharmony_ci { } 1758c2ecf20Sopenharmony_ci hp_sdc.ibf = 0; 1768c2ecf20Sopenharmony_ci write_unlock_irqrestore(lock, flags); 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci/************************ Interrupt context functions ************************/ 1818c2ecf20Sopenharmony_cistatic void hp_sdc_take(int irq, void *dev_id, uint8_t status, uint8_t data) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci hp_sdc_transaction *curr; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci read_lock(&hp_sdc.rtq_lock); 1868c2ecf20Sopenharmony_ci if (hp_sdc.rcurr < 0) { 1878c2ecf20Sopenharmony_ci read_unlock(&hp_sdc.rtq_lock); 1888c2ecf20Sopenharmony_ci return; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci curr = hp_sdc.tq[hp_sdc.rcurr]; 1918c2ecf20Sopenharmony_ci read_unlock(&hp_sdc.rtq_lock); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci curr->seq[curr->idx++] = status; 1948c2ecf20Sopenharmony_ci curr->seq[curr->idx++] = data; 1958c2ecf20Sopenharmony_ci hp_sdc.rqty -= 2; 1968c2ecf20Sopenharmony_ci hp_sdc.rtime = ktime_get(); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (hp_sdc.rqty <= 0) { 1998c2ecf20Sopenharmony_ci /* All data has been gathered. */ 2008c2ecf20Sopenharmony_ci if (curr->seq[curr->actidx] & HP_SDC_ACT_SEMAPHORE) 2018c2ecf20Sopenharmony_ci if (curr->act.semaphore) 2028c2ecf20Sopenharmony_ci up(curr->act.semaphore); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci if (curr->seq[curr->actidx] & HP_SDC_ACT_CALLBACK) 2058c2ecf20Sopenharmony_ci if (curr->act.irqhook) 2068c2ecf20Sopenharmony_ci curr->act.irqhook(irq, dev_id, status, data); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci curr->actidx = curr->idx; 2098c2ecf20Sopenharmony_ci curr->idx++; 2108c2ecf20Sopenharmony_ci /* Return control of this transaction */ 2118c2ecf20Sopenharmony_ci write_lock(&hp_sdc.rtq_lock); 2128c2ecf20Sopenharmony_ci hp_sdc.rcurr = -1; 2138c2ecf20Sopenharmony_ci hp_sdc.rqty = 0; 2148c2ecf20Sopenharmony_ci write_unlock(&hp_sdc.rtq_lock); 2158c2ecf20Sopenharmony_ci tasklet_schedule(&hp_sdc.task); 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic irqreturn_t hp_sdc_isr(int irq, void *dev_id) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci uint8_t status, data; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci status = hp_sdc_status_in8(); 2248c2ecf20Sopenharmony_ci /* Read data unconditionally to advance i8042. */ 2258c2ecf20Sopenharmony_ci data = hp_sdc_data_in8(); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci /* For now we are ignoring these until we get the SDC to behave. */ 2288c2ecf20Sopenharmony_ci if (((status & 0xf1) == 0x51) && data == 0x82) 2298c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci switch (status & HP_SDC_STATUS_IRQMASK) { 2328c2ecf20Sopenharmony_ci case 0: /* This case is not documented. */ 2338c2ecf20Sopenharmony_ci break; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci case HP_SDC_STATUS_USERTIMER: 2368c2ecf20Sopenharmony_ci case HP_SDC_STATUS_PERIODIC: 2378c2ecf20Sopenharmony_ci case HP_SDC_STATUS_TIMER: 2388c2ecf20Sopenharmony_ci read_lock(&hp_sdc.hook_lock); 2398c2ecf20Sopenharmony_ci if (hp_sdc.timer != NULL) 2408c2ecf20Sopenharmony_ci hp_sdc.timer(irq, dev_id, status, data); 2418c2ecf20Sopenharmony_ci read_unlock(&hp_sdc.hook_lock); 2428c2ecf20Sopenharmony_ci break; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci case HP_SDC_STATUS_REG: 2458c2ecf20Sopenharmony_ci hp_sdc_take(irq, dev_id, status, data); 2468c2ecf20Sopenharmony_ci break; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci case HP_SDC_STATUS_HILCMD: 2498c2ecf20Sopenharmony_ci case HP_SDC_STATUS_HILDATA: 2508c2ecf20Sopenharmony_ci read_lock(&hp_sdc.hook_lock); 2518c2ecf20Sopenharmony_ci if (hp_sdc.hil != NULL) 2528c2ecf20Sopenharmony_ci hp_sdc.hil(irq, dev_id, status, data); 2538c2ecf20Sopenharmony_ci read_unlock(&hp_sdc.hook_lock); 2548c2ecf20Sopenharmony_ci break; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci case HP_SDC_STATUS_PUP: 2578c2ecf20Sopenharmony_ci read_lock(&hp_sdc.hook_lock); 2588c2ecf20Sopenharmony_ci if (hp_sdc.pup != NULL) 2598c2ecf20Sopenharmony_ci hp_sdc.pup(irq, dev_id, status, data); 2608c2ecf20Sopenharmony_ci else 2618c2ecf20Sopenharmony_ci printk(KERN_INFO PREFIX "HP SDC reports successful PUP.\n"); 2628c2ecf20Sopenharmony_ci read_unlock(&hp_sdc.hook_lock); 2638c2ecf20Sopenharmony_ci break; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci default: 2668c2ecf20Sopenharmony_ci read_lock(&hp_sdc.hook_lock); 2678c2ecf20Sopenharmony_ci if (hp_sdc.cooked != NULL) 2688c2ecf20Sopenharmony_ci hp_sdc.cooked(irq, dev_id, status, data); 2698c2ecf20Sopenharmony_ci read_unlock(&hp_sdc.hook_lock); 2708c2ecf20Sopenharmony_ci break; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic irqreturn_t hp_sdc_nmisr(int irq, void *dev_id) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci int status; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci status = hp_sdc_status_in8(); 2828c2ecf20Sopenharmony_ci printk(KERN_WARNING PREFIX "NMI !\n"); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci#if 0 2858c2ecf20Sopenharmony_ci if (status & HP_SDC_NMISTATUS_FHS) { 2868c2ecf20Sopenharmony_ci read_lock(&hp_sdc.hook_lock); 2878c2ecf20Sopenharmony_ci if (hp_sdc.timer != NULL) 2888c2ecf20Sopenharmony_ci hp_sdc.timer(irq, dev_id, status, 0); 2898c2ecf20Sopenharmony_ci read_unlock(&hp_sdc.hook_lock); 2908c2ecf20Sopenharmony_ci } else { 2918c2ecf20Sopenharmony_ci /* TODO: pass this on to the HIL handler, or do SAK here? */ 2928c2ecf20Sopenharmony_ci printk(KERN_WARNING PREFIX "HIL NMI\n"); 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci#endif 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci/***************** Kernel (tasklet) context functions ****************/ 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ciunsigned long hp_sdc_put(void); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic void hp_sdc_tasklet(unsigned long foo) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci write_lock_irq(&hp_sdc.rtq_lock); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (hp_sdc.rcurr >= 0) { 3098c2ecf20Sopenharmony_ci ktime_t now = ktime_get(); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (ktime_after(now, ktime_add_us(hp_sdc.rtime, 3128c2ecf20Sopenharmony_ci HP_SDC_MAX_REG_DELAY))) { 3138c2ecf20Sopenharmony_ci hp_sdc_transaction *curr; 3148c2ecf20Sopenharmony_ci uint8_t tmp; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci curr = hp_sdc.tq[hp_sdc.rcurr]; 3178c2ecf20Sopenharmony_ci /* If this turns out to be a normal failure mode 3188c2ecf20Sopenharmony_ci * we'll need to figure out a way to communicate 3198c2ecf20Sopenharmony_ci * it back to the application. and be less verbose. 3208c2ecf20Sopenharmony_ci */ 3218c2ecf20Sopenharmony_ci printk(KERN_WARNING PREFIX "read timeout (%lldus)!\n", 3228c2ecf20Sopenharmony_ci ktime_us_delta(now, hp_sdc.rtime)); 3238c2ecf20Sopenharmony_ci curr->idx += hp_sdc.rqty; 3248c2ecf20Sopenharmony_ci hp_sdc.rqty = 0; 3258c2ecf20Sopenharmony_ci tmp = curr->seq[curr->actidx]; 3268c2ecf20Sopenharmony_ci curr->seq[curr->actidx] |= HP_SDC_ACT_DEAD; 3278c2ecf20Sopenharmony_ci if (tmp & HP_SDC_ACT_SEMAPHORE) 3288c2ecf20Sopenharmony_ci if (curr->act.semaphore) 3298c2ecf20Sopenharmony_ci up(curr->act.semaphore); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci if (tmp & HP_SDC_ACT_CALLBACK) { 3328c2ecf20Sopenharmony_ci /* Note this means that irqhooks may be called 3338c2ecf20Sopenharmony_ci * in tasklet/bh context. 3348c2ecf20Sopenharmony_ci */ 3358c2ecf20Sopenharmony_ci if (curr->act.irqhook) 3368c2ecf20Sopenharmony_ci curr->act.irqhook(0, NULL, 0, 0); 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci curr->actidx = curr->idx; 3408c2ecf20Sopenharmony_ci curr->idx++; 3418c2ecf20Sopenharmony_ci hp_sdc.rcurr = -1; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci write_unlock_irq(&hp_sdc.rtq_lock); 3458c2ecf20Sopenharmony_ci hp_sdc_put(); 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ciunsigned long hp_sdc_put(void) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci hp_sdc_transaction *curr; 3518c2ecf20Sopenharmony_ci uint8_t act; 3528c2ecf20Sopenharmony_ci int idx, curridx; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci int limit = 0; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci write_lock(&hp_sdc.lock); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci /* If i8042 buffers are full, we cannot do anything that 3598c2ecf20Sopenharmony_ci requires output, so we skip to the administrativa. */ 3608c2ecf20Sopenharmony_ci if (hp_sdc.ibf) { 3618c2ecf20Sopenharmony_ci hp_sdc_status_in8(); 3628c2ecf20Sopenharmony_ci if (hp_sdc.ibf) 3638c2ecf20Sopenharmony_ci goto finish; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci anew: 3678c2ecf20Sopenharmony_ci /* See if we are in the middle of a sequence. */ 3688c2ecf20Sopenharmony_ci if (hp_sdc.wcurr < 0) 3698c2ecf20Sopenharmony_ci hp_sdc.wcurr = 0; 3708c2ecf20Sopenharmony_ci read_lock_irq(&hp_sdc.rtq_lock); 3718c2ecf20Sopenharmony_ci if (hp_sdc.rcurr == hp_sdc.wcurr) 3728c2ecf20Sopenharmony_ci hp_sdc.wcurr++; 3738c2ecf20Sopenharmony_ci read_unlock_irq(&hp_sdc.rtq_lock); 3748c2ecf20Sopenharmony_ci if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) 3758c2ecf20Sopenharmony_ci hp_sdc.wcurr = 0; 3768c2ecf20Sopenharmony_ci curridx = hp_sdc.wcurr; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (hp_sdc.tq[curridx] != NULL) 3798c2ecf20Sopenharmony_ci goto start; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci while (++curridx != hp_sdc.wcurr) { 3828c2ecf20Sopenharmony_ci if (curridx >= HP_SDC_QUEUE_LEN) { 3838c2ecf20Sopenharmony_ci curridx = -1; /* Wrap to top */ 3848c2ecf20Sopenharmony_ci continue; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci read_lock_irq(&hp_sdc.rtq_lock); 3878c2ecf20Sopenharmony_ci if (hp_sdc.rcurr == curridx) { 3888c2ecf20Sopenharmony_ci read_unlock_irq(&hp_sdc.rtq_lock); 3898c2ecf20Sopenharmony_ci continue; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci read_unlock_irq(&hp_sdc.rtq_lock); 3928c2ecf20Sopenharmony_ci if (hp_sdc.tq[curridx] != NULL) 3938c2ecf20Sopenharmony_ci break; /* Found one. */ 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci if (curridx == hp_sdc.wcurr) { /* There's nothing queued to do. */ 3968c2ecf20Sopenharmony_ci curridx = -1; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci hp_sdc.wcurr = curridx; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci start: 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci /* Check to see if the interrupt mask needs to be set. */ 4038c2ecf20Sopenharmony_ci if (hp_sdc.set_im) { 4048c2ecf20Sopenharmony_ci hp_sdc_status_out8(hp_sdc.im | HP_SDC_CMD_SET_IM); 4058c2ecf20Sopenharmony_ci hp_sdc.set_im = 0; 4068c2ecf20Sopenharmony_ci goto finish; 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci if (hp_sdc.wcurr == -1) 4108c2ecf20Sopenharmony_ci goto done; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci curr = hp_sdc.tq[curridx]; 4138c2ecf20Sopenharmony_ci idx = curr->actidx; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (curr->actidx >= curr->endidx) { 4168c2ecf20Sopenharmony_ci hp_sdc.tq[curridx] = NULL; 4178c2ecf20Sopenharmony_ci /* Interleave outbound data between the transactions. */ 4188c2ecf20Sopenharmony_ci hp_sdc.wcurr++; 4198c2ecf20Sopenharmony_ci if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) 4208c2ecf20Sopenharmony_ci hp_sdc.wcurr = 0; 4218c2ecf20Sopenharmony_ci goto finish; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci act = curr->seq[idx]; 4258c2ecf20Sopenharmony_ci idx++; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (curr->idx >= curr->endidx) { 4288c2ecf20Sopenharmony_ci if (act & HP_SDC_ACT_DEALLOC) 4298c2ecf20Sopenharmony_ci kfree(curr); 4308c2ecf20Sopenharmony_ci hp_sdc.tq[curridx] = NULL; 4318c2ecf20Sopenharmony_ci /* Interleave outbound data between the transactions. */ 4328c2ecf20Sopenharmony_ci hp_sdc.wcurr++; 4338c2ecf20Sopenharmony_ci if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) 4348c2ecf20Sopenharmony_ci hp_sdc.wcurr = 0; 4358c2ecf20Sopenharmony_ci goto finish; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci while (act & HP_SDC_ACT_PRECMD) { 4398c2ecf20Sopenharmony_ci if (curr->idx != idx) { 4408c2ecf20Sopenharmony_ci idx++; 4418c2ecf20Sopenharmony_ci act &= ~HP_SDC_ACT_PRECMD; 4428c2ecf20Sopenharmony_ci break; 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci hp_sdc_status_out8(curr->seq[idx]); 4458c2ecf20Sopenharmony_ci curr->idx++; 4468c2ecf20Sopenharmony_ci /* act finished? */ 4478c2ecf20Sopenharmony_ci if ((act & HP_SDC_ACT_DURING) == HP_SDC_ACT_PRECMD) 4488c2ecf20Sopenharmony_ci goto actdone; 4498c2ecf20Sopenharmony_ci /* skip quantity field if data-out sequence follows. */ 4508c2ecf20Sopenharmony_ci if (act & HP_SDC_ACT_DATAOUT) 4518c2ecf20Sopenharmony_ci curr->idx++; 4528c2ecf20Sopenharmony_ci goto finish; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci if (act & HP_SDC_ACT_DATAOUT) { 4558c2ecf20Sopenharmony_ci int qty; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci qty = curr->seq[idx]; 4588c2ecf20Sopenharmony_ci idx++; 4598c2ecf20Sopenharmony_ci if (curr->idx - idx < qty) { 4608c2ecf20Sopenharmony_ci hp_sdc_data_out8(curr->seq[curr->idx]); 4618c2ecf20Sopenharmony_ci curr->idx++; 4628c2ecf20Sopenharmony_ci /* act finished? */ 4638c2ecf20Sopenharmony_ci if (curr->idx - idx >= qty && 4648c2ecf20Sopenharmony_ci (act & HP_SDC_ACT_DURING) == HP_SDC_ACT_DATAOUT) 4658c2ecf20Sopenharmony_ci goto actdone; 4668c2ecf20Sopenharmony_ci goto finish; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci idx += qty; 4698c2ecf20Sopenharmony_ci act &= ~HP_SDC_ACT_DATAOUT; 4708c2ecf20Sopenharmony_ci } else 4718c2ecf20Sopenharmony_ci while (act & HP_SDC_ACT_DATAREG) { 4728c2ecf20Sopenharmony_ci int mask; 4738c2ecf20Sopenharmony_ci uint8_t w7[4]; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci mask = curr->seq[idx]; 4768c2ecf20Sopenharmony_ci if (idx != curr->idx) { 4778c2ecf20Sopenharmony_ci idx++; 4788c2ecf20Sopenharmony_ci idx += !!(mask & 1); 4798c2ecf20Sopenharmony_ci idx += !!(mask & 2); 4808c2ecf20Sopenharmony_ci idx += !!(mask & 4); 4818c2ecf20Sopenharmony_ci idx += !!(mask & 8); 4828c2ecf20Sopenharmony_ci act &= ~HP_SDC_ACT_DATAREG; 4838c2ecf20Sopenharmony_ci break; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci w7[0] = (mask & 1) ? curr->seq[++idx] : hp_sdc.r7[0]; 4878c2ecf20Sopenharmony_ci w7[1] = (mask & 2) ? curr->seq[++idx] : hp_sdc.r7[1]; 4888c2ecf20Sopenharmony_ci w7[2] = (mask & 4) ? curr->seq[++idx] : hp_sdc.r7[2]; 4898c2ecf20Sopenharmony_ci w7[3] = (mask & 8) ? curr->seq[++idx] : hp_sdc.r7[3]; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci if (hp_sdc.wi > 0x73 || hp_sdc.wi < 0x70 || 4928c2ecf20Sopenharmony_ci w7[hp_sdc.wi - 0x70] == hp_sdc.r7[hp_sdc.wi - 0x70]) { 4938c2ecf20Sopenharmony_ci int i = 0; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci /* Need to point the write index register */ 4968c2ecf20Sopenharmony_ci while (i < 4 && w7[i] == hp_sdc.r7[i]) 4978c2ecf20Sopenharmony_ci i++; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci if (i < 4) { 5008c2ecf20Sopenharmony_ci hp_sdc_status_out8(HP_SDC_CMD_SET_D0 + i); 5018c2ecf20Sopenharmony_ci hp_sdc.wi = 0x70 + i; 5028c2ecf20Sopenharmony_ci goto finish; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci idx++; 5068c2ecf20Sopenharmony_ci if ((act & HP_SDC_ACT_DURING) == HP_SDC_ACT_DATAREG) 5078c2ecf20Sopenharmony_ci goto actdone; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci curr->idx = idx; 5108c2ecf20Sopenharmony_ci act &= ~HP_SDC_ACT_DATAREG; 5118c2ecf20Sopenharmony_ci break; 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci hp_sdc_data_out8(w7[hp_sdc.wi - 0x70]); 5158c2ecf20Sopenharmony_ci hp_sdc.r7[hp_sdc.wi - 0x70] = w7[hp_sdc.wi - 0x70]; 5168c2ecf20Sopenharmony_ci hp_sdc.wi++; /* write index register autoincrements */ 5178c2ecf20Sopenharmony_ci { 5188c2ecf20Sopenharmony_ci int i = 0; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci while ((i < 4) && w7[i] == hp_sdc.r7[i]) 5218c2ecf20Sopenharmony_ci i++; 5228c2ecf20Sopenharmony_ci if (i >= 4) { 5238c2ecf20Sopenharmony_ci curr->idx = idx + 1; 5248c2ecf20Sopenharmony_ci if ((act & HP_SDC_ACT_DURING) == 5258c2ecf20Sopenharmony_ci HP_SDC_ACT_DATAREG) 5268c2ecf20Sopenharmony_ci goto actdone; 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci goto finish; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci /* We don't go any further in the command if there is a pending read, 5328c2ecf20Sopenharmony_ci because we don't want interleaved results. */ 5338c2ecf20Sopenharmony_ci read_lock_irq(&hp_sdc.rtq_lock); 5348c2ecf20Sopenharmony_ci if (hp_sdc.rcurr >= 0) { 5358c2ecf20Sopenharmony_ci read_unlock_irq(&hp_sdc.rtq_lock); 5368c2ecf20Sopenharmony_ci goto finish; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci read_unlock_irq(&hp_sdc.rtq_lock); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci if (act & HP_SDC_ACT_POSTCMD) { 5428c2ecf20Sopenharmony_ci uint8_t postcmd; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci /* curr->idx should == idx at this point. */ 5458c2ecf20Sopenharmony_ci postcmd = curr->seq[idx]; 5468c2ecf20Sopenharmony_ci curr->idx++; 5478c2ecf20Sopenharmony_ci if (act & HP_SDC_ACT_DATAIN) { 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci /* Start a new read */ 5508c2ecf20Sopenharmony_ci hp_sdc.rqty = curr->seq[curr->idx]; 5518c2ecf20Sopenharmony_ci hp_sdc.rtime = ktime_get(); 5528c2ecf20Sopenharmony_ci curr->idx++; 5538c2ecf20Sopenharmony_ci /* Still need to lock here in case of spurious irq. */ 5548c2ecf20Sopenharmony_ci write_lock_irq(&hp_sdc.rtq_lock); 5558c2ecf20Sopenharmony_ci hp_sdc.rcurr = curridx; 5568c2ecf20Sopenharmony_ci write_unlock_irq(&hp_sdc.rtq_lock); 5578c2ecf20Sopenharmony_ci hp_sdc_status_out8(postcmd); 5588c2ecf20Sopenharmony_ci goto finish; 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci hp_sdc_status_out8(postcmd); 5618c2ecf20Sopenharmony_ci goto actdone; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci actdone: 5658c2ecf20Sopenharmony_ci if (act & HP_SDC_ACT_SEMAPHORE) 5668c2ecf20Sopenharmony_ci up(curr->act.semaphore); 5678c2ecf20Sopenharmony_ci else if (act & HP_SDC_ACT_CALLBACK) 5688c2ecf20Sopenharmony_ci curr->act.irqhook(0,NULL,0,0); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci if (curr->idx >= curr->endidx) { /* This transaction is over. */ 5718c2ecf20Sopenharmony_ci if (act & HP_SDC_ACT_DEALLOC) 5728c2ecf20Sopenharmony_ci kfree(curr); 5738c2ecf20Sopenharmony_ci hp_sdc.tq[curridx] = NULL; 5748c2ecf20Sopenharmony_ci } else { 5758c2ecf20Sopenharmony_ci curr->actidx = idx + 1; 5768c2ecf20Sopenharmony_ci curr->idx = idx + 2; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci /* Interleave outbound data between the transactions. */ 5798c2ecf20Sopenharmony_ci hp_sdc.wcurr++; 5808c2ecf20Sopenharmony_ci if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) 5818c2ecf20Sopenharmony_ci hp_sdc.wcurr = 0; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci finish: 5848c2ecf20Sopenharmony_ci /* If by some quirk IBF has cleared and our ISR has run to 5858c2ecf20Sopenharmony_ci see that that has happened, do it all again. */ 5868c2ecf20Sopenharmony_ci if (!hp_sdc.ibf && limit++ < 20) 5878c2ecf20Sopenharmony_ci goto anew; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci done: 5908c2ecf20Sopenharmony_ci if (hp_sdc.wcurr >= 0) 5918c2ecf20Sopenharmony_ci tasklet_schedule(&hp_sdc.task); 5928c2ecf20Sopenharmony_ci write_unlock(&hp_sdc.lock); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci return 0; 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci/******* Functions called in either user or kernel context ****/ 5988c2ecf20Sopenharmony_ciint __hp_sdc_enqueue_transaction(hp_sdc_transaction *this) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci int i; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci if (this == NULL) { 6038c2ecf20Sopenharmony_ci BUG(); 6048c2ecf20Sopenharmony_ci return -EINVAL; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci /* Can't have same transaction on queue twice */ 6088c2ecf20Sopenharmony_ci for (i = 0; i < HP_SDC_QUEUE_LEN; i++) 6098c2ecf20Sopenharmony_ci if (hp_sdc.tq[i] == this) 6108c2ecf20Sopenharmony_ci goto fail; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci this->actidx = 0; 6138c2ecf20Sopenharmony_ci this->idx = 1; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci /* Search for empty slot */ 6168c2ecf20Sopenharmony_ci for (i = 0; i < HP_SDC_QUEUE_LEN; i++) 6178c2ecf20Sopenharmony_ci if (hp_sdc.tq[i] == NULL) { 6188c2ecf20Sopenharmony_ci hp_sdc.tq[i] = this; 6198c2ecf20Sopenharmony_ci tasklet_schedule(&hp_sdc.task); 6208c2ecf20Sopenharmony_ci return 0; 6218c2ecf20Sopenharmony_ci } 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci printk(KERN_WARNING PREFIX "No free slot to add transaction.\n"); 6248c2ecf20Sopenharmony_ci return -EBUSY; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci fail: 6278c2ecf20Sopenharmony_ci printk(KERN_WARNING PREFIX "Transaction add failed: transaction already queued?\n"); 6288c2ecf20Sopenharmony_ci return -EINVAL; 6298c2ecf20Sopenharmony_ci} 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ciint hp_sdc_enqueue_transaction(hp_sdc_transaction *this) { 6328c2ecf20Sopenharmony_ci unsigned long flags; 6338c2ecf20Sopenharmony_ci int ret; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci write_lock_irqsave(&hp_sdc.lock, flags); 6368c2ecf20Sopenharmony_ci ret = __hp_sdc_enqueue_transaction(this); 6378c2ecf20Sopenharmony_ci write_unlock_irqrestore(&hp_sdc.lock,flags); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci return ret; 6408c2ecf20Sopenharmony_ci} 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ciint hp_sdc_dequeue_transaction(hp_sdc_transaction *this) 6438c2ecf20Sopenharmony_ci{ 6448c2ecf20Sopenharmony_ci unsigned long flags; 6458c2ecf20Sopenharmony_ci int i; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci write_lock_irqsave(&hp_sdc.lock, flags); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci /* TODO: don't remove it if it's not done. */ 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci for (i = 0; i < HP_SDC_QUEUE_LEN; i++) 6528c2ecf20Sopenharmony_ci if (hp_sdc.tq[i] == this) 6538c2ecf20Sopenharmony_ci hp_sdc.tq[i] = NULL; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci write_unlock_irqrestore(&hp_sdc.lock, flags); 6568c2ecf20Sopenharmony_ci return 0; 6578c2ecf20Sopenharmony_ci} 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci/********************** User context functions **************************/ 6628c2ecf20Sopenharmony_ciint hp_sdc_request_timer_irq(hp_sdc_irqhook *callback) 6638c2ecf20Sopenharmony_ci{ 6648c2ecf20Sopenharmony_ci if (callback == NULL || hp_sdc.dev == NULL) 6658c2ecf20Sopenharmony_ci return -EINVAL; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci write_lock_irq(&hp_sdc.hook_lock); 6688c2ecf20Sopenharmony_ci if (hp_sdc.timer != NULL) { 6698c2ecf20Sopenharmony_ci write_unlock_irq(&hp_sdc.hook_lock); 6708c2ecf20Sopenharmony_ci return -EBUSY; 6718c2ecf20Sopenharmony_ci } 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci hp_sdc.timer = callback; 6748c2ecf20Sopenharmony_ci /* Enable interrupts from the timers */ 6758c2ecf20Sopenharmony_ci hp_sdc.im &= ~HP_SDC_IM_FH; 6768c2ecf20Sopenharmony_ci hp_sdc.im &= ~HP_SDC_IM_PT; 6778c2ecf20Sopenharmony_ci hp_sdc.im &= ~HP_SDC_IM_TIMERS; 6788c2ecf20Sopenharmony_ci hp_sdc.set_im = 1; 6798c2ecf20Sopenharmony_ci write_unlock_irq(&hp_sdc.hook_lock); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci tasklet_schedule(&hp_sdc.task); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci return 0; 6848c2ecf20Sopenharmony_ci} 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ciint hp_sdc_request_hil_irq(hp_sdc_irqhook *callback) 6878c2ecf20Sopenharmony_ci{ 6888c2ecf20Sopenharmony_ci if (callback == NULL || hp_sdc.dev == NULL) 6898c2ecf20Sopenharmony_ci return -EINVAL; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci write_lock_irq(&hp_sdc.hook_lock); 6928c2ecf20Sopenharmony_ci if (hp_sdc.hil != NULL) { 6938c2ecf20Sopenharmony_ci write_unlock_irq(&hp_sdc.hook_lock); 6948c2ecf20Sopenharmony_ci return -EBUSY; 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci hp_sdc.hil = callback; 6988c2ecf20Sopenharmony_ci hp_sdc.im &= ~(HP_SDC_IM_HIL | HP_SDC_IM_RESET); 6998c2ecf20Sopenharmony_ci hp_sdc.set_im = 1; 7008c2ecf20Sopenharmony_ci write_unlock_irq(&hp_sdc.hook_lock); 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci tasklet_schedule(&hp_sdc.task); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci return 0; 7058c2ecf20Sopenharmony_ci} 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ciint hp_sdc_request_cooked_irq(hp_sdc_irqhook *callback) 7088c2ecf20Sopenharmony_ci{ 7098c2ecf20Sopenharmony_ci if (callback == NULL || hp_sdc.dev == NULL) 7108c2ecf20Sopenharmony_ci return -EINVAL; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci write_lock_irq(&hp_sdc.hook_lock); 7138c2ecf20Sopenharmony_ci if (hp_sdc.cooked != NULL) { 7148c2ecf20Sopenharmony_ci write_unlock_irq(&hp_sdc.hook_lock); 7158c2ecf20Sopenharmony_ci return -EBUSY; 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci /* Enable interrupts from the HIL MLC */ 7198c2ecf20Sopenharmony_ci hp_sdc.cooked = callback; 7208c2ecf20Sopenharmony_ci hp_sdc.im &= ~(HP_SDC_IM_HIL | HP_SDC_IM_RESET); 7218c2ecf20Sopenharmony_ci hp_sdc.set_im = 1; 7228c2ecf20Sopenharmony_ci write_unlock_irq(&hp_sdc.hook_lock); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci tasklet_schedule(&hp_sdc.task); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci return 0; 7278c2ecf20Sopenharmony_ci} 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ciint hp_sdc_release_timer_irq(hp_sdc_irqhook *callback) 7308c2ecf20Sopenharmony_ci{ 7318c2ecf20Sopenharmony_ci write_lock_irq(&hp_sdc.hook_lock); 7328c2ecf20Sopenharmony_ci if ((callback != hp_sdc.timer) || 7338c2ecf20Sopenharmony_ci (hp_sdc.timer == NULL)) { 7348c2ecf20Sopenharmony_ci write_unlock_irq(&hp_sdc.hook_lock); 7358c2ecf20Sopenharmony_ci return -EINVAL; 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci /* Disable interrupts from the timers */ 7398c2ecf20Sopenharmony_ci hp_sdc.timer = NULL; 7408c2ecf20Sopenharmony_ci hp_sdc.im |= HP_SDC_IM_TIMERS; 7418c2ecf20Sopenharmony_ci hp_sdc.im |= HP_SDC_IM_FH; 7428c2ecf20Sopenharmony_ci hp_sdc.im |= HP_SDC_IM_PT; 7438c2ecf20Sopenharmony_ci hp_sdc.set_im = 1; 7448c2ecf20Sopenharmony_ci write_unlock_irq(&hp_sdc.hook_lock); 7458c2ecf20Sopenharmony_ci tasklet_schedule(&hp_sdc.task); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci return 0; 7488c2ecf20Sopenharmony_ci} 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ciint hp_sdc_release_hil_irq(hp_sdc_irqhook *callback) 7518c2ecf20Sopenharmony_ci{ 7528c2ecf20Sopenharmony_ci write_lock_irq(&hp_sdc.hook_lock); 7538c2ecf20Sopenharmony_ci if ((callback != hp_sdc.hil) || 7548c2ecf20Sopenharmony_ci (hp_sdc.hil == NULL)) { 7558c2ecf20Sopenharmony_ci write_unlock_irq(&hp_sdc.hook_lock); 7568c2ecf20Sopenharmony_ci return -EINVAL; 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci hp_sdc.hil = NULL; 7608c2ecf20Sopenharmony_ci /* Disable interrupts from HIL only if there is no cooked driver. */ 7618c2ecf20Sopenharmony_ci if(hp_sdc.cooked == NULL) { 7628c2ecf20Sopenharmony_ci hp_sdc.im |= (HP_SDC_IM_HIL | HP_SDC_IM_RESET); 7638c2ecf20Sopenharmony_ci hp_sdc.set_im = 1; 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci write_unlock_irq(&hp_sdc.hook_lock); 7668c2ecf20Sopenharmony_ci tasklet_schedule(&hp_sdc.task); 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci return 0; 7698c2ecf20Sopenharmony_ci} 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ciint hp_sdc_release_cooked_irq(hp_sdc_irqhook *callback) 7728c2ecf20Sopenharmony_ci{ 7738c2ecf20Sopenharmony_ci write_lock_irq(&hp_sdc.hook_lock); 7748c2ecf20Sopenharmony_ci if ((callback != hp_sdc.cooked) || 7758c2ecf20Sopenharmony_ci (hp_sdc.cooked == NULL)) { 7768c2ecf20Sopenharmony_ci write_unlock_irq(&hp_sdc.hook_lock); 7778c2ecf20Sopenharmony_ci return -EINVAL; 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci hp_sdc.cooked = NULL; 7818c2ecf20Sopenharmony_ci /* Disable interrupts from HIL only if there is no raw HIL driver. */ 7828c2ecf20Sopenharmony_ci if(hp_sdc.hil == NULL) { 7838c2ecf20Sopenharmony_ci hp_sdc.im |= (HP_SDC_IM_HIL | HP_SDC_IM_RESET); 7848c2ecf20Sopenharmony_ci hp_sdc.set_im = 1; 7858c2ecf20Sopenharmony_ci } 7868c2ecf20Sopenharmony_ci write_unlock_irq(&hp_sdc.hook_lock); 7878c2ecf20Sopenharmony_ci tasklet_schedule(&hp_sdc.task); 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci return 0; 7908c2ecf20Sopenharmony_ci} 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci/************************* Keepalive timer task *********************/ 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_cistatic void hp_sdc_kicker(struct timer_list *unused) 7958c2ecf20Sopenharmony_ci{ 7968c2ecf20Sopenharmony_ci tasklet_schedule(&hp_sdc.task); 7978c2ecf20Sopenharmony_ci /* Re-insert the periodic task. */ 7988c2ecf20Sopenharmony_ci mod_timer(&hp_sdc.kicker, jiffies + HZ); 7998c2ecf20Sopenharmony_ci} 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci/************************** Module Initialization ***************************/ 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci#if defined(__hppa__) 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_cistatic const struct parisc_device_id hp_sdc_tbl[] __initconst = { 8068c2ecf20Sopenharmony_ci { 8078c2ecf20Sopenharmony_ci .hw_type = HPHW_FIO, 8088c2ecf20Sopenharmony_ci .hversion_rev = HVERSION_REV_ANY_ID, 8098c2ecf20Sopenharmony_ci .hversion = HVERSION_ANY_ID, 8108c2ecf20Sopenharmony_ci .sversion = 0x73, 8118c2ecf20Sopenharmony_ci }, 8128c2ecf20Sopenharmony_ci { 0, } 8138c2ecf20Sopenharmony_ci}; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(parisc, hp_sdc_tbl); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_cistatic int __init hp_sdc_init_hppa(struct parisc_device *d); 8188c2ecf20Sopenharmony_cistatic struct delayed_work moduleloader_work; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_cistatic struct parisc_driver hp_sdc_driver __refdata = { 8218c2ecf20Sopenharmony_ci .name = "hp_sdc", 8228c2ecf20Sopenharmony_ci .id_table = hp_sdc_tbl, 8238c2ecf20Sopenharmony_ci .probe = hp_sdc_init_hppa, 8248c2ecf20Sopenharmony_ci}; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci#endif /* __hppa__ */ 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_cistatic int __init hp_sdc_init(void) 8298c2ecf20Sopenharmony_ci{ 8308c2ecf20Sopenharmony_ci char *errstr; 8318c2ecf20Sopenharmony_ci hp_sdc_transaction t_sync; 8328c2ecf20Sopenharmony_ci uint8_t ts_sync[6]; 8338c2ecf20Sopenharmony_ci struct semaphore s_sync; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci rwlock_init(&hp_sdc.lock); 8368c2ecf20Sopenharmony_ci rwlock_init(&hp_sdc.ibf_lock); 8378c2ecf20Sopenharmony_ci rwlock_init(&hp_sdc.rtq_lock); 8388c2ecf20Sopenharmony_ci rwlock_init(&hp_sdc.hook_lock); 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci hp_sdc.timer = NULL; 8418c2ecf20Sopenharmony_ci hp_sdc.hil = NULL; 8428c2ecf20Sopenharmony_ci hp_sdc.pup = NULL; 8438c2ecf20Sopenharmony_ci hp_sdc.cooked = NULL; 8448c2ecf20Sopenharmony_ci hp_sdc.im = HP_SDC_IM_MASK; /* Mask maskable irqs */ 8458c2ecf20Sopenharmony_ci hp_sdc.set_im = 1; 8468c2ecf20Sopenharmony_ci hp_sdc.wi = 0xff; 8478c2ecf20Sopenharmony_ci hp_sdc.r7[0] = 0xff; 8488c2ecf20Sopenharmony_ci hp_sdc.r7[1] = 0xff; 8498c2ecf20Sopenharmony_ci hp_sdc.r7[2] = 0xff; 8508c2ecf20Sopenharmony_ci hp_sdc.r7[3] = 0xff; 8518c2ecf20Sopenharmony_ci hp_sdc.ibf = 1; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci memset(&hp_sdc.tq, 0, sizeof(hp_sdc.tq)); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci hp_sdc.wcurr = -1; 8568c2ecf20Sopenharmony_ci hp_sdc.rcurr = -1; 8578c2ecf20Sopenharmony_ci hp_sdc.rqty = 0; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci hp_sdc.dev_err = -ENODEV; 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci errstr = "IO not found for"; 8628c2ecf20Sopenharmony_ci if (!hp_sdc.base_io) 8638c2ecf20Sopenharmony_ci goto err0; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci errstr = "IRQ not found for"; 8668c2ecf20Sopenharmony_ci if (!hp_sdc.irq) 8678c2ecf20Sopenharmony_ci goto err0; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci hp_sdc.dev_err = -EBUSY; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci#if defined(__hppa__) 8728c2ecf20Sopenharmony_ci errstr = "IO not available for"; 8738c2ecf20Sopenharmony_ci if (request_region(hp_sdc.data_io, 2, hp_sdc_driver.name)) 8748c2ecf20Sopenharmony_ci goto err0; 8758c2ecf20Sopenharmony_ci#endif 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci errstr = "IRQ not available for"; 8788c2ecf20Sopenharmony_ci if (request_irq(hp_sdc.irq, &hp_sdc_isr, IRQF_SHARED, 8798c2ecf20Sopenharmony_ci "HP SDC", &hp_sdc)) 8808c2ecf20Sopenharmony_ci goto err1; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci errstr = "NMI not available for"; 8838c2ecf20Sopenharmony_ci if (request_irq(hp_sdc.nmi, &hp_sdc_nmisr, IRQF_SHARED, 8848c2ecf20Sopenharmony_ci "HP SDC NMI", &hp_sdc)) 8858c2ecf20Sopenharmony_ci goto err2; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci pr_info(PREFIX "HP SDC at 0x%08lx, IRQ %d (NMI IRQ %d)\n", 8888c2ecf20Sopenharmony_ci hp_sdc.base_io, hp_sdc.irq, hp_sdc.nmi); 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci hp_sdc_status_in8(); 8918c2ecf20Sopenharmony_ci hp_sdc_data_in8(); 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci tasklet_init(&hp_sdc.task, hp_sdc_tasklet, 0); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci /* Sync the output buffer registers, thus scheduling hp_sdc_tasklet. */ 8968c2ecf20Sopenharmony_ci t_sync.actidx = 0; 8978c2ecf20Sopenharmony_ci t_sync.idx = 1; 8988c2ecf20Sopenharmony_ci t_sync.endidx = 6; 8998c2ecf20Sopenharmony_ci t_sync.seq = ts_sync; 9008c2ecf20Sopenharmony_ci ts_sync[0] = HP_SDC_ACT_DATAREG | HP_SDC_ACT_SEMAPHORE; 9018c2ecf20Sopenharmony_ci ts_sync[1] = 0x0f; 9028c2ecf20Sopenharmony_ci ts_sync[2] = ts_sync[3] = ts_sync[4] = ts_sync[5] = 0; 9038c2ecf20Sopenharmony_ci t_sync.act.semaphore = &s_sync; 9048c2ecf20Sopenharmony_ci sema_init(&s_sync, 0); 9058c2ecf20Sopenharmony_ci hp_sdc_enqueue_transaction(&t_sync); 9068c2ecf20Sopenharmony_ci down(&s_sync); /* Wait for t_sync to complete */ 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci /* Create the keepalive task */ 9098c2ecf20Sopenharmony_ci timer_setup(&hp_sdc.kicker, hp_sdc_kicker, 0); 9108c2ecf20Sopenharmony_ci hp_sdc.kicker.expires = jiffies + HZ; 9118c2ecf20Sopenharmony_ci add_timer(&hp_sdc.kicker); 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci hp_sdc.dev_err = 0; 9148c2ecf20Sopenharmony_ci return 0; 9158c2ecf20Sopenharmony_ci err2: 9168c2ecf20Sopenharmony_ci free_irq(hp_sdc.irq, &hp_sdc); 9178c2ecf20Sopenharmony_ci err1: 9188c2ecf20Sopenharmony_ci release_region(hp_sdc.data_io, 2); 9198c2ecf20Sopenharmony_ci err0: 9208c2ecf20Sopenharmony_ci printk(KERN_WARNING PREFIX ": %s SDC IO=0x%p IRQ=0x%x NMI=0x%x\n", 9218c2ecf20Sopenharmony_ci errstr, (void *)hp_sdc.base_io, hp_sdc.irq, hp_sdc.nmi); 9228c2ecf20Sopenharmony_ci hp_sdc.dev = NULL; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci return hp_sdc.dev_err; 9258c2ecf20Sopenharmony_ci} 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci#if defined(__hppa__) 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_cistatic void request_module_delayed(struct work_struct *work) 9308c2ecf20Sopenharmony_ci{ 9318c2ecf20Sopenharmony_ci request_module("hp_sdc_mlc"); 9328c2ecf20Sopenharmony_ci} 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_cistatic int __init hp_sdc_init_hppa(struct parisc_device *d) 9358c2ecf20Sopenharmony_ci{ 9368c2ecf20Sopenharmony_ci int ret; 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci if (!d) 9398c2ecf20Sopenharmony_ci return 1; 9408c2ecf20Sopenharmony_ci if (hp_sdc.dev != NULL) 9418c2ecf20Sopenharmony_ci return 1; /* We only expect one SDC */ 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci hp_sdc.dev = d; 9448c2ecf20Sopenharmony_ci hp_sdc.irq = d->irq; 9458c2ecf20Sopenharmony_ci hp_sdc.nmi = d->aux_irq; 9468c2ecf20Sopenharmony_ci hp_sdc.base_io = d->hpa.start; 9478c2ecf20Sopenharmony_ci hp_sdc.data_io = d->hpa.start + 0x800; 9488c2ecf20Sopenharmony_ci hp_sdc.status_io = d->hpa.start + 0x801; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&moduleloader_work, request_module_delayed); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci ret = hp_sdc_init(); 9538c2ecf20Sopenharmony_ci /* after successful initialization give SDC some time to settle 9548c2ecf20Sopenharmony_ci * and then load the hp_sdc_mlc upper layer driver */ 9558c2ecf20Sopenharmony_ci if (!ret) 9568c2ecf20Sopenharmony_ci schedule_delayed_work(&moduleloader_work, 9578c2ecf20Sopenharmony_ci msecs_to_jiffies(2000)); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci return ret; 9608c2ecf20Sopenharmony_ci} 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci#endif /* __hppa__ */ 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_cistatic void hp_sdc_exit(void) 9658c2ecf20Sopenharmony_ci{ 9668c2ecf20Sopenharmony_ci /* do nothing if we don't have a SDC */ 9678c2ecf20Sopenharmony_ci if (!hp_sdc.dev) 9688c2ecf20Sopenharmony_ci return; 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci write_lock_irq(&hp_sdc.lock); 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci /* Turn off all maskable "sub-function" irq's. */ 9738c2ecf20Sopenharmony_ci hp_sdc_spin_ibf(); 9748c2ecf20Sopenharmony_ci sdc_writeb(HP_SDC_CMD_SET_IM | HP_SDC_IM_MASK, hp_sdc.status_io); 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci /* Wait until we know this has been processed by the i8042 */ 9778c2ecf20Sopenharmony_ci hp_sdc_spin_ibf(); 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci free_irq(hp_sdc.nmi, &hp_sdc); 9808c2ecf20Sopenharmony_ci free_irq(hp_sdc.irq, &hp_sdc); 9818c2ecf20Sopenharmony_ci write_unlock_irq(&hp_sdc.lock); 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci del_timer_sync(&hp_sdc.kicker); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci tasklet_kill(&hp_sdc.task); 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci#if defined(__hppa__) 9888c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&moduleloader_work); 9898c2ecf20Sopenharmony_ci if (unregister_parisc_driver(&hp_sdc_driver)) 9908c2ecf20Sopenharmony_ci printk(KERN_WARNING PREFIX "Error unregistering HP SDC"); 9918c2ecf20Sopenharmony_ci#endif 9928c2ecf20Sopenharmony_ci} 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_cistatic int __init hp_sdc_register(void) 9958c2ecf20Sopenharmony_ci{ 9968c2ecf20Sopenharmony_ci hp_sdc_transaction tq_init; 9978c2ecf20Sopenharmony_ci uint8_t tq_init_seq[5]; 9988c2ecf20Sopenharmony_ci struct semaphore tq_init_sem; 9998c2ecf20Sopenharmony_ci#if defined(__mc68000__) 10008c2ecf20Sopenharmony_ci unsigned char i; 10018c2ecf20Sopenharmony_ci#endif 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci if (hp_sdc_disabled) { 10048c2ecf20Sopenharmony_ci printk(KERN_WARNING PREFIX "HP SDC driver disabled by no_hpsdc=1.\n"); 10058c2ecf20Sopenharmony_ci return -ENODEV; 10068c2ecf20Sopenharmony_ci } 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci hp_sdc.dev = NULL; 10098c2ecf20Sopenharmony_ci hp_sdc.dev_err = 0; 10108c2ecf20Sopenharmony_ci#if defined(__hppa__) 10118c2ecf20Sopenharmony_ci if (register_parisc_driver(&hp_sdc_driver)) { 10128c2ecf20Sopenharmony_ci printk(KERN_WARNING PREFIX "Error registering SDC with system bus tree.\n"); 10138c2ecf20Sopenharmony_ci return -ENODEV; 10148c2ecf20Sopenharmony_ci } 10158c2ecf20Sopenharmony_ci#elif defined(__mc68000__) 10168c2ecf20Sopenharmony_ci if (!MACH_IS_HP300) 10178c2ecf20Sopenharmony_ci return -ENODEV; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci hp_sdc.irq = 1; 10208c2ecf20Sopenharmony_ci hp_sdc.nmi = 7; 10218c2ecf20Sopenharmony_ci hp_sdc.base_io = (unsigned long) 0xf0428000; 10228c2ecf20Sopenharmony_ci hp_sdc.data_io = (unsigned long) hp_sdc.base_io + 1; 10238c2ecf20Sopenharmony_ci hp_sdc.status_io = (unsigned long) hp_sdc.base_io + 3; 10248c2ecf20Sopenharmony_ci if (!copy_from_kernel_nofault(&i, (unsigned char *)hp_sdc.data_io, 1)) 10258c2ecf20Sopenharmony_ci hp_sdc.dev = (void *)1; 10268c2ecf20Sopenharmony_ci hp_sdc.dev_err = hp_sdc_init(); 10278c2ecf20Sopenharmony_ci#endif 10288c2ecf20Sopenharmony_ci if (hp_sdc.dev == NULL) { 10298c2ecf20Sopenharmony_ci printk(KERN_WARNING PREFIX "No SDC found.\n"); 10308c2ecf20Sopenharmony_ci return hp_sdc.dev_err; 10318c2ecf20Sopenharmony_ci } 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci sema_init(&tq_init_sem, 0); 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci tq_init.actidx = 0; 10368c2ecf20Sopenharmony_ci tq_init.idx = 1; 10378c2ecf20Sopenharmony_ci tq_init.endidx = 5; 10388c2ecf20Sopenharmony_ci tq_init.seq = tq_init_seq; 10398c2ecf20Sopenharmony_ci tq_init.act.semaphore = &tq_init_sem; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci tq_init_seq[0] = 10428c2ecf20Sopenharmony_ci HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN | HP_SDC_ACT_SEMAPHORE; 10438c2ecf20Sopenharmony_ci tq_init_seq[1] = HP_SDC_CMD_READ_KCC; 10448c2ecf20Sopenharmony_ci tq_init_seq[2] = 1; 10458c2ecf20Sopenharmony_ci tq_init_seq[3] = 0; 10468c2ecf20Sopenharmony_ci tq_init_seq[4] = 0; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci hp_sdc_enqueue_transaction(&tq_init); 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci down(&tq_init_sem); 10518c2ecf20Sopenharmony_ci up(&tq_init_sem); 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci if ((tq_init_seq[0] & HP_SDC_ACT_DEAD) == HP_SDC_ACT_DEAD) { 10548c2ecf20Sopenharmony_ci printk(KERN_WARNING PREFIX "Error reading config byte.\n"); 10558c2ecf20Sopenharmony_ci hp_sdc_exit(); 10568c2ecf20Sopenharmony_ci return -ENODEV; 10578c2ecf20Sopenharmony_ci } 10588c2ecf20Sopenharmony_ci hp_sdc.r11 = tq_init_seq[4]; 10598c2ecf20Sopenharmony_ci if (hp_sdc.r11 & HP_SDC_CFG_NEW) { 10608c2ecf20Sopenharmony_ci const char *str; 10618c2ecf20Sopenharmony_ci printk(KERN_INFO PREFIX "New style SDC\n"); 10628c2ecf20Sopenharmony_ci tq_init_seq[1] = HP_SDC_CMD_READ_XTD; 10638c2ecf20Sopenharmony_ci tq_init.actidx = 0; 10648c2ecf20Sopenharmony_ci tq_init.idx = 1; 10658c2ecf20Sopenharmony_ci down(&tq_init_sem); 10668c2ecf20Sopenharmony_ci hp_sdc_enqueue_transaction(&tq_init); 10678c2ecf20Sopenharmony_ci down(&tq_init_sem); 10688c2ecf20Sopenharmony_ci up(&tq_init_sem); 10698c2ecf20Sopenharmony_ci if ((tq_init_seq[0] & HP_SDC_ACT_DEAD) == HP_SDC_ACT_DEAD) { 10708c2ecf20Sopenharmony_ci printk(KERN_WARNING PREFIX "Error reading extended config byte.\n"); 10718c2ecf20Sopenharmony_ci return -ENODEV; 10728c2ecf20Sopenharmony_ci } 10738c2ecf20Sopenharmony_ci hp_sdc.r7e = tq_init_seq[4]; 10748c2ecf20Sopenharmony_ci HP_SDC_XTD_REV_STRINGS(hp_sdc.r7e & HP_SDC_XTD_REV, str) 10758c2ecf20Sopenharmony_ci printk(KERN_INFO PREFIX "Revision: %s\n", str); 10768c2ecf20Sopenharmony_ci if (hp_sdc.r7e & HP_SDC_XTD_BEEPER) 10778c2ecf20Sopenharmony_ci printk(KERN_INFO PREFIX "TI SN76494 beeper present\n"); 10788c2ecf20Sopenharmony_ci if (hp_sdc.r7e & HP_SDC_XTD_BBRTC) 10798c2ecf20Sopenharmony_ci printk(KERN_INFO PREFIX "OKI MSM-58321 BBRTC present\n"); 10808c2ecf20Sopenharmony_ci printk(KERN_INFO PREFIX "Spunking the self test register to force PUP " 10818c2ecf20Sopenharmony_ci "on next firmware reset.\n"); 10828c2ecf20Sopenharmony_ci tq_init_seq[0] = HP_SDC_ACT_PRECMD | 10838c2ecf20Sopenharmony_ci HP_SDC_ACT_DATAOUT | HP_SDC_ACT_SEMAPHORE; 10848c2ecf20Sopenharmony_ci tq_init_seq[1] = HP_SDC_CMD_SET_STR; 10858c2ecf20Sopenharmony_ci tq_init_seq[2] = 1; 10868c2ecf20Sopenharmony_ci tq_init_seq[3] = 0; 10878c2ecf20Sopenharmony_ci tq_init.actidx = 0; 10888c2ecf20Sopenharmony_ci tq_init.idx = 1; 10898c2ecf20Sopenharmony_ci tq_init.endidx = 4; 10908c2ecf20Sopenharmony_ci down(&tq_init_sem); 10918c2ecf20Sopenharmony_ci hp_sdc_enqueue_transaction(&tq_init); 10928c2ecf20Sopenharmony_ci down(&tq_init_sem); 10938c2ecf20Sopenharmony_ci up(&tq_init_sem); 10948c2ecf20Sopenharmony_ci } else 10958c2ecf20Sopenharmony_ci printk(KERN_INFO PREFIX "Old style SDC (1820-%s).\n", 10968c2ecf20Sopenharmony_ci (hp_sdc.r11 & HP_SDC_CFG_REV) ? "3300" : "2564/3087"); 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci return 0; 10998c2ecf20Sopenharmony_ci} 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_cimodule_init(hp_sdc_register); 11028c2ecf20Sopenharmony_cimodule_exit(hp_sdc_exit); 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci/* Timing notes: These measurements taken on my 64MHz 7100-LC (715/64) 11058c2ecf20Sopenharmony_ci * cycles cycles-adj time 11068c2ecf20Sopenharmony_ci * between two consecutive mfctl(16)'s: 4 n/a 63ns 11078c2ecf20Sopenharmony_ci * hp_sdc_spin_ibf when idle: 119 115 1.7us 11088c2ecf20Sopenharmony_ci * gsc_writeb status register: 83 79 1.2us 11098c2ecf20Sopenharmony_ci * IBF to clear after sending SET_IM: 6204 6006 93us 11108c2ecf20Sopenharmony_ci * IBF to clear after sending LOAD_RT: 4467 4352 68us 11118c2ecf20Sopenharmony_ci * IBF to clear after sending two LOAD_RTs: 18974 18859 295us 11128c2ecf20Sopenharmony_ci * READ_T1, read status/data, IRQ, call handler: 35564 n/a 556us 11138c2ecf20Sopenharmony_ci * cmd to ~IBF READ_T1 2nd time right after: 5158403 n/a 81ms 11148c2ecf20Sopenharmony_ci * between IRQ received and ~IBF for above: 2578877 n/a 40ms 11158c2ecf20Sopenharmony_ci * 11168c2ecf20Sopenharmony_ci * Performance stats after a run of this module configuring HIL and 11178c2ecf20Sopenharmony_ci * receiving a few mouse events: 11188c2ecf20Sopenharmony_ci * 11198c2ecf20Sopenharmony_ci * status in8 282508 cycles 7128 calls 11208c2ecf20Sopenharmony_ci * status out8 8404 cycles 341 calls 11218c2ecf20Sopenharmony_ci * data out8 1734 cycles 78 calls 11228c2ecf20Sopenharmony_ci * isr 174324 cycles 617 calls (includes take) 11238c2ecf20Sopenharmony_ci * take 1241 cycles 2 calls 11248c2ecf20Sopenharmony_ci * put 1411504 cycles 6937 calls 11258c2ecf20Sopenharmony_ci * task 1655209 cycles 6937 calls (includes put) 11268c2ecf20Sopenharmony_ci * 11278c2ecf20Sopenharmony_ci */ 1128