mirror of
https://github.com/torvalds/linux.git
synced 2024-11-27 14:41:39 +00:00
7d12e780e0
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead of passing regs around manually through all ~1800 interrupt handlers in the Linux kernel. The regs pointer is used in few places, but it potentially costs both stack space and code to pass it around. On the FRV arch, removing the regs parameter from all the genirq function results in a 20% speed up of the IRQ exit path (ie: from leaving timer_interrupt() to leaving do_IRQ()). Where appropriate, an arch may override the generic storage facility and do something different with the variable. On FRV, for instance, the address is maintained in GR28 at all times inside the kernel as part of general exception handling. Having looked over the code, it appears that the parameter may be handed down through up to twenty or so layers of functions. Consider a USB character device attached to a USB hub, attached to a USB controller that posts its interrupts through a cascaded auxiliary interrupt controller. A character device driver may want to pass regs to the sysrq handler through the input layer which adds another few layers of parameter passing. I've build this code with allyesconfig for x86_64 and i386. I've runtested the main part of the code on FRV and i386, though I can't test most of the drivers. I've also done partial conversion for powerpc and MIPS - these at least compile with minimal configurations. This will affect all archs. Mostly the changes should be relatively easy. Take do_IRQ(), store the regs pointer at the beginning, saving the old one: struct pt_regs *old_regs = set_irq_regs(regs); And put the old one back at the end: set_irq_regs(old_regs); Don't pass regs through to generic_handle_irq() or __do_IRQ(). In timer_interrupt(), this sort of change will be necessary: - update_process_times(user_mode(regs)); - profile_tick(CPU_PROFILING, regs); + update_process_times(user_mode(get_irq_regs())); + profile_tick(CPU_PROFILING); I'd like to move update_process_times()'s use of get_irq_regs() into itself, except that i386, alone of the archs, uses something other than user_mode(). Some notes on the interrupt handling in the drivers: (*) input_dev() is now gone entirely. The regs pointer is no longer stored in the input_dev struct. (*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does something different depending on whether it's been supplied with a regs pointer or not. (*) Various IRQ handler function pointers have been moved to type irq_handler_t. Signed-Off-By: David Howells <dhowells@redhat.com> (cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
289 lines
6.7 KiB
C
289 lines
6.7 KiB
C
/*
|
|
* PCBIT-D low-layer interface definitions
|
|
*
|
|
* Copyright (C) 1996 Universidade de Lisboa
|
|
*
|
|
* Written by Pedro Roque Marques (roque@di.fc.ul.pt)
|
|
*
|
|
* This software may be used and distributed according to the terms of
|
|
* the GNU General Public License, incorporated herein by reference.
|
|
*/
|
|
|
|
/*
|
|
* 19991203 - Fernando Carvalho - takion@superbofh.org
|
|
* Hacked to compile with egcs and run with current version of isdn modules
|
|
*/
|
|
|
|
#ifndef LAYER2_H
|
|
#define LAYER2_H
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <asm/byteorder.h>
|
|
|
|
#define BANK1 0x0000U /* PC -> Board */
|
|
#define BANK2 0x01ffU /* Board -> PC */
|
|
#define BANK3 0x03feU /* Att Board */
|
|
#define BANK4 0x03ffU /* Att PC */
|
|
|
|
#define BANKLEN 0x01FFU
|
|
|
|
#define LOAD_ZONE_START 0x03f8U
|
|
#define LOAD_ZONE_END 0x03fdU
|
|
|
|
#define LOAD_RETRY 18000000
|
|
|
|
|
|
|
|
/* TAM - XX - C - S - NUM */
|
|
#define PREHDR_LEN 8
|
|
/* TT - M - I - TH - TD */
|
|
#define FRAME_HDR_LEN 8
|
|
|
|
#define MSG_CONN_REQ 0x08000100
|
|
#define MSG_CONN_CONF 0x00000101
|
|
#define MSG_CONN_IND 0x00000102
|
|
#define MSG_CONN_RESP 0x08000103
|
|
|
|
#define MSG_CONN_ACTV_REQ 0x08000300
|
|
#define MSG_CONN_ACTV_CONF 0x00000301
|
|
#define MSG_CONN_ACTV_IND 0x00000302
|
|
#define MSG_CONN_ACTV_RESP 0x08000303
|
|
|
|
#define MSG_DISC_REQ 0x08000400
|
|
#define MSG_DISC_CONF 0x00000401
|
|
#define MSG_DISC_IND 0x00000402
|
|
#define MSG_DISC_RESP 0x08000403
|
|
|
|
#define MSG_TDATA_REQ 0x0908E200
|
|
#define MSG_TDATA_CONF 0x0000E201
|
|
#define MSG_TDATA_IND 0x0000E202
|
|
#define MSG_TDATA_RESP 0x0908E203
|
|
|
|
#define MSG_SELP_REQ 0x09004000
|
|
#define MSG_SELP_CONF 0x00004001
|
|
|
|
#define MSG_ACT_TRANSP_REQ 0x0908E000
|
|
#define MSG_ACT_TRANSP_CONF 0x0000E001
|
|
|
|
#define MSG_STPROT_REQ 0x09004100
|
|
#define MSG_STPROT_CONF 0x00004101
|
|
|
|
#define MSG_PING188_REQ 0x09030500
|
|
#define MSG_PING188_CONF 0x000005bc
|
|
|
|
#define MSG_WATCH188 0x09030400
|
|
|
|
#define MSG_API_ON 0x08020102
|
|
#define MSG_POOL_PCBIT 0x08020400
|
|
#define MSG_POOL_PCBIT_CONF 0x00000401
|
|
|
|
#define MSG_INFO_IND 0x00002602
|
|
#define MSG_INFO_RESP 0x08002603
|
|
|
|
#define MSG_DEBUG_188 0x0000ff00
|
|
|
|
/*
|
|
|
|
long 4 3 2 1
|
|
Intel 1 2 3 4
|
|
*/
|
|
|
|
#ifdef __LITTLE_ENDIAN
|
|
#define SET_MSG_SCMD(msg, ch) (msg = (msg & 0xffffff00) | (((ch) & 0xff)))
|
|
#define SET_MSG_CMD(msg, ch) (msg = (msg & 0xffff00ff) | (((ch) & 0xff) << 8))
|
|
#define SET_MSG_PROC(msg, ch) (msg = (msg & 0xff00ffff) | (((ch) & 0xff) << 16))
|
|
#define SET_MSG_CPU(msg, ch) (msg = (msg & 0x00ffffff) | (((ch) & 0xff) << 24))
|
|
|
|
#define GET_MSG_SCMD(msg) ((msg) & 0xFF)
|
|
#define GET_MSG_CMD(msg) ((msg) >> 8 & 0xFF)
|
|
#define GET_MSG_PROC(msg) ((msg) >> 16 & 0xFF)
|
|
#define GET_MSG_CPU(msg) ((msg) >> 24)
|
|
|
|
#else
|
|
#error "Non-Intel CPU"
|
|
#endif
|
|
|
|
#define MAX_QUEUED 7
|
|
|
|
#define SCHED_READ 0x01
|
|
#define SCHED_WRITE 0x02
|
|
|
|
#define SET_RUN_TIMEOUT 2*HZ /* 2 seconds */
|
|
|
|
struct frame_buf {
|
|
ulong msg;
|
|
unsigned int refnum;
|
|
unsigned int dt_len;
|
|
unsigned int hdr_len;
|
|
struct sk_buff *skb;
|
|
unsigned int copied;
|
|
struct frame_buf * next;
|
|
};
|
|
|
|
extern int pcbit_l2_write(struct pcbit_dev * dev, ulong msg, ushort refnum,
|
|
struct sk_buff *skb, unsigned short hdr_len);
|
|
|
|
extern irqreturn_t pcbit_irq_handler(int interrupt, void *);
|
|
|
|
extern struct pcbit_dev * dev_pcbit[MAX_PCBIT_CARDS];
|
|
|
|
#ifdef DEBUG
|
|
static __inline__ void log_state(struct pcbit_dev *dev) {
|
|
printk(KERN_DEBUG "writeptr = %ld\n",
|
|
(ulong) (dev->writeptr - dev->sh_mem));
|
|
printk(KERN_DEBUG "readptr = %ld\n",
|
|
(ulong) (dev->readptr - (dev->sh_mem + BANK2)));
|
|
printk(KERN_DEBUG "{rcv_seq=%01x, send_seq=%01x, unack_seq=%01x}\n",
|
|
dev->rcv_seq, dev->send_seq, dev->unack_seq);
|
|
}
|
|
#endif
|
|
|
|
static __inline__ struct pcbit_dev * chan2dev(struct pcbit_chan * chan)
|
|
{
|
|
struct pcbit_dev * dev;
|
|
int i;
|
|
|
|
|
|
for (i=0; i<MAX_PCBIT_CARDS; i++)
|
|
if ((dev=dev_pcbit[i]))
|
|
if (dev->b1 == chan || dev->b2 == chan)
|
|
return dev;
|
|
return NULL;
|
|
|
|
}
|
|
|
|
static __inline__ struct pcbit_dev * finddev(int id)
|
|
{
|
|
struct pcbit_dev * dev;
|
|
int i;
|
|
|
|
for (i=0; i<MAX_PCBIT_CARDS; i++)
|
|
if ((dev=dev_pcbit[i]))
|
|
if (dev->id == id)
|
|
return dev;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* Support routines for reading and writing in the board
|
|
*/
|
|
|
|
static __inline__ void pcbit_writeb(struct pcbit_dev *dev, unsigned char dt)
|
|
{
|
|
writeb(dt, dev->writeptr++);
|
|
if (dev->writeptr == dev->sh_mem + BANKLEN)
|
|
dev->writeptr = dev->sh_mem;
|
|
}
|
|
|
|
static __inline__ void pcbit_writew(struct pcbit_dev *dev, unsigned short dt)
|
|
{
|
|
int dist;
|
|
|
|
dist = BANKLEN - (dev->writeptr - dev->sh_mem);
|
|
switch (dist) {
|
|
case 2:
|
|
writew(dt, dev->writeptr);
|
|
dev->writeptr = dev->sh_mem;
|
|
break;
|
|
case 1:
|
|
writeb((u_char) (dt & 0x00ffU), dev->writeptr);
|
|
dev->writeptr = dev->sh_mem;
|
|
writeb((u_char) (dt >> 8), dev->writeptr++);
|
|
break;
|
|
default:
|
|
writew(dt, dev->writeptr);
|
|
dev->writeptr += 2;
|
|
break;
|
|
};
|
|
}
|
|
|
|
static __inline__ void memcpy_topcbit(struct pcbit_dev * dev, u_char * data,
|
|
int len)
|
|
{
|
|
int diff;
|
|
|
|
diff = len - (BANKLEN - (dev->writeptr - dev->sh_mem) );
|
|
|
|
if (diff > 0)
|
|
{
|
|
memcpy_toio(dev->writeptr, data, len - diff);
|
|
memcpy_toio(dev->sh_mem, data + (len - diff), diff);
|
|
dev->writeptr = dev->sh_mem + diff;
|
|
}
|
|
else
|
|
{
|
|
memcpy_toio(dev->writeptr, data, len);
|
|
|
|
dev->writeptr += len;
|
|
if (diff == 0)
|
|
dev->writeptr = dev->sh_mem;
|
|
}
|
|
}
|
|
|
|
static __inline__ unsigned char pcbit_readb(struct pcbit_dev *dev)
|
|
{
|
|
unsigned char val;
|
|
|
|
val = readb(dev->readptr++);
|
|
if (dev->readptr == dev->sh_mem + BANK2 + BANKLEN)
|
|
dev->readptr = dev->sh_mem + BANK2;
|
|
|
|
return val;
|
|
}
|
|
|
|
static __inline__ unsigned short pcbit_readw(struct pcbit_dev *dev)
|
|
{
|
|
int dist;
|
|
unsigned short val;
|
|
|
|
dist = BANKLEN - ( dev->readptr - (dev->sh_mem + BANK2 ) );
|
|
switch (dist) {
|
|
case 2:
|
|
val = readw(dev->readptr);
|
|
dev->readptr = dev->sh_mem + BANK2;
|
|
break;
|
|
case 1:
|
|
val = readb(dev->readptr);
|
|
dev->readptr = dev->sh_mem + BANK2;
|
|
val = (readb(dev->readptr++) << 8) | val;
|
|
break;
|
|
default:
|
|
val = readw(dev->readptr);
|
|
dev->readptr += 2;
|
|
break;
|
|
};
|
|
return val;
|
|
}
|
|
|
|
static __inline__ void memcpy_frompcbit(struct pcbit_dev * dev, u_char * data, int len)
|
|
{
|
|
int diff;
|
|
|
|
diff = len - (BANKLEN - (dev->readptr - (dev->sh_mem + BANK2) ) );
|
|
if (diff > 0)
|
|
{
|
|
memcpy_fromio(data, dev->readptr, len - diff);
|
|
memcpy_fromio(data + (len - diff), dev->sh_mem + BANK2 , diff);
|
|
dev->readptr = dev->sh_mem + BANK2 + diff;
|
|
}
|
|
else
|
|
{
|
|
memcpy_fromio(data, dev->readptr, len);
|
|
dev->readptr += len;
|
|
if (diff == 0)
|
|
dev->readptr = dev->sh_mem + BANK2;
|
|
}
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|