/* socal.c: Sparc SUNW,socal (SOC+) Fibre Channel Sbus adapter support.
*
* Copyright (C) 1998,1999 Jakub Jelinek (jj@ultra.linux.cz)
*
* Sources:
* Fibre Channel Physical & Signaling Interface (FC-PH), dpANS, 1994
* dpANS Fibre Channel Protocol for SCSI (X3.269-199X), Rev. 012, 1995
* SOC+ Programming Guide 0.1
* Fibre Channel Arbitrated Loop (FC-AL), dpANS rev. 4.5, 1995
*
* Supported hardware:
* On-board SOC+ adapters of Ultra Enterprise servers and sun4d.
*/
static char *version =
"socal.c: SOC+ driver v1.1 9/Feb/99 Jakub Jelinek (jj@ultra.linux.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/system.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 SOCALDEBUG */
/* #define HAVE_SOCAL_UCODE */
/* #define USE_64BIT_MODE */
#include "fcp_impl.h"
#include "socal.h"
#ifdef HAVE_SOCAL_UCODE
#include "socal_asm.h"
#endif
#define socal_printk printk ("socal%d: ", s->socal_no); printk
#ifdef SOCALDEBUG
#define SOD(x) socal_printk x;
#else
#define SOD(x)
#endif
#define for_each_socal(s) for (s = socals; s; s = s->next)
struct socal *socals = NULL;
static void socal_copy_from_xram(void *d, void __iomem *xram, long size)
{
u32 *dp = (u32 *) d;
while (size) {
*dp++ = sbus_readl(xram);
xram += sizeof(u32);
size -= sizeof(u32);
}
}
static void socal_copy_to_xram(void __iomem *xram, void *s, long size)
{
u32 *sp = (u32 *) s;
while (size) {
u32 val = *sp++;
sbus_writel(val, xram);
xram += sizeof(u32);
size -= sizeof(u32);
}
}
#ifdef HAVE_SOCAL_UCODE
static void socal_bzero(unsigned long xram, int size)
{
while (size) {
sbus_writel(0, xram);
xram += sizeof(u32);
size -= sizeof(u32);
}
}
#endif
static inline void socal_disable(struct socal *s)
{
sbus_writel(0, s->regs + IMASK);
sbus_writel(SOCAL_CMD_SOFT_RESET, s->regs + CMD);
}
static inline void socal_enable(struct socal *s)
{
SOD(("enable %08x\n", s->cfg))
sbus_writel(0, s->regs + SAE);
sbus_writel(s->cfg, s->regs + CFG);
sbus_writel(SOCAL_CMD_RSP_QALL, s->regs + CMD);
SOCAL_SETIMASK(s, SOCAL_IMASK_RSP_QALL | SOCAL_IMASK_SAE);
SOD(("imask %08x %08x\n", s->imask, sbus_readl(s->regs + IMASK)));
}
static void socal_reset(fc_channel *fc)
{
socal_port *port = (socal_port *)fc;
struct socal *s = port->s;
/* FIXME */
socal_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 */
socal_enable(s);
}
static inline void socal_solicited(struct socal *s, unsigned long qno)
{
socal_rsp *hwrsp;
socal_cq *sw_cq;
int token;
int status;
fc_channel *fc;
sw_cq = &s->rsp[qno];
/* Finally an improvement against old SOC :) */
sw_cq->in = sbus_readb(s->regs + RESP + qno);
SOD (("socal_solicited, %d packets arrived\n",
(sw_cq->in - sw_cq->out) & sw_cq->last))
for (;;) {
hwrsp = (socal_rsp *)sw_cq->pool + sw_cq->out;
SOD(("hwrsp %p out %d\n", hwrsp, sw_cq->out))
#if defined(SOCALDEBUG) && 0
{
u32 *u = (u32 *)hwrsp;
SOD(("%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x\n",
u[0],u[1],u[2],u[3],u[4],u[5],u[6],u[7]))
u += 8;
SOD(("%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x\n",
u[0],u[1],u[2],