2008-01-01 23:55:23 +03:00
/*
* Intel IXP4xx Queue Manager driver for Linux
*
* Copyright ( C ) 2007 Krzysztof Halasa < khc @ pm . waw . pl >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation .
*/
# include <linux/ioport.h>
# include <linux/interrupt.h>
# include <linux/kernel.h>
# include <linux/module.h>
2008-08-05 19:14:15 +04:00
# include <mach/qmgr.h>
2008-01-01 23:55:23 +03:00
struct qmgr_regs __iomem * qmgr_regs ;
static struct resource * mem_res ;
static spinlock_t qmgr_lock ;
static u32 used_sram_bitmap [ 4 ] ; /* 128 16-dword pages */
2009-02-20 03:01:33 +03:00
static void ( * irq_handlers [ QUEUES ] ) ( void * pdev ) ;
static void * irq_pdevs [ QUEUES ] ;
2008-01-01 23:55:23 +03:00
2008-12-22 02:26:38 +03:00
# if DEBUG_QMGR
char qmgr_queue_descs [ QUEUES ] [ 32 ] ;
# endif
2008-01-01 23:55:23 +03:00
void qmgr_set_irq ( unsigned int queue , int src ,
void ( * handler ) ( void * pdev ) , void * pdev )
{
unsigned long flags ;
spin_lock_irqsave ( & qmgr_lock , flags ) ;
2009-02-20 03:01:33 +03:00
if ( queue < HALF_QUEUES ) {
u32 __iomem * reg ;
int bit ;
BUG_ON ( src > QUEUE_IRQ_SRC_NOT_FULL ) ;
reg = & qmgr_regs - > irqsrc [ queue > > 3 ] ; /* 8 queues per u32 */
bit = ( queue % 8 ) * 4 ; /* 3 bits + 1 reserved bit per queue */
__raw_writel ( ( __raw_readl ( reg ) & ~ ( 7 < < bit ) ) | ( src < < bit ) ,
reg ) ;
} else
/* IRQ source for queues 32-63 is fixed */
BUG_ON ( src ! = QUEUE_IRQ_SRC_NOT_NEARLY_EMPTY ) ;
2008-01-01 23:55:23 +03:00
irq_handlers [ queue ] = handler ;
irq_pdevs [ queue ] = pdev ;
spin_unlock_irqrestore ( & qmgr_lock , flags ) ;
}
2009-05-24 01:36:03 +04:00
static irqreturn_t qmgr_irq1_a0 ( int irq , void * pdev )
{
int i , ret = 0 ;
2009-04-28 21:32:55 +04:00
u32 en_bitmap , src , stat ;
2009-05-24 01:36:03 +04:00
/* ACK - it may clear any bits so don't rely on it */
__raw_writel ( 0xFFFFFFFF , & qmgr_regs - > irqstat [ 0 ] ) ;
2009-04-28 21:32:55 +04:00
en_bitmap = qmgr_regs - > irqen [ 0 ] ;
while ( en_bitmap ) {
i = __fls ( en_bitmap ) ; /* number of the last "low" queue */
en_bitmap & = ~ BIT ( i ) ;
2009-05-24 01:36:03 +04:00
src = qmgr_regs - > irqsrc [ i > > 3 ] ;
stat = qmgr_regs - > stat1 [ i > > 3 ] ;
if ( src & 4 ) /* the IRQ condition is inverted */
stat = ~ stat ;
if ( stat & BIT ( src & 3 ) ) {
irq_handlers [ i ] ( irq_pdevs [ i ] ) ;
ret = IRQ_HANDLED ;
}
}
return ret ;
}
static irqreturn_t qmgr_irq2_a0 ( int irq , void * pdev )
{
int i , ret = 0 ;
u32 req_bitmap ;
/* ACK - it may clear any bits so don't rely on it */
__raw_writel ( 0xFFFFFFFF , & qmgr_regs - > irqstat [ 1 ] ) ;
req_bitmap = qmgr_regs - > irqen [ 1 ] & qmgr_regs - > statne_h ;
2009-04-28 21:32:55 +04:00
while ( req_bitmap ) {
i = __fls ( req_bitmap ) ; /* number of the last "high" queue */
req_bitmap & = ~ BIT ( i ) ;
2009-05-24 01:36:03 +04:00
irq_handlers [ HALF_QUEUES + i ] ( irq_pdevs [ HALF_QUEUES + i ] ) ;
ret = IRQ_HANDLED ;
}
return ret ;
}
2009-02-20 03:01:33 +03:00
static irqreturn_t qmgr_irq ( int irq , void * pdev )
2008-01-01 23:55:23 +03:00
{
2009-02-20 03:01:33 +03:00
int i , half = ( irq = = IRQ_IXP4XX_QM1 ? 0 : 1 ) ;
2009-04-28 21:32:55 +04:00
u32 req_bitmap = __raw_readl ( & qmgr_regs - > irqstat [ half ] ) ;
2008-01-01 23:55:23 +03:00
2009-04-28 21:32:55 +04:00
if ( ! req_bitmap )
return 0 ;
__raw_writel ( req_bitmap , & qmgr_regs - > irqstat [ half ] ) ; /* ACK */
while ( req_bitmap ) {
i = __fls ( req_bitmap ) ; /* number of the last queue */
req_bitmap & = ~ BIT ( i ) ;
i + = half * HALF_QUEUES ;
irq_handlers [ i ] ( irq_pdevs [ i ] ) ;
}
return IRQ_HANDLED ;
2008-01-01 23:55:23 +03:00
}
void qmgr_enable_irq ( unsigned int queue )
{
unsigned long flags ;
2009-02-20 03:01:33 +03:00
int half = queue / 32 ;
u32 mask = 1 < < ( queue & ( HALF_QUEUES - 1 ) ) ;
2008-01-01 23:55:23 +03:00
spin_lock_irqsave ( & qmgr_lock , flags ) ;
2009-02-20 03:01:33 +03:00
__raw_writel ( __raw_readl ( & qmgr_regs - > irqen [ half ] ) | mask ,
& qmgr_regs - > irqen [ half ] ) ;
2008-01-01 23:55:23 +03:00
spin_unlock_irqrestore ( & qmgr_lock , flags ) ;
}
void qmgr_disable_irq ( unsigned int queue )
{
unsigned long flags ;
2009-02-20 03:01:33 +03:00
int half = queue / 32 ;
u32 mask = 1 < < ( queue & ( HALF_QUEUES - 1 ) ) ;
2008-01-01 23:55:23 +03:00
spin_lock_irqsave ( & qmgr_lock , flags ) ;
2009-02-20 03:01:33 +03:00
__raw_writel ( __raw_readl ( & qmgr_regs - > irqen [ half ] ) & ~ mask ,
& qmgr_regs - > irqen [ half ] ) ;
__raw_writel ( mask , & qmgr_regs - > irqstat [ half ] ) ; /* clear */
2008-01-01 23:55:23 +03:00
spin_unlock_irqrestore ( & qmgr_lock , flags ) ;
}
static inline void shift_mask ( u32 * mask )
{
mask [ 3 ] = mask [ 3 ] < < 1 | mask [ 2 ] > > 31 ;
mask [ 2 ] = mask [ 2 ] < < 1 | mask [ 1 ] > > 31 ;
mask [ 1 ] = mask [ 1 ] < < 1 | mask [ 0 ] > > 31 ;
mask [ 0 ] < < = 1 ;
}
2008-12-22 02:26:38 +03:00
# if DEBUG_QMGR
2008-01-01 23:55:23 +03:00
int qmgr_request_queue ( unsigned int queue , unsigned int len /* dwords */ ,
unsigned int nearly_empty_watermark ,
2008-12-22 02:26:38 +03:00
unsigned int nearly_full_watermark ,
const char * desc_format , const char * name )
# else
int __qmgr_request_queue ( unsigned int queue , unsigned int len /* dwords */ ,
unsigned int nearly_empty_watermark ,
unsigned int nearly_full_watermark )
# endif
2008-01-01 23:55:23 +03:00
{
u32 cfg , addr = 0 , mask [ 4 ] ; /* in 16-dwords */
int err ;
2009-02-20 03:01:33 +03:00
BUG_ON ( queue > = QUEUES ) ;
2008-01-01 23:55:23 +03:00
if ( ( nearly_empty_watermark | nearly_full_watermark ) & ~ 7 )
return - EINVAL ;
switch ( len ) {
case 16 :
cfg = 0 < < 24 ;
mask [ 0 ] = 0x1 ;
break ;
case 32 :
cfg = 1 < < 24 ;
mask [ 0 ] = 0x3 ;
break ;
case 64 :
cfg = 2 < < 24 ;
mask [ 0 ] = 0xF ;
break ;
case 128 :
cfg = 3 < < 24 ;
mask [ 0 ] = 0xFF ;
break ;
default :
return - EINVAL ;
}
cfg | = nearly_empty_watermark < < 26 ;
cfg | = nearly_full_watermark < < 29 ;
len / = 16 ; /* in 16-dwords: 1, 2, 4 or 8 */
mask [ 1 ] = mask [ 2 ] = mask [ 3 ] = 0 ;
if ( ! try_module_get ( THIS_MODULE ) )
return - ENODEV ;
spin_lock_irq ( & qmgr_lock ) ;
if ( __raw_readl ( & qmgr_regs - > sram [ queue ] ) ) {
err = - EBUSY ;
goto err ;
}
while ( 1 ) {
if ( ! ( used_sram_bitmap [ 0 ] & mask [ 0 ] ) & &
! ( used_sram_bitmap [ 1 ] & mask [ 1 ] ) & &
! ( used_sram_bitmap [ 2 ] & mask [ 2 ] ) & &
! ( used_sram_bitmap [ 3 ] & mask [ 3 ] ) )
break ; /* found free space */
addr + + ;
shift_mask ( mask ) ;
if ( addr + len > ARRAY_SIZE ( qmgr_regs - > sram ) ) {
printk ( KERN_ERR " qmgr: no free SRAM space for "
" queue %i \n " , queue ) ;
err = - ENOMEM ;
goto err ;
}
}
used_sram_bitmap [ 0 ] | = mask [ 0 ] ;
used_sram_bitmap [ 1 ] | = mask [ 1 ] ;
used_sram_bitmap [ 2 ] | = mask [ 2 ] ;
used_sram_bitmap [ 3 ] | = mask [ 3 ] ;
__raw_writel ( cfg | ( addr < < 14 ) , & qmgr_regs - > sram [ queue ] ) ;
2008-12-22 02:26:38 +03:00
# if DEBUG_QMGR
snprintf ( qmgr_queue_descs [ queue ] , sizeof ( qmgr_queue_descs [ 0 ] ) ,
desc_format , name ) ;
printk ( KERN_DEBUG " qmgr: requested queue %s(%i) addr = 0x%02X \n " ,
qmgr_queue_descs [ queue ] , queue , addr ) ;
2008-01-01 23:55:23 +03:00
# endif
2008-12-22 02:26:38 +03:00
spin_unlock_irq ( & qmgr_lock ) ;
2008-01-01 23:55:23 +03:00
return 0 ;
err :
spin_unlock_irq ( & qmgr_lock ) ;
module_put ( THIS_MODULE ) ;
return err ;
}
void qmgr_release_queue ( unsigned int queue )
{
u32 cfg , addr , mask [ 4 ] ;
2009-02-20 03:01:33 +03:00
BUG_ON ( queue > = QUEUES ) ; /* not in valid range */
2008-01-01 23:55:23 +03:00
spin_lock_irq ( & qmgr_lock ) ;
cfg = __raw_readl ( & qmgr_regs - > sram [ queue ] ) ;
addr = ( cfg > > 14 ) & 0xFF ;
BUG_ON ( ! addr ) ; /* not requested */
switch ( ( cfg > > 24 ) & 3 ) {
case 0 : mask [ 0 ] = 0x1 ; break ;
case 1 : mask [ 0 ] = 0x3 ; break ;
case 2 : mask [ 0 ] = 0xF ; break ;
case 3 : mask [ 0 ] = 0xFF ; break ;
}
2008-04-20 21:06:39 +04:00
mask [ 1 ] = mask [ 2 ] = mask [ 3 ] = 0 ;
2008-01-01 23:55:23 +03:00
while ( addr - - )
shift_mask ( mask ) ;
2008-12-22 02:26:38 +03:00
# if DEBUG_QMGR
printk ( KERN_DEBUG " qmgr: releasing queue %s(%i) \n " ,
qmgr_queue_descs [ queue ] , queue ) ;
qmgr_queue_descs [ queue ] [ 0 ] = ' \x0 ' ;
# endif
2010-05-04 01:22:06 +04:00
while ( ( addr = qmgr_get_entry ( queue ) ) )
printk ( KERN_ERR " qmgr: released queue %i not empty: 0x%08X \n " ,
queue , addr ) ;
2008-01-01 23:55:23 +03:00
__raw_writel ( 0 , & qmgr_regs - > sram [ queue ] ) ;
used_sram_bitmap [ 0 ] & = ~ mask [ 0 ] ;
used_sram_bitmap [ 1 ] & = ~ mask [ 1 ] ;
used_sram_bitmap [ 2 ] & = ~ mask [ 2 ] ;
used_sram_bitmap [ 3 ] & = ~ mask [ 3 ] ;
irq_handlers [ queue ] = NULL ; /* catch IRQ bugs */
spin_unlock_irq ( & qmgr_lock ) ;
module_put ( THIS_MODULE ) ;
}
static int qmgr_init ( void )
{
int i , err ;
2009-05-24 01:36:03 +04:00
irq_handler_t handler1 , handler2 ;
2008-01-01 23:55:23 +03:00
mem_res = request_mem_region ( IXP4XX_QMGR_BASE_PHYS ,
IXP4XX_QMGR_REGION_SIZE ,
" IXP4xx Queue Manager " ) ;
if ( mem_res = = NULL )
return - EBUSY ;
qmgr_regs = ioremap ( IXP4XX_QMGR_BASE_PHYS , IXP4XX_QMGR_REGION_SIZE ) ;
if ( qmgr_regs = = NULL ) {
err = - ENOMEM ;
goto error_map ;
}
/* reset qmgr registers */
for ( i = 0 ; i < 4 ; i + + ) {
__raw_writel ( 0x33333333 , & qmgr_regs - > stat1 [ i ] ) ;
__raw_writel ( 0 , & qmgr_regs - > irqsrc [ i ] ) ;
}
for ( i = 0 ; i < 2 ; i + + ) {
__raw_writel ( 0 , & qmgr_regs - > stat2 [ i ] ) ;
__raw_writel ( 0xFFFFFFFF , & qmgr_regs - > irqstat [ i ] ) ; /* clear */
__raw_writel ( 0 , & qmgr_regs - > irqen [ i ] ) ;
}
2009-02-20 03:01:33 +03:00
__raw_writel ( 0xFFFFFFFF , & qmgr_regs - > statne_h ) ;
__raw_writel ( 0 , & qmgr_regs - > statf_h ) ;
2008-01-01 23:55:23 +03:00
for ( i = 0 ; i < QUEUES ; i + + )
__raw_writel ( 0 , & qmgr_regs - > sram [ i ] ) ;
2009-05-24 01:36:03 +04:00
if ( cpu_is_ixp42x_rev_a0 ( ) ) {
handler1 = qmgr_irq1_a0 ;
handler2 = qmgr_irq2_a0 ;
} else
handler1 = handler2 = qmgr_irq ;
err = request_irq ( IRQ_IXP4XX_QM1 , handler1 , 0 , " IXP4xx Queue Manager " ,
NULL ) ;
2008-01-01 23:55:23 +03:00
if ( err ) {
2009-05-24 01:36:03 +04:00
printk ( KERN_ERR " qmgr: failed to request IRQ%i (%i) \n " ,
IRQ_IXP4XX_QM1 , err ) ;
2008-01-01 23:55:23 +03:00
goto error_irq ;
}
2009-05-24 01:36:03 +04:00
err = request_irq ( IRQ_IXP4XX_QM2 , handler2 , 0 , " IXP4xx Queue Manager " ,
NULL ) ;
2009-02-20 03:01:33 +03:00
if ( err ) {
2009-05-24 01:36:03 +04:00
printk ( KERN_ERR " qmgr: failed to request IRQ%i (%i) \n " ,
IRQ_IXP4XX_QM2 , err ) ;
2009-02-20 03:01:33 +03:00
goto error_irq2 ;
}
2008-01-01 23:55:23 +03:00
used_sram_bitmap [ 0 ] = 0xF ; /* 4 first pages reserved for config */
spin_lock_init ( & qmgr_lock ) ;
printk ( KERN_INFO " IXP4xx Queue Manager initialized. \n " ) ;
return 0 ;
2009-02-20 03:01:33 +03:00
error_irq2 :
free_irq ( IRQ_IXP4XX_QM1 , NULL ) ;
2008-01-01 23:55:23 +03:00
error_irq :
iounmap ( qmgr_regs ) ;
error_map :
release_mem_region ( IXP4XX_QMGR_BASE_PHYS , IXP4XX_QMGR_REGION_SIZE ) ;
return err ;
}
static void qmgr_remove ( void )
{
free_irq ( IRQ_IXP4XX_QM1 , NULL ) ;
2009-02-20 03:01:33 +03:00
free_irq ( IRQ_IXP4XX_QM2 , NULL ) ;
2008-01-01 23:55:23 +03:00
synchronize_irq ( IRQ_IXP4XX_QM1 ) ;
2009-02-20 03:01:33 +03:00
synchronize_irq ( IRQ_IXP4XX_QM2 ) ;
2008-01-01 23:55:23 +03:00
iounmap ( qmgr_regs ) ;
release_mem_region ( IXP4XX_QMGR_BASE_PHYS , IXP4XX_QMGR_REGION_SIZE ) ;
}
module_init ( qmgr_init ) ;
module_exit ( qmgr_remove ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_AUTHOR ( " Krzysztof Halasa " ) ;
EXPORT_SYMBOL ( qmgr_regs ) ;
EXPORT_SYMBOL ( qmgr_set_irq ) ;
EXPORT_SYMBOL ( qmgr_enable_irq ) ;
EXPORT_SYMBOL ( qmgr_disable_irq ) ;
2008-12-22 02:26:38 +03:00
# if DEBUG_QMGR
EXPORT_SYMBOL ( qmgr_queue_descs ) ;
2008-01-01 23:55:23 +03:00
EXPORT_SYMBOL ( qmgr_request_queue ) ;
2008-12-22 02:26:38 +03:00
# else
EXPORT_SYMBOL ( __qmgr_request_queue ) ;
# endif
2008-01-01 23:55:23 +03:00
EXPORT_SYMBOL ( qmgr_release_queue ) ;