2005-04-16 15:20:36 -07:00
/*
2011-10-24 01:11:18 +11:00
* Operating System Services ( OSS ) chip handling
2005-04-16 15:20:36 -07:00
* Written by Joshua M . Thompson ( funaho @ jurai . org )
*
*
* This chip is used in the IIfx in place of VIA # 2. It acts like a fancy
* VIA chip with prorammable interrupt levels .
*
* 990502 ( jmt ) - Major rewrite for new interrupt architecture as well as some
* recent insights into OSS operational details .
2007-10-20 01:20:32 +02:00
* 990610 ( jmt ) - Now taking full advantage of the OSS . Interrupts are mapped
2005-04-16 15:20:36 -07:00
* to mostly match the A / UX interrupt scheme supported on the
* VIA side . Also added support for enabling the ISM irq again
* since we now have a functional IOP manager .
*/
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/delay.h>
# include <linux/init.h>
2011-07-13 21:48:30 +02:00
# include <linux/irq.h>
2005-04-16 15:20:36 -07:00
# include <asm/macintosh.h>
# include <asm/macints.h>
# include <asm/mac_via.h>
# include <asm/mac_oss.h>
int oss_present ;
volatile struct mac_oss * oss ;
/*
* Initialize the OSS
*
* The OSS " detection " code is actually in via_init ( ) which is always called
* before us . Thus we can count on oss_present being valid on entry .
*/
void __init oss_init ( void )
{
int i ;
if ( ! oss_present ) return ;
oss = ( struct mac_oss * ) OSS_BASE ;
/* Disable all interrupts. Unlike a VIA it looks like we */
/* do this by setting the source's interrupt level to zero. */
2015-03-30 12:22:30 +11:00
for ( i = 0 ; i < OSS_NUM_SOURCES ; i + + )
2011-10-24 01:11:18 +11:00
oss - > irq_level [ i ] = 0 ;
2005-04-16 15:20:36 -07:00
}
/*
* Initialize OSS for Nubus access
*/
void __init oss_nubus_init ( void )
{
}
/*
2011-10-24 01:11:18 +11:00
* Handle miscellaneous OSS interrupts .
2005-04-16 15:20:36 -07:00
*/
2015-09-14 10:42:37 +02:00
static void oss_irq ( struct irq_desc * desc )
2011-08-10 12:48:29 +02:00
{
2011-10-24 01:11:18 +11:00
int events = oss - > irq_pending &
2015-07-31 21:56:10 +02:00
( OSS_IP_IOPSCC | OSS_IP_SCSI | OSS_IP_IOPISM ) ;
2011-08-10 12:48:29 +02:00
2011-10-24 01:11:18 +11:00
if ( events & OSS_IP_IOPSCC ) {
oss - > irq_pending & = ~ OSS_IP_IOPSCC ;
generic_handle_irq ( IRQ_MAC_SCC ) ;
}
if ( events & OSS_IP_SCSI ) {
2011-08-10 12:48:29 +02:00
oss - > irq_pending & = ~ OSS_IP_SCSI ;
generic_handle_irq ( IRQ_MAC_SCSI ) ;
2011-10-24 01:11:18 +11:00
}
if ( events & OSS_IP_IOPISM ) {
oss - > irq_pending & = ~ OSS_IP_IOPISM ;
generic_handle_irq ( IRQ_MAC_ADB ) ;
2011-08-10 12:48:29 +02:00
}
}
2005-04-16 15:20:36 -07:00
/*
* Nubus IRQ handler , OSS style
*
* Unlike the VIA / RBV this is on its own autovector interrupt level .
*/
2015-09-14 10:42:37 +02:00
static void oss_nubus_irq ( struct irq_desc * desc )
2011-08-10 12:48:29 +02:00
{
int events , irq_bit , i ;
events = oss - > irq_pending & OSS_IP_NUBUS ;
if ( ! events )
return ;
/* There are only six slots on the OSS, not seven */
i = 6 ;
irq_bit = 0x40 ;
do {
- - i ;
irq_bit > > = 1 ;
if ( events & irq_bit ) {
oss - > irq_pending & = ~ irq_bit ;
generic_handle_irq ( NUBUS_SOURCE_BASE + i ) ;
}
} while ( events & ( irq_bit - 1 ) ) ;
}
/*
* Register the OSS and NuBus interrupt dispatchers .
2011-10-24 01:11:18 +11:00
*
* This IRQ mapping is laid out with two things in mind : first , we try to keep
* things on their own levels to avoid having to do double - dispatches . Second ,
* the levels match as closely as possible the alternate IRQ mapping mode ( aka
* " A/UX mode " ) available on some VIA machines .
2011-08-10 12:48:29 +02:00
*/
2011-10-24 01:11:18 +11:00
# define OSS_IRQLEV_IOPISM IRQ_AUTO_1
# define OSS_IRQLEV_SCSI IRQ_AUTO_2
# define OSS_IRQLEV_NUBUS IRQ_AUTO_3
# define OSS_IRQLEV_IOPSCC IRQ_AUTO_4
# define OSS_IRQLEV_VIA1 IRQ_AUTO_6
2011-08-10 12:48:29 +02:00
void __init oss_register_interrupts ( void )
{
2011-10-24 01:11:18 +11:00
irq_set_chained_handler ( OSS_IRQLEV_IOPISM , oss_irq ) ;
irq_set_chained_handler ( OSS_IRQLEV_SCSI , oss_irq ) ;
irq_set_chained_handler ( OSS_IRQLEV_NUBUS , oss_nubus_irq ) ;
irq_set_chained_handler ( OSS_IRQLEV_IOPSCC , oss_irq ) ;
irq_set_chained_handler ( OSS_IRQLEV_VIA1 , via1_irq ) ;
/* OSS_VIA1 gets enabled here because it has no machspec interrupt. */
oss - > irq_level [ OSS_VIA1 ] = IRQ_AUTO_6 ;
2011-08-10 12:48:29 +02:00
}
2005-04-16 15:20:36 -07:00
/*
* Enable an OSS interrupt
*
* It looks messy but it ' s rather straightforward . The switch ( ) statement
* just maps the machspec interrupt numbers to the right OSS interrupt
* source ( if the OSS handles that interrupt ) and then sets the interrupt
* level for that source to nonzero , thus enabling the interrupt .
*/
void oss_irq_enable ( int irq ) {
switch ( irq ) {
2009-11-17 20:06:48 +11:00
case IRQ_MAC_SCC :
2005-04-16 15:20:36 -07:00
oss - > irq_level [ OSS_IOPSCC ] = OSS_IRQLEV_IOPSCC ;
2011-10-24 01:11:18 +11:00
return ;
2005-04-16 15:20:36 -07:00
case IRQ_MAC_ADB :
oss - > irq_level [ OSS_IOPISM ] = OSS_IRQLEV_IOPISM ;
2011-10-24 01:11:18 +11:00
return ;
2005-04-16 15:20:36 -07:00
case IRQ_MAC_SCSI :
oss - > irq_level [ OSS_SCSI ] = OSS_IRQLEV_SCSI ;
2011-10-24 01:11:18 +11:00
return ;
2005-04-16 15:20:36 -07:00
case IRQ_NUBUS_9 :
case IRQ_NUBUS_A :
case IRQ_NUBUS_B :
case IRQ_NUBUS_C :
case IRQ_NUBUS_D :
case IRQ_NUBUS_E :
irq - = NUBUS_SOURCE_BASE ;
oss - > irq_level [ irq ] = OSS_IRQLEV_NUBUS ;
2011-10-24 01:11:18 +11:00
return ;
2005-04-16 15:20:36 -07:00
}
2011-10-24 01:11:18 +11:00
if ( IRQ_SRC ( irq ) = = 1 )
via_irq_enable ( irq ) ;
2005-04-16 15:20:36 -07:00
}
/*
* Disable an OSS interrupt
*
* Same as above except we set the source ' s interrupt level to zero ,
* to disable the interrupt .
*/
void oss_irq_disable ( int irq ) {
switch ( irq ) {
2009-11-17 20:06:48 +11:00
case IRQ_MAC_SCC :
2011-10-24 01:11:18 +11:00
oss - > irq_level [ OSS_IOPSCC ] = 0 ;
return ;
2005-04-16 15:20:36 -07:00
case IRQ_MAC_ADB :
2011-10-24 01:11:18 +11:00
oss - > irq_level [ OSS_IOPISM ] = 0 ;
return ;
2005-04-16 15:20:36 -07:00
case IRQ_MAC_SCSI :
2011-10-24 01:11:18 +11:00
oss - > irq_level [ OSS_SCSI ] = 0 ;
return ;
2005-04-16 15:20:36 -07:00
case IRQ_NUBUS_9 :
case IRQ_NUBUS_A :
case IRQ_NUBUS_B :
case IRQ_NUBUS_C :
case IRQ_NUBUS_D :
case IRQ_NUBUS_E :
irq - = NUBUS_SOURCE_BASE ;
2011-10-24 01:11:18 +11:00
oss - > irq_level [ irq ] = 0 ;
return ;
2005-04-16 15:20:36 -07:00
}
2011-10-24 01:11:18 +11:00
if ( IRQ_SRC ( irq ) = = 1 )
via_irq_disable ( irq ) ;
2005-04-16 15:20:36 -07:00
}