mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-12 13:55:32 +00:00
![David Howells](/assets/img/avatar_default.png)
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)
292 lines
7.6 KiB
C
292 lines
7.6 KiB
C
#ifndef _NM256_H_
|
||
#define _NM256_H_
|
||
|
||
#include <linux/spinlock.h>
|
||
#include <linux/interrupt.h>
|
||
|
||
#include "ac97.h"
|
||
|
||
/* The revisions that we currently handle. */
|
||
enum nm256rev {
|
||
REV_NM256AV, REV_NM256ZX
|
||
};
|
||
|
||
/* Per-card structure. */
|
||
struct nm256_info
|
||
{
|
||
/* Magic number used to verify that this struct is valid. */
|
||
#define NM_MAGIC_SIG 0x55aa00ff
|
||
int magsig;
|
||
|
||
/* Revision number */
|
||
enum nm256rev rev;
|
||
|
||
struct ac97_hwint mdev;
|
||
|
||
/* Our audio device numbers. */
|
||
int dev[2];
|
||
|
||
/* The # of times each device has been opened. (Should only be
|
||
0 or 1). */
|
||
int opencnt[2];
|
||
|
||
/* We use two devices, because we can do simultaneous play and record.
|
||
This keeps track of which device is being used for what purpose;
|
||
these are the actual device numbers. */
|
||
int dev_for_play;
|
||
int dev_for_record;
|
||
|
||
spinlock_t lock;
|
||
|
||
/* The mixer device. */
|
||
int mixer_oss_dev;
|
||
|
||
/*
|
||
* Can only be opened once for each operation. These aren't set
|
||
* until an actual I/O operation is performed; this allows one
|
||
* device to be open for read/write without inhibiting I/O to
|
||
* the other device.
|
||
*/
|
||
int is_open_play;
|
||
int is_open_record;
|
||
|
||
/* Non-zero if we're currently playing a sample. */
|
||
int playing;
|
||
/* Ditto for recording a sample. */
|
||
int recording;
|
||
|
||
/* The two memory ports. */
|
||
struct nm256_ports {
|
||
/* Physical address of the port. */
|
||
u32 physaddr;
|
||
/* Our mapped-in pointer. */
|
||
char __iomem *ptr;
|
||
/* PTR's offset within the physical port. */
|
||
u32 start_offset;
|
||
/* And the offset of the end of the buffer. */
|
||
u32 end_offset;
|
||
} port[2];
|
||
|
||
/* The following are offsets within memory port 1. */
|
||
u32 coeffBuf;
|
||
u32 allCoeffBuf;
|
||
|
||
/* Record and playback buffers. */
|
||
u32 abuf1, abuf2;
|
||
|
||
/* Offset of the AC97 mixer in memory port 2. */
|
||
u32 mixer;
|
||
|
||
/* Offset of the mixer status register in memory port 2. */
|
||
u32 mixer_status_offset;
|
||
|
||
/* Non-zero if we have written initial values to the mixer. */
|
||
u8 mixer_values_init;
|
||
|
||
/*
|
||
* Status mask bit; (*mixer_status_loc & mixer_status_mask) == 0 means
|
||
* it's ready.
|
||
*/
|
||
u16 mixer_status_mask;
|
||
|
||
/* The sizes of the playback and record ring buffers. */
|
||
u32 playbackBufferSize;
|
||
u32 recordBufferSize;
|
||
|
||
/* Are the coefficient values in the memory cache current? */
|
||
u8 coeffsCurrent;
|
||
|
||
/* For writes, the amount we last wrote. */
|
||
u32 requested_amt;
|
||
/* The start of the block currently playing. */
|
||
u32 curPlayPos;
|
||
|
||
/* The amount of data we were requested to record. */
|
||
u32 requestedRecAmt;
|
||
/* The offset of the currently-recording block. */
|
||
u32 curRecPos;
|
||
/* The destination buffer. */
|
||
char *recBuf;
|
||
|
||
/* Our IRQ number. */
|
||
int irq;
|
||
|
||
/* A flag indicating how many times we've grabbed the IRQ. */
|
||
int has_irq;
|
||
|
||
/* The card interrupt service routine. */
|
||
irq_handler_t introutine;
|
||
|
||
/* Current audio config, cached. */
|
||
struct sinfo {
|
||
u32 samplerate;
|
||
u8 bits;
|
||
u8 stereo;
|
||
} sinfo[2]; /* goes with each device */
|
||
|
||
/* The cards are stored in a chain; this is the next card. */
|
||
struct nm256_info *next_card;
|
||
};
|
||
|
||
/* The BIOS signature. */
|
||
#define NM_SIGNATURE 0x4e4d0000
|
||
/* Signature mask. */
|
||
#define NM_SIG_MASK 0xffff0000
|
||
|
||
/* Size of the second memory area. */
|
||
#define NM_PORT2_SIZE 4096
|
||
|
||
/* The base offset of the mixer in the second memory area. */
|
||
#define NM_MIXER_OFFSET 0x600
|
||
|
||
/* The maximum size of a coefficient entry. */
|
||
#define NM_MAX_COEFFICIENT 0x5000
|
||
|
||
/* The interrupt register. */
|
||
#define NM_INT_REG 0xa04
|
||
/* And its bits. */
|
||
#define NM_PLAYBACK_INT 0x40
|
||
#define NM_RECORD_INT 0x100
|
||
#define NM_MISC_INT_1 0x4000
|
||
#define NM_MISC_INT_2 0x1
|
||
#define NM_ACK_INT(CARD, X) nm256_writePort16((CARD), 2, NM_INT_REG, (X) << 1)
|
||
|
||
/* The AV's "mixer ready" status bit and location. */
|
||
#define NM_MIXER_STATUS_OFFSET 0xa04
|
||
#define NM_MIXER_READY_MASK 0x0800
|
||
#define NM_MIXER_PRESENCE 0xa06
|
||
#define NM_PRESENCE_MASK 0x0050
|
||
#define NM_PRESENCE_VALUE 0x0040
|
||
|
||
/*
|
||
* For the ZX. It uses the same interrupt register, but it holds 32
|
||
* bits instead of 16.
|
||
*/
|
||
#define NM2_PLAYBACK_INT 0x10000
|
||
#define NM2_RECORD_INT 0x80000
|
||
#define NM2_MISC_INT_1 0x8
|
||
#define NM2_MISC_INT_2 0x2
|
||
#define NM2_ACK_INT(CARD, X) nm256_writePort32((CARD), 2, NM_INT_REG, (X))
|
||
|
||
/* The ZX's "mixer ready" status bit and location. */
|
||
#define NM2_MIXER_STATUS_OFFSET 0xa06
|
||
#define NM2_MIXER_READY_MASK 0x0800
|
||
|
||
/* The playback registers start from here. */
|
||
#define NM_PLAYBACK_REG_OFFSET 0x0
|
||
/* The record registers start from here. */
|
||
#define NM_RECORD_REG_OFFSET 0x200
|
||
|
||
/* The rate register is located 2 bytes from the start of the register area. */
|
||
#define NM_RATE_REG_OFFSET 2
|
||
|
||
/* Mono/stereo flag, number of bits on playback, and rate mask. */
|
||
#define NM_RATE_STEREO 1
|
||
#define NM_RATE_BITS_16 2
|
||
#define NM_RATE_MASK 0xf0
|
||
|
||
/* Playback enable register. */
|
||
#define NM_PLAYBACK_ENABLE_REG (NM_PLAYBACK_REG_OFFSET + 0x1)
|
||
#define NM_PLAYBACK_ENABLE_FLAG 1
|
||
#define NM_PLAYBACK_ONESHOT 2
|
||
#define NM_PLAYBACK_FREERUN 4
|
||
|
||
/* Mutes the audio output. */
|
||
#define NM_AUDIO_MUTE_REG (NM_PLAYBACK_REG_OFFSET + 0x18)
|
||
#define NM_AUDIO_MUTE_LEFT 0x8000
|
||
#define NM_AUDIO_MUTE_RIGHT 0x0080
|
||
|
||
/* Recording enable register. */
|
||
#define NM_RECORD_ENABLE_REG (NM_RECORD_REG_OFFSET + 0)
|
||
#define NM_RECORD_ENABLE_FLAG 1
|
||
#define NM_RECORD_FREERUN 2
|
||
|
||
#define NM_RBUFFER_START (NM_RECORD_REG_OFFSET + 0x4)
|
||
#define NM_RBUFFER_END (NM_RECORD_REG_OFFSET + 0x10)
|
||
#define NM_RBUFFER_WMARK (NM_RECORD_REG_OFFSET + 0xc)
|
||
#define NM_RBUFFER_CURRP (NM_RECORD_REG_OFFSET + 0x8)
|
||
|
||
#define NM_PBUFFER_START (NM_PLAYBACK_REG_OFFSET + 0x4)
|
||
#define NM_PBUFFER_END (NM_PLAYBACK_REG_OFFSET + 0x14)
|
||
#define NM_PBUFFER_WMARK (NM_PLAYBACK_REG_OFFSET + 0xc)
|
||
#define NM_PBUFFER_CURRP (NM_PLAYBACK_REG_OFFSET + 0x8)
|
||
|
||
/* A few trivial routines to make it easier to work with the registers
|
||
on the chip. */
|
||
|
||
/* This is a common code portion used to fix up the port offsets. */
|
||
#define NM_FIX_PORT \
|
||
if (port < 1 || port > 2 || card == NULL) \
|
||
return -1; \
|
||
\
|
||
if (offset < card->port[port - 1].start_offset \
|
||
|| offset >= card->port[port - 1].end_offset) { \
|
||
printk (KERN_ERR "Bad access: port %d, offset 0x%x\n", port, offset); \
|
||
return -1; \
|
||
} \
|
||
offset -= card->port[port - 1].start_offset;
|
||
|
||
#define DEFwritePortX(X, func) \
|
||
static inline int nm256_writePort##X (struct nm256_info *card,\
|
||
int port, int offset, int value)\
|
||
{\
|
||
u##X __iomem *addr;\
|
||
\
|
||
if (nm256_debug > 1)\
|
||
printk (KERN_DEBUG "Writing 0x%x to %d:0x%x\n", value, port, offset);\
|
||
\
|
||
NM_FIX_PORT;\
|
||
\
|
||
addr = (u##X __iomem *)(card->port[port - 1].ptr + offset);\
|
||
func (value, addr);\
|
||
return 0;\
|
||
}
|
||
|
||
DEFwritePortX (8, writeb)
|
||
DEFwritePortX (16, writew)
|
||
DEFwritePortX (32, writel)
|
||
|
||
#define DEFreadPortX(X, func) \
|
||
static inline u##X nm256_readPort##X (struct nm256_info *card,\
|
||
int port, int offset)\
|
||
{\
|
||
u##X __iomem *addr;\
|
||
\
|
||
NM_FIX_PORT\
|
||
\
|
||
addr = (u##X __iomem *)(card->port[port - 1].ptr + offset);\
|
||
return func(addr);\
|
||
}
|
||
|
||
DEFreadPortX (8, readb)
|
||
DEFreadPortX (16, readw)
|
||
DEFreadPortX (32, readl)
|
||
|
||
static inline int
|
||
nm256_writeBuffer8 (struct nm256_info *card, u8 *src, int port, int offset,
|
||
int amt)
|
||
{
|
||
NM_FIX_PORT;
|
||
memcpy_toio (card->port[port - 1].ptr + offset, src, amt);
|
||
return 0;
|
||
}
|
||
|
||
static inline int
|
||
nm256_readBuffer8 (struct nm256_info *card, u8 *dst, int port, int offset,
|
||
int amt)
|
||
{
|
||
NM_FIX_PORT;
|
||
memcpy_fromio (dst, card->port[port - 1].ptr + offset, amt);
|
||
return 0;
|
||
}
|
||
|
||
/* Returns a non-zero value if we should use the coefficient cache. */
|
||
static int nm256_cachedCoefficients (struct nm256_info *card);
|
||
|
||
#endif
|
||
|
||
/*
|
||
* Local variables:
|
||
* c-basic-offset: 4
|
||
* End:
|
||
*/
|