📄 voyager_cat.c
字号:
/* -*- mode: c; c-basic-offset: 8 -*- *//* Copyright (C) 1999,2001 * * Author: J.E.J.Bottomley@HansenPartnership.com * * linux/arch/i386/kernel/voyager_cat.c * * 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 voidcat_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 voidcat_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 voidcat_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 intcat_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 intcat_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 intcat_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 intcat_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"));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -