1
linux/arch/x86/mach-voyager/voyager_cat.c
Ingo Molnar ed5e233284 x86, voyager: fix ioremap_nocache()
James Bottomley reported that the following commit:

| commit 6371b49599
| Author: Ingo Molnar <mingo@elte.hu>
| Date:   Wed Jan 30 13:33:40 2008 +0100
|
|     x86: change ioremap() to default to uncached

broke Voyager.

James says:

" it broke a class of voyager machines: those which
  rely on the quad interrupt controller (QIC).  The precis of why they
  broke is because the QIC does IPIs (or CPIs in its terminology) via
  cache line interference: you interrupt a processor by moving a
  designated memory area to write exclusive in the cache (by simply
  writing to the line) and the CPU acks the interrupt by moving it back to
  read shared (by reading from it).  That area, is, of course, mapped by
  ioremap, so reversing the ioremap semantics and adding the uncached bit
  completely breaks the QIC. "

Sorry about that!

Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
2008-04-30 23:15:34 +02:00

1198 lines
35 KiB
C

/* -*- mode: c; c-basic-offset: 8 -*- */
/* Copyright (C) 1999,2001
*
* Author: J.E.J.Bottomley@HansenPartnership.com
*
* This file contains all the logic for manipulating the CAT bus
* in a level 5 machine.
*
* The CAT bus is a serial configuration and test bus. Its primary
* uses are to probe the initial configuration of the system and to
* diagnose error conditions when a system interrupt occurs. The low
* level interface is fairly primitive, so most of this file consists
* of bit shift manipulations to send and receive packets on the
* serial bus */
#include <linux/types.h>
#include <linux/completion.h>
#include <linux/sched.h>
#include <asm/voyager.h>
#include <asm/vic.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <asm/io.h>
#ifdef VOYAGER_CAT_DEBUG
#define CDEBUG(x) printk x
#else
#define CDEBUG(x)
#endif
/* the CAT command port */
#define CAT_CMD (sspb + 0xe)
/* the CAT data port */
#define CAT_DATA (sspb + 0xd)
/* the internal cat functions */
static void cat_pack(__u8 * msg, __u16 start_bit, __u8 * data, __u16 num_bits);
static void cat_unpack(__u8 * msg, __u16 start_bit, __u8 * data,
__u16 num_bits);
static void cat_build_header(__u8 * header, const __u16 len,
const __u16 smallest_reg_bits,
const __u16 longest_reg_bits);
static int cat_sendinst(voyager_module_t * modp, voyager_asic_t * asicp,
__u8 reg, __u8 op);
static int cat_getdata(voyager_module_t * modp, voyager_asic_t * asicp,
__u8 reg, __u8 * value);
static int cat_shiftout(__u8 * data, __u16 data_bytes, __u16 header_bytes,
__u8 pad_bits);
static int cat_write(voyager_module_t * modp, voyager_asic_t * asicp, __u8 reg,
__u8 value);
static int cat_read(voyager_module_t * modp, voyager_asic_t * asicp, __u8 reg,
__u8 * value);
static int cat_subread(voyager_module_t * modp, voyager_asic_t * asicp,
__u16 offset, __u16 len, void *buf);
static int cat_senddata(voyager_module_t * modp, voyager_asic_t * asicp,
__u8 reg, __u8 value);
static int cat_disconnect(voyager_module_t * modp, voyager_asic_t * asicp);
static int cat_connect(voyager_module_t * modp, voyager_asic_t * asicp);
static inline const char *cat_module_name(int module_id)
{
switch (module_id) {
case 0x10:
return "Processor Slot 0";
case 0x11:
return "Processor Slot 1";
case 0x12:
return "Processor Slot 2";
case 0x13:
return "Processor Slot 4";
case 0x14:
return "Memory Slot 0";
case 0x15:
return "Memory Slot 1";
case 0x18:
return "Primary Microchannel";
case 0x19:
return "Secondary Microchannel";
case 0x1a:
return "Power Supply Interface";
case 0x1c:
return "Processor Slot 5";
case 0x1d:
return "Processor Slot 6";
case 0x1e:
return "Processor Slot 7";
case 0x1f:
return "Processor Slot 8";
default:
return "Unknown Module";
}
}
static int sspb = 0; /* stores the super port location */
int voyager_8slot = 0; /* set to true if a 51xx monster */
voyager_module_t *voyager_cat_list;
/* the I/O port assignments for the VIC and QIC */
static struct resource vic_res = {
.name = "Voyager Interrupt Controller",
.start = 0xFC00,
.end = 0xFC6F
};
static struct resource qic_res = {
.name = "Quad Interrupt Controller",
.start = 0xFC70,
.end = 0xFCFF
};
/* This function is used to pack a data bit stream inside a message.
* It writes num_bits of the data buffer in msg starting at start_bit.
* Note: This function assumes that any unused bit in the data stream
* is set to zero so that the ors will work correctly */
static void
cat_pack(__u8 * msg, const __u16 start_bit, __u8 * data, const __u16 num_bits)
{
/* compute initial shift needed */
const __u16 offset = start_bit % BITS_PER_BYTE;
__u16 len = num_bits / BITS_PER_BYTE;
__u16 byte = start_bit / BITS_PER_BYTE;
__u16 residue = (num_bits % BITS_PER_BYTE) + offset;
int i;
/* adjust if we have more than a byte of residue */
if (residue >= BITS_PER_BYTE) {
residue -= BITS_PER_BYTE;
len++;
}
/* clear out the bits. We assume here that if len==0 then
* residue >= offset. This is always true for the catbus
* operations */
msg[byte] &= 0xff << (BITS_PER_BYTE - offset);
msg[byte++] |= data[0] >> offset;
if (len == 0)
return;
for (i = 1; i < len; i++)
msg[byte++] = (data[i - 1] << (BITS_PER_BYTE - offset))
| (data[i] >> offset);
if (residue != 0) {
__u8 mask = 0xff >> residue;
__u8 last_byte = data[i - 1] << (BITS_PER_BYTE - offset)
| (data[i] >> offset);
last_byte &= ~mask;
msg[byte] &= mask;
msg[byte] |= last_byte;
}
return;
}
/* unpack the data again (same arguments as cat_pack()). data buffer
* must be zero populated.
*
* Function: given a message string move to start_bit and copy num_bits into
* data (starting at bit 0 in data).
*/
static void
cat_unpack(__u8 * msg, const __u16 start_bit, __u8 * data, const __u16 num_bits)
{
/* compute initial shift needed */
const __u16 offset = start_bit % BITS_PER_BYTE;
__u16 len = num_bits / BITS_PER_BYTE;
const __u8 last_bits = num_bits % BITS_PER_BYTE;
__u16 byte = start_bit / BITS_PER_BYTE;
int i;
if (last_bits != 0)
len++;
/* special case: want < 8 bits from msg and we can get it from
* a single byte of the msg */
if (len == 0 && BITS_PER_BYTE - offset >= num_bits) {
data[0] = msg[byte] << offset;
data[0] &= 0xff >> (BITS_PER_BYTE - num_bits);
return;
}
for (i = 0; i < len; i++) {
/* this annoying if has to be done just in case a read of
* msg one beyond the array causes a panic */
if (offset != 0) {
data[i] = msg[byte++] << offset;
data[i] |= msg[byte] >> (BITS_PER_BYTE - offset);
} else {
data[i] = msg[byte++];
}
}
/* do we need to truncate the final byte */
if (last_bits != 0) {
data[i - 1] &= 0xff << (BITS_PER_BYTE - last_bits);
}
return;
}
static void
cat_build_header(__u8 * header, const __u16 len, const __u16 smallest_reg_bits,
const __u16 longest_reg_bits)
{
int i;
__u16 start_bit = (smallest_reg_bits - 1) % BITS_PER_BYTE;
__u8 *last_byte = &header[len - 1];
if (start_bit == 0)
start_bit = 1; /* must have at least one bit in the hdr */
for (i = 0; i < len; i++)
header[i] = 0;
for (i = start_bit; i > 0; i--)
*last_byte = ((*last_byte) << 1) + 1;
}
static int
cat_sendinst(voyager_module_t * modp, voyager_asic_t * asicp, __u8 reg, __u8 op)
{
__u8 parity, inst, inst_buf[4] = { 0 };
__u8 iseq[VOYAGER_MAX_SCAN_PATH], hseq[VOYAGER_MAX_REG_SIZE];
__u16 ibytes, hbytes, padbits;
int i;
/*
* Parity is the parity of the register number + 1 (READ_REGISTER
* and WRITE_REGISTER always add '1' to the number of bits == 1)
*/
parity = (__u8) (1 + (reg & 0x01) +
((__u8) (reg & 0x02) >> 1) +
((__u8) (reg & 0x04) >> 2) +
((__u8) (reg & 0x08) >> 3)) % 2;
inst = ((parity << 7) | (reg << 2) | op);
outb(VOYAGER_CAT_IRCYC, CAT_CMD);
if (!modp->scan_path_connected) {
if (asicp->asic_id != VOYAGER_CAT_ID) {
printk
("**WARNING***: cat_sendinst has disconnected scan path not to CAT asic\n");
return 1;
}
outb(VOYAGER_CAT_HEADER, CAT_DATA);
outb(inst, CAT_DATA);
if (inb(CAT_DATA) != VOYAGER_CAT_HEADER) {
CDEBUG(("VOYAGER CAT: cat_sendinst failed to get CAT_HEADER\n"));
return 1;
}
return 0;
}
ibytes = modp->inst_bits / BITS_PER_BYTE;
if ((padbits = modp->inst_bits % BITS_PER_BYTE) != 0) {
padbits = BITS_PER_BYTE - padbits;
ibytes++;
}
hbytes = modp->largest_reg / BITS_PER_BYTE;
if (modp->largest_reg % BITS_PER_BYTE)
hbytes++;
CDEBUG(("cat_sendinst: ibytes=%d, hbytes=%d\n", ibytes, hbytes));
/* initialise the instruction sequence to 0xff */
for (i = 0; i < ibytes + hbytes; i++)
iseq[i] = 0xff;
cat_build_header(hseq, hbytes, modp->smallest_reg, modp->largest_reg);
cat_pack(iseq, modp->inst_bits, hseq, hbytes * BITS_PER_BYTE);
inst_buf[0] = inst;
inst_buf[1] = 0xFF >> (modp->largest_reg % BITS_PER_BYTE);
cat_pack(iseq, asicp->bit_location, inst_buf, asicp->ireg_length);
#ifdef VOYAGER_CAT_DEBUG
printk("ins = 0x%x, iseq: ", inst);
for (i = 0; i < ibytes + hbytes; i++)
printk("0x%x ", iseq[i]);
printk("\n");
#endif
if (cat_shiftout(iseq, ibytes, hbytes, padbits)) {
CDEBUG(("VOYAGER CAT: cat_sendinst: cat_shiftout failed\n"));
return 1;
}
CDEBUG(("CAT SHIFTOUT DONE\n"));
return 0;
}
static int
cat_getdata(voyager_module_t * modp, voyager_asic_t * asicp, __u8 reg,
__u8 * value)
{
if (!modp->scan_path_connected) {
if (asicp->asic_id != VOYAGER_CAT_ID) {
CDEBUG(("VOYAGER CAT: ERROR: cat_getdata to CAT asic with scan path connected\n"));
return 1;
}
if (reg > VOYAGER_SUBADDRHI)
outb(VOYAGER_CAT_RUN, CAT_CMD);
outb(VOYAGER_CAT_DRCYC, CAT_CMD);
outb(VOYAGER_CAT_HEADER, CAT_DATA);
*value = inb(CAT_DATA);
outb(0xAA, CAT_DATA);
if (inb(CAT_DATA) != VOYAGER_CAT_HEADER) {
CDEBUG(("cat_getdata: failed to get VOYAGER_CAT_HEADER\n"));
return 1;
}
return 0;
} else {
__u16 sbits = modp->num_asics - 1 + asicp->ireg_length;
__u16 sbytes = sbits / BITS_PER_BYTE;
__u16 tbytes;
__u8 string[VOYAGER_MAX_SCAN_PATH],
trailer[VOYAGER_MAX_REG_SIZE];
__u8 padbits;
int i;
outb(VOYAGER_CAT_DRCYC, CAT_CMD);
if ((padbits = sbits % BITS_PER_BYTE) != 0) {
padbits = BITS_PER_BYTE - padbits;
sbytes++;
}
tbytes = asicp->ireg_length / BITS_PER_BYTE;
if (asicp->ireg_length % BITS_PER_BYTE)
tbytes++;
CDEBUG(("cat_getdata: tbytes = %d, sbytes = %d, padbits = %d\n",
tbytes, sbytes, padbits));
cat_build_header(trailer, tbytes, 1, asicp->ireg_length);
for (i = tbytes - 1; i >= 0; i--) {
outb(trailer[i], CAT_DATA);
string[sbytes + i] = inb(CAT_DATA);
}
for (i = sbytes - 1; i >= 0; i--) {
outb(0xaa, CAT_DATA);
string[i] = inb(CAT_DATA);
}
*value = 0;
cat_unpack(string,
padbits + (tbytes * BITS_PER_BYTE) +
asicp->asic_location, value, asicp->ireg_length);
#ifdef VOYAGER_CAT_DEBUG
printk("value=0x%x, string: ", *value);
for (i = 0; i < tbytes + sbytes; i++)
printk("0x%x ", string[i]);
printk("\n");
#endif
/* sanity check the rest of the return */
for (i = 0; i < tbytes; i++) {
__u8 input = 0;
cat_unpack(string, padbits + (i * BITS_PER_BYTE),
&input, BITS_PER_BYTE);
if (trailer[i] != input) {
CDEBUG(("cat_getdata: failed to sanity check rest of ret(%d) 0x%x != 0x%x\n", i, input, trailer[i]));
return 1;
}
}
CDEBUG(("cat_getdata DONE\n"));
return 0;
}
}
static int
cat_shiftout(__u8 * data, __u16 data_bytes, __u16 header_bytes, __u8 pad_bits)
{
int i;
for (i = data_bytes + header_bytes - 1; i >= header_bytes; i--)
outb(data[i], CAT_DATA);
for (i = header_bytes - 1; i >= 0; i--) {
__u8 header = 0;
__u8 input;
outb(data[i], CAT_DATA);
input = inb(CAT_DATA);
CDEBUG(("cat_shiftout: returned 0x%x\n", input));
cat_unpack(data, ((data_bytes + i) * BITS_PER_BYTE) - pad_bits,
&header, BITS_PER_BYTE);
if (input != header) {
CDEBUG(("VOYAGER CAT: cat_shiftout failed to return header 0x%x != 0x%x\n", input, header));
return 1;
}
}
return 0;
}
static int
cat_senddata(voyager_module_t * modp, voyager_asic_t * asicp,
__u8 reg, __u8 value)
{
outb(VOYAGER_CAT_DRCYC, CAT_CMD);
if (!modp->scan_path_connected) {
if (asicp->asic_id != VOYAGER_CAT_ID) {
CDEBUG(("VOYAGER CAT: ERROR: scan path disconnected when asic != CAT\n"));
return 1;
}
outb(VOYAGER_CAT_HEADER, CAT_DATA);
outb(value, CAT_DATA);
if (inb(CAT_DATA) != VOYAGER_CAT_HEADER) {
CDEBUG(("cat_senddata: failed to get correct header response to sent data\n"));
return 1;
}
if (reg > VOYAGER_SUBADDRHI) {
outb(VOYAGER_CAT_RUN, CAT_CMD);
outb(VOYAGER_CAT_END, CAT_CMD);
outb(VOYAGER_CAT_RUN, CAT_CMD);
}
return 0;
} else {
__u16 hbytes = asicp->ireg_length / BITS_PER_BYTE;
__u16 dbytes =
(modp->num_asics - 1 + asicp->ireg_length) / BITS_PER_BYTE;
__u8 padbits, dseq[VOYAGER_MAX_SCAN_PATH],
hseq[VOYAGER_MAX_REG_SIZE];
int i;
if ((padbits = (modp->num_asics - 1
+ asicp->ireg_length) % BITS_PER_BYTE) != 0) {
padbits = BITS_PER_BYTE - padbits;
dbytes++;
}
if (asicp->ireg_length % BITS_PER_BYTE)
hbytes++;
cat_build_header(hseq, hbytes, 1, asicp->ireg_length);
for (i = 0; i < dbytes + hbytes; i++)
dseq[i] = 0xff;
CDEBUG(("cat_senddata: dbytes=%d, hbytes=%d, padbits=%d\n",
dbytes, hbytes, padbits));
cat_pack(dseq, modp->num_asics - 1 + asicp->ireg_length,
hseq, hbytes * BITS_PER_BYTE);
cat_pack(dseq, asicp->asic_location, &value,
asicp->ireg_length);
#ifdef VOYAGER_CAT_DEBUG
printk("dseq ");
for (i = 0; i < hbytes + dbytes; i++) {
printk("0x%x ", dseq[i]);
}
printk("\n");
#endif
return cat_shiftout(dseq, dbytes, hbytes, padbits);
}
}
static int
cat_write(voyager_module_t * modp, voyager_asic_t * asicp, __u8 reg, __u8 value)
{
if (cat_sendinst(modp, asicp, reg, VOYAGER_WRITE_CONFIG))
return 1;
return cat_senddata(modp, asicp, reg, value);
}
static int
cat_read(voyager_module_t * modp, voyager_asic_t * asicp, __u8 reg,
__u8 * value)
{
if (cat_sendinst(modp, asicp, reg, VOYAGER_READ_CONFIG))
return 1;
return cat_getdata(modp, asicp, reg, value);
}
static int
cat_subaddrsetup(voyager_module_t * modp, voyager_asic_t * asicp, __u16 offset,
__u16 len)
{
__u8 val;
if (len > 1) {
/* set auto increment */
__u8 newval;
if (cat_read(modp, asicp, VOYAGER_AUTO_INC_REG, &val)) {
CDEBUG(("cat_subaddrsetup: read of VOYAGER_AUTO_INC_REG failed\n"));
return 1;
}
CDEBUG(("cat_subaddrsetup: VOYAGER_AUTO_INC_REG = 0x%x\n",
val));
newval = val | VOYAGER_AUTO_INC;
if (newval != val) {
if (cat_write(modp, asicp, VOYAGER_AUTO_INC_REG, val)) {
CDEBUG(("cat_subaddrsetup: write to VOYAGER_AUTO_INC_REG failed\n"));
return 1;
}
}
}
if (cat_write(modp, asicp, VOYAGER_SUBADDRLO, (__u8) (offset & 0xff))) {
CDEBUG(("cat_subaddrsetup: write to SUBADDRLO failed\n"));
return 1;
}
if (asicp->subaddr > VOYAGER_SUBADDR_LO) {
if (cat_write
(modp, asicp, VOYAGER_SUBADDRHI, (__u8) (offset >> 8))) {
CDEBUG(("cat_subaddrsetup: write to SUBADDRHI failed\n"));
return 1;
}
cat_read(modp, asicp, VOYAGER_SUBADDRHI, &val);
CDEBUG(("cat_subaddrsetup: offset = %d, hi = %d\n", offset,
val));
}
cat_read(modp, asicp, VOYAGER_SUBADDRLO, &val);
CDEBUG(("cat_subaddrsetup: offset = %d, lo = %d\n", offset, val));
return 0;
}
static int
cat_subwrite(voyager_module_t * modp, voyager_asic_t * asicp, __u16 offset,
__u16 len, void *buf)
{
int i, retval;
/* FIXME: need special actions for VOYAGER_CAT_ID here */
if (asicp->asic_id == VOYAGER_CAT_ID) {
CDEBUG(("cat_subwrite: ATTEMPT TO WRITE TO CAT ASIC\n"));
/* FIXME -- This is supposed to be handled better
* There is a problem writing to the cat asic in the
* PSI. The 30us delay seems to work, though */
udelay(30);
}
if ((retval = cat_subaddrsetup(modp, asicp, offset, len)) != 0) {
printk("cat_subwrite: cat_subaddrsetup FAILED\n");
return retval;
}
if (cat_sendinst
(modp, asicp, VOYAGER_SUBADDRDATA, VOYAGER_WRITE_CONFIG)) {
printk("cat_subwrite: cat_sendinst FAILED\n");
return 1;
}
for (i = 0; i < len; i++) {
if (cat_senddata(modp, asicp, 0xFF, ((__u8 *) buf)[i])) {
printk
("cat_subwrite: cat_sendata element at %d FAILED\n",
i);
return 1;
}
}
return 0;
}
static int
cat_subread(voyager_module_t * modp, voyager_asic_t * asicp, __u16 offset,
__u16 len, void *buf)
{
int i, retval;
if ((retval = cat_subaddrsetup(modp, asicp, offset, len)) != 0) {
CDEBUG(("cat_subread: cat_subaddrsetup FAILED\n"));
return retval;
}
if (cat_sendinst(modp, asicp, VOYAGER_SUBADDRDATA, VOYAGER_READ_CONFIG)) {
CDEBUG(("cat_subread: cat_sendinst failed\n"));
return 1;
}
for (i = 0; i < len; i++) {
if (cat_getdata(modp, asicp, 0xFF, &((__u8 *) buf)[i])) {
CDEBUG(("cat_subread: cat_getdata element %d failed\n",
i));
return 1;
}
}
return 0;
}
/* buffer for storing EPROM data read in during initialisation */
static __initdata __u8 eprom_buf[0xFFFF];
static voyager_module_t *voyager_initial_module;
/* Initialise the cat bus components. We assume this is called by the
* boot cpu *after* all memory initialisation has been done (so we can
* use kmalloc) but before smp initialisation, so we can probe the SMP
* configuration and pick up necessary information. */
void __init voyager_cat_init(void)
{
voyager_module_t **modpp = &voyager_initial_module;
voyager_asic_t **asicpp;
voyager_asic_t *qabc_asic = NULL;
int i, j;
unsigned long qic_addr = 0;
__u8 qabc_data[0x20];
__u8 num_submodules, val;
voyager_eprom_hdr_t *eprom_hdr = (voyager_eprom_hdr_t *) & eprom_buf[0];
__u8 cmos[4];
unsigned long addr;
/* initiallise the SUS mailbox */
for (i = 0; i < sizeof(cmos); i++)
cmos[i] = voyager_extended_cmos_read(VOYAGER_DUMP_LOCATION + i);
addr = *(unsigned long *)cmos;
if ((addr & 0xff000000) != 0xff000000) {
printk(KERN_ERR
"Voyager failed to get SUS mailbox (addr = 0x%lx\n",
addr);
} else {
static struct resource res;
res.name = "voyager SUS";
res.start = addr;
res.end = addr + 0x3ff;
request_resource(&iomem_resource, &res);
voyager_SUS = (struct voyager_SUS *)
ioremap(addr, 0x400);
printk(KERN_NOTICE "Voyager SUS mailbox version 0x%x\n",
voyager_SUS->SUS_version);
voyager_SUS->kernel_version = VOYAGER_MAILBOX_VERSION;
voyager_SUS->kernel_flags = VOYAGER_OS_HAS_SYSINT;
}
/* clear the processor counts */
voyager_extended_vic_processors = 0;
voyager_quad_processors = 0;
printk("VOYAGER: beginning CAT bus probe\n");
/* set up the SuperSet Port Block which tells us where the
* CAT communication port is */
sspb = inb(VOYAGER_SSPB_RELOCATION_PORT) * 0x100;
VDEBUG(("VOYAGER DEBUG: sspb = 0x%x\n", sspb));
/* now find out if were 8 slot or normal */
if ((inb(VIC_PROC_WHO_AM_I) & EIGHT_SLOT_IDENTIFIER)
== EIGHT_SLOT_IDENTIFIER) {
voyager_8slot = 1;
printk(KERN_NOTICE
"Voyager: Eight slot 51xx configuration detected\n");
}
for (i = VOYAGER_MIN_MODULE; i <= VOYAGER_MAX_MODULE; i++) {
__u8 input;
int asic;
__u16 eprom_size;
__u16 sp_offset;
outb(VOYAGER_CAT_DESELECT, VOYAGER_CAT_CONFIG_PORT);
outb(i, VOYAGER_CAT_CONFIG_PORT);
/* check the presence of the module */
outb(VOYAGER_CAT_RUN, CAT_CMD);
outb(VOYAGER_CAT_IRCYC, CAT_CMD);
outb(VOYAGER_CAT_HEADER, CAT_DATA);
/* stream series of alternating 1's and 0's to stimulate
* response */
outb(0xAA, CAT_DATA);
input = inb(CAT_DATA);
outb(VOYAGER_CAT_END, CAT_CMD);
if (input != VOYAGER_CAT_HEADER) {
continue;
}
CDEBUG(("VOYAGER DEBUG: found module id 0x%x, %s\n", i,
cat_module_name(i)));
*modpp = kmalloc(sizeof(voyager_module_t), GFP_KERNEL); /*&voyager_module_storage[cat_count++]; */
if (*modpp == NULL) {
printk("**WARNING** kmalloc failure in cat_init\n");
continue;
}
memset(*modpp, 0, sizeof(voyager_module_t));
/* need temporary asic for cat_subread. It will be
* filled in correctly later */
(*modpp)->asic = kmalloc(sizeof(voyager_asic_t), GFP_KERNEL); /*&voyager_asic_storage[asic_count]; */
if ((*modpp)->asic == NULL) {
printk("**WARNING** kmalloc failure in cat_init\n");
continue;
}
memset((*modpp)->asic, 0, sizeof(voyager_asic_t));
(*modpp)->asic->asic_id = VOYAGER_CAT_ID;
(*modpp)->asic->subaddr = VOYAGER_SUBADDR_HI;
(*modpp)->module_addr = i;
(*modpp)->scan_path_connected = 0;
if (i == VOYAGER_PSI) {
/* Exception leg for modules with no EEPROM */
printk("Module \"%s\"\n", cat_module_name(i));
continue;
}
CDEBUG(("cat_init: Reading eeprom for module 0x%x at offset %d\n", i, VOYAGER_XSUM_END_OFFSET));
outb(VOYAGER_CAT_RUN, CAT_CMD);
cat_disconnect(*modpp, (*modpp)->asic);
if (cat_subread(*modpp, (*modpp)->asic,
VOYAGER_XSUM_END_OFFSET, sizeof(eprom_size),
&eprom_size)) {
printk
("**WARNING**: Voyager couldn't read EPROM size for module 0x%x\n",
i);
outb(VOYAGER_CAT_END, CAT_CMD);
continue;
}
if (eprom_size > sizeof(eprom_buf)) {
printk
("**WARNING**: Voyager insufficient size to read EPROM data, module 0x%x. Need %d\n",
i, eprom_size);
outb(VOYAGER_CAT_END, CAT_CMD);
continue;
}
outb(VOYAGER_CAT_END, CAT_CMD);
outb(VOYAGER_CAT_RUN, CAT_CMD);
CDEBUG(("cat_init: module 0x%x, eeprom_size %d\n", i,
eprom_size));
if (cat_subread
(*modpp, (*modpp)->asic, 0, eprom_size, eprom_buf)) {
outb(VOYAGER_CAT_END, CAT_CMD);
continue;
}
outb(VOYAGER_CAT_END, CAT_CMD);
printk("Module \"%s\", version 0x%x, tracer 0x%x, asics %d\n",
cat_module_name(i), eprom_hdr->version_id,
*((__u32 *) eprom_hdr->tracer), eprom_hdr->num_asics);
(*modpp)->ee_size = eprom_hdr->ee_size;
(*modpp)->num_asics = eprom_hdr->num_asics;
asicpp = &((*modpp)->asic);
sp_offset = eprom_hdr->scan_path_offset;
/* All we really care about are the Quad cards. We
* identify them because they are in a processor slot
* and have only four asics */
if ((i < 0x10 || (i >= 0x14 && i < 0x1c) || i > 0x1f)) {
modpp = &((*modpp)->next);
continue;
}
/* Now we know it's in a processor slot, does it have
* a quad baseboard submodule */
outb(VOYAGER_CAT_RUN, CAT_CMD);
cat_read(*modpp, (*modpp)->asic, VOYAGER_SUBMODPRESENT,
&num_submodules);
/* lowest two bits, active low */
num_submodules = ~(0xfc | num_submodules);
CDEBUG(("VOYAGER CAT: %d submodules present\n",
num_submodules));
if (num_submodules == 0) {
/* fill in the dyadic extended processors */
__u8 cpu = i & 0x07;
printk("Module \"%s\": Dyadic Processor Card\n",
cat_module_name(i));
voyager_extended_vic_processors |= (1 << cpu);
cpu += 4;
voyager_extended_vic_processors |= (1 << cpu);
outb(VOYAGER_CAT_END, CAT_CMD);
continue;
}
/* now we want to read the asics on the first submodule,
* which should be the quad base board */
cat_read(*modpp, (*modpp)->asic, VOYAGER_SUBMODSELECT, &val);
CDEBUG(("cat_init: SUBMODSELECT value = 0x%x\n", val));
val = (val & 0x7c) | VOYAGER_QUAD_BASEBOARD;
cat_write(*modpp, (*modpp)->asic, VOYAGER_SUBMODSELECT, val);
outb(VOYAGER_CAT_END, CAT_CMD);
CDEBUG(("cat_init: Reading eeprom for module 0x%x at offset %d\n", i, VOYAGER_XSUM_END_OFFSET));
outb(VOYAGER_CAT_RUN, CAT_CMD);
cat_disconnect(*modpp, (*modpp)->asic);
if (cat_subread(*modpp, (*modpp)->asic,
VOYAGER_XSUM_END_OFFSET, sizeof(eprom_size),
&eprom_size)) {
printk
("**WARNING**: Voyager couldn't read EPROM size for module 0x%x\n",
i);
outb(VOYAGER_CAT_END, CAT_CMD);
continue;
}
if (eprom_size > sizeof(eprom_buf)) {
printk
("**WARNING**: Voyager insufficient size to read EPROM data, module 0x%x. Need %d\n",
i, eprom_size);
outb(VOYAGER_CAT_END, CAT_CMD);
continue;
}
outb(VOYAGER_CAT_END, CAT_CMD);
outb(VOYAGER_CAT_RUN, CAT_CMD);
CDEBUG(("cat_init: module 0x%x, eeprom_size %d\n", i,
eprom_size));
if (cat_subread
(*modpp, (*modpp)->asic, 0, eprom_size, eprom_buf)) {
outb(VOYAGER_CAT_END, CAT_CMD);
continue;
}
outb(VOYAGER_CAT_END, CAT_CMD);
/* Now do everything for the QBB submodule 1 */
(*modpp)->ee_size = eprom_hdr->ee_size;
(*modpp)->num_asics = eprom_hdr->num_asics;
asicpp = &((*modpp)->asic);
sp_offset = eprom_hdr->scan_path_offset;
/* get rid of the dummy CAT asic and read the real one */
kfree((*modpp)->asic);
for (asic = 0; asic < (*modpp)->num_asics; asic++) {
int j;
voyager_asic_t *asicp = *asicpp = kzalloc(sizeof(voyager_asic_t), GFP_KERNEL); /*&voyager_asic_storage[asic_count++]; */
voyager_sp_table_t *sp_table;
voyager_at_t *asic_table;
voyager_jtt_t *jtag_table;
if (asicp == NULL) {
printk
("**WARNING** kmalloc failure in cat_init\n");
continue;
}
asicpp = &(asicp->next);
asicp->asic_location = asic;
sp_table =
(voyager_sp_table_t *) (eprom_buf + sp_offset);
asicp->asic_id = sp_table->asic_id;
asic_table =
(voyager_at_t *) (eprom_buf +
sp_table->asic_data_offset);
for (j = 0; j < 4; j++)
asicp->jtag_id[j] = asic_table->jtag_id[j];
jtag_table =
(voyager_jtt_t *) (eprom_buf +
asic_table->jtag_offset);
asicp->ireg_length = jtag_table->ireg_len;
asicp->bit_location = (*modpp)->inst_bits;
(*modpp)->inst_bits += asicp->ireg_length;
if (asicp->ireg_length > (*modpp)->largest_reg)
(*modpp)->largest_reg = asicp->ireg_length;
if (asicp->ireg_length < (*modpp)->smallest_reg ||
(*modpp)->smallest_reg == 0)
(*modpp)->smallest_reg = asicp->ireg_length;
CDEBUG(("asic 0x%x, ireg_length=%d, bit_location=%d\n",
asicp->asic_id, asicp->ireg_length,
asicp->bit_location));
if (asicp->asic_id == VOYAGER_QUAD_QABC) {
CDEBUG(("VOYAGER CAT: QABC ASIC found\n"));
qabc_asic = asicp;
}
sp_offset += sizeof(voyager_sp_table_t);
}
CDEBUG(("Module inst_bits = %d, largest_reg = %d, smallest_reg=%d\n", (*modpp)->inst_bits, (*modpp)->largest_reg, (*modpp)->smallest_reg));
/* OK, now we have the QUAD ASICs set up, use them.
* we need to:
*
* 1. Find the Memory area for the Quad CPIs.
* 2. Find the Extended VIC processor
* 3. Configure a second extended VIC processor (This
* cannot be done for the 51xx.
* */
outb(VOYAGER_CAT_RUN, CAT_CMD);
cat_connect(*modpp, (*modpp)->asic);
CDEBUG(("CAT CONNECTED!!\n"));
cat_subread(*modpp, qabc_asic, 0, sizeof(qabc_data), qabc_data);
qic_addr = qabc_data[5] << 8;
qic_addr = (qic_addr | qabc_data[6]) << 8;
qic_addr = (qic_addr | qabc_data[7]) << 8;
printk
("Module \"%s\": Quad Processor Card; CPI 0x%lx, SET=0x%x\n",
cat_module_name(i), qic_addr, qabc_data[8]);
#if 0 /* plumbing fails---FIXME */
if ((qabc_data[8] & 0xf0) == 0) {
/* FIXME: 32 way 8 CPU slot monster cannot be
* plumbed this way---need to check for it */
printk("Plumbing second Extended Quad Processor\n");
/* second VIC line hardwired to Quad CPU 1 */
qabc_data[8] |= 0x20;
cat_subwrite(*modpp, qabc_asic, 8, 1, &qabc_data[8]);
#ifdef VOYAGER_CAT_DEBUG
/* verify plumbing */
cat_subread(*modpp, qabc_asic, 8, 1, &qabc_data[8]);
if ((qabc_data[8] & 0xf0) == 0) {
CDEBUG(("PLUMBING FAILED: 0x%x\n",
qabc_data[8]));
}
#endif
}
#endif
{
struct resource *res =
kzalloc(sizeof(struct resource), GFP_KERNEL);
res->name = kmalloc(128, GFP_KERNEL);
sprintf((char *)res->name, "Voyager %s Quad CPI",
cat_module_name(i));
res->start = qic_addr;
res->end = qic_addr + 0x3ff;
request_resource(&iomem_resource, res);
}
qic_addr = (unsigned long)ioremap_cache(qic_addr, 0x400);
for (j = 0; j < 4; j++) {
__u8 cpu;
if (voyager_8slot) {
/* 8 slot has a different mapping,
* each slot has only one vic line, so
* 1 cpu in each slot must be < 8 */
cpu = (i & 0x07) + j * 8;
} else {
cpu = (i & 0x03) + j * 4;
}
if ((qabc_data[8] & (1 << j))) {
voyager_extended_vic_processors |= (1 << cpu);
}
if (qabc_data[8] & (1 << (j + 4))) {
/* Second SET register plumbed: Quad
* card has two VIC connected CPUs.
* Secondary cannot be booted as a VIC
* CPU */
voyager_extended_vic_processors |= (1 << cpu);
voyager_allowed_boot_processors &=
(~(1 << cpu));
}
voyager_quad_processors |= (1 << cpu);
voyager_quad_cpi_addr[cpu] = (struct voyager_qic_cpi *)
(qic_addr + (j << 8));
CDEBUG(("CPU%d: CPI address 0x%lx\n", cpu,
(unsigned long)voyager_quad_cpi_addr[cpu]));
}
outb(VOYAGER_CAT_END, CAT_CMD);
*asicpp = NULL;
modpp = &((*modpp)->next);
}
*modpp = NULL;
printk
("CAT Bus Initialisation finished: extended procs 0x%x, quad procs 0x%x, allowed vic boot = 0x%x\n",
voyager_extended_vic_processors, voyager_quad_processors,
voyager_allowed_boot_processors);
request_resource(&ioport_resource, &vic_res);
if (voyager_quad_processors)
request_resource(&ioport_resource, &qic_res);
/* set up the front power switch */
}
int voyager_cat_readb(__u8 module, __u8 asic, int reg)
{
return 0;
}
static int cat_disconnect(voyager_module_t * modp, voyager_asic_t * asicp)
{
__u8 val;
int err = 0;
if (!modp->scan_path_connected)
return 0;
if (asicp->asic_id != VOYAGER_CAT_ID) {
CDEBUG(("cat_disconnect: ASIC is not CAT\n"));
return 1;
}
err = cat_read(modp, asicp, VOYAGER_SCANPATH, &val);
if (err) {
CDEBUG(("cat_disconnect: failed to read SCANPATH\n"));
return err;
}
val &= VOYAGER_DISCONNECT_ASIC;
err = cat_write(modp, asicp, VOYAGER_SCANPATH, val);
if (err) {
CDEBUG(("cat_disconnect: failed to write SCANPATH\n"));
return err;
}
outb(VOYAGER_CAT_END, CAT_CMD);
outb(VOYAGER_CAT_RUN, CAT_CMD);
modp->scan_path_connected = 0;
return 0;
}
static int cat_connect(voyager_module_t * modp, voyager_asic_t * asicp)
{
__u8 val;
int err = 0;
if (modp->scan_path_connected)
return 0;
if (asicp->asic_id != VOYAGER_CAT_ID) {
CDEBUG(("cat_connect: ASIC is not CAT\n"));
return 1;
}
err = cat_read(modp, asicp, VOYAGER_SCANPATH, &val);
if (err) {
CDEBUG(("cat_connect: failed to read SCANPATH\n"));
return err;
}
val |= VOYAGER_CONNECT_ASIC;
err = cat_write(modp, asicp, VOYAGER_SCANPATH, val);
if (err) {
CDEBUG(("cat_connect: failed to write SCANPATH\n"));
return err;
}
outb(VOYAGER_CAT_END, CAT_CMD);
outb(VOYAGER_CAT_RUN, CAT_CMD);
modp->scan_path_connected = 1;
return 0;
}
void voyager_cat_power_off(void)
{
/* Power the machine off by writing to the PSI over the CAT
* bus */
__u8 data;
voyager_module_t psi = { 0 };
voyager_asic_t psi_asic = { 0 };
psi.asic = &psi_asic;
psi.asic->asic_id = VOYAGER_CAT_ID;
psi.asic->subaddr = VOYAGER_SUBADDR_HI;
psi.module_addr = VOYAGER_PSI;
psi.scan_path_connected = 0;
outb(VOYAGER_CAT_END, CAT_CMD);
/* Connect the PSI to the CAT Bus */
outb(VOYAGER_CAT_DESELECT, VOYAGER_CAT_CONFIG_PORT);
outb(VOYAGER_PSI, VOYAGER_CAT_CONFIG_PORT);
outb(VOYAGER_CAT_RUN, CAT_CMD);
cat_disconnect(&psi, &psi_asic);
/* Read the status */
cat_subread(&psi, &psi_asic, VOYAGER_PSI_GENERAL_REG, 1, &data);
outb(VOYAGER_CAT_END, CAT_CMD);
CDEBUG(("PSI STATUS 0x%x\n", data));
/* These two writes are power off prep and perform */
data = PSI_CLEAR;
outb(VOYAGER_CAT_RUN, CAT_CMD);
cat_subwrite(&psi, &psi_asic, VOYAGER_PSI_GENERAL_REG, 1, &data);
outb(VOYAGER_CAT_END, CAT_CMD);
data = PSI_POWER_DOWN;
outb(VOYAGER_CAT_RUN, CAT_CMD);
cat_subwrite(&psi, &psi_asic, VOYAGER_PSI_GENERAL_REG, 1, &data);
outb(VOYAGER_CAT_END, CAT_CMD);
}
struct voyager_status voyager_status = { 0 };
void voyager_cat_psi(__u8 cmd, __u16 reg, __u8 * data)
{
voyager_module_t psi = { 0 };
voyager_asic_t psi_asic = { 0 };
psi.asic = &psi_asic;
psi.asic->asic_id = VOYAGER_CAT_ID;
psi.asic->subaddr = VOYAGER_SUBADDR_HI;
psi.module_addr = VOYAGER_PSI;
psi.scan_path_connected = 0;
outb(VOYAGER_CAT_END, CAT_CMD);
/* Connect the PSI to the CAT Bus */
outb(VOYAGER_CAT_DESELECT, VOYAGER_CAT_CONFIG_PORT);
outb(VOYAGER_PSI, VOYAGER_CAT_CONFIG_PORT);
outb(VOYAGER_CAT_RUN, CAT_CMD);
cat_disconnect(&psi, &psi_asic);
switch (cmd) {
case VOYAGER_PSI_READ:
cat_read(&psi, &psi_asic, reg, data);
break;
case VOYAGER_PSI_WRITE:
cat_write(&psi, &psi_asic, reg, *data);
break;
case VOYAGER_PSI_SUBREAD:
cat_subread(&psi, &psi_asic, reg, 1, data);
break;
case VOYAGER_PSI_SUBWRITE:
cat_subwrite(&psi, &psi_asic, reg, 1, data);
break;
default:
printk(KERN_ERR "Voyager PSI, unrecognised command %d\n", cmd);
break;
}
outb(VOYAGER_CAT_END, CAT_CMD);
}
void voyager_cat_do_common_interrupt(void)
{
/* This is caused either by a memory parity error or something
* in the PSI */
__u8 data;
voyager_module_t psi = { 0 };
voyager_asic_t psi_asic = { 0 };
struct voyager_psi psi_reg;
int i;
re_read:
psi.asic = &psi_asic;
psi.asic->asic_id = VOYAGER_CAT_ID;
psi.asic->subaddr = VOYAGER_SUBADDR_HI;
psi.module_addr = VOYAGER_PSI;
psi.scan_path_connected = 0;
outb(VOYAGER_CAT_END, CAT_CMD);
/* Connect the PSI to the CAT Bus */
outb(VOYAGER_CAT_DESELECT, VOYAGER_CAT_CONFIG_PORT);
outb(VOYAGER_PSI, VOYAGER_CAT_CONFIG_PORT);
outb(VOYAGER_CAT_RUN, CAT_CMD);
cat_disconnect(&psi, &psi_asic);
/* Read the status. NOTE: Need to read *all* the PSI regs here
* otherwise the cmn int will be reasserted */
for (i = 0; i < sizeof(psi_reg.regs); i++) {
cat_read(&psi, &psi_asic, i, &((__u8 *) & psi_reg.regs)[i]);
}
outb(VOYAGER_CAT_END, CAT_CMD);
if ((psi_reg.regs.checkbit & 0x02) == 0) {
psi_reg.regs.checkbit |= 0x02;
cat_write(&psi, &psi_asic, 5, psi_reg.regs.checkbit);
printk("VOYAGER RE-READ PSI\n");
goto re_read;
}
outb(VOYAGER_CAT_RUN, CAT_CMD);
for (i = 0; i < sizeof(psi_reg.subregs); i++) {
/* This looks strange, but the PSI doesn't do auto increment
* correctly */
cat_subread(&psi, &psi_asic, VOYAGER_PSI_SUPPLY_REG + i,
1, &((__u8 *) & psi_reg.subregs)[i]);
}
outb(VOYAGER_CAT_END, CAT_CMD);
#ifdef VOYAGER_CAT_DEBUG
printk("VOYAGER PSI: ");
for (i = 0; i < sizeof(psi_reg.regs); i++)
printk("%02x ", ((__u8 *) & psi_reg.regs)[i]);
printk("\n ");
for (i = 0; i < sizeof(psi_reg.subregs); i++)
printk("%02x ", ((__u8 *) & psi_reg.subregs)[i]);
printk("\n");
#endif
if (psi_reg.regs.intstatus & PSI_MON) {
/* switch off or power fail */
if (psi_reg.subregs.supply & PSI_SWITCH_OFF) {
if (voyager_status.switch_off) {
printk(KERN_ERR
"Voyager front panel switch turned off again---Immediate power off!\n");
voyager_cat_power_off();
/* not reached */
} else {
printk(KERN_ERR
"Voyager front panel switch turned off\n");
voyager_status.switch_off = 1;
voyager_status.request_from_kernel = 1;
wake_up_process(voyager_thread);
}
/* Tell the hardware we're taking care of the
* shutdown, otherwise it will power the box off
* within 3 seconds of the switch being pressed and,
* which is much more important to us, continue to
* assert the common interrupt */
data = PSI_CLR_SWITCH_OFF;
outb(VOYAGER_CAT_RUN, CAT_CMD);
cat_subwrite(&psi, &psi_asic, VOYAGER_PSI_SUPPLY_REG,
1, &data);
outb(VOYAGER_CAT_END, CAT_CMD);
} else {
VDEBUG(("Voyager ac fail reg 0x%x\n",
psi_reg.subregs.ACfail));
if ((psi_reg.subregs.ACfail & AC_FAIL_STAT_CHANGE) == 0) {
/* No further update */
return;
}
#if 0
/* Don't bother trying to find out who failed.
* FIXME: This probably makes the code incorrect on
* anything other than a 345x */
for (i = 0; i < 5; i++) {
if (psi_reg.subregs.ACfail & (1 << i)) {
break;
}
}
printk(KERN_NOTICE "AC FAIL IN SUPPLY %d\n", i);
#endif
/* DON'T do this: it shuts down the AC PSI
outb(VOYAGER_CAT_RUN, CAT_CMD);
data = PSI_MASK_MASK | i;
cat_subwrite(&psi, &psi_asic, VOYAGER_PSI_MASK,
1, &data);
outb(VOYAGER_CAT_END, CAT_CMD);
*/
printk(KERN_ERR "Voyager AC power failure\n");
outb(VOYAGER_CAT_RUN, CAT_CMD);
data = PSI_COLD_START;
cat_subwrite(&psi, &psi_asic, VOYAGER_PSI_GENERAL_REG,
1, &data);
outb(VOYAGER_CAT_END, CAT_CMD);
voyager_status.power_fail = 1;
voyager_status.request_from_kernel = 1;
wake_up_process(voyager_thread);
}
} else if (psi_reg.regs.intstatus & PSI_FAULT) {
/* Major fault! */
printk(KERN_ERR
"Voyager PSI Detected major fault, immediate power off!\n");
voyager_cat_power_off();
/* not reached */
} else if (psi_reg.regs.intstatus & (PSI_DC_FAIL | PSI_ALARM
| PSI_CURRENT | PSI_DVM
| PSI_PSCFAULT | PSI_STAT_CHG)) {
/* other psi fault */
printk(KERN_WARNING "Voyager PSI status 0x%x\n", data);
/* clear the PSI fault */
outb(VOYAGER_CAT_RUN, CAT_CMD);
cat_write(&psi, &psi_asic, VOYAGER_PSI_STATUS_REG, 0);
outb(VOYAGER_CAT_END, CAT_CMD);
}
}