2005-04-17 02:20:36 +04:00
/* soc.c: Sparc SUNW,soc (Serial Optical Channel) Fibre Channel Sbus adapter support.
*
* Copyright ( C ) 1996 , 1997 , 1999 Jakub Jelinek ( jj @ ultra . linux . cz )
* Copyright ( C ) 1997 , 1998 Jirka Hanika ( geo @ ff . cuni . cz )
*
* Sources :
* Fibre Channel Physical & Signaling Interface ( FC - PH ) , dpANS , 1994
* dpANS Fibre Channel Protocol for SCSI ( X3 .269 - 199 X ) , Rev . 012 , 1995
*
* Supported hardware :
* Tested on SOC sbus card bought with SS1000 in Linux running on SS5 and Ultra1 .
* For SOC sbus cards , you have to make sure your FCode is 1.52 or later .
* If you have older FCode , you should try to upgrade or get SOC microcode from Sun
* ( the microcode is present in Solaris soc driver as well ) . In that case you need
* to # define HAVE_SOC_UCODE and format the microcode into soc_asm . c . For the exact
* format mail me and I will tell you . I cannot offer you the actual microcode though ,
* unless Sun confirms they don ' t mind .
*/
static char * version =
" soc.c:v1.3 9/Feb/99 Jakub Jelinek (jj@ultra.linux.cz), Jirka Hanika (geo@ff.cuni.cz) \n " ;
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/types.h>
# include <linux/fcntl.h>
# include <linux/interrupt.h>
# include <linux/ptrace.h>
# include <linux/ioport.h>
# include <linux/in.h>
# include <linux/slab.h>
# include <linux/string.h>
# include <linux/init.h>
# include <linux/bitops.h>
# include <asm/io.h>
# include <asm/dma.h>
# include <linux/errno.h>
# include <asm/byteorder.h>
# include <asm/openprom.h>
# include <asm/oplib.h>
# include <asm/pgtable.h>
# include <asm/irq.h>
/* #define SOCDEBUG */
/* #define HAVE_SOC_UCODE */
# include "fcp_impl.h"
# include "soc.h"
# ifdef HAVE_SOC_UCODE
# include "soc_asm.h"
# endif
# define soc_printk printk ("soc%d: ", s->soc_no); printk
# ifdef SOCDEBUG
# define SOD(x) soc_printk x;
# else
# define SOD(x)
# endif
# define for_each_soc(s) for (s = socs; s; s = s->next)
struct soc * socs = NULL ;
static inline void soc_disable ( struct soc * s )
{
sbus_writel ( 0 , s - > regs + IMASK ) ;
sbus_writel ( SOC_CMD_SOFT_RESET , s - > regs + CMD ) ;
}
static inline void soc_enable ( struct soc * s )
{
SOD ( ( " enable %08x \n " , s - > cfg ) )
sbus_writel ( 0 , s - > regs + SAE ) ;
sbus_writel ( s - > cfg , s - > regs + CFG ) ;
sbus_writel ( SOC_CMD_RSP_QALL , s - > regs + CMD ) ;
SOC_SETIMASK ( s , SOC_IMASK_RSP_QALL | SOC_IMASK_SAE ) ;
SOD ( ( " imask %08lx %08lx \n " , s - > imask , sbus_readl ( s - > regs + IMAK ) ) ) ;
}
static void soc_reset ( fc_channel * fc )
{
soc_port * port = ( soc_port * ) fc ;
struct soc * s = port - > s ;
/* FIXME */
soc_disable ( s ) ;
s - > req [ 0 ] . seqno = 1 ;
s - > req [ 1 ] . seqno = 1 ;
s - > rsp [ 0 ] . seqno = 1 ;
s - > rsp [ 1 ] . seqno = 1 ;
s - > req [ 0 ] . in = 0 ;
s - > req [ 1 ] . in = 0 ;
s - > rsp [ 0 ] . in = 0 ;
s - > rsp [ 1 ] . in = 0 ;
s - > req [ 0 ] . out = 0 ;
s - > req [ 1 ] . out = 0 ;
s - > rsp [ 0 ] . out = 0 ;
s - > rsp [ 1 ] . out = 0 ;
/* FIXME */
soc_enable ( s ) ;
}
static inline void soc_solicited ( struct soc * s )
{
fc_hdr fchdr ;
soc_rsp __iomem * hwrsp ;
soc_cq_rsp * sw_cq ;
int token ;
int status ;
fc_channel * fc ;
sw_cq = & s - > rsp [ SOC_SOLICITED_RSP_Q ] ;
if ( sw_cq - > pool = = NULL )
sw_cq - > pool = ( soc_req __iomem * )
( s - > xram + xram_get_32low ( ( xram_p ) & sw_cq - > hw_cq - > address ) ) ;
sw_cq - > in = xram_get_8 ( ( xram_p ) & sw_cq - > hw_cq - > in ) ;
SOD ( ( " soc_solicited, %d pkts arrived \n " , ( sw_cq - > in - sw_cq - > out ) & sw_cq - > last ) )
for ( ; ; ) {
hwrsp = ( soc_rsp __iomem * ) sw_cq - > pool + sw_cq - > out ;
token = xram_get_32low ( ( xram_p ) & hwrsp - > shdr . token ) ;
status = xram_get_32low ( ( xram_p ) & hwrsp - > status ) ;
fc = ( fc_channel * ) ( & s - > port [ ( token > > 11 ) & 1 ] ) ;
if ( status = = SOC_OK ) {
fcp_receive_solicited ( fc , token > > 12 ,
token & ( ( 1 < < 11 ) - 1 ) ,
FC_STATUS_OK , NULL ) ;
} else {
xram_copy_from ( & fchdr , ( xram_p ) & hwrsp - > fchdr , sizeof ( fchdr ) ) ;
/* We have intentionally defined FC_STATUS_* constants
* to match SOC_ * constants , otherwise we ' d have to
* translate status .
*/
fcp_receive_solicited ( fc , token > > 12 ,
token & ( ( 1 < < 11 ) - 1 ) ,
status , & fchdr ) ;
}
if ( + + sw_cq - > out > sw_cq - > last ) {
sw_cq - > seqno + + ;
sw_cq - > out = 0 ;
}
if ( sw_cq - > out = = sw_cq - > in ) {
sw_cq - > in = xram_get_8 ( ( xram_p ) & sw_cq - > hw_cq - > in ) ;
if ( sw_cq - > out = = sw_cq - > in ) {
/* Tell the hardware about it */
sbus_writel ( ( sw_cq - > out < < 24 ) |
( SOC_CMD_RSP_QALL &
~ ( SOC_CMD_RSP_Q0 < < SOC_SOLICITED_RSP_Q ) ) ,
s - > regs + CMD ) ;
/* Read it, so that we're sure it has been updated */
sbus_readl ( s - > regs + CMD ) ;
sw_cq - > in = xram_get_8 ( ( xram_p ) & sw_cq - > hw_cq - > in ) ;
if ( sw_cq - > out = = sw_cq - > in )
break ;
}
}
}
}
static inline void soc_request ( struct soc * s , u32 cmd )
{
SOC_SETIMASK ( s , s - > imask & ~ ( cmd & SOC_CMD_REQ_QALL ) ) ;
SOD ( ( " imask %08lx %08lx \n " , s - > imask , sbus_readl ( s - > regs + IMASK ) ) ) ;
SOD ( ( " Queues available %08x OUT %X %X \n " , cmd ,
xram_get_8 ( ( xram_p ) & s - > req [ 0 ] . hw_cq - > out ) ,
xram_get_8 ( ( xram_p ) & s - > req [ 0 ] . hw_cq - > out ) ) )
if ( s - > port [ s - > curr_port ] . fc . state ! = FC_STATE_OFFLINE ) {
fcp_queue_empty ( ( fc_channel * ) & ( s - > port [ s - > curr_port ] ) ) ;
if ( ( ( s - > req [ 1 ] . in + 1 ) & s - > req [ 1 ] . last ) ! = ( s - > req [ 1 ] . out ) )
fcp_queue_empty ( ( fc_channel * ) & ( s - > port [ 1 - s - > curr_port ] ) ) ;
} else {
fcp_queue_empty ( ( fc_channel * ) & ( s - > port [ 1 - s - > curr_port ] ) ) ;
}
if ( s - > port [ 1 - s - > curr_port ] . fc . state ! = FC_STATE_OFFLINE )
s - > curr_port ^ = 1 ;
}
static inline void soc_unsolicited ( struct soc * s )
{
soc_rsp __iomem * hwrsp , * hwrspc ;
soc_cq_rsp * sw_cq ;
int count ;
int status ;
int flags ;
fc_channel * fc ;
sw_cq = & s - > rsp [ SOC_UNSOLICITED_RSP_Q ] ;
if ( sw_cq - > pool = = NULL )
sw_cq - > pool = ( soc_req __iomem * )
( s - > xram + ( xram_get_32low ( ( xram_p ) & sw_cq - > hw_cq - > address ) ) ) ;
sw_cq - > in = xram_get_8 ( ( xram_p ) & sw_cq - > hw_cq - > in ) ;
SOD ( ( " soc_unsolicited, %d packets arrived \n " , ( sw_cq - > in - sw_cq - > out ) & sw_cq - > last ) )
while ( sw_cq - > in ! = sw_cq - > out ) {
/* ...real work per entry here... */
hwrsp = ( soc_rsp __iomem * ) sw_cq - > pool + sw_cq - > out ;
hwrspc = NULL ;
flags = xram_get_16 ( ( xram_p ) & hwrsp - > shdr . flags ) ;
count = xram_get_8 ( ( xram_p ) & hwrsp - > count ) ;
fc = ( fc_channel * ) & s - > port [ flags & SOC_PORT_B ] ;
SOD ( ( " FC %08lx fcp_state_change %08lx \n " ,
( long ) fc , ( long ) fc - > fcp_state_change ) )
if ( count ! = 1 ) {
/* Ugh, continuation entries */
u8 in ;
if ( count ! = 2 ) {
printk ( " %s: Too many continuations entries %d \n " ,
fc - > name , count ) ;
goto update_out ;
}
in = sw_cq - > in ;
if ( in < sw_cq - > out ) in + = sw_cq - > last + 1 ;
if ( in < sw_cq - > out + 2 ) {
/* Ask the hardware if they haven't arrived yet. */
sbus_writel ( ( sw_cq - > out < < 24 ) |
( SOC_CMD_RSP_QALL &
~ ( SOC_CMD_RSP_Q0 < < SOC_UNSOLICITED_RSP_Q ) ) ,
s - > regs + CMD ) ;
/* Read it, so that we're sure it has been updated */
sbus_readl ( s - > regs + CMD ) ;
sw_cq - > in = xram_get_8 ( ( xram_p ) & sw_cq - > hw_cq - > in ) ;
in = sw_cq - > in ;
if ( in < sw_cq - > out )
in + = sw_cq - > last + 1 ;
if ( in < sw_cq - > out + 2 ) /* Nothing came, let us wait */
return ;
}
if ( sw_cq - > out = = sw_cq - > last )
hwrspc = ( soc_rsp __iomem * ) sw_cq - > pool ;
else
hwrspc = hwrsp + 1 ;
}
switch ( flags & ~ SOC_PORT_B ) {
case SOC_STATUS :
status = xram_get_32low ( ( xram_p ) & hwrsp - > status ) ;
switch ( status ) {
case SOC_ONLINE :
SOD ( ( " State change to ONLINE \n " ) ) ;
fcp_state_change ( fc , FC_STATE_ONLINE ) ;
break ;
case SOC_OFFLINE :
SOD ( ( " State change to OFFLINE \n " ) ) ;
fcp_state_change ( fc , FC_STATE_OFFLINE ) ;
break ;
default :
printk ( " %s: Unknown STATUS no %d \n " ,
fc - > name , status ) ;
break ;
}
break ;
case ( SOC_UNSOLICITED | SOC_FC_HDR ) :
{
int r_ctl = xram_get_8 ( ( xram_p ) & hwrsp - > fchdr ) ;
unsigned len ;
char buf [ 64 ] ;
if ( ( r_ctl & 0xf0 ) = = R_CTL_EXTENDED_SVC ) {
len = xram_get_32 ( ( xram_p ) & hwrsp - > shdr . bytecnt ) ;
if ( len < 4 | | ! hwrspc ) {
printk ( " %s: Invalid R_CTL %02x "
" continuation entries \n " ,
fc - > name , r_ctl ) ;
} else {
if ( len > 60 )
len = 60 ;
xram_copy_from ( buf , ( xram_p ) hwrspc ,
( len + 3 ) & ~ 3 ) ;
if ( * ( u32 * ) buf = = LS_DISPLAY ) {
int i ;
for ( i = 4 ; i < len ; i + + )
if ( buf [ i ] = = ' \n ' )
buf [ i ] = ' ' ;
buf [ len ] = 0 ;
printk ( " %s message: %s \n " ,
fc - > name , buf + 4 ) ;
} else {
printk ( " %s: Unknown LS_CMD "
" %02x \n " , fc - > name ,
buf [ 0 ] ) ;
}
}
} else {
printk ( " %s: Unsolicited R_CTL %02x "
" not handled \n " , fc - > name , r_ctl ) ;
}
}
break ;
default :
printk ( " %s: Unexpected flags %08x \n " , fc - > name , flags ) ;
break ;
} ;
update_out :
if ( + + sw_cq - > out > sw_cq - > last ) {
sw_cq - > seqno + + ;
sw_cq - > out = 0 ;
}
if ( hwrspc ) {
if ( + + sw_cq - > out > sw_cq - > last ) {
sw_cq - > seqno + + ;
sw_cq - > out = 0 ;
}
}
if ( sw_cq - > out = = sw_cq - > in ) {
sw_cq - > in = xram_get_8 ( ( xram_p ) & sw_cq - > hw_cq - > in ) ;
if ( sw_cq - > out = = sw_cq - > in ) {
/* Tell the hardware about it */
sbus_writel ( ( sw_cq - > out < < 24 ) |
( SOC_CMD_RSP_QALL &
~ ( SOC_CMD_RSP_Q0 < < SOC_UNSOLICITED_RSP_Q ) ) ,
s - > regs + CMD ) ;
/* Read it, so that we're sure it has been updated */
sbus_readl ( s - > regs + CMD ) ;
sw_cq - > in = xram_get_8 ( ( xram_p ) & sw_cq - > hw_cq - > in ) ;
}
}
}
}
static irqreturn_t soc_intr ( int irq , void * dev_id , struct pt_regs * regs )
{
u32 cmd ;
unsigned long flags ;
register struct soc * s = ( struct soc * ) dev_id ;
spin_lock_irqsave ( & s - > lock , flags ) ;
cmd = sbus_readl ( s - > regs + CMD ) ;
for ( ; ( cmd = SOC_INTR ( s , cmd ) ) ; cmd = sbus_readl ( s - > regs + CMD ) ) {
if ( cmd & SOC_CMD_RSP_Q1 ) soc_unsolicited ( s ) ;
if ( cmd & SOC_CMD_RSP_Q0 ) soc_solicited ( s ) ;
if ( cmd & SOC_CMD_REQ_QALL ) soc_request ( s , cmd ) ;
}
spin_unlock_irqrestore ( & s - > lock , flags ) ;
return IRQ_HANDLED ;
}
# define TOKEN(proto, port, token) (((proto)<<12)|(token)|(port))
static int soc_hw_enque ( fc_channel * fc , fcp_cmnd * fcmd )
{
soc_port * port = ( soc_port * ) fc ;
struct soc * s = port - > s ;
int qno ;
soc_cq_req * sw_cq ;
int cq_next_in ;
soc_req * request ;
fc_hdr * fch ;
int i ;
if ( fcmd - > proto = = TYPE_SCSI_FCP )
qno = 1 ;
else
qno = 0 ;
SOD ( ( " Putting a FCP packet type %d into hw queue %d \n " , fcmd - > proto , qno ) )
if ( s - > imask & ( SOC_IMASK_REQ_Q0 < < qno ) ) {
SOD ( ( " EIO %08x \n " , s - > imask ) )
return - EIO ;
}
sw_cq = s - > req + qno ;
cq_next_in = ( sw_cq - > in + 1 ) & sw_cq - > last ;
if ( cq_next_in = = sw_cq - > out & &
cq_next_in = = ( sw_cq - > out = xram_get_8 ( ( xram_p ) & sw_cq - > hw_cq - > out ) ) ) {
SOD ( ( " %d IN %d OUT %d LAST %d \n " , qno , sw_cq - > in , sw_cq - > out , sw_cq - > last ) )
SOC_SETIMASK ( s , s - > imask | ( SOC_IMASK_REQ_Q0 < < qno ) ) ;
SOD ( ( " imask %08lx %08lx \n " , s - > imask , sbus_readl ( s - > regs + IMASK ) ) ) ;
/* If queue is full, just say NO */
return - EBUSY ;
}
request = sw_cq - > pool + sw_cq - > in ;
fch = & request - > fchdr ;
switch ( fcmd - > proto ) {
case TYPE_SCSI_FCP :
request - > shdr . token = TOKEN ( TYPE_SCSI_FCP , port - > mask , fcmd - > token ) ;
request - > data [ 0 ] . base = fc - > dma_scsi_cmd + fcmd - > token * sizeof ( fcp_cmd ) ;
request - > data [ 0 ] . count = sizeof ( fcp_cmd ) ;
request - > data [ 1 ] . base = fc - > dma_scsi_rsp + fcmd - > token * fc - > rsp_size ;
request - > data [ 1 ] . count = fc - > rsp_size ;
if ( fcmd - > data ) {
request - > shdr . segcnt = 3 ;
i = fc - > scsi_cmd_pool [ fcmd - > token ] . fcp_data_len ;
request - > shdr . bytecnt = i ;
request - > data [ 2 ] . base = fcmd - > data ;
request - > data [ 2 ] . count = i ;
request - > type =
( fc - > scsi_cmd_pool [ fcmd - > token ] . fcp_cntl & FCP_CNTL_WRITE ) ?
SOC_CQTYPE_IO_WRITE : SOC_CQTYPE_IO_READ ;
} else {
request - > shdr . segcnt = 2 ;
request - > shdr . bytecnt = 0 ;
request - > data [ 2 ] . base = 0 ;
request - > data [ 2 ] . count = 0 ;
request - > type = SOC_CQTYPE_SIMPLE ;
}
FILL_FCHDR_RCTL_DID ( fch , R_CTL_COMMAND , fc - > did ) ;
FILL_FCHDR_SID ( fch , fc - > sid ) ;
FILL_FCHDR_TYPE_FCTL ( fch , TYPE_SCSI_FCP ,
F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE ) ;
FILL_FCHDR_SEQ_DF_SEQ ( fch , 0 , 0 , 0 ) ;
FILL_FCHDR_OXRX ( fch , 0xffff , 0xffff ) ;
fch - > param = 0 ;
request - > shdr . flags = port - > flags ;
request - > shdr . class = 2 ;
break ;
case PROTO_OFFLINE :
memset ( request , 0 , sizeof ( * request ) ) ;
request - > shdr . token = TOKEN ( PROTO_OFFLINE , port - > mask , fcmd - > token ) ;
request - > type = SOC_CQTYPE_OFFLINE ;
FILL_FCHDR_RCTL_DID ( fch , R_CTL_COMMAND , fc - > did ) ;
FILL_FCHDR_SID ( fch , fc - > sid ) ;
FILL_FCHDR_TYPE_FCTL ( fch , TYPE_SCSI_FCP ,
F_CTL_FIRST_SEQ | F_CTL_SEQ_INITIATIVE ) ;
FILL_FCHDR_SEQ_DF_SEQ ( fch , 0 , 0 , 0 ) ;
FILL_FCHDR_OXRX ( fch , 0xffff , 0xffff ) ;
request - > shdr . flags = port - > flags ;
break ;
case PROTO_REPORT_AL_MAP :
/* SOC only supports Point-to-Point topology, no FC-AL, sorry... */
return - ENOSYS ;
default :
request - > shdr . token = TOKEN ( fcmd - > proto , port - > mask , fcmd - > token ) ;
request - > shdr . class = 2 ;
request - > shdr . flags = port - > flags ;
memcpy ( fch , & fcmd - > fch , sizeof ( fc_hdr ) ) ;
request - > data [ 0 ] . count = fcmd - > cmdlen ;
request - > data [ 1 ] . count = fcmd - > rsplen ;
request - > type = fcmd - > class ;
switch ( fcmd - > class ) {
case FC_CLASS_OUTBOUND :
request - > data [ 0 ] . base = fcmd - > cmd ;
request - > data [ 0 ] . count = fcmd - > cmdlen ;
request - > type = SOC_CQTYPE_OUTBOUND ;
request - > shdr . bytecnt = fcmd - > cmdlen ;
request - > shdr . segcnt = 1 ;
break ;
case FC_CLASS_INBOUND :
request - > data [ 0 ] . base = fcmd - > rsp ;
request - > data [ 0 ] . count = fcmd - > rsplen ;
request - > type = SOC_CQTYPE_INBOUND ;
request - > shdr . bytecnt = 0 ;
request - > shdr . segcnt = 1 ;
break ;
case FC_CLASS_SIMPLE :
request - > data [ 0 ] . base = fcmd - > cmd ;
request - > data [ 1 ] . base = fcmd - > rsp ;
request - > data [ 0 ] . count = fcmd - > cmdlen ;
request - > data [ 1 ] . count = fcmd - > rsplen ;
request - > type = SOC_CQTYPE_SIMPLE ;
request - > shdr . bytecnt = fcmd - > cmdlen ;
request - > shdr . segcnt = 2 ;
break ;
case FC_CLASS_IO_READ :
case FC_CLASS_IO_WRITE :
request - > data [ 0 ] . base = fcmd - > cmd ;
request - > data [ 1 ] . base = fcmd - > rsp ;
request - > data [ 0 ] . count = fcmd - > cmdlen ;
request - > data [ 1 ] . count = fcmd - > rsplen ;
request - > type =
( fcmd - > class = = FC_CLASS_IO_READ ) ?
SOC_CQTYPE_IO_READ : SOC_CQTYPE_IO_WRITE ;
if ( fcmd - > data ) {
request - > data [ 2 ] . base = fcmd - > data ;
request - > data [ 2 ] . count = fcmd - > datalen ;
request - > shdr . bytecnt = fcmd - > datalen ;
request - > shdr . segcnt = 3 ;
} else {
request - > shdr . bytecnt = 0 ;
request - > shdr . segcnt = 2 ;
}
break ;
} ;
break ;
} ;
request - > count = 1 ;
request - > flags = 0 ;
request - > seqno = sw_cq - > seqno ;
/* And now tell the SOC about it */
if ( + + sw_cq - > in > sw_cq - > last ) {
sw_cq - > in = 0 ;
sw_cq - > seqno + + ;
}
SOD ( ( " Putting %08x into cmd \n " ,
SOC_CMD_RSP_QALL | ( sw_cq - > in < < 24 ) | ( SOC_CMD_REQ_Q0 < < qno ) ) )
sbus_writel ( SOC_CMD_RSP_QALL | ( sw_cq - > in < < 24 ) | ( SOC_CMD_REQ_Q0 < < qno ) ,
s - > regs + CMD ) ;
/* Read so that command is completed. */
sbus_readl ( s - > regs + CMD ) ;
return 0 ;
}
static inline void soc_download_fw ( struct soc * s )
{
# ifdef HAVE_SOC_UCODE
xram_copy_to ( s - > xram , soc_ucode , sizeof ( soc_ucode ) ) ;
xram_bzero ( s - > xram + sizeof ( soc_ucode ) , 32768 - sizeof ( soc_ucode ) ) ;
# endif
}
/* Check for what the best SBUS burst we can use happens
* to be on this machine .
*/
static inline void soc_init_bursts ( struct soc * s , struct sbus_dev * sdev )
{
int bsizes , bsizes_more ;
bsizes = ( prom_getintdefault ( sdev - > prom_node , " burst-sizes " , 0xff ) & 0xff ) ;
bsizes_more = ( prom_getintdefault ( sdev - > bus - > prom_node , " burst-sizes " , 0xff ) & 0xff ) ;
bsizes & = bsizes_more ;
if ( ( bsizes & 0x7f ) = = 0x7f )
s - > cfg = SOC_CFG_BURST_64 ;
else if ( ( bsizes & 0x3f ) = = 0x3f )
s - > cfg = SOC_CFG_BURST_32 ;
else if ( ( bsizes & 0x1f ) = = 0x1f )
s - > cfg = SOC_CFG_BURST_16 ;
else
s - > cfg = SOC_CFG_BURST_4 ;
}
static inline void soc_init ( struct sbus_dev * sdev , int no )
{
unsigned char tmp [ 60 ] ;
int propl ;
struct soc * s ;
static int version_printed = 0 ;
soc_hw_cq cq [ 8 ] ;
int size , i ;
int irq ;
2005-11-07 12:01:24 +03:00
s = kzalloc ( sizeof ( struct soc ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( s = = NULL )
return ;
spin_lock_init ( & s - > lock ) ;
s - > soc_no = no ;
SOD ( ( " socs %08lx soc_intr %08lx soc_hw_enque %08x \n " ,
( long ) socs , ( long ) soc_intr , ( long ) soc_hw_enque ) )
if ( version_printed + + = = 0 )
printk ( version ) ;
s - > port [ 0 ] . fc . module = THIS_MODULE ;
s - > port [ 1 ] . fc . module = THIS_MODULE ;
s - > next = socs ;
socs = s ;
s - > port [ 0 ] . fc . dev = sdev ;
s - > port [ 1 ] . fc . dev = sdev ;
s - > port [ 0 ] . s = s ;
s - > port [ 1 ] . s = s ;
s - > port [ 0 ] . fc . next = & s - > port [ 1 ] . fc ;
/* World Wide Name of SOC */
propl = prom_getproperty ( sdev - > prom_node , " soc-wwn " , tmp , sizeof ( tmp ) ) ;
if ( propl ! = sizeof ( fc_wwn ) ) {
s - > wwn . naaid = NAAID_IEEE ;
s - > wwn . lo = 0x12345678 ;
} else
memcpy ( & s - > wwn , tmp , sizeof ( fc_wwn ) ) ;
propl = prom_getproperty ( sdev - > prom_node , " port-wwns " , tmp , sizeof ( tmp ) ) ;
if ( propl ! = 2 * sizeof ( fc_wwn ) ) {
s - > port [ 0 ] . fc . wwn_nport . naaid = NAAID_IEEE_EXT ;
s - > port [ 0 ] . fc . wwn_nport . hi = s - > wwn . hi ;
s - > port [ 0 ] . fc . wwn_nport . lo = s - > wwn . lo ;
s - > port [ 1 ] . fc . wwn_nport . naaid = NAAID_IEEE_EXT ;
s - > port [ 1 ] . fc . wwn_nport . nportid = 1 ;
s - > port [ 1 ] . fc . wwn_nport . hi = s - > wwn . hi ;
s - > port [ 1 ] . fc . wwn_nport . lo = s - > wwn . lo ;
} else {
memcpy ( & s - > port [ 0 ] . fc . wwn_nport , tmp , sizeof ( fc_wwn ) ) ;
memcpy ( & s - > port [ 1 ] . fc . wwn_nport , tmp + sizeof ( fc_wwn ) , sizeof ( fc_wwn ) ) ;
}
memcpy ( & s - > port [ 0 ] . fc . wwn_node , & s - > wwn , sizeof ( fc_wwn ) ) ;
memcpy ( & s - > port [ 1 ] . fc . wwn_node , & s - > wwn , sizeof ( fc_wwn ) ) ;
SOD ( ( " Got wwns %08x%08x ports %08x%08x and %08x%08x \n " ,
* ( u32 * ) & s - > port [ 0 ] . fc . wwn_nport , s - > port [ 0 ] . fc . wwn_nport . lo ,
* ( u32 * ) & s - > port [ 0 ] . fc . wwn_nport , s - > port [ 0 ] . fc . wwn_nport . lo ,
* ( u32 * ) & s - > port [ 1 ] . fc . wwn_nport , s - > port [ 1 ] . fc . wwn_nport . lo ) )
s - > port [ 0 ] . fc . sid = 1 ;
s - > port [ 1 ] . fc . sid = 17 ;
s - > port [ 0 ] . fc . did = 2 ;
s - > port [ 1 ] . fc . did = 18 ;
s - > port [ 0 ] . fc . reset = soc_reset ;
s - > port [ 1 ] . fc . reset = soc_reset ;
if ( sdev - > num_registers = = 1 ) {
/* Probably SunFire onboard SOC */
s - > xram = sbus_ioremap ( & sdev - > resource [ 0 ] , 0 ,
0x10000UL , " soc xram " ) ;
s - > regs = sbus_ioremap ( & sdev - > resource [ 0 ] , 0x10000UL ,
0x10UL , " soc regs " ) ;
} else {
/* Probably SOC sbus card */
s - > xram = sbus_ioremap ( & sdev - > resource [ 1 ] , 0 ,
sdev - > reg_addrs [ 1 ] . reg_size , " soc xram " ) ;
s - > regs = sbus_ioremap ( & sdev - > resource [ 2 ] , 0 ,
sdev - > reg_addrs [ 2 ] . reg_size , " soc regs " ) ;
}
soc_init_bursts ( s , sdev ) ;
SOD ( ( " Disabling SOC \n " ) )
soc_disable ( s ) ;
irq = sdev - > irqs [ 0 ] ;
if ( request_irq ( irq , soc_intr , SA_SHIRQ , " SOC " , ( void * ) s ) ) {
soc_printk ( " Cannot order irq %d to go \n " , irq ) ;
socs = s - > next ;
return ;
}
SOD ( ( " SOC uses IRQ%s \n " , __irq_itoa ( irq ) ) )
s - > port [ 0 ] . fc . irq = irq ;
s - > port [ 1 ] . fc . irq = irq ;
sprintf ( s - > port [ 0 ] . fc . name , " soc%d port A " , no ) ;
sprintf ( s - > port [ 1 ] . fc . name , " soc%d port B " , no ) ;
s - > port [ 0 ] . flags = SOC_FC_HDR | SOC_PORT_A ;
s - > port [ 1 ] . flags = SOC_FC_HDR | SOC_PORT_B ;
s - > port [ 1 ] . mask = ( 1 < < 11 ) ;
s - > port [ 0 ] . fc . hw_enque = soc_hw_enque ;
s - > port [ 1 ] . fc . hw_enque = soc_hw_enque ;
soc_download_fw ( s ) ;
SOD ( ( " Downloaded firmware \n " ) )
/* Now setup xram circular queues */
memset ( cq , 0 , sizeof ( cq ) ) ;
size = ( SOC_CQ_REQ0_SIZE + SOC_CQ_REQ1_SIZE ) * sizeof ( soc_req ) ;
s - > req_cpu = sbus_alloc_consistent ( sdev , size , & s - > req_dvma ) ;
s - > req [ 0 ] . pool = s - > req_cpu ;
cq [ 0 ] . address = s - > req_dvma ;
s - > req [ 1 ] . pool = s - > req [ 0 ] . pool + SOC_CQ_REQ0_SIZE ;
s - > req [ 0 ] . hw_cq = ( soc_hw_cq __iomem * ) ( s - > xram + SOC_CQ_REQ_OFFSET ) ;
s - > req [ 1 ] . hw_cq = ( soc_hw_cq __iomem * ) ( s - > xram + SOC_CQ_REQ_OFFSET + sizeof ( soc_hw_cq ) ) ;
s - > rsp [ 0 ] . hw_cq = ( soc_hw_cq __iomem * ) ( s - > xram + SOC_CQ_RSP_OFFSET ) ;
s - > rsp [ 1 ] . hw_cq = ( soc_hw_cq __iomem * ) ( s - > xram + SOC_CQ_RSP_OFFSET + sizeof ( soc_hw_cq ) ) ;
cq [ 1 ] . address = cq [ 0 ] . address + ( SOC_CQ_REQ0_SIZE * sizeof ( soc_req ) ) ;
cq [ 4 ] . address = 1 ;
cq [ 5 ] . address = 1 ;
cq [ 0 ] . last = SOC_CQ_REQ0_SIZE - 1 ;
cq [ 1 ] . last = SOC_CQ_REQ1_SIZE - 1 ;
cq [ 4 ] . last = SOC_CQ_RSP0_SIZE - 1 ;
cq [ 5 ] . last = SOC_CQ_RSP1_SIZE - 1 ;
for ( i = 0 ; i < 8 ; i + + )
cq [ i ] . seqno = 1 ;
s - > req [ 0 ] . last = SOC_CQ_REQ0_SIZE - 1 ;
s - > req [ 1 ] . last = SOC_CQ_REQ1_SIZE - 1 ;
s - > rsp [ 0 ] . last = SOC_CQ_RSP0_SIZE - 1 ;
s - > rsp [ 1 ] . last = SOC_CQ_RSP1_SIZE - 1 ;
s - > req [ 0 ] . seqno = 1 ;
s - > req [ 1 ] . seqno = 1 ;
s - > rsp [ 0 ] . seqno = 1 ;
s - > rsp [ 1 ] . seqno = 1 ;
xram_copy_to ( s - > xram + SOC_CQ_REQ_OFFSET , cq , sizeof ( cq ) ) ;
/* Make our sw copy of SOC service parameters */
xram_copy_from ( s - > serv_params , s - > xram + 0x140 , sizeof ( s - > serv_params ) ) ;
s - > port [ 0 ] . fc . common_svc = ( common_svc_parm * ) s - > serv_params ;
s - > port [ 0 ] . fc . class_svcs = ( svc_parm * ) ( s - > serv_params + 0x20 ) ;
s - > port [ 1 ] . fc . common_svc = ( common_svc_parm * ) & s - > serv_params ;
s - > port [ 1 ] . fc . class_svcs = ( svc_parm * ) ( s - > serv_params + 0x20 ) ;
soc_enable ( s ) ;
SOD ( ( " Enabled SOC \n " ) )
}
static int __init soc_probe ( void )
{
struct sbus_bus * sbus ;
struct sbus_dev * sdev = NULL ;
struct soc * s ;
int cards = 0 ;
for_each_sbus ( sbus ) {
for_each_sbusdev ( sdev , sbus ) {
if ( ! strcmp ( sdev - > prom_name , " SUNW,soc " ) ) {
soc_init ( sdev , cards ) ;
cards + + ;
}
}
}
if ( ! cards ) return - EIO ;
for_each_soc ( s )
if ( s - > next )
s - > port [ 1 ] . fc . next = & s - > next - > port [ 0 ] . fc ;
fcp_init ( & socs - > port [ 0 ] . fc ) ;
return 0 ;
}
static void __exit soc_cleanup ( void )
{
struct soc * s ;
int irq ;
struct sbus_dev * sdev ;
for_each_soc ( s ) {
irq = s - > port [ 0 ] . fc . irq ;
free_irq ( irq , s ) ;
fcp_release ( & ( s - > port [ 0 ] . fc ) , 2 ) ;
sdev = s - > port [ 0 ] . fc . dev ;
if ( sdev - > num_registers = = 1 ) {
sbus_iounmap ( s - > xram , 0x10000UL ) ;
sbus_iounmap ( s - > regs , 0x10UL ) ;
} else {
sbus_iounmap ( s - > xram , sdev - > reg_addrs [ 1 ] . reg_size ) ;
sbus_iounmap ( s - > regs , sdev - > reg_addrs [ 2 ] . reg_size ) ;
}
sbus_free_consistent ( sdev ,
( SOC_CQ_REQ0_SIZE + SOC_CQ_REQ1_SIZE ) * sizeof ( soc_req ) ,
s - > req_cpu , s - > req_dvma ) ;
}
}
module_init ( soc_probe ) ;
module_exit ( soc_cleanup ) ;
MODULE_LICENSE ( " GPL " ) ;