Commit 2b8a659c authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] OSS CS4232 locking fixes

Patch from: Peter Waechtler <pwaechtler@mac.com>

Addresses BUGME bug #331.  "OSS CS4232 nasty spinlock printks on boot"

The locking in some OSS modules is really lousy.
Because save_flags/cli/restore_flags could be used recursivly - the
programmers pushed the locking too far the lower level.

Because on ISA cards the register sets are usually multiplexed
you had to write to an address latch and then access the data port
in an "atomic" manner.

I suggest removing the locking from ad_read/ad_write +
ad_{enter|leave}_MCE and clamping the locks wherever the functions
are called. I hope the attached patch does that correctly.

Yes, I don't like all the timeout loops while holding the locks:
high chances that a cpu is spinning in interrupt context :(
parent f979631f
...@@ -209,15 +209,12 @@ static void ad1848_tmr_reprogram(int dev); ...@@ -209,15 +209,12 @@ static void ad1848_tmr_reprogram(int dev);
static int ad_read(ad1848_info * devc, int reg) static int ad_read(ad1848_info * devc, int reg)
{ {
unsigned long flags;
int x; int x;
int timeout = 900000; int timeout = 900000;
while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */ while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */
timeout--; timeout--;
spin_lock_irqsave(&devc->lock,flags);
if(reg < 32) if(reg < 32)
{ {
outb(((unsigned char) (reg & 0xff) | devc->MCE_bit), io_Index_Addr(devc)); outb(((unsigned char) (reg & 0xff) | devc->MCE_bit), io_Index_Addr(devc));
...@@ -233,21 +230,17 @@ static int ad_read(ad1848_info * devc, int reg) ...@@ -233,21 +230,17 @@ static int ad_read(ad1848_info * devc, int reg)
outb(((unsigned char) (xra & 0xff)), io_Indexed_Data(devc)); outb(((unsigned char) (xra & 0xff)), io_Indexed_Data(devc));
x = inb(io_Indexed_Data(devc)); x = inb(io_Indexed_Data(devc));
} }
spin_unlock_irqrestore(&devc->lock,flags);
return x; return x;
} }
static void ad_write(ad1848_info * devc, int reg, int data) static void ad_write(ad1848_info * devc, int reg, int data)
{ {
unsigned long flags;
int timeout = 900000; int timeout = 900000;
while (timeout > 0 && inb(devc->base) == 0x80) /* Are we initializing */ while (timeout > 0 && inb(devc->base) == 0x80) /* Are we initializing */
timeout--; timeout--;
spin_lock_irqsave(&devc->lock,flags);
if(reg < 32) if(reg < 32)
{ {
outb(((unsigned char) (reg & 0xff) | devc->MCE_bit), io_Index_Addr(devc)); outb(((unsigned char) (reg & 0xff) | devc->MCE_bit), io_Index_Addr(devc));
...@@ -263,7 +256,6 @@ static void ad_write(ad1848_info * devc, int reg, int data) ...@@ -263,7 +256,6 @@ static void ad_write(ad1848_info * devc, int reg, int data)
outb(((unsigned char) (xra & 0xff)), io_Indexed_Data(devc)); outb(((unsigned char) (xra & 0xff)), io_Indexed_Data(devc));
outb((unsigned char) (data & 0xff), io_Indexed_Data(devc)); outb((unsigned char) (data & 0xff), io_Indexed_Data(devc));
} }
spin_unlock_irqrestore(&devc->lock,flags);
} }
static void wait_for_calibration(ad1848_info * devc) static void wait_for_calibration(ad1848_info * devc)
...@@ -319,37 +311,29 @@ static void ad_unmute(ad1848_info * devc) ...@@ -319,37 +311,29 @@ static void ad_unmute(ad1848_info * devc)
static void ad_enter_MCE(ad1848_info * devc) static void ad_enter_MCE(ad1848_info * devc)
{ {
unsigned long flags;
int timeout = 1000; int timeout = 1000;
unsigned short prev; unsigned short prev;
while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */ while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */
timeout--; timeout--;
spin_lock_irqsave(&devc->lock,flags);
devc->MCE_bit = 0x40; devc->MCE_bit = 0x40;
prev = inb(io_Index_Addr(devc)); prev = inb(io_Index_Addr(devc));
if (prev & 0x40) if (prev & 0x40)
{ {
spin_unlock_irqrestore(&devc->lock,flags);
return; return;
} }
outb((devc->MCE_bit), io_Index_Addr(devc)); outb((devc->MCE_bit), io_Index_Addr(devc));
spin_unlock_irqrestore(&devc->lock,flags);
} }
static void ad_leave_MCE(ad1848_info * devc) static void ad_leave_MCE(ad1848_info * devc)
{ {
unsigned long flags;
unsigned char prev, acal; unsigned char prev, acal;
int timeout = 1000; int timeout = 1000;
while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */ while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */
timeout--; timeout--;
spin_lock_irqsave(&devc->lock,flags);
acal = ad_read(devc, 9); acal = ad_read(devc, 9);
devc->MCE_bit = 0x00; devc->MCE_bit = 0x00;
...@@ -358,19 +342,18 @@ static void ad_leave_MCE(ad1848_info * devc) ...@@ -358,19 +342,18 @@ static void ad_leave_MCE(ad1848_info * devc)
if ((prev & 0x40) == 0) /* Not in MCE mode */ if ((prev & 0x40) == 0) /* Not in MCE mode */
{ {
spin_unlock_irqrestore(&devc->lock,flags);
return; return;
} }
outb((0x00), io_Index_Addr(devc)); /* Clear the MCE bit */ outb((0x00), io_Index_Addr(devc)); /* Clear the MCE bit */
if (acal & 0x08) /* Auto calibration is enabled */ if (acal & 0x08) /* Auto calibration is enabled */
wait_for_calibration(devc); wait_for_calibration(devc);
spin_unlock_irqrestore(&devc->lock,flags);
} }
static int ad1848_set_recmask(ad1848_info * devc, int mask) static int ad1848_set_recmask(ad1848_info * devc, int mask)
{ {
unsigned char recdev; unsigned char recdev;
int i, n; int i, n;
unsigned long flags;
mask &= devc->supported_rec_devices; mask &= devc->supported_rec_devices;
...@@ -392,6 +375,7 @@ static int ad1848_set_recmask(ad1848_info * devc, int mask) ...@@ -392,6 +375,7 @@ static int ad1848_set_recmask(ad1848_info * devc, int mask)
if (mask & (1 << i)) if (mask & (1 << i))
n++; n++;
spin_lock_irqsave(&devc->lock,flags);
if (!soundpro) { if (!soundpro) {
if (n == 0) if (n == 0)
mask = SOUND_MASK_MIC; mask = SOUND_MASK_MIC;
...@@ -460,6 +444,7 @@ static int ad1848_set_recmask(ad1848_info * devc, int mask) ...@@ -460,6 +444,7 @@ static int ad1848_set_recmask(ad1848_info * devc, int mask)
} }
} }
} }
spin_unlock_irqrestore(&devc->lock,flags);
/* Rename the mixer bits back if necessary */ /* Rename the mixer bits back if necessary */
for (i = 0; i < 32; i++) for (i = 0; i < 32; i++)
...@@ -527,6 +512,7 @@ static void ad1848_mixer_set_channel(ad1848_info *devc, int dev, int value, int ...@@ -527,6 +512,7 @@ static void ad1848_mixer_set_channel(ad1848_info *devc, int dev, int value, int
{ {
int regoffs, muteregoffs; int regoffs, muteregoffs;
unsigned char val, muteval; unsigned char val, muteval;
unsigned long flags;
regoffs = devc->mix_devices[dev][channel].regno; regoffs = devc->mix_devices[dev][channel].regno;
muteregoffs = devc->mix_devices[dev][channel].mutereg; muteregoffs = devc->mix_devices[dev][channel].mutereg;
...@@ -539,12 +525,14 @@ static void ad1848_mixer_set_channel(ad1848_info *devc, int dev, int value, int ...@@ -539,12 +525,14 @@ static void ad1848_mixer_set_channel(ad1848_info *devc, int dev, int value, int
else else
change_bits(devc, &val, &val, dev, channel, value); change_bits(devc, &val, &val, dev, channel, value);
spin_lock_irqsave(&devc->lock,flags);
ad_write(devc, regoffs, val); ad_write(devc, regoffs, val);
devc->saved_regs[regoffs] = val; devc->saved_regs[regoffs] = val;
if (muteregoffs != regoffs) { if (muteregoffs != regoffs) {
ad_write(devc, muteregoffs, muteval); ad_write(devc, muteregoffs, muteval);
devc->saved_regs[muteregoffs] = muteval; devc->saved_regs[muteregoffs] = muteval;
} }
spin_unlock_irqrestore(&devc->lock,flags);
} }
static int ad1848_mixer_set(ad1848_info * devc, int dev, int value) static int ad1848_mixer_set(ad1848_info * devc, int dev, int value)
...@@ -600,6 +588,7 @@ static void ad1848_mixer_reset(ad1848_info * devc) ...@@ -600,6 +588,7 @@ static void ad1848_mixer_reset(ad1848_info * devc)
{ {
int i; int i;
char name[32]; char name[32];
unsigned long flags;
devc->mix_devices = &(ad1848_mix_devices[0]); devc->mix_devices = &(ad1848_mix_devices[0]);
...@@ -666,6 +655,7 @@ static void ad1848_mixer_reset(ad1848_info * devc) ...@@ -666,6 +655,7 @@ static void ad1848_mixer_reset(ad1848_info * devc)
devc->mixer_output_port = devc->levels[31] | AUDIO_HEADPHONE | AUDIO_LINE_OUT; devc->mixer_output_port = devc->levels[31] | AUDIO_HEADPHONE | AUDIO_LINE_OUT;
spin_lock_irqsave(&devc->lock,flags);
if (!soundpro) { if (!soundpro) {
if (devc->mixer_output_port & AUDIO_SPEAKER) if (devc->mixer_output_port & AUDIO_SPEAKER)
ad_write(devc, 26, ad_read(devc, 26) & ~0x40); /* Unmute mono out */ ad_write(devc, 26, ad_read(devc, 26) & ~0x40); /* Unmute mono out */
...@@ -679,6 +669,7 @@ static void ad1848_mixer_reset(ad1848_info * devc) ...@@ -679,6 +669,7 @@ static void ad1848_mixer_reset(ad1848_info * devc)
/* Enable surround mode and SB16 mixer */ /* Enable surround mode and SB16 mixer */
ad_write(devc, 16, 0x60); ad_write(devc, 16, 0x60);
} }
spin_unlock_irqrestore(&devc->lock,flags);
} }
static int ad1848_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) static int ad1848_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
...@@ -693,14 +684,17 @@ static int ad1848_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg) ...@@ -693,14 +684,17 @@ static int ad1848_mixer_ioctl(int dev, unsigned int cmd, caddr_t arg)
if (val != 0xffff) if (val != 0xffff)
{ {
unsigned long flags;
val &= (AUDIO_SPEAKER | AUDIO_HEADPHONE | AUDIO_LINE_OUT); val &= (AUDIO_SPEAKER | AUDIO_HEADPHONE | AUDIO_LINE_OUT);
devc->mixer_output_port = val; devc->mixer_output_port = val;
val |= AUDIO_HEADPHONE | AUDIO_LINE_OUT; /* Always on */ val |= AUDIO_HEADPHONE | AUDIO_LINE_OUT; /* Always on */
devc->mixer_output_port = val; devc->mixer_output_port = val;
spin_lock_irqsave(&devc->lock,flags);
if (val & AUDIO_SPEAKER) if (val & AUDIO_SPEAKER)
ad_write(devc, 26, ad_read(devc, 26) & ~0x40); /* Unmute mono out */ ad_write(devc, 26, ad_read(devc, 26) & ~0x40); /* Unmute mono out */
else else
ad_write(devc, 26, ad_read(devc, 26) | 0x40); /* Mute mono out */ ad_write(devc, 26, ad_read(devc, 26) | 0x40); /* Mute mono out */
spin_unlock_irqrestore(&devc->lock,flags);
} }
val = devc->mixer_output_port; val = devc->mixer_output_port;
return put_user(val, (int *)arg); return put_user(val, (int *)arg);
...@@ -985,10 +979,11 @@ static int ad1848_open(int dev, int mode) ...@@ -985,10 +979,11 @@ static int ad1848_open(int dev, int mode)
devc = (ad1848_info *) audio_devs[dev]->devc; devc = (ad1848_info *) audio_devs[dev]->devc;
portc = (ad1848_port_info *) audio_devs[dev]->portc; portc = (ad1848_port_info *) audio_devs[dev]->portc;
spin_lock_irqsave(&devc->lock,flags); /* here we don't have to protect against intr */
spin_lock(&devc->lock);
if (portc->open_mode || (devc->open_mode & mode)) if (portc->open_mode || (devc->open_mode & mode))
{ {
spin_unlock_irqrestore(&devc->lock,flags); spin_unlock(&devc->lock);
return -EBUSY; return -EBUSY;
} }
devc->dual_dma = 0; devc->dual_dma = 0;
...@@ -1001,17 +996,19 @@ static int ad1848_open(int dev, int mode) ...@@ -1001,17 +996,19 @@ static int ad1848_open(int dev, int mode)
devc->audio_mode = 0; devc->audio_mode = 0;
devc->open_mode |= mode; devc->open_mode |= mode;
portc->open_mode = mode; portc->open_mode = mode;
spin_unlock(&devc->lock);
ad1848_trigger(dev, 0); ad1848_trigger(dev, 0);
if (mode & OPEN_READ) if (mode & OPEN_READ)
devc->record_dev = dev; devc->record_dev = dev;
if (mode & OPEN_WRITE) if (mode & OPEN_WRITE)
devc->playback_dev = dev; devc->playback_dev = dev;
spin_unlock_irqrestore(&devc->lock,flags);
/* /*
* Mute output until the playback really starts. This decreases clicking (hope so). * Mute output until the playback really starts. This decreases clicking (hope so).
*/ */
spin_lock_irqsave(&devc->lock,flags);
ad_mute(devc); ad_mute(devc);
spin_unlock_irqrestore(&devc->lock,flags);
return 0; return 0;
} }
...@@ -1024,11 +1021,11 @@ static void ad1848_close(int dev) ...@@ -1024,11 +1021,11 @@ static void ad1848_close(int dev)
DEB(printk("ad1848_close(void)\n")); DEB(printk("ad1848_close(void)\n"));
spin_lock_irqsave(&devc->lock,flags);
devc->intr_active = 0; devc->intr_active = 0;
ad1848_halt(dev); ad1848_halt(dev);
spin_lock_irqsave(&devc->lock,flags);
devc->audio_mode = 0; devc->audio_mode = 0;
devc->open_mode &= ~portc->open_mode; devc->open_mode &= ~portc->open_mode;
portc->open_mode = 0; portc->open_mode = 0;
...@@ -1585,6 +1582,7 @@ int ad1848_detect(int io_base, int *ad_flags, int *osp) ...@@ -1585,6 +1582,7 @@ int ad1848_detect(int io_base, int *ad_flags, int *osp)
printk(KERN_ERR "ad1848.c: Port %x not free.\n", io_base); printk(KERN_ERR "ad1848.c: Port %x not free.\n", io_base);
return 0; return 0;
} }
spin_lock_init(&devc->lock);
devc->base = io_base; devc->base = io_base;
devc->irq_ok = 0; devc->irq_ok = 0;
devc->timer_running = 0; devc->timer_running = 0;
...@@ -1969,7 +1967,6 @@ int ad1848_init (char *name, int io_base, int irq, int dma_playback, ...@@ -1969,7 +1967,6 @@ int ad1848_init (char *name, int io_base, int irq, int dma_playback,
ad1848_port_info *portc = NULL; ad1848_port_info *portc = NULL;
spin_lock_init(&devc->lock);
devc->irq = (irq > 0) ? irq : 0; devc->irq = (irq > 0) ? irq : 0;
devc->open_mode = 0; devc->open_mode = 0;
devc->timer_ticks = 0; devc->timer_ticks = 0;
...@@ -2112,6 +2109,7 @@ int ad1848_init (char *name, int io_base, int irq, int dma_playback, ...@@ -2112,6 +2109,7 @@ int ad1848_init (char *name, int io_base, int irq, int dma_playback,
int ad1848_control(int cmd, int arg) int ad1848_control(int cmd, int arg)
{ {
ad1848_info *devc; ad1848_info *devc;
unsigned long flags;
if (nr_ad1848_devs < 1) if (nr_ad1848_devs < 1)
return -ENODEV; return -ENODEV;
...@@ -2123,9 +2121,11 @@ int ad1848_control(int cmd, int arg) ...@@ -2123,9 +2121,11 @@ int ad1848_control(int cmd, int arg)
case AD1848_SET_XTAL: /* Change clock frequency of AD1845 (only ) */ case AD1848_SET_XTAL: /* Change clock frequency of AD1845 (only ) */
if (devc->model != MD_1845 || devc->model != MD_1845_SSCAPE) if (devc->model != MD_1845 || devc->model != MD_1845_SSCAPE)
return -EINVAL; return -EINVAL;
spin_lock_irqsave(&devc->lock,flags);
ad_enter_MCE(devc); ad_enter_MCE(devc);
ad_write(devc, 29, (ad_read(devc, 29) & 0x1f) | (arg << 5)); ad_write(devc, 29, (ad_read(devc, 29) & 0x1f) | (arg << 5));
ad_leave_MCE(devc); ad_leave_MCE(devc);
spin_unlock_irqrestore(&devc->lock,flags);
break; break;
case AD1848_MIXER_REROUTE: case AD1848_MIXER_REROUTE:
...@@ -2253,8 +2253,10 @@ void adintr(int irq, void *dev_id, struct pt_regs *dummy) ...@@ -2253,8 +2253,10 @@ void adintr(int irq, void *dev_id, struct pt_regs *dummy)
} }
else if (devc->model != MD_1848) else if (devc->model != MD_1848)
{ {
spin_lock(&devc->lock);
alt_stat = ad_read(devc, 24); alt_stat = ad_read(devc, 24);
ad_write(devc, 24, ad_read(devc, 24) & ~alt_stat); /* Selective ack */ ad_write(devc, 24, ad_read(devc, 24) & ~alt_stat); /* Selective ack */
spin_unlock(&devc->lock);
} }
if ((devc->open_mode & OPEN_READ) && (devc->audio_mode & PCM_ENABLE_INPUT) && (alt_stat & 0x20)) if ((devc->open_mode & OPEN_READ) && (devc->audio_mode & PCM_ENABLE_INPUT) && (alt_stat & 0x20))
...@@ -2831,11 +2833,8 @@ static int ad1848_suspend(ad1848_info *devc) ...@@ -2831,11 +2833,8 @@ static int ad1848_suspend(ad1848_info *devc)
static int ad1848_resume(ad1848_info *devc) static int ad1848_resume(ad1848_info *devc)
{ {
unsigned long flags;
int mixer_levels[32], i; int mixer_levels[32], i;
spin_lock_irqsave(&devc->lock,flags);
/* Thinkpad is a bit more of PITA than normal. The BIOS tends to /* Thinkpad is a bit more of PITA than normal. The BIOS tends to
restore it in a different config to the one we use. Need to restore it in a different config to the one we use. Need to
fix this somehow */ fix this somehow */
...@@ -2851,7 +2850,7 @@ static int ad1848_resume(ad1848_info *devc) ...@@ -2851,7 +2850,7 @@ static int ad1848_resume(ad1848_info *devc)
if (!devc->subtype) { if (!devc->subtype) {
static signed char interrupt_bits[12] = { -1, -1, -1, -1, -1, 0x00, -1, 0x08, -1, 0x10, 0x18, 0x20 }; static signed char interrupt_bits[12] = { -1, -1, -1, -1, -1, 0x00, -1, 0x08, -1, 0x10, 0x18, 0x20 };
static char dma_bits[4] = { 1, 2, 0, 3 }; static char dma_bits[4] = { 1, 2, 0, 3 };
unsigned long flags;
signed char bits; signed char bits;
char dma2_bit = 0; char dma2_bit = 0;
...@@ -2860,10 +2859,11 @@ static int ad1848_resume(ad1848_info *devc) ...@@ -2860,10 +2859,11 @@ static int ad1848_resume(ad1848_info *devc)
bits = interrupt_bits[devc->irq]; bits = interrupt_bits[devc->irq];
if (bits == -1) { if (bits == -1) {
printk(KERN_ERR "MSS: Bad IRQ %d\n", devc->irq); printk(KERN_ERR "MSS: Bad IRQ %d\n", devc->irq);
spin_unlock_irqrestore(&devc->lock,flags);
return -1; return -1;
} }
spin_lock_irqsave(&devc->lock,flags);
outb((bits | 0x40), config_port); outb((bits | 0x40), config_port);
if (devc->dma2 != -1 && devc->dma2 != devc->dma1) if (devc->dma2 != -1 && devc->dma2 != devc->dma1)
...@@ -2873,9 +2873,9 @@ static int ad1848_resume(ad1848_info *devc) ...@@ -2873,9 +2873,9 @@ static int ad1848_resume(ad1848_info *devc)
dma2_bit = 0x04; dma2_bit = 0x04;
outb((bits | dma_bits[devc->dma1] | dma2_bit), config_port); outb((bits | dma_bits[devc->dma1] | dma2_bit), config_port);
spin_unlock_irqrestore(&devc->lock,flags);
} }
spin_unlock_irqrestore(&devc->lock,flags);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment