- * hp_sdc_put does all writing to the SDC. ISR can run on a different
- * CPU than hp_sdc_put, but only one CPU runs hp_sdc_put at a time
+ * hp_sdc_put does all writing to the SDC. ISR can run on a different
+ * CPU than hp_sdc_put, but only one CPU runs hp_sdc_put at a time
- * All data coming back from the SDC is sent via interrupt and can be read
- * fully in the ISR, so there are no latency/throughput problems there.
- * The problem is with output, due to the slow clock speed of the SDC
- * compared to the CPU. This should not be too horrible most of the time,
- * but if used with HIL devices that support the multibyte transfer command,
- * keeping outbound throughput flowing at the 6500KBps that the HIL is
+ * All data coming back from the SDC is sent via interrupt and can be read
+ * fully in the ISR, so there are no latency/throughput problems there.
+ * The problem is with output, due to the slow clock speed of the SDC
+ * compared to the CPU. This should not be too horrible most of the time,
+ * but if used with HIL devices that support the multibyte transfer command,
+ * keeping outbound throughput flowing at the 6500KBps that the HIL is
- * Busy polling for IBF clear wastes CPU cycles and bus cycles. hp_sdc.ibf
- * is set to 0 when the IBF flag in the status register has cleared. ISR
- * may do this, and may also access the parts of queued transactions related
- * to reading data back from the SDC, but otherwise will not touch the
+ * Busy polling for IBF clear wastes CPU cycles and bus cycles. hp_sdc.ibf
+ * is set to 0 when the IBF flag in the status register has cleared. ISR
+ * may do this, and may also access the parts of queued transactions related
+ * to reading data back from the SDC, but otherwise will not touch the
* hp_sdc state. Whenever a register is written hp_sdc.ibf is set to 1.
*
* The i8042 write index and the values in the 4-byte input buffer
* starting at 0x70 are kept track of in hp_sdc.wi, and .r7[], respectively,
* hp_sdc state. Whenever a register is written hp_sdc.ibf is set to 1.
*
* The i8042 write index and the values in the 4-byte input buffer
* starting at 0x70 are kept track of in hp_sdc.wi, and .r7[], respectively,
* do not need to be locked since they are only ever accessed by hp_sdc_put.
*
* A timer task schedules the tasklet once per second just to make
* do not need to be locked since they are only ever accessed by hp_sdc_put.
*
* A timer task schedules the tasklet once per second just to make
+static unsigned int hp_sdc_disabled;
+module_param_named(no_hpsdc, hp_sdc_disabled, bool, 0);
+MODULE_PARM_DESC(no_hpsdc, "Do not enable HP SDC driver.");
+
static hp_i8042_sdc hp_sdc; /* All driver state is kept in here. */
/*************** primitives for use in any context *********************/
static hp_i8042_sdc hp_sdc; /* All driver state is kept in here. */
/*************** primitives for use in any context *********************/
uint8_t status;
unsigned long flags;
write_lock_irqsave(&hp_sdc.ibf_lock, flags);
status = sdc_readb(hp_sdc.status_io);
uint8_t status;
unsigned long flags;
write_lock_irqsave(&hp_sdc.ibf_lock, flags);
status = sdc_readb(hp_sdc.status_io);
sdc_writeb(val, hp_sdc.status_io);
write_unlock_irqrestore(&hp_sdc.ibf_lock, flags);
}
sdc_writeb(val, hp_sdc.status_io);
write_unlock_irqrestore(&hp_sdc.ibf_lock, flags);
}
-/* Care must be taken to only invoke hp_sdc_spin_ibf when
- * absolutely needed, or in rarely invoked subroutines.
- * Not only does it waste CPU cycles, it also wastes bus cycles.
+/* Care must be taken to only invoke hp_sdc_spin_ibf when
+ * absolutely needed, or in rarely invoked subroutines.
+ * Not only does it waste CPU cycles, it also wastes bus cycles.
hp_sdc.ibf = 0;
write_unlock_irqrestore(lock, flags);
}
/************************ Interrupt context functions ************************/
hp_sdc.ibf = 0;
write_unlock_irqrestore(lock, flags);
}
/************************ Interrupt context functions ************************/
-static void hp_sdc_take (int irq, void *dev_id, uint8_t status, uint8_t data) {
+static void hp_sdc_take(int irq, void *dev_id, uint8_t status, uint8_t data)
+{
- if(curr->seq[curr->actidx] & HP_SDC_ACT_SEMAPHORE) {
- if (curr->act.semaphore) up(curr->act.semaphore);
- }
- if(curr->seq[curr->actidx] & HP_SDC_ACT_CALLBACK) {
+ if (curr->seq[curr->actidx] & HP_SDC_ACT_SEMAPHORE)
+ if (curr->act.semaphore)
+ up(curr->act.semaphore);
+
+ if (curr->seq[curr->actidx] & HP_SDC_ACT_CALLBACK)
if (curr->act.irqhook)
curr->act.irqhook(irq, dev_id, status, data);
if (curr->act.irqhook)
curr->act.irqhook(irq, dev_id, status, data);
curr->actidx = curr->idx;
curr->idx++;
/* Return control of this transaction */
write_lock(&hp_sdc.rtq_lock);
curr->actidx = curr->idx;
curr->idx++;
/* Return control of this transaction */
write_lock(&hp_sdc.rtq_lock);
- case HP_SDC_STATUS_USERTIMER:
- case HP_SDC_STATUS_PERIODIC:
- case HP_SDC_STATUS_TIMER:
+
+ case HP_SDC_STATUS_USERTIMER:
+ case HP_SDC_STATUS_PERIODIC:
+ case HP_SDC_STATUS_TIMER:
hp_sdc.timer(irq, dev_id, status, data);
read_unlock(&hp_sdc.hook_lock);
break;
hp_sdc.timer(irq, dev_id, status, data);
read_unlock(&hp_sdc.hook_lock);
break;
hp_sdc_take(irq, dev_id, status, data);
break;
hp_sdc_take(irq, dev_id, status, data);
break;
read_lock(&hp_sdc.hook_lock);
if (hp_sdc.hil != NULL)
hp_sdc.hil(irq, dev_id, status, data);
read_unlock(&hp_sdc.hook_lock);
break;
read_lock(&hp_sdc.hook_lock);
if (hp_sdc.hil != NULL)
hp_sdc.hil(irq, dev_id, status, data);
read_unlock(&hp_sdc.hook_lock);
break;
read_lock(&hp_sdc.hook_lock);
if (hp_sdc.pup != NULL)
hp_sdc.pup(irq, dev_id, status, data);
read_lock(&hp_sdc.hook_lock);
if (hp_sdc.pup != NULL)
hp_sdc.pup(irq, dev_id, status, data);
read_lock(&hp_sdc.hook_lock);
if (hp_sdc.cooked != NULL)
hp_sdc.cooked(irq, dev_id, status, data);
read_unlock(&hp_sdc.hook_lock);
break;
}
read_lock(&hp_sdc.hook_lock);
if (hp_sdc.cooked != NULL)
hp_sdc.cooked(irq, dev_id, status, data);
read_unlock(&hp_sdc.hook_lock);
break;
}
hp_sdc.timer(irq, dev_id, status, 0);
read_unlock(&hp_sdc.hook_lock);
hp_sdc.timer(irq, dev_id, status, 0);
read_unlock(&hp_sdc.hook_lock);
/* TODO: pass this on to the HIL handler, or do SAK here? */
printk(KERN_WARNING PREFIX "HIL NMI\n");
}
#endif
/* TODO: pass this on to the HIL handler, or do SAK here? */
printk(KERN_WARNING PREFIX "HIL NMI\n");
}
#endif
- if (curr->act.irqhook)
- curr->act.irqhook(0, 0, 0, 0);
+ if (curr->act.irqhook)
+ curr->act.irqhook(0, NULL, 0, 0);
w7[0] = (mask & 1) ? curr->seq[++idx] : hp_sdc.r7[0];
w7[1] = (mask & 2) ? curr->seq[++idx] : hp_sdc.r7[1];
w7[2] = (mask & 4) ? curr->seq[++idx] : hp_sdc.r7[2];
w7[3] = (mask & 8) ? curr->seq[++idx] : hp_sdc.r7[3];
w7[0] = (mask & 1) ? curr->seq[++idx] : hp_sdc.r7[0];
w7[1] = (mask & 2) ? curr->seq[++idx] : hp_sdc.r7[1];
w7[2] = (mask & 4) ? curr->seq[++idx] : hp_sdc.r7[2];
w7[3] = (mask & 8) ? curr->seq[++idx] : hp_sdc.r7[3];
do_gettimeofday(&hp_sdc.rtv);
curr->idx++;
/* Still need to lock here in case of spurious irq. */
write_lock_irq(&hp_sdc.rtq_lock);
do_gettimeofday(&hp_sdc.rtv);
curr->idx++;
/* Still need to lock here in case of spurious irq. */
write_lock_irq(&hp_sdc.rtq_lock);
- }
- else if (act & HP_SDC_ACT_CALLBACK) {
- curr->act.irqhook(0,0,0,0);
- }
+ else if (act & HP_SDC_ACT_CALLBACK)
+ curr->act.irqhook(0,NULL,0,0);
+
-int hp_sdc_dequeue_transaction(hp_sdc_transaction *this) {
+int hp_sdc_enqueue_transaction(hp_sdc_transaction *this) {
+ unsigned long flags;
+ int ret;
+
+ write_lock_irqsave(&hp_sdc.lock, flags);
+ ret = __hp_sdc_enqueue_transaction(this);
+ write_unlock_irqrestore(&hp_sdc.lock,flags);
+
+ return ret;
+}
+
+int hp_sdc_dequeue_transaction(hp_sdc_transaction *this)
+{
-int hp_sdc_request_timer_irq(hp_sdc_irqhook *callback) {
-
- if (callback == NULL || hp_sdc.dev == NULL) {
+int hp_sdc_request_timer_irq(hp_sdc_irqhook *callback)
+{
+ if (callback == NULL || hp_sdc.dev == NULL)
write_lock_irq(&hp_sdc.hook_lock);
if (hp_sdc.timer != NULL) {
write_unlock_irq(&hp_sdc.hook_lock);
write_lock_irq(&hp_sdc.hook_lock);
if (hp_sdc.timer != NULL) {
write_unlock_irq(&hp_sdc.hook_lock);
-int hp_sdc_request_hil_irq(hp_sdc_irqhook *callback) {
-
- if (callback == NULL || hp_sdc.dev == NULL) {
+int hp_sdc_request_hil_irq(hp_sdc_irqhook *callback)
+{
+ if (callback == NULL || hp_sdc.dev == NULL)
write_lock_irq(&hp_sdc.hook_lock);
if (hp_sdc.hil != NULL) {
write_unlock_irq(&hp_sdc.hook_lock);
write_lock_irq(&hp_sdc.hook_lock);
if (hp_sdc.hil != NULL) {
write_unlock_irq(&hp_sdc.hook_lock);
-int hp_sdc_request_cooked_irq(hp_sdc_irqhook *callback) {
-
- if (callback == NULL || hp_sdc.dev == NULL) {
+int hp_sdc_request_cooked_irq(hp_sdc_irqhook *callback)
+{
+ if (callback == NULL || hp_sdc.dev == NULL)
write_lock_irq(&hp_sdc.hook_lock);
if (hp_sdc.cooked != NULL) {
write_unlock_irq(&hp_sdc.hook_lock);
write_lock_irq(&hp_sdc.hook_lock);
if (hp_sdc.cooked != NULL) {
write_unlock_irq(&hp_sdc.hook_lock);
write_lock_irq(&hp_sdc.hook_lock);
if ((callback != hp_sdc.timer) ||
(hp_sdc.timer == NULL)) {
write_lock_irq(&hp_sdc.hook_lock);
if ((callback != hp_sdc.timer) ||
(hp_sdc.timer == NULL)) {
write_lock_irq(&hp_sdc.hook_lock);
if ((callback != hp_sdc.cooked) ||
(hp_sdc.cooked == NULL)) {
write_lock_irq(&hp_sdc.hook_lock);
if ((callback != hp_sdc.cooked) ||
(hp_sdc.cooked == NULL)) {
tasklet_schedule(&hp_sdc.task);
/* Re-insert the periodic task. */
mod_timer(&hp_sdc.kicker, jiffies + HZ);
tasklet_schedule(&hp_sdc.task);
/* Re-insert the periodic task. */
mod_timer(&hp_sdc.kicker, jiffies + HZ);
.hversion_rev = HVERSION_REV_ANY_ID,
.hversion = HVERSION_ANY_ID,
.hversion_rev = HVERSION_REV_ANY_ID,
.hversion = HVERSION_ANY_ID,
- rwlock_init(&hp_sdc.lock);
- rwlock_init(&hp_sdc.ibf_lock);
- rwlock_init(&hp_sdc.rtq_lock);
- rwlock_init(&hp_sdc.hook_lock);
+ rwlock_init(&hp_sdc.lock);
+ rwlock_init(&hp_sdc.ibf_lock);
+ rwlock_init(&hp_sdc.rtq_lock);
+ rwlock_init(&hp_sdc.hook_lock);
- if(request_irq(hp_sdc.irq, &hp_sdc_isr, 0, "HP SDC",
- (void *) hp_sdc.base_io)) goto err1;
+ if (request_irq(hp_sdc.irq, &hp_sdc_isr, IRQF_SHARED|IRQF_SAMPLE_RANDOM,
+ "HP SDC", &hp_sdc))
+ goto err1;
- if (request_irq(hp_sdc.nmi, &hp_sdc_nmisr, 0, "HP SDC NMI",
- (void *) hp_sdc.base_io)) goto err2;
+ if (request_irq(hp_sdc.nmi, &hp_sdc_nmisr, IRQF_SHARED,
+ "HP SDC NMI", &hp_sdc))
+ goto err2;
errstr, (void *)hp_sdc.base_io, hp_sdc.irq, hp_sdc.nmi);
hp_sdc.dev = NULL;
errstr, (void *)hp_sdc.base_io, hp_sdc.irq, hp_sdc.nmi);
hp_sdc.dev = NULL;
- free_irq(hp_sdc.nmi, NULL);
- free_irq(hp_sdc.irq, NULL);
+ free_irq(hp_sdc.nmi, &hp_sdc);
+ free_irq(hp_sdc.irq, &hp_sdc);
write_unlock_irq(&hp_sdc.lock);
del_timer(&hp_sdc.kicker);
tasklet_kill(&hp_sdc.task);
write_unlock_irq(&hp_sdc.lock);
del_timer(&hp_sdc.kicker);
tasklet_kill(&hp_sdc.task);
hp_sdc.r7e = tq_init_seq[4];
HP_SDC_XTD_REV_STRINGS(hp_sdc.r7e & HP_SDC_XTD_REV, str)
printk(KERN_INFO PREFIX "Revision: %s\n", str);
hp_sdc.r7e = tq_init_seq[4];
HP_SDC_XTD_REV_STRINGS(hp_sdc.r7e & HP_SDC_XTD_REV, str)
printk(KERN_INFO PREFIX "Revision: %s\n", str);
printk(KERN_INFO PREFIX "Spunking the self test register to force PUP "
"on next firmware reset.\n");
printk(KERN_INFO PREFIX "Spunking the self test register to force PUP "
"on next firmware reset.\n");
* cycles cycles-adj time
* between two consecutive mfctl(16)'s: 4 n/a 63ns
* hp_sdc_spin_ibf when idle: 119 115 1.7us
* gsc_writeb status register: 83 79 1.2us
* IBF to clear after sending SET_IM: 6204 6006 93us
* cycles cycles-adj time
* between two consecutive mfctl(16)'s: 4 n/a 63ns
* hp_sdc_spin_ibf when idle: 119 115 1.7us
* gsc_writeb status register: 83 79 1.2us
* IBF to clear after sending SET_IM: 6204 6006 93us
* IBF to clear after sending two LOAD_RTs: 18974 18859 295us
* READ_T1, read status/data, IRQ, call handler: 35564 n/a 556us
* cmd to ~IBF READ_T1 2nd time right after: 5158403 n/a 81ms
* IBF to clear after sending two LOAD_RTs: 18974 18859 295us
* READ_T1, read status/data, IRQ, call handler: 35564 n/a 556us
* cmd to ~IBF READ_T1 2nd time right after: 5158403 n/a 81ms