2005-04-17 02:20:36 +04:00
/*
* Apple Peripheral System Controller ( PSC )
*
* The PSC is used on the AV Macs to control IO functions not handled
* by the VIAs ( Ethernet , DSP , SCC ) .
*
* TO DO :
*
* Try to figure out what ' s going on in pIFR5 and pIFR6 . There seem to be
* persisant interrupt conditions in those registers and I have no idea what
* they are . Granted it doesn ' t affect since we ' re not enabling any interrupts
* on those levels at the moment , but it would be nice to know . I have a feeling
* they aren ' t actually interrupt lines but data lines ( to the DSP ? )
*/
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/delay.h>
# include <linux/init.h>
2011-07-13 23:48:30 +04:00
# include <linux/irq.h>
2005-04-17 02:20:36 +04:00
# include <asm/traps.h>
# include <asm/macintosh.h>
# include <asm/macints.h>
# include <asm/mac_psc.h>
# define DEBUG_PSC
volatile __u8 * psc ;
2015-09-29 11:24:05 +03:00
EXPORT_SYMBOL_GPL ( psc ) ;
2005-04-17 02:20:36 +04:00
/*
* Debugging dump , used in various places to see what ' s going on .
*/
2008-07-17 23:16:25 +04:00
static void psc_debug_dump ( void )
2005-04-17 02:20:36 +04:00
{
int i ;
2015-09-29 10:27:22 +03:00
if ( ! psc )
return ;
2005-04-17 02:20:36 +04:00
for ( i = 0x30 ; i < 0x70 ; i + = 0x10 ) {
printk ( " PSC #%d: IFR = 0x%02X IER = 0x%02X \n " ,
i > > 4 ,
( int ) psc_read_byte ( pIFRbase + i ) ,
( int ) psc_read_byte ( pIERbase + i ) ) ;
}
}
/*
* Try to kill all DMA channels on the PSC . Not sure how this his
* supposed to work ; this is code lifted from macmace . c and then
* expanded to cover what I think are the other 7 channels .
*/
2013-06-25 23:15:24 +04:00
static __init void psc_dma_die_die_die ( void )
2005-04-17 02:20:36 +04:00
{
int i ;
printk ( " Killing all PSC DMA channels... " ) ;
for ( i = 0 ; i < 9 ; i + + ) {
psc_write_word ( PSC_CTL_BASE + ( i < < 4 ) , 0x8800 ) ;
psc_write_word ( PSC_CTL_BASE + ( i < < 4 ) , 0x1000 ) ;
psc_write_word ( PSC_CMD_BASE + ( i < < 5 ) , 0x1100 ) ;
psc_write_word ( PSC_CMD_BASE + ( i < < 5 ) + 0x10 , 0x1100 ) ;
}
printk ( " done! \n " ) ;
}
/*
* Initialize the PSC . For now this just involves shutting down all
* interrupt sources using the IERs .
*/
void __init psc_init ( void )
{
int i ;
if ( macintosh_config - > ident ! = MAC_MODEL_C660
& & macintosh_config - > ident ! = MAC_MODEL_Q840 )
{
psc = NULL ;
return ;
}
/*
* The PSC is always at the same spot , but using psc
2010-12-10 16:55:42 +03:00
* keeps things consistent with the psc_xxxx functions .
2005-04-17 02:20:36 +04:00
*/
psc = ( void * ) PSC_BASE ;
printk ( " PSC detected at %p \n " , psc ) ;
psc_dma_die_die_die ( ) ;
# ifdef DEBUG_PSC
psc_debug_dump ( ) ;
# endif
/*
* Mask and clear all possible interrupts
*/
for ( i = 0x30 ; i < 0x70 ; i + = 0x10 ) {
psc_write_byte ( pIERbase + i , 0x0F ) ;
psc_write_byte ( pIFRbase + i , 0x0F ) ;
}
}
/*
2011-08-10 14:48:29 +04:00
* PSC interrupt handler . It ' s a lot like the VIA interrupt handler .
2005-04-17 02:20:36 +04:00
*/
2015-09-14 11:42:37 +03:00
static void psc_irq ( struct irq_desc * desc )
2005-04-17 02:20:36 +04:00
{
2011-08-10 14:48:29 +04:00
unsigned int offset = ( unsigned int ) irq_desc_get_handler_data ( desc ) ;
2015-07-31 22:56:10 +03:00
unsigned int irq = irq_desc_get_irq ( desc ) ;
2011-08-10 14:48:29 +04:00
int pIFR = pIFRbase + offset ;
int pIER = pIERbase + offset ;
int irq_num ;
unsigned char irq_bit , events ;
2005-04-17 02:20:36 +04:00
2011-08-10 14:48:29 +04:00
events = psc_read_byte ( pIFR ) & psc_read_byte ( pIER ) & 0xF ;
if ( ! events )
return ;
irq_num = irq < < 3 ;
irq_bit = 1 ;
do {
if ( events & irq_bit ) {
psc_write_byte ( pIFR , irq_bit ) ;
generic_handle_irq ( irq_num ) ;
}
irq_num + + ;
irq_bit < < = 1 ;
} while ( events > = irq_bit ) ;
}
/*
* Register the PSC interrupt dispatchers for autovector interrupts 3 - 6.
*/
void __init psc_register_interrupts ( void )
{
m68k/psc: Fix race in installing chained IRQ handler
Fix a race where a pending interrupt could be received and the handler
called before the handler's data has been setup, by converting to
irq_set_chained_handler_and_data().
Search and conversion was done with coccinelle:
@@
expression E1, E2, E3;
@@
(
-if (irq_set_chained_handler(E1, E3) != 0)
- BUG();
|
-irq_set_chained_handler(E1, E3);
)
-irq_set_handler_data(E1, E2);
+irq_set_chained_handler_and_data(E1, E3, E2);
@@
expression E1, E2, E3;
@@
(
-if (irq_set_chained_handler(E1, E3) != 0)
- BUG();
...
|
-irq_set_chained_handler(E1, E3);
...
)
-irq_set_handler_data(E1, E2);
+irq_set_chained_handler_and_data(E1, E3, E2);
Reported-by: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Julia Lawall <Julia.Lawall@lip6.fr>
Cc: Joshua Thompson <funaho@jurai.org>
Cc: Geert Uytterhoeven <geert@linux-m68k.org>
Cc: linux-m68k@lists.linux-m68k.org
2015-06-21 22:00:30 +03:00
irq_set_chained_handler_and_data ( IRQ_AUTO_3 , psc_irq , ( void * ) 0x30 ) ;
irq_set_chained_handler_and_data ( IRQ_AUTO_4 , psc_irq , ( void * ) 0x40 ) ;
irq_set_chained_handler_and_data ( IRQ_AUTO_5 , psc_irq , ( void * ) 0x50 ) ;
irq_set_chained_handler_and_data ( IRQ_AUTO_6 , psc_irq , ( void * ) 0x60 ) ;
2011-08-10 14:48:29 +04:00
}
2005-04-17 02:20:36 +04:00
void psc_irq_enable ( int irq ) {
int irq_src = IRQ_SRC ( irq ) ;
int irq_idx = IRQ_IDX ( irq ) ;
int pIER = pIERbase + ( irq_src < < 4 ) ;
psc_write_byte ( pIER , ( 1 < < irq_idx ) | 0x80 ) ;
}
void psc_irq_disable ( int irq ) {
int irq_src = IRQ_SRC ( irq ) ;
int irq_idx = IRQ_IDX ( irq ) ;
int pIER = pIERbase + ( irq_src < < 4 ) ;
psc_write_byte ( pIER , 1 < < irq_idx ) ;
}