162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * HP i8042-based System Device Controller driver. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2001 Brian S. Julin 562306a36Sopenharmony_ci * All rights reserved. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 862306a36Sopenharmony_ci * modification, are permitted provided that the following conditions 962306a36Sopenharmony_ci * are met: 1062306a36Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright 1162306a36Sopenharmony_ci * notice, this list of conditions, and the following disclaimer, 1262306a36Sopenharmony_ci * without modification. 1362306a36Sopenharmony_ci * 2. The name of the author may not be used to endorse or promote products 1462306a36Sopenharmony_ci * derived from this software without specific prior written permission. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * Alternatively, this software may be distributed under the terms of the 1762306a36Sopenharmony_ci * GNU General Public License ("GPL"). 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2062306a36Sopenharmony_ci * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2162306a36Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2262306a36Sopenharmony_ci * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 2362306a36Sopenharmony_ci * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2462306a36Sopenharmony_ci * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2562306a36Sopenharmony_ci * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2662306a36Sopenharmony_ci * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2762306a36Sopenharmony_ci * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * References: 3062306a36Sopenharmony_ci * System Device Controller Microprocessor Firmware Theory of Operation 3162306a36Sopenharmony_ci * for Part Number 1820-4784 Revision B. Dwg No. A-1820-4784-2 3262306a36Sopenharmony_ci * Helge Deller's original hilkbd.c port for PA-RISC. 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * Driver theory of operation: 3662306a36Sopenharmony_ci * 3762306a36Sopenharmony_ci * hp_sdc_put does all writing to the SDC. ISR can run on a different 3862306a36Sopenharmony_ci * CPU than hp_sdc_put, but only one CPU runs hp_sdc_put at a time 3962306a36Sopenharmony_ci * (it cannot really benefit from SMP anyway.) A tasket fit this perfectly. 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * All data coming back from the SDC is sent via interrupt and can be read 4262306a36Sopenharmony_ci * fully in the ISR, so there are no latency/throughput problems there. 4362306a36Sopenharmony_ci * The problem is with output, due to the slow clock speed of the SDC 4462306a36Sopenharmony_ci * compared to the CPU. This should not be too horrible most of the time, 4562306a36Sopenharmony_ci * but if used with HIL devices that support the multibyte transfer command, 4662306a36Sopenharmony_ci * keeping outbound throughput flowing at the 6500KBps that the HIL is 4762306a36Sopenharmony_ci * capable of is more than can be done at HZ=100. 4862306a36Sopenharmony_ci * 4962306a36Sopenharmony_ci * Busy polling for IBF clear wastes CPU cycles and bus cycles. hp_sdc.ibf 5062306a36Sopenharmony_ci * is set to 0 when the IBF flag in the status register has cleared. ISR 5162306a36Sopenharmony_ci * may do this, and may also access the parts of queued transactions related 5262306a36Sopenharmony_ci * to reading data back from the SDC, but otherwise will not touch the 5362306a36Sopenharmony_ci * hp_sdc state. Whenever a register is written hp_sdc.ibf is set to 1. 5462306a36Sopenharmony_ci * 5562306a36Sopenharmony_ci * The i8042 write index and the values in the 4-byte input buffer 5662306a36Sopenharmony_ci * starting at 0x70 are kept track of in hp_sdc.wi, and .r7[], respectively, 5762306a36Sopenharmony_ci * to minimize the amount of IO needed to the SDC. However these values 5862306a36Sopenharmony_ci * do not need to be locked since they are only ever accessed by hp_sdc_put. 5962306a36Sopenharmony_ci * 6062306a36Sopenharmony_ci * A timer task schedules the tasklet once per second just to make 6162306a36Sopenharmony_ci * sure it doesn't freeze up and to allow for bad reads to time out. 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#include <linux/hp_sdc.h> 6562306a36Sopenharmony_ci#include <linux/errno.h> 6662306a36Sopenharmony_ci#include <linux/init.h> 6762306a36Sopenharmony_ci#include <linux/module.h> 6862306a36Sopenharmony_ci#include <linux/ioport.h> 6962306a36Sopenharmony_ci#include <linux/time.h> 7062306a36Sopenharmony_ci#include <linux/semaphore.h> 7162306a36Sopenharmony_ci#include <linux/slab.h> 7262306a36Sopenharmony_ci#include <linux/hil.h> 7362306a36Sopenharmony_ci#include <asm/io.h> 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/* Machine-specific abstraction */ 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#if defined(__hppa__) 7862306a36Sopenharmony_ci# include <asm/parisc-device.h> 7962306a36Sopenharmony_ci# define sdc_readb(p) gsc_readb(p) 8062306a36Sopenharmony_ci# define sdc_writeb(v,p) gsc_writeb((v),(p)) 8162306a36Sopenharmony_ci#elif defined(__mc68000__) 8262306a36Sopenharmony_ci#include <linux/uaccess.h> 8362306a36Sopenharmony_ci# define sdc_readb(p) in_8(p) 8462306a36Sopenharmony_ci# define sdc_writeb(v,p) out_8((p),(v)) 8562306a36Sopenharmony_ci#else 8662306a36Sopenharmony_ci# error "HIL is not supported on this platform" 8762306a36Sopenharmony_ci#endif 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#define PREFIX "HP SDC: " 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ciMODULE_AUTHOR("Brian S. Julin <bri@calyx.com>"); 9262306a36Sopenharmony_ciMODULE_DESCRIPTION("HP i8042-based SDC Driver"); 9362306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ciEXPORT_SYMBOL(hp_sdc_request_timer_irq); 9662306a36Sopenharmony_ciEXPORT_SYMBOL(hp_sdc_request_hil_irq); 9762306a36Sopenharmony_ciEXPORT_SYMBOL(hp_sdc_request_cooked_irq); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ciEXPORT_SYMBOL(hp_sdc_release_timer_irq); 10062306a36Sopenharmony_ciEXPORT_SYMBOL(hp_sdc_release_hil_irq); 10162306a36Sopenharmony_ciEXPORT_SYMBOL(hp_sdc_release_cooked_irq); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ciEXPORT_SYMBOL(__hp_sdc_enqueue_transaction); 10462306a36Sopenharmony_ciEXPORT_SYMBOL(hp_sdc_enqueue_transaction); 10562306a36Sopenharmony_ciEXPORT_SYMBOL(hp_sdc_dequeue_transaction); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic bool hp_sdc_disabled; 10862306a36Sopenharmony_cimodule_param_named(no_hpsdc, hp_sdc_disabled, bool, 0); 10962306a36Sopenharmony_ciMODULE_PARM_DESC(no_hpsdc, "Do not enable HP SDC driver."); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic hp_i8042_sdc hp_sdc; /* All driver state is kept in here. */ 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/*************** primitives for use in any context *********************/ 11462306a36Sopenharmony_cistatic inline uint8_t hp_sdc_status_in8(void) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci uint8_t status; 11762306a36Sopenharmony_ci unsigned long flags; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci write_lock_irqsave(&hp_sdc.ibf_lock, flags); 12062306a36Sopenharmony_ci status = sdc_readb(hp_sdc.status_io); 12162306a36Sopenharmony_ci if (!(status & HP_SDC_STATUS_IBF)) 12262306a36Sopenharmony_ci hp_sdc.ibf = 0; 12362306a36Sopenharmony_ci write_unlock_irqrestore(&hp_sdc.ibf_lock, flags); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return status; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic inline uint8_t hp_sdc_data_in8(void) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci return sdc_readb(hp_sdc.data_io); 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic inline void hp_sdc_status_out8(uint8_t val) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci unsigned long flags; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci write_lock_irqsave(&hp_sdc.ibf_lock, flags); 13862306a36Sopenharmony_ci hp_sdc.ibf = 1; 13962306a36Sopenharmony_ci if ((val & 0xf0) == 0xe0) 14062306a36Sopenharmony_ci hp_sdc.wi = 0xff; 14162306a36Sopenharmony_ci sdc_writeb(val, hp_sdc.status_io); 14262306a36Sopenharmony_ci write_unlock_irqrestore(&hp_sdc.ibf_lock, flags); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic inline void hp_sdc_data_out8(uint8_t val) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci unsigned long flags; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci write_lock_irqsave(&hp_sdc.ibf_lock, flags); 15062306a36Sopenharmony_ci hp_sdc.ibf = 1; 15162306a36Sopenharmony_ci sdc_writeb(val, hp_sdc.data_io); 15262306a36Sopenharmony_ci write_unlock_irqrestore(&hp_sdc.ibf_lock, flags); 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci/* Care must be taken to only invoke hp_sdc_spin_ibf when 15662306a36Sopenharmony_ci * absolutely needed, or in rarely invoked subroutines. 15762306a36Sopenharmony_ci * Not only does it waste CPU cycles, it also wastes bus cycles. 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_cistatic inline void hp_sdc_spin_ibf(void) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci unsigned long flags; 16262306a36Sopenharmony_ci rwlock_t *lock; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci lock = &hp_sdc.ibf_lock; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci read_lock_irqsave(lock, flags); 16762306a36Sopenharmony_ci if (!hp_sdc.ibf) { 16862306a36Sopenharmony_ci read_unlock_irqrestore(lock, flags); 16962306a36Sopenharmony_ci return; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci read_unlock(lock); 17262306a36Sopenharmony_ci write_lock(lock); 17362306a36Sopenharmony_ci while (sdc_readb(hp_sdc.status_io) & HP_SDC_STATUS_IBF) 17462306a36Sopenharmony_ci { } 17562306a36Sopenharmony_ci hp_sdc.ibf = 0; 17662306a36Sopenharmony_ci write_unlock_irqrestore(lock, flags); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci/************************ Interrupt context functions ************************/ 18162306a36Sopenharmony_cistatic void hp_sdc_take(int irq, void *dev_id, uint8_t status, uint8_t data) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci hp_sdc_transaction *curr; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci read_lock(&hp_sdc.rtq_lock); 18662306a36Sopenharmony_ci if (hp_sdc.rcurr < 0) { 18762306a36Sopenharmony_ci read_unlock(&hp_sdc.rtq_lock); 18862306a36Sopenharmony_ci return; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci curr = hp_sdc.tq[hp_sdc.rcurr]; 19162306a36Sopenharmony_ci read_unlock(&hp_sdc.rtq_lock); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci curr->seq[curr->idx++] = status; 19462306a36Sopenharmony_ci curr->seq[curr->idx++] = data; 19562306a36Sopenharmony_ci hp_sdc.rqty -= 2; 19662306a36Sopenharmony_ci hp_sdc.rtime = ktime_get(); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (hp_sdc.rqty <= 0) { 19962306a36Sopenharmony_ci /* All data has been gathered. */ 20062306a36Sopenharmony_ci if (curr->seq[curr->actidx] & HP_SDC_ACT_SEMAPHORE) 20162306a36Sopenharmony_ci if (curr->act.semaphore) 20262306a36Sopenharmony_ci up(curr->act.semaphore); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (curr->seq[curr->actidx] & HP_SDC_ACT_CALLBACK) 20562306a36Sopenharmony_ci if (curr->act.irqhook) 20662306a36Sopenharmony_ci curr->act.irqhook(irq, dev_id, status, data); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci curr->actidx = curr->idx; 20962306a36Sopenharmony_ci curr->idx++; 21062306a36Sopenharmony_ci /* Return control of this transaction */ 21162306a36Sopenharmony_ci write_lock(&hp_sdc.rtq_lock); 21262306a36Sopenharmony_ci hp_sdc.rcurr = -1; 21362306a36Sopenharmony_ci hp_sdc.rqty = 0; 21462306a36Sopenharmony_ci write_unlock(&hp_sdc.rtq_lock); 21562306a36Sopenharmony_ci tasklet_schedule(&hp_sdc.task); 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic irqreturn_t hp_sdc_isr(int irq, void *dev_id) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci uint8_t status, data; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci status = hp_sdc_status_in8(); 22462306a36Sopenharmony_ci /* Read data unconditionally to advance i8042. */ 22562306a36Sopenharmony_ci data = hp_sdc_data_in8(); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* For now we are ignoring these until we get the SDC to behave. */ 22862306a36Sopenharmony_ci if (((status & 0xf1) == 0x51) && data == 0x82) 22962306a36Sopenharmony_ci return IRQ_HANDLED; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci switch (status & HP_SDC_STATUS_IRQMASK) { 23262306a36Sopenharmony_ci case 0: /* This case is not documented. */ 23362306a36Sopenharmony_ci break; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci case HP_SDC_STATUS_USERTIMER: 23662306a36Sopenharmony_ci case HP_SDC_STATUS_PERIODIC: 23762306a36Sopenharmony_ci case HP_SDC_STATUS_TIMER: 23862306a36Sopenharmony_ci read_lock(&hp_sdc.hook_lock); 23962306a36Sopenharmony_ci if (hp_sdc.timer != NULL) 24062306a36Sopenharmony_ci hp_sdc.timer(irq, dev_id, status, data); 24162306a36Sopenharmony_ci read_unlock(&hp_sdc.hook_lock); 24262306a36Sopenharmony_ci break; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci case HP_SDC_STATUS_REG: 24562306a36Sopenharmony_ci hp_sdc_take(irq, dev_id, status, data); 24662306a36Sopenharmony_ci break; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci case HP_SDC_STATUS_HILCMD: 24962306a36Sopenharmony_ci case HP_SDC_STATUS_HILDATA: 25062306a36Sopenharmony_ci read_lock(&hp_sdc.hook_lock); 25162306a36Sopenharmony_ci if (hp_sdc.hil != NULL) 25262306a36Sopenharmony_ci hp_sdc.hil(irq, dev_id, status, data); 25362306a36Sopenharmony_ci read_unlock(&hp_sdc.hook_lock); 25462306a36Sopenharmony_ci break; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci case HP_SDC_STATUS_PUP: 25762306a36Sopenharmony_ci read_lock(&hp_sdc.hook_lock); 25862306a36Sopenharmony_ci if (hp_sdc.pup != NULL) 25962306a36Sopenharmony_ci hp_sdc.pup(irq, dev_id, status, data); 26062306a36Sopenharmony_ci else 26162306a36Sopenharmony_ci printk(KERN_INFO PREFIX "HP SDC reports successful PUP.\n"); 26262306a36Sopenharmony_ci read_unlock(&hp_sdc.hook_lock); 26362306a36Sopenharmony_ci break; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci default: 26662306a36Sopenharmony_ci read_lock(&hp_sdc.hook_lock); 26762306a36Sopenharmony_ci if (hp_sdc.cooked != NULL) 26862306a36Sopenharmony_ci hp_sdc.cooked(irq, dev_id, status, data); 26962306a36Sopenharmony_ci read_unlock(&hp_sdc.hook_lock); 27062306a36Sopenharmony_ci break; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci return IRQ_HANDLED; 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic irqreturn_t hp_sdc_nmisr(int irq, void *dev_id) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci int status; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci status = hp_sdc_status_in8(); 28262306a36Sopenharmony_ci printk(KERN_WARNING PREFIX "NMI !\n"); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci#if 0 28562306a36Sopenharmony_ci if (status & HP_SDC_NMISTATUS_FHS) { 28662306a36Sopenharmony_ci read_lock(&hp_sdc.hook_lock); 28762306a36Sopenharmony_ci if (hp_sdc.timer != NULL) 28862306a36Sopenharmony_ci hp_sdc.timer(irq, dev_id, status, 0); 28962306a36Sopenharmony_ci read_unlock(&hp_sdc.hook_lock); 29062306a36Sopenharmony_ci } else { 29162306a36Sopenharmony_ci /* TODO: pass this on to the HIL handler, or do SAK here? */ 29262306a36Sopenharmony_ci printk(KERN_WARNING PREFIX "HIL NMI\n"); 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci#endif 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci return IRQ_HANDLED; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci/***************** Kernel (tasklet) context functions ****************/ 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ciunsigned long hp_sdc_put(void); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic void hp_sdc_tasklet(unsigned long foo) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci write_lock_irq(&hp_sdc.rtq_lock); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (hp_sdc.rcurr >= 0) { 30962306a36Sopenharmony_ci ktime_t now = ktime_get(); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (ktime_after(now, ktime_add_us(hp_sdc.rtime, 31262306a36Sopenharmony_ci HP_SDC_MAX_REG_DELAY))) { 31362306a36Sopenharmony_ci hp_sdc_transaction *curr; 31462306a36Sopenharmony_ci uint8_t tmp; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci curr = hp_sdc.tq[hp_sdc.rcurr]; 31762306a36Sopenharmony_ci /* If this turns out to be a normal failure mode 31862306a36Sopenharmony_ci * we'll need to figure out a way to communicate 31962306a36Sopenharmony_ci * it back to the application. and be less verbose. 32062306a36Sopenharmony_ci */ 32162306a36Sopenharmony_ci printk(KERN_WARNING PREFIX "read timeout (%lldus)!\n", 32262306a36Sopenharmony_ci ktime_us_delta(now, hp_sdc.rtime)); 32362306a36Sopenharmony_ci curr->idx += hp_sdc.rqty; 32462306a36Sopenharmony_ci hp_sdc.rqty = 0; 32562306a36Sopenharmony_ci tmp = curr->seq[curr->actidx]; 32662306a36Sopenharmony_ci curr->seq[curr->actidx] |= HP_SDC_ACT_DEAD; 32762306a36Sopenharmony_ci if (tmp & HP_SDC_ACT_SEMAPHORE) 32862306a36Sopenharmony_ci if (curr->act.semaphore) 32962306a36Sopenharmony_ci up(curr->act.semaphore); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (tmp & HP_SDC_ACT_CALLBACK) { 33262306a36Sopenharmony_ci /* Note this means that irqhooks may be called 33362306a36Sopenharmony_ci * in tasklet/bh context. 33462306a36Sopenharmony_ci */ 33562306a36Sopenharmony_ci if (curr->act.irqhook) 33662306a36Sopenharmony_ci curr->act.irqhook(0, NULL, 0, 0); 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci curr->actidx = curr->idx; 34062306a36Sopenharmony_ci curr->idx++; 34162306a36Sopenharmony_ci hp_sdc.rcurr = -1; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci write_unlock_irq(&hp_sdc.rtq_lock); 34562306a36Sopenharmony_ci hp_sdc_put(); 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ciunsigned long hp_sdc_put(void) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci hp_sdc_transaction *curr; 35162306a36Sopenharmony_ci uint8_t act; 35262306a36Sopenharmony_ci int idx, curridx; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci int limit = 0; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci write_lock(&hp_sdc.lock); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci /* If i8042 buffers are full, we cannot do anything that 35962306a36Sopenharmony_ci requires output, so we skip to the administrativa. */ 36062306a36Sopenharmony_ci if (hp_sdc.ibf) { 36162306a36Sopenharmony_ci hp_sdc_status_in8(); 36262306a36Sopenharmony_ci if (hp_sdc.ibf) 36362306a36Sopenharmony_ci goto finish; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci anew: 36762306a36Sopenharmony_ci /* See if we are in the middle of a sequence. */ 36862306a36Sopenharmony_ci if (hp_sdc.wcurr < 0) 36962306a36Sopenharmony_ci hp_sdc.wcurr = 0; 37062306a36Sopenharmony_ci read_lock_irq(&hp_sdc.rtq_lock); 37162306a36Sopenharmony_ci if (hp_sdc.rcurr == hp_sdc.wcurr) 37262306a36Sopenharmony_ci hp_sdc.wcurr++; 37362306a36Sopenharmony_ci read_unlock_irq(&hp_sdc.rtq_lock); 37462306a36Sopenharmony_ci if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) 37562306a36Sopenharmony_ci hp_sdc.wcurr = 0; 37662306a36Sopenharmony_ci curridx = hp_sdc.wcurr; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if (hp_sdc.tq[curridx] != NULL) 37962306a36Sopenharmony_ci goto start; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci while (++curridx != hp_sdc.wcurr) { 38262306a36Sopenharmony_ci if (curridx >= HP_SDC_QUEUE_LEN) { 38362306a36Sopenharmony_ci curridx = -1; /* Wrap to top */ 38462306a36Sopenharmony_ci continue; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci read_lock_irq(&hp_sdc.rtq_lock); 38762306a36Sopenharmony_ci if (hp_sdc.rcurr == curridx) { 38862306a36Sopenharmony_ci read_unlock_irq(&hp_sdc.rtq_lock); 38962306a36Sopenharmony_ci continue; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci read_unlock_irq(&hp_sdc.rtq_lock); 39262306a36Sopenharmony_ci if (hp_sdc.tq[curridx] != NULL) 39362306a36Sopenharmony_ci break; /* Found one. */ 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci if (curridx == hp_sdc.wcurr) { /* There's nothing queued to do. */ 39662306a36Sopenharmony_ci curridx = -1; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci hp_sdc.wcurr = curridx; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci start: 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci /* Check to see if the interrupt mask needs to be set. */ 40362306a36Sopenharmony_ci if (hp_sdc.set_im) { 40462306a36Sopenharmony_ci hp_sdc_status_out8(hp_sdc.im | HP_SDC_CMD_SET_IM); 40562306a36Sopenharmony_ci hp_sdc.set_im = 0; 40662306a36Sopenharmony_ci goto finish; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (hp_sdc.wcurr == -1) 41062306a36Sopenharmony_ci goto done; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci curr = hp_sdc.tq[curridx]; 41362306a36Sopenharmony_ci idx = curr->actidx; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (curr->actidx >= curr->endidx) { 41662306a36Sopenharmony_ci hp_sdc.tq[curridx] = NULL; 41762306a36Sopenharmony_ci /* Interleave outbound data between the transactions. */ 41862306a36Sopenharmony_ci hp_sdc.wcurr++; 41962306a36Sopenharmony_ci if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) 42062306a36Sopenharmony_ci hp_sdc.wcurr = 0; 42162306a36Sopenharmony_ci goto finish; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci act = curr->seq[idx]; 42562306a36Sopenharmony_ci idx++; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (curr->idx >= curr->endidx) { 42862306a36Sopenharmony_ci if (act & HP_SDC_ACT_DEALLOC) 42962306a36Sopenharmony_ci kfree(curr); 43062306a36Sopenharmony_ci hp_sdc.tq[curridx] = NULL; 43162306a36Sopenharmony_ci /* Interleave outbound data between the transactions. */ 43262306a36Sopenharmony_ci hp_sdc.wcurr++; 43362306a36Sopenharmony_ci if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) 43462306a36Sopenharmony_ci hp_sdc.wcurr = 0; 43562306a36Sopenharmony_ci goto finish; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci while (act & HP_SDC_ACT_PRECMD) { 43962306a36Sopenharmony_ci if (curr->idx != idx) { 44062306a36Sopenharmony_ci idx++; 44162306a36Sopenharmony_ci act &= ~HP_SDC_ACT_PRECMD; 44262306a36Sopenharmony_ci break; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci hp_sdc_status_out8(curr->seq[idx]); 44562306a36Sopenharmony_ci curr->idx++; 44662306a36Sopenharmony_ci /* act finished? */ 44762306a36Sopenharmony_ci if ((act & HP_SDC_ACT_DURING) == HP_SDC_ACT_PRECMD) 44862306a36Sopenharmony_ci goto actdone; 44962306a36Sopenharmony_ci /* skip quantity field if data-out sequence follows. */ 45062306a36Sopenharmony_ci if (act & HP_SDC_ACT_DATAOUT) 45162306a36Sopenharmony_ci curr->idx++; 45262306a36Sopenharmony_ci goto finish; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci if (act & HP_SDC_ACT_DATAOUT) { 45562306a36Sopenharmony_ci int qty; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci qty = curr->seq[idx]; 45862306a36Sopenharmony_ci idx++; 45962306a36Sopenharmony_ci if (curr->idx - idx < qty) { 46062306a36Sopenharmony_ci hp_sdc_data_out8(curr->seq[curr->idx]); 46162306a36Sopenharmony_ci curr->idx++; 46262306a36Sopenharmony_ci /* act finished? */ 46362306a36Sopenharmony_ci if (curr->idx - idx >= qty && 46462306a36Sopenharmony_ci (act & HP_SDC_ACT_DURING) == HP_SDC_ACT_DATAOUT) 46562306a36Sopenharmony_ci goto actdone; 46662306a36Sopenharmony_ci goto finish; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci idx += qty; 46962306a36Sopenharmony_ci act &= ~HP_SDC_ACT_DATAOUT; 47062306a36Sopenharmony_ci } else 47162306a36Sopenharmony_ci while (act & HP_SDC_ACT_DATAREG) { 47262306a36Sopenharmony_ci int mask; 47362306a36Sopenharmony_ci uint8_t w7[4]; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci mask = curr->seq[idx]; 47662306a36Sopenharmony_ci if (idx != curr->idx) { 47762306a36Sopenharmony_ci idx++; 47862306a36Sopenharmony_ci idx += !!(mask & 1); 47962306a36Sopenharmony_ci idx += !!(mask & 2); 48062306a36Sopenharmony_ci idx += !!(mask & 4); 48162306a36Sopenharmony_ci idx += !!(mask & 8); 48262306a36Sopenharmony_ci act &= ~HP_SDC_ACT_DATAREG; 48362306a36Sopenharmony_ci break; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci w7[0] = (mask & 1) ? curr->seq[++idx] : hp_sdc.r7[0]; 48762306a36Sopenharmony_ci w7[1] = (mask & 2) ? curr->seq[++idx] : hp_sdc.r7[1]; 48862306a36Sopenharmony_ci w7[2] = (mask & 4) ? curr->seq[++idx] : hp_sdc.r7[2]; 48962306a36Sopenharmony_ci w7[3] = (mask & 8) ? curr->seq[++idx] : hp_sdc.r7[3]; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (hp_sdc.wi > 0x73 || hp_sdc.wi < 0x70 || 49262306a36Sopenharmony_ci w7[hp_sdc.wi - 0x70] == hp_sdc.r7[hp_sdc.wi - 0x70]) { 49362306a36Sopenharmony_ci int i = 0; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci /* Need to point the write index register */ 49662306a36Sopenharmony_ci while (i < 4 && w7[i] == hp_sdc.r7[i]) 49762306a36Sopenharmony_ci i++; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (i < 4) { 50062306a36Sopenharmony_ci hp_sdc_status_out8(HP_SDC_CMD_SET_D0 + i); 50162306a36Sopenharmony_ci hp_sdc.wi = 0x70 + i; 50262306a36Sopenharmony_ci goto finish; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci idx++; 50662306a36Sopenharmony_ci if ((act & HP_SDC_ACT_DURING) == HP_SDC_ACT_DATAREG) 50762306a36Sopenharmony_ci goto actdone; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci curr->idx = idx; 51062306a36Sopenharmony_ci act &= ~HP_SDC_ACT_DATAREG; 51162306a36Sopenharmony_ci break; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci hp_sdc_data_out8(w7[hp_sdc.wi - 0x70]); 51562306a36Sopenharmony_ci hp_sdc.r7[hp_sdc.wi - 0x70] = w7[hp_sdc.wi - 0x70]; 51662306a36Sopenharmony_ci hp_sdc.wi++; /* write index register autoincrements */ 51762306a36Sopenharmony_ci { 51862306a36Sopenharmony_ci int i = 0; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci while ((i < 4) && w7[i] == hp_sdc.r7[i]) 52162306a36Sopenharmony_ci i++; 52262306a36Sopenharmony_ci if (i >= 4) { 52362306a36Sopenharmony_ci curr->idx = idx + 1; 52462306a36Sopenharmony_ci if ((act & HP_SDC_ACT_DURING) == 52562306a36Sopenharmony_ci HP_SDC_ACT_DATAREG) 52662306a36Sopenharmony_ci goto actdone; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci goto finish; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci /* We don't go any further in the command if there is a pending read, 53262306a36Sopenharmony_ci because we don't want interleaved results. */ 53362306a36Sopenharmony_ci read_lock_irq(&hp_sdc.rtq_lock); 53462306a36Sopenharmony_ci if (hp_sdc.rcurr >= 0) { 53562306a36Sopenharmony_ci read_unlock_irq(&hp_sdc.rtq_lock); 53662306a36Sopenharmony_ci goto finish; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci read_unlock_irq(&hp_sdc.rtq_lock); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci if (act & HP_SDC_ACT_POSTCMD) { 54262306a36Sopenharmony_ci uint8_t postcmd; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci /* curr->idx should == idx at this point. */ 54562306a36Sopenharmony_ci postcmd = curr->seq[idx]; 54662306a36Sopenharmony_ci curr->idx++; 54762306a36Sopenharmony_ci if (act & HP_SDC_ACT_DATAIN) { 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci /* Start a new read */ 55062306a36Sopenharmony_ci hp_sdc.rqty = curr->seq[curr->idx]; 55162306a36Sopenharmony_ci hp_sdc.rtime = ktime_get(); 55262306a36Sopenharmony_ci curr->idx++; 55362306a36Sopenharmony_ci /* Still need to lock here in case of spurious irq. */ 55462306a36Sopenharmony_ci write_lock_irq(&hp_sdc.rtq_lock); 55562306a36Sopenharmony_ci hp_sdc.rcurr = curridx; 55662306a36Sopenharmony_ci write_unlock_irq(&hp_sdc.rtq_lock); 55762306a36Sopenharmony_ci hp_sdc_status_out8(postcmd); 55862306a36Sopenharmony_ci goto finish; 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci hp_sdc_status_out8(postcmd); 56162306a36Sopenharmony_ci goto actdone; 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci actdone: 56562306a36Sopenharmony_ci if (act & HP_SDC_ACT_SEMAPHORE) 56662306a36Sopenharmony_ci up(curr->act.semaphore); 56762306a36Sopenharmony_ci else if (act & HP_SDC_ACT_CALLBACK) 56862306a36Sopenharmony_ci curr->act.irqhook(0,NULL,0,0); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci if (curr->idx >= curr->endidx) { /* This transaction is over. */ 57162306a36Sopenharmony_ci if (act & HP_SDC_ACT_DEALLOC) 57262306a36Sopenharmony_ci kfree(curr); 57362306a36Sopenharmony_ci hp_sdc.tq[curridx] = NULL; 57462306a36Sopenharmony_ci } else { 57562306a36Sopenharmony_ci curr->actidx = idx + 1; 57662306a36Sopenharmony_ci curr->idx = idx + 2; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci /* Interleave outbound data between the transactions. */ 57962306a36Sopenharmony_ci hp_sdc.wcurr++; 58062306a36Sopenharmony_ci if (hp_sdc.wcurr >= HP_SDC_QUEUE_LEN) 58162306a36Sopenharmony_ci hp_sdc.wcurr = 0; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci finish: 58462306a36Sopenharmony_ci /* If by some quirk IBF has cleared and our ISR has run to 58562306a36Sopenharmony_ci see that that has happened, do it all again. */ 58662306a36Sopenharmony_ci if (!hp_sdc.ibf && limit++ < 20) 58762306a36Sopenharmony_ci goto anew; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci done: 59062306a36Sopenharmony_ci if (hp_sdc.wcurr >= 0) 59162306a36Sopenharmony_ci tasklet_schedule(&hp_sdc.task); 59262306a36Sopenharmony_ci write_unlock(&hp_sdc.lock); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci return 0; 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci/******* Functions called in either user or kernel context ****/ 59862306a36Sopenharmony_ciint __hp_sdc_enqueue_transaction(hp_sdc_transaction *this) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci int i; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (this == NULL) { 60362306a36Sopenharmony_ci BUG(); 60462306a36Sopenharmony_ci return -EINVAL; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci /* Can't have same transaction on queue twice */ 60862306a36Sopenharmony_ci for (i = 0; i < HP_SDC_QUEUE_LEN; i++) 60962306a36Sopenharmony_ci if (hp_sdc.tq[i] == this) 61062306a36Sopenharmony_ci goto fail; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci this->actidx = 0; 61362306a36Sopenharmony_ci this->idx = 1; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci /* Search for empty slot */ 61662306a36Sopenharmony_ci for (i = 0; i < HP_SDC_QUEUE_LEN; i++) 61762306a36Sopenharmony_ci if (hp_sdc.tq[i] == NULL) { 61862306a36Sopenharmony_ci hp_sdc.tq[i] = this; 61962306a36Sopenharmony_ci tasklet_schedule(&hp_sdc.task); 62062306a36Sopenharmony_ci return 0; 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci printk(KERN_WARNING PREFIX "No free slot to add transaction.\n"); 62462306a36Sopenharmony_ci return -EBUSY; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci fail: 62762306a36Sopenharmony_ci printk(KERN_WARNING PREFIX "Transaction add failed: transaction already queued?\n"); 62862306a36Sopenharmony_ci return -EINVAL; 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ciint hp_sdc_enqueue_transaction(hp_sdc_transaction *this) { 63262306a36Sopenharmony_ci unsigned long flags; 63362306a36Sopenharmony_ci int ret; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci write_lock_irqsave(&hp_sdc.lock, flags); 63662306a36Sopenharmony_ci ret = __hp_sdc_enqueue_transaction(this); 63762306a36Sopenharmony_ci write_unlock_irqrestore(&hp_sdc.lock,flags); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci return ret; 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ciint hp_sdc_dequeue_transaction(hp_sdc_transaction *this) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci unsigned long flags; 64562306a36Sopenharmony_ci int i; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci write_lock_irqsave(&hp_sdc.lock, flags); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci /* TODO: don't remove it if it's not done. */ 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci for (i = 0; i < HP_SDC_QUEUE_LEN; i++) 65262306a36Sopenharmony_ci if (hp_sdc.tq[i] == this) 65362306a36Sopenharmony_ci hp_sdc.tq[i] = NULL; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci write_unlock_irqrestore(&hp_sdc.lock, flags); 65662306a36Sopenharmony_ci return 0; 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci/********************** User context functions **************************/ 66262306a36Sopenharmony_ciint hp_sdc_request_timer_irq(hp_sdc_irqhook *callback) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci if (callback == NULL || hp_sdc.dev == NULL) 66562306a36Sopenharmony_ci return -EINVAL; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci write_lock_irq(&hp_sdc.hook_lock); 66862306a36Sopenharmony_ci if (hp_sdc.timer != NULL) { 66962306a36Sopenharmony_ci write_unlock_irq(&hp_sdc.hook_lock); 67062306a36Sopenharmony_ci return -EBUSY; 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci hp_sdc.timer = callback; 67462306a36Sopenharmony_ci /* Enable interrupts from the timers */ 67562306a36Sopenharmony_ci hp_sdc.im &= ~HP_SDC_IM_FH; 67662306a36Sopenharmony_ci hp_sdc.im &= ~HP_SDC_IM_PT; 67762306a36Sopenharmony_ci hp_sdc.im &= ~HP_SDC_IM_TIMERS; 67862306a36Sopenharmony_ci hp_sdc.set_im = 1; 67962306a36Sopenharmony_ci write_unlock_irq(&hp_sdc.hook_lock); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci tasklet_schedule(&hp_sdc.task); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci return 0; 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ciint hp_sdc_request_hil_irq(hp_sdc_irqhook *callback) 68762306a36Sopenharmony_ci{ 68862306a36Sopenharmony_ci if (callback == NULL || hp_sdc.dev == NULL) 68962306a36Sopenharmony_ci return -EINVAL; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci write_lock_irq(&hp_sdc.hook_lock); 69262306a36Sopenharmony_ci if (hp_sdc.hil != NULL) { 69362306a36Sopenharmony_ci write_unlock_irq(&hp_sdc.hook_lock); 69462306a36Sopenharmony_ci return -EBUSY; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci hp_sdc.hil = callback; 69862306a36Sopenharmony_ci hp_sdc.im &= ~(HP_SDC_IM_HIL | HP_SDC_IM_RESET); 69962306a36Sopenharmony_ci hp_sdc.set_im = 1; 70062306a36Sopenharmony_ci write_unlock_irq(&hp_sdc.hook_lock); 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci tasklet_schedule(&hp_sdc.task); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci return 0; 70562306a36Sopenharmony_ci} 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ciint hp_sdc_request_cooked_irq(hp_sdc_irqhook *callback) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci if (callback == NULL || hp_sdc.dev == NULL) 71062306a36Sopenharmony_ci return -EINVAL; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci write_lock_irq(&hp_sdc.hook_lock); 71362306a36Sopenharmony_ci if (hp_sdc.cooked != NULL) { 71462306a36Sopenharmony_ci write_unlock_irq(&hp_sdc.hook_lock); 71562306a36Sopenharmony_ci return -EBUSY; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci /* Enable interrupts from the HIL MLC */ 71962306a36Sopenharmony_ci hp_sdc.cooked = callback; 72062306a36Sopenharmony_ci hp_sdc.im &= ~(HP_SDC_IM_HIL | HP_SDC_IM_RESET); 72162306a36Sopenharmony_ci hp_sdc.set_im = 1; 72262306a36Sopenharmony_ci write_unlock_irq(&hp_sdc.hook_lock); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci tasklet_schedule(&hp_sdc.task); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci return 0; 72762306a36Sopenharmony_ci} 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ciint hp_sdc_release_timer_irq(hp_sdc_irqhook *callback) 73062306a36Sopenharmony_ci{ 73162306a36Sopenharmony_ci write_lock_irq(&hp_sdc.hook_lock); 73262306a36Sopenharmony_ci if ((callback != hp_sdc.timer) || 73362306a36Sopenharmony_ci (hp_sdc.timer == NULL)) { 73462306a36Sopenharmony_ci write_unlock_irq(&hp_sdc.hook_lock); 73562306a36Sopenharmony_ci return -EINVAL; 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci /* Disable interrupts from the timers */ 73962306a36Sopenharmony_ci hp_sdc.timer = NULL; 74062306a36Sopenharmony_ci hp_sdc.im |= HP_SDC_IM_TIMERS; 74162306a36Sopenharmony_ci hp_sdc.im |= HP_SDC_IM_FH; 74262306a36Sopenharmony_ci hp_sdc.im |= HP_SDC_IM_PT; 74362306a36Sopenharmony_ci hp_sdc.set_im = 1; 74462306a36Sopenharmony_ci write_unlock_irq(&hp_sdc.hook_lock); 74562306a36Sopenharmony_ci tasklet_schedule(&hp_sdc.task); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci return 0; 74862306a36Sopenharmony_ci} 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ciint hp_sdc_release_hil_irq(hp_sdc_irqhook *callback) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci write_lock_irq(&hp_sdc.hook_lock); 75362306a36Sopenharmony_ci if ((callback != hp_sdc.hil) || 75462306a36Sopenharmony_ci (hp_sdc.hil == NULL)) { 75562306a36Sopenharmony_ci write_unlock_irq(&hp_sdc.hook_lock); 75662306a36Sopenharmony_ci return -EINVAL; 75762306a36Sopenharmony_ci } 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci hp_sdc.hil = NULL; 76062306a36Sopenharmony_ci /* Disable interrupts from HIL only if there is no cooked driver. */ 76162306a36Sopenharmony_ci if(hp_sdc.cooked == NULL) { 76262306a36Sopenharmony_ci hp_sdc.im |= (HP_SDC_IM_HIL | HP_SDC_IM_RESET); 76362306a36Sopenharmony_ci hp_sdc.set_im = 1; 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci write_unlock_irq(&hp_sdc.hook_lock); 76662306a36Sopenharmony_ci tasklet_schedule(&hp_sdc.task); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci return 0; 76962306a36Sopenharmony_ci} 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ciint hp_sdc_release_cooked_irq(hp_sdc_irqhook *callback) 77262306a36Sopenharmony_ci{ 77362306a36Sopenharmony_ci write_lock_irq(&hp_sdc.hook_lock); 77462306a36Sopenharmony_ci if ((callback != hp_sdc.cooked) || 77562306a36Sopenharmony_ci (hp_sdc.cooked == NULL)) { 77662306a36Sopenharmony_ci write_unlock_irq(&hp_sdc.hook_lock); 77762306a36Sopenharmony_ci return -EINVAL; 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci hp_sdc.cooked = NULL; 78162306a36Sopenharmony_ci /* Disable interrupts from HIL only if there is no raw HIL driver. */ 78262306a36Sopenharmony_ci if(hp_sdc.hil == NULL) { 78362306a36Sopenharmony_ci hp_sdc.im |= (HP_SDC_IM_HIL | HP_SDC_IM_RESET); 78462306a36Sopenharmony_ci hp_sdc.set_im = 1; 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci write_unlock_irq(&hp_sdc.hook_lock); 78762306a36Sopenharmony_ci tasklet_schedule(&hp_sdc.task); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci return 0; 79062306a36Sopenharmony_ci} 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci/************************* Keepalive timer task *********************/ 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_cistatic void hp_sdc_kicker(struct timer_list *unused) 79562306a36Sopenharmony_ci{ 79662306a36Sopenharmony_ci tasklet_schedule(&hp_sdc.task); 79762306a36Sopenharmony_ci /* Re-insert the periodic task. */ 79862306a36Sopenharmony_ci mod_timer(&hp_sdc.kicker, jiffies + HZ); 79962306a36Sopenharmony_ci} 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci/************************** Module Initialization ***************************/ 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci#if defined(__hppa__) 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_cistatic const struct parisc_device_id hp_sdc_tbl[] __initconst = { 80662306a36Sopenharmony_ci { 80762306a36Sopenharmony_ci .hw_type = HPHW_FIO, 80862306a36Sopenharmony_ci .hversion_rev = HVERSION_REV_ANY_ID, 80962306a36Sopenharmony_ci .hversion = HVERSION_ANY_ID, 81062306a36Sopenharmony_ci .sversion = 0x73, 81162306a36Sopenharmony_ci }, 81262306a36Sopenharmony_ci { 0, } 81362306a36Sopenharmony_ci}; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(parisc, hp_sdc_tbl); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_cistatic int __init hp_sdc_init_hppa(struct parisc_device *d); 81862306a36Sopenharmony_cistatic struct delayed_work moduleloader_work; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_cistatic struct parisc_driver hp_sdc_driver __refdata = { 82162306a36Sopenharmony_ci .name = "hp_sdc", 82262306a36Sopenharmony_ci .id_table = hp_sdc_tbl, 82362306a36Sopenharmony_ci .probe = hp_sdc_init_hppa, 82462306a36Sopenharmony_ci}; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci#endif /* __hppa__ */ 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_cistatic int __init hp_sdc_init(void) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci char *errstr; 83162306a36Sopenharmony_ci hp_sdc_transaction t_sync; 83262306a36Sopenharmony_ci uint8_t ts_sync[6]; 83362306a36Sopenharmony_ci struct semaphore s_sync; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci rwlock_init(&hp_sdc.lock); 83662306a36Sopenharmony_ci rwlock_init(&hp_sdc.ibf_lock); 83762306a36Sopenharmony_ci rwlock_init(&hp_sdc.rtq_lock); 83862306a36Sopenharmony_ci rwlock_init(&hp_sdc.hook_lock); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci hp_sdc.timer = NULL; 84162306a36Sopenharmony_ci hp_sdc.hil = NULL; 84262306a36Sopenharmony_ci hp_sdc.pup = NULL; 84362306a36Sopenharmony_ci hp_sdc.cooked = NULL; 84462306a36Sopenharmony_ci hp_sdc.im = HP_SDC_IM_MASK; /* Mask maskable irqs */ 84562306a36Sopenharmony_ci hp_sdc.set_im = 1; 84662306a36Sopenharmony_ci hp_sdc.wi = 0xff; 84762306a36Sopenharmony_ci hp_sdc.r7[0] = 0xff; 84862306a36Sopenharmony_ci hp_sdc.r7[1] = 0xff; 84962306a36Sopenharmony_ci hp_sdc.r7[2] = 0xff; 85062306a36Sopenharmony_ci hp_sdc.r7[3] = 0xff; 85162306a36Sopenharmony_ci hp_sdc.ibf = 1; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci memset(&hp_sdc.tq, 0, sizeof(hp_sdc.tq)); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci hp_sdc.wcurr = -1; 85662306a36Sopenharmony_ci hp_sdc.rcurr = -1; 85762306a36Sopenharmony_ci hp_sdc.rqty = 0; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci hp_sdc.dev_err = -ENODEV; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci errstr = "IO not found for"; 86262306a36Sopenharmony_ci if (!hp_sdc.base_io) 86362306a36Sopenharmony_ci goto err0; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci errstr = "IRQ not found for"; 86662306a36Sopenharmony_ci if (!hp_sdc.irq) 86762306a36Sopenharmony_ci goto err0; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci hp_sdc.dev_err = -EBUSY; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci#if defined(__hppa__) 87262306a36Sopenharmony_ci errstr = "IO not available for"; 87362306a36Sopenharmony_ci if (request_region(hp_sdc.data_io, 2, hp_sdc_driver.name)) 87462306a36Sopenharmony_ci goto err0; 87562306a36Sopenharmony_ci#endif 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci errstr = "IRQ not available for"; 87862306a36Sopenharmony_ci if (request_irq(hp_sdc.irq, &hp_sdc_isr, IRQF_SHARED, 87962306a36Sopenharmony_ci "HP SDC", &hp_sdc)) 88062306a36Sopenharmony_ci goto err1; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci errstr = "NMI not available for"; 88362306a36Sopenharmony_ci if (request_irq(hp_sdc.nmi, &hp_sdc_nmisr, IRQF_SHARED, 88462306a36Sopenharmony_ci "HP SDC NMI", &hp_sdc)) 88562306a36Sopenharmony_ci goto err2; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci pr_info(PREFIX "HP SDC at 0x%08lx, IRQ %d (NMI IRQ %d)\n", 88862306a36Sopenharmony_ci hp_sdc.base_io, hp_sdc.irq, hp_sdc.nmi); 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci hp_sdc_status_in8(); 89162306a36Sopenharmony_ci hp_sdc_data_in8(); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci tasklet_init(&hp_sdc.task, hp_sdc_tasklet, 0); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci /* Sync the output buffer registers, thus scheduling hp_sdc_tasklet. */ 89662306a36Sopenharmony_ci t_sync.actidx = 0; 89762306a36Sopenharmony_ci t_sync.idx = 1; 89862306a36Sopenharmony_ci t_sync.endidx = 6; 89962306a36Sopenharmony_ci t_sync.seq = ts_sync; 90062306a36Sopenharmony_ci ts_sync[0] = HP_SDC_ACT_DATAREG | HP_SDC_ACT_SEMAPHORE; 90162306a36Sopenharmony_ci ts_sync[1] = 0x0f; 90262306a36Sopenharmony_ci ts_sync[2] = ts_sync[3] = ts_sync[4] = ts_sync[5] = 0; 90362306a36Sopenharmony_ci t_sync.act.semaphore = &s_sync; 90462306a36Sopenharmony_ci sema_init(&s_sync, 0); 90562306a36Sopenharmony_ci hp_sdc_enqueue_transaction(&t_sync); 90662306a36Sopenharmony_ci down(&s_sync); /* Wait for t_sync to complete */ 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci /* Create the keepalive task */ 90962306a36Sopenharmony_ci timer_setup(&hp_sdc.kicker, hp_sdc_kicker, 0); 91062306a36Sopenharmony_ci hp_sdc.kicker.expires = jiffies + HZ; 91162306a36Sopenharmony_ci add_timer(&hp_sdc.kicker); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci hp_sdc.dev_err = 0; 91462306a36Sopenharmony_ci return 0; 91562306a36Sopenharmony_ci err2: 91662306a36Sopenharmony_ci free_irq(hp_sdc.irq, &hp_sdc); 91762306a36Sopenharmony_ci err1: 91862306a36Sopenharmony_ci release_region(hp_sdc.data_io, 2); 91962306a36Sopenharmony_ci err0: 92062306a36Sopenharmony_ci printk(KERN_WARNING PREFIX ": %s SDC IO=0x%p IRQ=0x%x NMI=0x%x\n", 92162306a36Sopenharmony_ci errstr, (void *)hp_sdc.base_io, hp_sdc.irq, hp_sdc.nmi); 92262306a36Sopenharmony_ci hp_sdc.dev = NULL; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci return hp_sdc.dev_err; 92562306a36Sopenharmony_ci} 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci#if defined(__hppa__) 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_cistatic void request_module_delayed(struct work_struct *work) 93062306a36Sopenharmony_ci{ 93162306a36Sopenharmony_ci request_module("hp_sdc_mlc"); 93262306a36Sopenharmony_ci} 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_cistatic int __init hp_sdc_init_hppa(struct parisc_device *d) 93562306a36Sopenharmony_ci{ 93662306a36Sopenharmony_ci int ret; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci if (!d) 93962306a36Sopenharmony_ci return 1; 94062306a36Sopenharmony_ci if (hp_sdc.dev != NULL) 94162306a36Sopenharmony_ci return 1; /* We only expect one SDC */ 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci hp_sdc.dev = d; 94462306a36Sopenharmony_ci hp_sdc.irq = d->irq; 94562306a36Sopenharmony_ci hp_sdc.nmi = d->aux_irq; 94662306a36Sopenharmony_ci hp_sdc.base_io = d->hpa.start; 94762306a36Sopenharmony_ci hp_sdc.data_io = d->hpa.start + 0x800; 94862306a36Sopenharmony_ci hp_sdc.status_io = d->hpa.start + 0x801; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci INIT_DELAYED_WORK(&moduleloader_work, request_module_delayed); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci ret = hp_sdc_init(); 95362306a36Sopenharmony_ci /* after successful initialization give SDC some time to settle 95462306a36Sopenharmony_ci * and then load the hp_sdc_mlc upper layer driver */ 95562306a36Sopenharmony_ci if (!ret) 95662306a36Sopenharmony_ci schedule_delayed_work(&moduleloader_work, 95762306a36Sopenharmony_ci msecs_to_jiffies(2000)); 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci return ret; 96062306a36Sopenharmony_ci} 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci#endif /* __hppa__ */ 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_cistatic void hp_sdc_exit(void) 96562306a36Sopenharmony_ci{ 96662306a36Sopenharmony_ci /* do nothing if we don't have a SDC */ 96762306a36Sopenharmony_ci if (!hp_sdc.dev) 96862306a36Sopenharmony_ci return; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci write_lock_irq(&hp_sdc.lock); 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci /* Turn off all maskable "sub-function" irq's. */ 97362306a36Sopenharmony_ci hp_sdc_spin_ibf(); 97462306a36Sopenharmony_ci sdc_writeb(HP_SDC_CMD_SET_IM | HP_SDC_IM_MASK, hp_sdc.status_io); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci /* Wait until we know this has been processed by the i8042 */ 97762306a36Sopenharmony_ci hp_sdc_spin_ibf(); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci free_irq(hp_sdc.nmi, &hp_sdc); 98062306a36Sopenharmony_ci free_irq(hp_sdc.irq, &hp_sdc); 98162306a36Sopenharmony_ci write_unlock_irq(&hp_sdc.lock); 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci del_timer_sync(&hp_sdc.kicker); 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci tasklet_kill(&hp_sdc.task); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci#if defined(__hppa__) 98862306a36Sopenharmony_ci cancel_delayed_work_sync(&moduleloader_work); 98962306a36Sopenharmony_ci if (unregister_parisc_driver(&hp_sdc_driver)) 99062306a36Sopenharmony_ci printk(KERN_WARNING PREFIX "Error unregistering HP SDC"); 99162306a36Sopenharmony_ci#endif 99262306a36Sopenharmony_ci} 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_cistatic int __init hp_sdc_register(void) 99562306a36Sopenharmony_ci{ 99662306a36Sopenharmony_ci hp_sdc_transaction tq_init; 99762306a36Sopenharmony_ci uint8_t tq_init_seq[5]; 99862306a36Sopenharmony_ci struct semaphore tq_init_sem; 99962306a36Sopenharmony_ci#if defined(__mc68000__) 100062306a36Sopenharmony_ci unsigned char i; 100162306a36Sopenharmony_ci#endif 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci if (hp_sdc_disabled) { 100462306a36Sopenharmony_ci printk(KERN_WARNING PREFIX "HP SDC driver disabled by no_hpsdc=1.\n"); 100562306a36Sopenharmony_ci return -ENODEV; 100662306a36Sopenharmony_ci } 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci hp_sdc.dev = NULL; 100962306a36Sopenharmony_ci hp_sdc.dev_err = 0; 101062306a36Sopenharmony_ci#if defined(__hppa__) 101162306a36Sopenharmony_ci if (register_parisc_driver(&hp_sdc_driver)) { 101262306a36Sopenharmony_ci printk(KERN_WARNING PREFIX "Error registering SDC with system bus tree.\n"); 101362306a36Sopenharmony_ci return -ENODEV; 101462306a36Sopenharmony_ci } 101562306a36Sopenharmony_ci#elif defined(__mc68000__) 101662306a36Sopenharmony_ci if (!MACH_IS_HP300) 101762306a36Sopenharmony_ci return -ENODEV; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci hp_sdc.irq = 1; 102062306a36Sopenharmony_ci hp_sdc.nmi = 7; 102162306a36Sopenharmony_ci hp_sdc.base_io = (unsigned long) 0xf0428000; 102262306a36Sopenharmony_ci hp_sdc.data_io = (unsigned long) hp_sdc.base_io + 1; 102362306a36Sopenharmony_ci hp_sdc.status_io = (unsigned long) hp_sdc.base_io + 3; 102462306a36Sopenharmony_ci if (!copy_from_kernel_nofault(&i, (unsigned char *)hp_sdc.data_io, 1)) 102562306a36Sopenharmony_ci hp_sdc.dev = (void *)1; 102662306a36Sopenharmony_ci hp_sdc.dev_err = hp_sdc_init(); 102762306a36Sopenharmony_ci#endif 102862306a36Sopenharmony_ci if (hp_sdc.dev == NULL) { 102962306a36Sopenharmony_ci printk(KERN_WARNING PREFIX "No SDC found.\n"); 103062306a36Sopenharmony_ci return hp_sdc.dev_err; 103162306a36Sopenharmony_ci } 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci sema_init(&tq_init_sem, 0); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci tq_init.actidx = 0; 103662306a36Sopenharmony_ci tq_init.idx = 1; 103762306a36Sopenharmony_ci tq_init.endidx = 5; 103862306a36Sopenharmony_ci tq_init.seq = tq_init_seq; 103962306a36Sopenharmony_ci tq_init.act.semaphore = &tq_init_sem; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci tq_init_seq[0] = 104262306a36Sopenharmony_ci HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN | HP_SDC_ACT_SEMAPHORE; 104362306a36Sopenharmony_ci tq_init_seq[1] = HP_SDC_CMD_READ_KCC; 104462306a36Sopenharmony_ci tq_init_seq[2] = 1; 104562306a36Sopenharmony_ci tq_init_seq[3] = 0; 104662306a36Sopenharmony_ci tq_init_seq[4] = 0; 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci hp_sdc_enqueue_transaction(&tq_init); 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci down(&tq_init_sem); 105162306a36Sopenharmony_ci up(&tq_init_sem); 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci if ((tq_init_seq[0] & HP_SDC_ACT_DEAD) == HP_SDC_ACT_DEAD) { 105462306a36Sopenharmony_ci printk(KERN_WARNING PREFIX "Error reading config byte.\n"); 105562306a36Sopenharmony_ci hp_sdc_exit(); 105662306a36Sopenharmony_ci return -ENODEV; 105762306a36Sopenharmony_ci } 105862306a36Sopenharmony_ci hp_sdc.r11 = tq_init_seq[4]; 105962306a36Sopenharmony_ci if (hp_sdc.r11 & HP_SDC_CFG_NEW) { 106062306a36Sopenharmony_ci const char *str; 106162306a36Sopenharmony_ci printk(KERN_INFO PREFIX "New style SDC\n"); 106262306a36Sopenharmony_ci tq_init_seq[1] = HP_SDC_CMD_READ_XTD; 106362306a36Sopenharmony_ci tq_init.actidx = 0; 106462306a36Sopenharmony_ci tq_init.idx = 1; 106562306a36Sopenharmony_ci down(&tq_init_sem); 106662306a36Sopenharmony_ci hp_sdc_enqueue_transaction(&tq_init); 106762306a36Sopenharmony_ci down(&tq_init_sem); 106862306a36Sopenharmony_ci up(&tq_init_sem); 106962306a36Sopenharmony_ci if ((tq_init_seq[0] & HP_SDC_ACT_DEAD) == HP_SDC_ACT_DEAD) { 107062306a36Sopenharmony_ci printk(KERN_WARNING PREFIX "Error reading extended config byte.\n"); 107162306a36Sopenharmony_ci return -ENODEV; 107262306a36Sopenharmony_ci } 107362306a36Sopenharmony_ci hp_sdc.r7e = tq_init_seq[4]; 107462306a36Sopenharmony_ci HP_SDC_XTD_REV_STRINGS(hp_sdc.r7e & HP_SDC_XTD_REV, str) 107562306a36Sopenharmony_ci printk(KERN_INFO PREFIX "Revision: %s\n", str); 107662306a36Sopenharmony_ci if (hp_sdc.r7e & HP_SDC_XTD_BEEPER) 107762306a36Sopenharmony_ci printk(KERN_INFO PREFIX "TI SN76494 beeper present\n"); 107862306a36Sopenharmony_ci if (hp_sdc.r7e & HP_SDC_XTD_BBRTC) 107962306a36Sopenharmony_ci printk(KERN_INFO PREFIX "OKI MSM-58321 BBRTC present\n"); 108062306a36Sopenharmony_ci printk(KERN_INFO PREFIX "Spunking the self test register to force PUP " 108162306a36Sopenharmony_ci "on next firmware reset.\n"); 108262306a36Sopenharmony_ci tq_init_seq[0] = HP_SDC_ACT_PRECMD | 108362306a36Sopenharmony_ci HP_SDC_ACT_DATAOUT | HP_SDC_ACT_SEMAPHORE; 108462306a36Sopenharmony_ci tq_init_seq[1] = HP_SDC_CMD_SET_STR; 108562306a36Sopenharmony_ci tq_init_seq[2] = 1; 108662306a36Sopenharmony_ci tq_init_seq[3] = 0; 108762306a36Sopenharmony_ci tq_init.actidx = 0; 108862306a36Sopenharmony_ci tq_init.idx = 1; 108962306a36Sopenharmony_ci tq_init.endidx = 4; 109062306a36Sopenharmony_ci down(&tq_init_sem); 109162306a36Sopenharmony_ci hp_sdc_enqueue_transaction(&tq_init); 109262306a36Sopenharmony_ci down(&tq_init_sem); 109362306a36Sopenharmony_ci up(&tq_init_sem); 109462306a36Sopenharmony_ci } else 109562306a36Sopenharmony_ci printk(KERN_INFO PREFIX "Old style SDC (1820-%s).\n", 109662306a36Sopenharmony_ci (hp_sdc.r11 & HP_SDC_CFG_REV) ? "3300" : "2564/3087"); 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci return 0; 109962306a36Sopenharmony_ci} 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_cimodule_init(hp_sdc_register); 110262306a36Sopenharmony_cimodule_exit(hp_sdc_exit); 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci/* Timing notes: These measurements taken on my 64MHz 7100-LC (715/64) 110562306a36Sopenharmony_ci * cycles cycles-adj time 110662306a36Sopenharmony_ci * between two consecutive mfctl(16)'s: 4 n/a 63ns 110762306a36Sopenharmony_ci * hp_sdc_spin_ibf when idle: 119 115 1.7us 110862306a36Sopenharmony_ci * gsc_writeb status register: 83 79 1.2us 110962306a36Sopenharmony_ci * IBF to clear after sending SET_IM: 6204 6006 93us 111062306a36Sopenharmony_ci * IBF to clear after sending LOAD_RT: 4467 4352 68us 111162306a36Sopenharmony_ci * IBF to clear after sending two LOAD_RTs: 18974 18859 295us 111262306a36Sopenharmony_ci * READ_T1, read status/data, IRQ, call handler: 35564 n/a 556us 111362306a36Sopenharmony_ci * cmd to ~IBF READ_T1 2nd time right after: 5158403 n/a 81ms 111462306a36Sopenharmony_ci * between IRQ received and ~IBF for above: 2578877 n/a 40ms 111562306a36Sopenharmony_ci * 111662306a36Sopenharmony_ci * Performance stats after a run of this module configuring HIL and 111762306a36Sopenharmony_ci * receiving a few mouse events: 111862306a36Sopenharmony_ci * 111962306a36Sopenharmony_ci * status in8 282508 cycles 7128 calls 112062306a36Sopenharmony_ci * status out8 8404 cycles 341 calls 112162306a36Sopenharmony_ci * data out8 1734 cycles 78 calls 112262306a36Sopenharmony_ci * isr 174324 cycles 617 calls (includes take) 112362306a36Sopenharmony_ci * take 1241 cycles 2 calls 112462306a36Sopenharmony_ci * put 1411504 cycles 6937 calls 112562306a36Sopenharmony_ci * task 1655209 cycles 6937 calls (includes put) 112662306a36Sopenharmony_ci * 112762306a36Sopenharmony_ci */ 1128