Skip to content

Instantly share code, notes, and snippets.

@leftbrainstrain
Last active May 19, 2026 00:34
Show Gist options
  • Select an option

  • Save leftbrainstrain/fcd1e6ebae85ae10f69b7e2eaacd3682 to your computer and use it in GitHub Desktop.

Select an option

Save leftbrainstrain/fcd1e6ebae85ae10f69b7e2eaacd3682 to your computer and use it in GitHub Desktop.
PS2-TOOL MRP Linux 2.2 dsnet driver untested RE prototype
#define BUILD_VER "1.16.19"
#define BUILD_REV "$Revision: 1.4 $"
#define BUILD_DATE __DATE__
#define BUILD_TIME __TIME__
//mrp_revision occurs before kernel_version in original
//0x000
static char *mrp_revision = BUILD_REV;
#include "mrp.h"
//FIXME these macros not working for me yet
//MODULE_LICENSE("AFL");
//MODULE_AUTHOR("LBS");
//MODULE_DESCRIPTION("MRP");
/*********************************************/
//source:
// File: .../local/sce/driver/dsnet/2.2.14/mrp.o
// MD5: e2e08efd4ac02283fbedacd8056759dc
//0x000
static int mrp_debug = 1;
//0x000
static int unexpected_interrupts = 0;
// interrupt names (guesses)
//0x000
static char *intr_names[] = {
"SIO" // com port receive
,"SI1" // com port acknowledge send
,"SI2" // com port ready for initialization
,"RST" // RESET
,"RXC" // RX Completion/Counter/Control
,"TXC" // TX Completion/Counter/Control
,"REF" // RX Empty Flag
,"RAE" // RX Almost Empty
,"RHF" // RX Half Full
,"RAF" // RX Almost Full
,"RFF" // RX Full Flag
,"TEF" // TX Empty Flag
,"TAE" // TX Almost Empty
,"THF" // TX Half Full
,"TAF" // TX Almost Full
,"TFF" // TX Full Flag
};
//0x000
static struct proc_dir_entry mrp_proc_de = {
namelen: 3,
name: "mrp",
get_info: &mrp_get_info,
mode: S_IFREG|S_IRUGO, // S_IFREG = regular file, S_TRUGO = 0444 perm
nlink: 1
};
//0x000
struct file_operations mrp_fops = {
read: mrp_read,
write: mrp_write,
poll: mrp_poll,
ioctl: mrp_ioctl,
open: mrp_open,
release: mrp_release
};
//0x000
int mrp_major;
//0x000
static struct mrp_unit mrp_units[MRP_MAX_UNITS];
/*
* dump FPGA register values
*
* invoked by mrp_interrupt
*/
void mrp_dump_regs(struct mrp_regs *regs)
{
//0x00010005
if (mrp_debug > 2) {
//0x00010012
printk("* BID=%04x RST=%04x CPS=%04x CPR=%04x\n"
,((uint32_t)(regs->bid) & 0xFFFF)
,((uint32_t)(regs->rst) & 0xFFFF)
,((uint32_t)(regs->cps) & 0xFFFF)
,((uint32_t)(regs->cpr) & 0xFFFF));
printk("* FST=%04x TXC=%04x RXC=%04x F1C=%04x\n"
,((uint32_t)(regs->fst) & 0xFFFF)
,((uint32_t)(regs->txc) & 0xFFFF)
,((uint32_t)(regs->rxc) & 0xFFFF)
,((uint32_t)(regs->f1c) & 0xFFFF));
printk("* IST=%04x ISP=%04x IER=%04x CSI=%04x\n"
,((uint32_t)(regs->ist) & 0xFFFF)
,((uint32_t)(regs->isp) & 0xFFFF)
,((uint32_t)(regs->ier) & 0xFFFF)
,((uint32_t)(regs->csi) & 0xFFFF));
printk("* FSI=%04x AEO=%04x AFO=%04x\n"
,((uint32_t)(regs->fsi) & 0xFFFF)
,((uint32_t)(regs->aeo) & 0xFFFF)
,((uint32_t)(regs->afo) & 0xFFFF));
}
return;
}
/*
* transfer data from send_buf into FIFO's available space
*
* invoked by mrp_interrupt
*/
int mrp_send(mrp_unit *unit)
{
uint8_t device_status;
uint16_t fst, ier;
uint16_t i;
uint32_t fifo_free_sz_lw;
uint32_t *send_ptr;
uint32_t *fifo_ptr;
int send_buf_len_lw;
device_status = unit->device_status;
//0x00010144
fst = readw(&unit->las2b->fst);
//0x00010156
if (mrp_debug > 1) {
//0x0001015f
printk("mrp_send: slen=%d fst=0x%x\n",unit->send_len,(uint32_t)fst);
}
//0x000
if (fst & MRP_FIFO_TX_EF) {
//0x000
fifo_free_sz_lw = MRP_FIFO_MAX;
}
else if (fst & MRP_FIFO_TX_AE) {
//0x000
fifo_free_sz_lw = MRP_FIFO_LWM_FREE;
if (device_status & MRP_DEV_B3) {
fifo_free_sz_lw = MRP_FIFO_LWM_FREE_B3;
}
}
else if (!(fst & MRP_FIFO_TX_HF)) {
//0x000
fifo_free_sz_lw = MRP_FIFO_HALF;
}
else if (!(fst & MRP_FIFO_TX_AF)) { //0x000
//0x000
fifo_free_sz_lw = MRP_FIFO_HWM_FREE;
if (device_status & MRP_DEV_B3) {
fifo_free_sz_lw = MRP_FIFO_HWM_FREE_B3;
}
}
else if (!(fst & MRP_FIFO_TX_FF)) {
//0x000
fifo_free_sz_lw = 1;
}
else {
//0x000
fifo_free_sz_lw = 0;
}
//0x000
send_ptr = unit->send_ptr;
//0x000
send_buf_len_lw = (((uint32_t)unit->send_buf + unit->send_len - (uint32_t)send_ptr) + ( sizeof(uint32_t)-1)) / sizeof(uint32_t);
//0x000
if (fifo_free_sz_lw < send_buf_len_lw) {
send_buf_len_lw = fifo_free_sz_lw;
}
//0x000
if (send_buf_len_lw > 0) {
//0x000
fifo_ptr = unit->las1b;
//0x000
if (mrp_debug > 2) {
printk("mrp_put_fifo: nw=%d\n",send_buf_len_lw);
}
//0x000
switch(send_buf_len_lw & 7) {
case 7: writel(*send_ptr++,fifo_ptr);
case 6: writel(*send_ptr++,fifo_ptr);
case 5: writel(*send_ptr++,fifo_ptr);
case 4: writel(*send_ptr++,fifo_ptr);
case 3: writel(*send_ptr++,fifo_ptr);
case 2: writel(*send_ptr++,fifo_ptr);
case 1: writel(*send_ptr++,fifo_ptr);
default:
//0x000
for (i = (send_buf_len_lw/8); i > 0; --i) {
writel(send_ptr[0],fifo_ptr);
writel(send_ptr[1],fifo_ptr);
writel(send_ptr[2],fifo_ptr);
writel(send_ptr[3],fifo_ptr);
writel(send_ptr[4],fifo_ptr);
writel(send_ptr[5],fifo_ptr);
writel(send_ptr[6],fifo_ptr);
writel(send_ptr[7],fifo_ptr);
send_ptr += 8;
}
}
//0x000
unit->send_ptr += send_buf_len_lw;
}
// check if data in send buf has been sent yet
//0x000
if ((uint32_t)unit->send_ptr < ((uint32_t)unit->send_buf + unit->send_len)) {
// enable interrupt from MRP. the interrupt indicates TX FIFO no longer almost full
//0x000
ier = readw(&unit->las2b->ier);
ier |= MRP_INT_TAF;
writew(ier,&unit->las2b->ier);
}
else {
// data send completed
// set TX complete/control/counter
//0x000
writew(1,&unit->las2b->txc);
// done sending data. deassert SBUSY.
//0x000
unit->device_status &= ~MRP_DEV_SBUSY;
wake_up(&unit->send_wait);
}
//0x000
return send_buf_len_lw;
}
/*
* transfers data from RX FIFO to recv_buf.
*
* invoked by mrp_interrupt.
*/
int mrp_recv(struct mrp_unit *unit, uint16_t ist)
{
uint32_t device_status;
struct mrp_regs *regs;
uint16_t fst, ier;
uint32_t i;
uint32_t *fifo_ptr;
uint32_t *recv_ptr;
uint32_t recv_buf_free_lw;
uint32_t fifo_len_lw;
//0x000
regs = unit->las2b;
//0x000
fst = readw(&regs->fst);
//0x000
device_status = unit->device_status;
//0x000
if (mrp_debug > 1) {
//0x000
printk("mrp_recv: fst=0x%x stat=0x%x\n", fst, ist);
}
//0x000
if (device_status & MRP_DEV_RDONE) {
ier = readw(&regs->ier);
ier &= ~(MRP_INT_RXC | MRP_INT_RAE);
writew(ier,&regs->ier);
return 0;
}
// Determine approximate FIFO depth
//0x000
if (fst & MRP_FIFO_RX_FF) {
//0x000
fifo_len_lw = MRP_FIFO_MAX;
}
else if (fst & MRP_FIFO_RX_AF) {
//0x000
//0x000
fifo_len_lw = MRP_FIFO_HWM;
if (device_status & MRP_DEV_B3) {
fifo_len_lw = MRP_FIFO_HWM_B3;
}
}
else if (fst & MRP_FIFO_RX_HF) {
//0x000
fifo_len_lw = MRP_FIFO_HALF;
}
else if (!(fst & MRP_FIFO_RX_AE)) {
//0x000
fifo_len_lw = MRP_FIFO_LWM;
if (device_status & MRP_DEV_B3) {
fifo_len_lw = MRP_FIFO_LWM_B3;
}
}
else if (!(fst & MRP_FIFO_RX_EF)) {
//0x000
fifo_len_lw = 1;
}
else {
//0x000
fifo_len_lw = 0;
}
//0x000
if (unit->recv_len > 0) {
if (fifo_len_lw == 0) {
//0x000004f0
writew(1,&regs->rxc);
//0x000
ier = readw(&regs->ier);
//0x000
ier |= (MRP_INT_RXC | MRP_INT_RAE);
//0x000
writew(ier,&regs->ier);
//0x000
return 0;
}
//0x000
unit->recv_ptr = unit->recv_buf;
if (mrp_debug > 2) {
//0x000
printk("mrp_get_fifo: nw=%d\n",1);
}
//transfer single lword
//0x0000046
*((uint32_t *)unit->recv_buf) = readl(unit->las1b);
//0x000
// examine first two dwords
if (*(uint16_t *)unit->recv_buf == MRP_DECI1_MAGIC && *(uint16_t *)(((uint32_t)unit->recv_buf) + 2) > 0x20) {
//0x000
printk("mrp: DECI1 detected, waiting reset\n");
//0x000
writew(MRP_FIFO_RX_RST, &regs->fst);
//0x000
udelay(10);
//0x000
writew(0,&regs->fst);
//0x000
writew(1,&regs->rxc);
//0x000
ier = readw(&regs->ier);
//0x000004d3
ier |= (MRP_INT_RXC | MRP_INT_RAE | MRP_INT_SI2);
//0x000
writew(ier, &regs->ier);
//0x000
return 0;
}
//0x000
unit->recv_ptr++;
unit->recv_len = ((uint16_t*)unit->recv_buf)[0];
--fifo_len_lw;
}
//0x000
recv_buf_free_lw = ((uint32_t)unit->recv_buf + unit->recv_len - (uint32_t)unit->recv_ptr + (sizeof(int)-1)) / sizeof(int);
//0x000
if ((!(ist & MRP_INT_RXC)) && (fifo_len_lw < recv_buf_free_lw)) {
//0x000
recv_buf_free_lw = fifo_len_lw;
}
//0x000
if (recv_buf_free_lw > 0) {
//0x000
fifo_ptr = unit->las1b;
//0x000
recv_ptr = unit->recv_ptr;
//0x000
if (mrp_debug > 2) {
//0x000
printk("mrp_get_fifo: nw=%d\n", recv_buf_free_lw);
}
//0x000
switch (recv_buf_free_lw & 7) {
case 7: *recv_ptr++ = readl(fifo_ptr);
case 6: *recv_ptr++ = readl(fifo_ptr);
case 5: *recv_ptr++ = readl(fifo_ptr);
case 4: *recv_ptr++ = readl(fifo_ptr);
case 3: *recv_ptr++ = readl(fifo_ptr);
case 2: *recv_ptr++ = readl(fifo_ptr);
case 1: *recv_ptr++ = readl(fifo_ptr);
//0x000
default:
//0x000
//0x000
for (i = (recv_buf_free_lw/8); i > 0; --i) {
//0x000
recv_ptr[0] = readl(fifo_ptr);
recv_ptr[1] = readl(fifo_ptr);
recv_ptr[2] = readl(fifo_ptr);
recv_ptr[3] = readl(fifo_ptr);
recv_ptr[4] = readl(fifo_ptr);
recv_ptr[5] = readl(fifo_ptr);
recv_ptr[6] = readl(fifo_ptr);
recv_ptr[7] = readl(fifo_ptr);
recv_ptr += 8;
}
}
//0x000
unit->recv_ptr += recv_buf_free_lw;
}
if ((uint32_t)unit->recv_ptr >= ((uint32_t)unit->recv_buf + unit->recv_len)) {
//0x000
writew(1,&regs->rxc);
//0x000
unit->device_status |= MRP_DEV_RDONE;
//0x000
wake_up(unit->recv_wait);
}
else {
//0x000
ier = readw(&regs->ier);
//0x000
ier |= (MRP_INT_RXC | MRP_INT_RAE);
//0x000
writew(ier,&regs->ier);
}
//0x000
return recv_buf_free_lw;
}
int mrp_reset(struct mrp_unit *unit)
{
struct mrp_regs *regs;
uint16_t bid, aeo, afo;
uint32_t stat3_save;
//uint16_t i;
int retval;
uint32_t delay_count;
//0x000
retval = 0;
//0x000
regs = unit->las2b;
//0x000
if (mrp_debug > 1) {
printk("mrp_reset:\n");
}
//0x000
bid = readw(&regs->bid);
//0x000
if (bid != MRP_BID_PIF && bid != MRP_BID_B3) {
//0x000
return -EIO;
}
//0x000
if (bid != MRP_BID_B3) {
//0x000
unit->device_status &= ~MRP_DEV_B3;
}
else {
//0x000
unit->device_status |= MRP_DEV_B3;
}
// disable interrupts and reset MRP itself
//0x00000711
writew(0, &regs->ier); // disable interrupts
//0x0000071e
writew(MRP_RST_BACKPLANE, &regs->rst); // reset MRP itself, or component controlling MRP
// reset FIFO devices attached to MRP
//0x000
udelay(10);
//0x000
writew((MRP_FIFO_TX_RST | MRP_FIFO_TX_OE | MRP_FIFO_RX_RST | MRP_FIFO_RX_OE), &regs->fst);
//0x00000746
udelay(10);
//0x00000760
writew(0, &regs->fst);
// configure FIFO devices
//0x000
if (!(unit->device_status & MRP_DEV_B3)) {
//0x000
aeo = MRP_FIFO_LWM & MRP_BUF_MASK;
writew(aeo, &regs->aeo);
//0x000
afo = MRP_FIFO_HWM_FREE & MRP_BUF_MASK;
writew(afo, &regs->afo);
//0x000
aeo &= ~MRP_FIFO_OFFSET_SEL_RX; // set output to TX FIFO
//0x000
writew(aeo, &regs->aeo);
//0x000
aeo |= MRP_FIFO_OFFSET_LOAD; // strobe FPGA to configure offset
//0x000
writew(aeo, &regs->aeo);
//0x000
delay_count = 0;
//0x000
retval = 0;
do {
// wait for signal to flip to indicate state change completed
//0x000
udelay(10);
if (!((aeo = readw(&regs->aeo)) & MRP_FIFO_OFFSET_LOAD))
break;
//0x000
delay_count++;
}
//0x000
while (delay_count < 100);
//0x000
if (delay_count >= 100) {
//0x000
if (mrp_debug > 1) {
//0x000
printk("mrp_reset: Tx LDE - timeout\n");
}
//0x000
retval = -EIO;
}
//0x000
aeo |= MRP_FIFO_OFFSET_SEL_RX; // sets output to RX FIFO. yes, on AEO.
//0x000
writew(aeo, &regs->aeo);
//0x000
aeo |= MRP_FIFO_OFFSET_LOAD; // strobe FPGA to configure offset
//0x000
writew(aeo, &regs->aeo);
//0x000
delay_count = 0;
do {
// wait for signal to flip to indicate state change completed
//0x000
udelay(10);
//0x000
if (!((aeo = readw(&regs->aeo)) & MRP_FIFO_OFFSET_LOAD))
break;
//0x000
delay_count++;
}
//0x000
while (delay_count < 100);
//0x000
if (delay_count >= 100) {
//0x0001081c
if (mrp_debug > 1) {
//0x00010825
printk("mrp_reset: Rx LDE - timeout\n");
}
//0x000
retval = -EIO;
}
}
// pulse important-looking reset lines, immediately reset. subsystems within MRP?
//0x000
writew((MRP_RST_PIF | MRP_RST_BOOT | MRP_RST_POWEROFF), &regs->rst);
//0x000
writew(0, &regs->rst);
//0x000
udelay(10);
// set the FIFO status pins we use for interrupts as active-low
// but why? could triggers on FPGA on be for high-to-low transition on this model?
// or invert inputs from FIFO, then only low-to-high transition when placed in fst register?
//0x000
writew((MRP_INT_TFF | MRP_INT_TAF | MRP_INT_THF | MRP_INT_RAE | MRP_INT_REF), &regs->isp);
// set up initial com port interrupt mask
//0x000
writew((MRP_INT_SI2 | MRP_INT_SI1 | MRP_INT_SI0), &regs->csi);
// set up initial interrupt mask
//0x000
writew((MRP_INT_RAE | MRP_INT_RXC | MRP_INT_RST | MRP_INT_SI2 | MRP_INT_SI0), &regs->ier);
// set up initial device status
//0x000
unit->device_status &= ~(MRP_DEV_WBOOT | MRP_DEV_CPBUSY | MRP_DEV_RDONE | MRP_DEV_SBUSY);
unit->device_status |= (MRP_DEV_WRESET | MRP_DEV_RESET);
//0x000
wake_up(&unit->send_wait);
// set up buffers for communication
//0x000
unit->recv_len = 0;
unit->send_len = 0;
unit->scq_ptr = 0;
unit->scq_idx = 0;
unit->scq_len = 0;
unit->rcq_ptr = 0;
unit->rcq_idx = 0;
unit->rcq_len = 0;
// reset statistics
//0x000
stat3_save = unit->stats[3];
//0x000
memset(unit->stats,0,sizeof(unit->stats));
// stat name at index 3 is "RST", in case you're curious
//0x000
unit->stats[3] = stat3_save;
//0x000
return retval;
}
/*
* asserts BOOT on interrupt lines.
*
* invoked by mrp_interrupt on SI2 interrupt.
*/
int mrp_bootp(mrp_unit *unit)
{
uint16_t tmp, ier, cpr;
int rc;
struct mrp_regs *regs;
//0x000
regs = unit->las2b;
//0x000
rc = 1;
//TODO figure out meaning of mask value put in cps
// 0x1300 = 0x1F00 & ~( 0x0800 | 0x0400 )
// 0x0400 = MRP_DEV_WRESET
// 0x0800 = MRP_DEV_WBOOT
//
// 0x1300 also at DECI2DRP:0x00000340
//0x000
tmp = 0x1f00;
//0x000
cpr = readw(&regs->cpr);
//0x000
if (cpr == 0x1300) {
//0x000
tmp = 0x1300;
}
else {
//0x000
rc = -1;
}
//0x000
writew(tmp, &regs->cps);
//0x000
writew(MRP_INT_SI2, &regs->csi);
//0x000
//0x000
writew(MRP_INT_SI2, &regs->fsi);
//0x000
ier = readw(&regs->ier);
//0x000
ier |= MRP_INT_SI2;
//0x000
writew(ier, &regs->ier);
//0x000
return rc;
}
/*
* read a byte from CPR register into inbound comm queue, assert SI0 when done.
*
* invoked by mrp_interrupt on SI0 interrupt.
*/
void mrp_cpr(mrp_unit *unit)
{
uint32_t debugchar;
struct mrp_regs *regs;
uint16_t cpr, ier;
//0x000
regs = unit->las2b;
//0x000
if (unit->rcq_len < MRP_FIFO_MAX) {
//0x000
cpr = readw(&regs->cpr);
//0x000
if (mrp_debug > 1) {
// turn non-printable characters into '?'
//0x000
if ((cpr - 0x20) < 0x5f) {
//0x000
debugchar = cpr & 0x00ff;
}
else {
//0x000
debugchar = L'?';
}
//0x000
printk("mrp_cpr: cpr=%04x (%c)\n",cpr,debugchar);
}
//0x000
unit->rcq[unit->rcq_idx & MRP_BUF_MASK] = (uint8_t)cpr;
//0x000
++unit->rcq_idx;
//0x000
++unit->rcq_len;
//0x000
writew(MRP_INT_SI0, &regs->csi);
writew(MRP_INT_SI1, &regs->fsi);
//0x000
ier = readw(&regs->ier);
//0x000
ier |= MRP_INT_SI0;
//0x000
writew(ier, &regs->ier);
//0x000
wake_up(&unit->cpr_wait);
}
//0x000
return;
}
/*
* take a byte from outbound comm queue and put in CPS register, assert SI1/CPREAD when done.
*
* invoked by mrp_interrupt on SI1 interrupt.
* invoked by mrp_write if CPOPEN set.
*/
void mrp_cps(mrp_unit *unit)
{
struct mrp_regs *regs;
uint16_t ier;
//0x000
regs = unit->las2b;
//0x000
if ((!(unit->device_status & MRP_DEV_CPBUSY)) && (unit->scq_len > 0)) {
//0x000
unit->device_status |= MRP_DEV_CPBUSY;
// dereference+copy 8-bit value to AX, 16-bit copy AX to 16-bit register CPS
//0x000
writew(unit->scq[unit->scq_ptr & MRP_BUF_MASK], &regs->cps);
//0x000
++unit->scq_ptr;
//0x000
--unit->scq_len;
//0x000
writew(MRP_INT_SI0, &regs->fsi);
//0x000
ier = readw(&regs->ier);
//0x000
ier |= MRP_INT_SI1;
//0x000
writew(ier, &regs->ier);
//0x000
wake_up(&unit->cps_wait);
}
//0x000
return;
}
/*
* Set up a timer, as well as PCI9050 configuration.
*
* currently referenced from mrp_init w/o clear invocation
*/
void mrp_b3timer(uint32_t unit_idx)
{
uint32_t device_status;
uint32_t cntrl_value;
mrp_unit *unit;
//0x000
if (unit_idx < MRP_MAX_UNITS) {
unit = &mrp_units[unit_idx];
device_status = unit->device_status;
//0x00000af8
if (device_status & MRP_DEV_VALID) {
//0x000
cntrl_value = readl(unit->las0b + PCI9050_CNTRL);
//0x000
if (device_status & MRP_DEV_POWEROFF) {
if (device_status & MRP_DEV_B3) {
//0x000
cntrl_value &= ~(PCI9050_CNTRL_USER3_SIG|PCI9050_CNTRL_USER3_FUN|PCI9050_CNTRL_USER2_SIG|PCI9050_CNTRL_USER2_FUN);
//0x000
cntrl_value |= (PCI9050_CNTRL_USER3_SIG|PCI9050_CNTRL_USER3_DIR|PCI9050_CNTRL_USER2_DIR);
/*
* end state:
* ... (USER0/USER1 are input pulled low, by default) ...
* bit 0 = PCI9050_CNTRL_USER2_FUN = 0 ==> pin USER2/CS2# is USER2
* bit 1 = PCI9050_CNTRL_USER2_DIR = 1 ==> pin USER2 is output
* bit 2 = PCI9050_CNTRL_USER2_SIG = 0 ==> pin USER2 is active-low
* bit 9 = PCI9050_CNTRL_USER3_FUN = 0 ==> pin USER3/CS3# is USER3
* bit 10 = PCI9050_CNTRL_USER3_DIR = 1 ==> pin USER3 is output
* bit 11 = PCI9050_CNTRL_USER3_SIG = 1 ==> pin USER3 is active-high
*/
}
else {
//0x000
cntrl_value &= ~(PCI9050_CNTRL_USER3_SIG|PCI9050_CNTRL_USER3_FUN|PCI9050_CNTRL_USER0_SIG|PCI9050_CNTRL_USER0_FUN);
//0x000
cntrl_value |= (PCI9050_CNTRL_USER3_DIR|PCI9050_CNTRL_USER0_SIG|PCI9050_CNTRL_USER0_DIR);
/*
* end state:
* bit 0 = PCI9050_CNTRL_USER0_FUN = 0 ==> pin USER0/WAITO# is USER0 (as opposed to WAITO#)
* bit 1 = PCI9050_CNTRL_USER0_DIR = 1 ==> pin USER0 is output
* bit 2 = PCI9050_CNTRL_USER0_SIG = 0 ==> pin USER0 is active-low
* ... (USER1/USER2 are input pulled low, by default) ...
* bit 9 = PCI9050_CNTRL_USER3_FUN = 0 ==> pin USER3/CS3# is USER3 (as opposed to CS3#)
* bit 10 = PCI9050_CNTRL_USER3_DIR = 1 ==> pin USER3 is output
* bit 11 = PCI9050_CNTRL_USER3_SIG = 0 ==> pin USER3 is active-low
*/
}
// save updated pin configuration to CNTRL register
//0x000
writel(cntrl_value, unit->las0b + PCI9050_CNTRL);
}
else {
if (device_status & MRP_DEV_B3) {
//0x000
cntrl_value &= ~(PCI9050_CNTRL_USER3_SIG|PCI9050_CNTRL_USER3_FUN|PCI9050_CNTRL_USER2_SIG|PCI9050_CNTRL_USER2_FUN);
//0x000
cntrl_value |= (PCI9050_CNTRL_USER3_DIR|PCI9050_CNTRL_USER2_SIG|PCI9050_CNTRL_USER2_DIR);
/*
* end state:
* ... (USER0/USER1 are input, pulled low by default) ...
* bit 6 = PCI9050_CNTRL_USER2_FUN = 0 ==> pin USER2/CS2# is USER2
* bit 7 = PCI9050_CNTRL_USER2_DIR = 1 ==> pin USER2 is output
* bit 8 = PCI9050_CNTRL_USER2_SIG = 1 ==> pin USER2 is active-high
* bit 9 = PCI9050_CNTRL_USER3_FUN = 0 ==> pin USER3/CS3# is USER3 (as opposed to CS3#)
* bit 10 = PCI9050_CNTRL_USER3_DIR = 1 ==> pin USER3 is output
* bit 11 = PCI9050_CNTRL_USER3_SIG = 0 ==> pin USER3 is active-low
*/
}
else {
//0x000
cntrl_value &= ~(PCI9050_CNTRL_USER3_SIG|PCI9050_CNTRL_USER3_FUN|PCI9050_CNTRL_USER0_SIG|PCI9050_CNTRL_USER0_FUN);
//0x000
cntrl_value |= (PCI9050_CNTRL_USER3_SIG|PCI9050_CNTRL_USER3_DIR|PCI9050_CNTRL_USER0_DIR);
// theory: USER3 tied to FPGA /RESET pin
/*
* end state:
* bit 0 = PCI9050_CNTRL_USER0_FUN = 0 ==> pin USER0/WAITO# is USER0
* bit 1 = PCI9050_CNTRL_USER0_DIR = 1 ==> pin USER0 is output
* bit 2 = PCI9050_CNTRL_USER0_SIG = 0 ==> pin USER0 is active-low
* ... (USER1/USER2 are input pulled low, by default) ...
* bit 9 = PCI9050_CNTRL_USER3_FUN = 0 ==> pin USER3/CS3# is USER3 (as opposed to CS3#)
* bit 10 = PCI9050_CNTRL_USER3_DIR = 1 ==> pin USER3 is output
* bit 11 = PCI9050_CNTRL_USER3_SIG = 1 ==> pin USER3 is active-high
*/
}
// save updated pin configuration to CNTRL register
//0x000
writel(cntrl_value, unit->las0b + PCI9050_CNTRL);
//0x000
unit->init_timer.expires = jiffies + 100;
//0x000
add_timer(&unit->init_timer);
}
}
}
//0x000
return;
}
/*
* handle interrupts, invoke appropriate functions.
*
* configured as the driver's interrupt handler in mrp_init.
*/
void mrp_interrupt(int irq, struct mrp_unit *unit, struct pt_regs *ptr)
{
struct mrp_regs *regs;
uint16_t ist, ier, rxc, rst, intrs;
uint32_t i;
// Validate unit pointer
//0x000
if ((unit < &mrp_units[0]) || (unit >= &mrp_units[MRP_MAX_UNITS])) {
return;
}
// Check whether LINT1 interrupt is asserted
//0x000
if (!(readl(unit->las0b + PCI9050_INTCSR) & PCI9050_INTCSR_LINT1_ACT)) {
//0x000
return;
}
//0x000
unit->nintr++; // Count total interrupts
//0x000
regs = unit->las2b;
ist = readw(&regs->ist);
ier = readw(&regs->ier);
rxc = readw(&regs->rxc);
//0x000
rst = readw(&regs->rst);
// LINT1 might not be cleared when RXC is non-zero
//TODO double check RXC flag check/set
if ((ist & MRP_INT_RXC) && (rxc)) {
//0x000
ist |= MRP_INT_RXC;
writew(ist, &regs->ist);
//0x000
unit->nfixed++;
}
// Mask interrupt status with enable register
//0x000
intrs = ist & ier;
// Update per-bit interrupt statistics
//0x000
//0x000
for (i = 0; i < 16; ++i) {
//0x000
unit->stats[i] += (intrs >> i) & 1;
}
//0x000
if (mrp_debug > 1) {
//0x000
printk("mrp_interrupt: stat=0x%04x\n", intrs);
//0x000
mrp_dump_regs(regs);
}
// if communication with PIF established and interrupts for
// data receive are enabled
//0x000
if (intrs & (MRP_INT_TAF | MRP_INT_RXC | MRP_INT_RAE)) {
// Handle TX FIFO Almost Full interrupt.
// assume this interrupt means TX FIFO is no longer almost full, so data can be sent.
//0x000
if (intrs & MRP_INT_TAF) {
//0x000
ier &= ~MRP_INT_TAF;
//0x000
writew(ier, &regs->ier);
//0x000
mrp_send(unit);
}
// Handle RX FIFO Almost Empty interrupt or RX Complete Interrupt
// assume this interrupt means RX FIFO is no longer almost empty, or data receive (RX) pending
//0x000
if (intrs & (MRP_INT_RXC | MRP_INT_RAE)) {
//0x000
ier &= ~(MRP_INT_RXC | MRP_INT_RAE);
//0x000
writew(ier,&regs->ier);
//0x000
mrp_recv(unit, intrs);
}
}
//0x000
else if (intrs & MRP_INT_RST) {
// Clear any reset flags that should not be set during unit reset
//0x000
rst &= (MRP_RST_PIF | MRP_RST_BOOT | MRP_RST_POWEROFF);
//0x000
writew(rst, &regs->rst);
// if RESET asserted, PIF communication is available
//0x000
if (rst & MRP_RST_PIF)
//0x000
unit->driver_status |= MRP_DRV_PIF;
// if BOOT is asserted, boot protocol ready
//0x000
if (rst & MRP_RST_BOOT)
//0x000
unit->driver_status |= MRP_DRV_BPR;
//TODO review assembly for condition
if ((!(unit->device_status & MRP_DEV_B3))
&& !(rst & MRP_RST_POWEROFF)) {
// power on complete, but FIFO not ready
//0x000
unit->device_status |= (MRP_DEV_POWEROFF | MRP_DEV_EPIPE);
//0x000
wake_up(&unit->recv_wait);
//0x000
//0x000
wake_up(&unit->send_wait);
}
else if (unit->device_status & MRP_DEV_WRESET) {
//0x000
unit->device_status &= ~MRP_DEV_WRESET;
//0x000
unit->device_status |= MRP_DEV_WBOOT;
}
else {
//0x000
mrp_reset(unit);
}
}
//0x000
else if (intrs & MRP_INT_SI2) {
// handle com port ready for initialization
//TODO assembly appears to evaluate pointers in struct in this block
//0x000
ier &= ~MRP_INT_SI2;
//0x000
writew(ier, &regs->ier);
//0x000
if (mrp_bootp(unit) >= 1) {
//boot complete
//0x000
unit->device_status &= ~MRP_DEV_WBOOT;
//FIFO not ready
//0x000
unit->device_status |= MRP_DEV_EPIPE;
//0x000
wake_up(&unit->recv_wait);
//0x000
//0x000
wake_up(&unit->send_wait);
}
}
//0x000
else if (intrs & MRP_INT_SI0) {
// receive inbound com port bytes
do {
//0x000
ier &= ~MRP_INT_SI0;
//0x000
writew(ier, &regs->ier);
//0x000
unit->stats_rcv_cp++;
//0x000
mrp_cpr(unit);
//0x000
udelay(10);
//0x000
ist = readw(&regs->ist);
//0x000
ier = readw(&regs->ier);
}
//0x000
while (ist & ier & MRP_INT_SI0);
}
//0x000
else if (intrs & MRP_INT_SI1) {
// sent byte acknowledged, attempt to send any additional outbound bytes via com port
//0x000
ier &= ~MRP_INT_SI1;
//0x000
writew(ier, &regs->ier);
//0x000
writew(MRP_INT_SI1, &regs->csi);
//0x000
unit->device_status &= ~MRP_DEV_CPBUSY;
//0x000
mrp_cps(unit);
//0x000
//0x000
wake_up(&unit->cps_wait);
}
else {
// Unexpected interrupt
//0x000
printk("mrp%d: unexpected interrupt", readw(&regs->bid) & 3);
//0x000
printk(" (stat=0x%x ist=0x%x ier=0x%x rxc=0x%x)\n", intrs, ist, ier, rxc);
//0x000
++unexpected_interrupts;
//0x000
if (unexpected_interrupts > 20) {
//0x000
writew(0, &regs->ier); // disable all interrupts
}
}
}
/*
* handle read() call
*/
ssize_t mrp_read (struct file *file, char *user_buf, size_t user_buf_sz, loff_t *off)
{
uint16_t bid, ier;
uint8_t unit_idx;
struct mrp_unit *unit;
struct mrp_regs *regs;
//uint32_t device_status;
uint8_t *recv_buf;
uint8_t recv_ch;
uint32_t read_len;
uint32_t i;
//0x000
unit_idx = MINOR(file->f_dentry->d_inode->i_rdev) & 0x3;
//0x000
if (mrp_debug > 1) {
printk("mrp_read: count=%d\n", user_buf_sz);
}
//0x000
if (unit_idx >= MRP_MAX_UNITS) {
return -ENODEV;
}
//0x000
//0x000
unit = &mrp_units[unit_idx];
//0x000
if (!(unit->device_status & MRP_DEV_VALID)) {
return -ENODEV;
}
regs = unit->las2b;
bid = readw(&regs->bid);
//if in PIO mode
//0x000
if (bid & MRP_BID_PIO_MODE) {
// ... and com port open
//0x000
if (!(unit->device_status & MRP_DEV_CPOPEN)) {
//0x000
return -ENODEV;
}
//0x000
if (i = verify_area(VERIFY_WRITE,user_buf,user_buf_sz)) {
return i;
}
i = 0;
read_len = 0;
//0x000
if (0 < user_buf_sz) {
while (read_len < user_buf_sz) {
read_len = unit->rcq_len;
//0x000
while (read_len == 0) {
//0x000
if (readl(unit->las1b) & 0x0800) {
if (0 < i) {
return i;
}
//0x000
return -EAGAIN;
}
//0x000
interruptible_sleep_on(&unit->cpr_wait);
//0x000
if (signal_pending(current)) {
//0x000
return -EINTR;
}
//0x000
read_len = unit->rcq_len;
}
//0x000
recv_ch = unit->rcq[unit->rcq_idx & MRP_BUF_MASK];
//0x000
++unit->rcq_idx;
//0x000
--unit->rcq_len;
//0x000
*user_buf++ = recv_ch;
++read_len;
};
}
//0x000
ier = readw(&regs->ier);
//0x000
ier |= MRP_INT_SI0;
//0x000
writew(ier,&regs->ier);
//0x000
return read_len;
}
else {
//0x000
if (!(unit->device_status & MRP_DEV_OPENED)) {
return -EIO;
}
// FIFOs not ready
//0x000
if (unit->device_status & MRP_DEV_EPIPE) {
// set FIFOs as ready, don't actually initialize FIFOs, return error
//0x000
unit->device_status &= ~MRP_DEV_EPIPE;
//0x000
return -EPIPE;
}
//if device has finished initialization
//0x000
if (!(unit->device_status & MRP_DEV_RESET)) {
return -EIO;
}
//0x000
//0x000
if (i = verify_area(VERIFY_WRITE,user_buf,user_buf_sz)) {
//0x000
return i;
}
// Check for ... cleared buffer?
// is FIFO data somehow tied to rst register?
//0x000
if (readl(unit->las1b) & 0x0800) {
//0x000
return -EAGAIN;
}
//0x000
interruptible_sleep_on(&unit->recv_wait);
//0x000
if (signal_pending(current)) {
//0x000
//0x000
return -EINTR;
}
read_len = (uint32_t)unit->recv_len;
//0x000
if (user_buf_sz < read_len) {
//0x000
return -EINVAL;
}
recv_buf = (uint8_t *)unit->recv_buf;
memcpy_tofs(user_buf,recv_buf,read_len);
//0x000
unit->recv_len = 0;
//0x000
unit->device_status &= ~MRP_DEV_RDONE;
//0x000
ier = readw(&regs->ier);
// Re-enable receive interrupt
//0x000
ier |= (MRP_INT_RXC | MRP_INT_RAE);
//0x000
writew(ier, &regs->ier);
//0x000
return read_len;
}
}
/*
* handle write() call.
*
* copies data from user_buf to outbound comm queue or outbound data queue, invokes mrp_cps or mrp_send, respectively.
*/
ssize_t mrp_write (struct file * file, const char *user_buf, size_t user_buf_sz, loff_t *off)
{
uint16_t bid;
uint32_t unit_idx;
struct mrp_unit *unit;
int transferred = 0;
uint32_t i;
//0x000
unit_idx = MINOR(file->f_dentry->d_inode->i_rdev) & 0x3;
transferred = 0;
if (mrp_debug > 1) {
printk("mrp_write: count=%d\n", user_buf_sz);
}
//0x000
if (unit_idx >= MRP_MAX_UNITS) {
return -ENODEV;
}
//0x000
unit = &mrp_units[unit_idx];
//0x000
if (!(unit->device_status & MRP_DEV_VALID)) {
//0x000
return -ENODEV;
}
bid = readw(&unit->las2b->bid);
//0x000
if (!(bid & MRP_BID_PIO_MODE)) {
// FIFO write mode
//0x000
if (!(unit->device_status & MRP_DEV_OPENED)) {
//0x000
return -ENODEV;
}
//0x000
if (!(unit->device_status & MRP_DEV_RESET)) {
//0x000
return -EIO;
}
//0x000
if (user_buf_sz <= 7 || user_buf_sz >= 0x10000) {
//0x000
return -EINVAL;
}
//0x000
if (i = verify_area(VERIFY_WRITE, user_buf, user_buf_sz)) {
//0x000
return i;
}
while (1) {
//0x000
if (!(unit->device_status & MRP_DEV_SBUSY)) {
// Write into DMA-style shared buffer
//0x000
unit->send_buf[user_buf_sz / 4] = 0; // Pad or sentinel
memcpy_fromfs(unit->send_buf,user_buf,user_buf_sz);
// Check transfer
// first 2 bytes appear to be buffer size
//0x000
if (user_buf_sz != *(short *)unit->send_buf) {
//0x000
return -EINVAL;
}
//0x000
unit->device_status |= MRP_DEV_SBUSY;
unit->send_len = user_buf_sz;
//0x000
mrp_send(unit);
//0x000
return user_buf_sz;
}
// Blocking write: wait
//0x000
if (file->f_flags & O_NONBLOCK) {
//0x000112a1
return -EAGAIN;
}
//0x000
interruptible_sleep_on(&unit->send_wait);
//0x000
if (signal_pending(current)) {
//0x000
return -EINTR;
}
}
}
else {
// Software (PIO) write path
if (!(unit->device_status & MRP_DEV_CPOPEN)) {
//0x000
return -ENODEV;
}
//0x000
if (i = verify_area(VERIFY_WRITE, user_buf, user_buf_sz)) {
return i;
}
//0x000
//0x000
for (i = 0; i < user_buf_sz; i++) {
//TODO this loop doesn't make sense to me. yes, the loop condition appears correct.
//0x000
while (unit->scq_len > (MRP_BUF_SIZE-1)) {
//0x000
if (file->f_flags & O_NONBLOCK) {
//0x000
if (transferred > 0) {
//0x000
return transferred;
}
else {
//0x000
return -EAGAIN;
}
}
//0x000
interruptible_sleep_on(&unit->send_wait);
//0x000
if (signal_pending(current)) {
return -EINTR;
}
}
//0x000
unit->scq[unit->scq_ptr & MRP_BUF_MASK] = ((char *)user_buf)[i];
//0x000
unit->scq_ptr++;
//0x000
unit->scq_len++;
transferred++;
}
//0x000
mrp_cps(unit);
//0x000
return transferred;
}
//0x000
return -ENODEV;
}
unsigned int mrp_poll(struct file *file, struct poll_table_struct *entry)
{
uint16_t bid;
uint8_t unit_idx;
struct mrp_unit *unit;
uint rc = 0;
unit_idx = MINOR(file->f_dentry->d_inode->i_rdev);
if (unit_idx >= MRP_MAX_UNITS) {
return 0;
}
unit = &mrp_units[unit_idx];
bid = readw(&unit->las2b->bid);
if (!(bid & MRP_BID_PIO_MODE)) {
poll_wait(file,unit->recv_wait,entry);
poll_wait(file,unit->send_wait,entry);
if (!(unit->device_status & (MRP_DEV_RDONE | MRP_DEV_EPIPE))) {
rc = 0x41;
}
if (!(unit->device_status & MRP_DEV_SBUSY)) {
return rc;
}
}
else {
poll_wait(file,unit->cpr_wait,entry);
poll_wait(file,unit->cps_wait,entry);
if (0 < (int)mrp_units[unit_idx].rcq_len) {
rc = 0x41;
}
if (0xfff < (int)mrp_units[unit_idx].scq_len) {
return rc;
}
}
return rc | 0x104;
}
/*
* handle open() call.
*
* deassert WBOOT, set device status as OPENED or CPOPENED, increment module ref count.
*/
int mrp_open(struct inode *inode, struct file *file)
{
uint8_t unit_idx;
struct mrp_unit *unit;
uint16_t bid;
//0x000
unit_idx = MINOR(inode->i_rdev) & 0x3;
//0x000
if (mrp_debug > 1) {
//0x000
printk("mrp_open: index=%d\n", unit_idx);
}
//0x000
if (unit_idx >= MRP_MAX_UNITS) {
//0x000
return -ENODEV;
}
unit = &mrp_units[unit_idx];
//0x000
if (!(unit->device_status & MRP_DEV_VALID)) {
//0x000
return -ENODEV;
}
bid = readw(&unit->las2b->bid);
//0x000
if (bid != MRP_BID_PIF && bid != MRP_BID_B3) {
//0x000
return -EIO;
}
//0x000
unit->device_status &= ~MRP_DEV_WBOOT;
// If the BID is 0x4127 (indicating B3 unit), set B3 flag
// BID of the DTL-T10000 and DTL-T15000 is 0x4126, at least at boot
//0x000
if (bid == MRP_BID_B3) {
//0x000
unit->device_status |= MRP_DEV_B3;
}
// Check if this is a com port (PIO) device
//0x000
if (bid & MRP_BID_PIO_MODE) {
//0x000
if (!(unit->device_status & MRP_DEV_CPOPEN)) {
// com port device: ensure CPOPEN is not already set
//0x000
unit->device_status |= MRP_DEV_CPOPEN;
MOD_INC_USE_COUNT;
return 0;
}
}
else {
// Not a com port device: must not already be open
//0x000
if (!(unit->device_status & MRP_DEV_OPENED)) {
//0x000
unit->device_status |= MRP_DEV_OPENED;
//0x000
MOD_INC_USE_COUNT;
//0x000
return 0;
}
}
//0x000
return -EBUSY;
}
int mrp_release(struct inode *inode, struct file *file)
{
uint16_t bid;
uint8_t unit_idx;
mrp_unit *unit;
//0x000
unit_idx = MINOR(inode->i_rdev) & 0x3;
//0x000
if (mrp_debug > 1) {
//0x000
printk("mrp_release: index=%d\n",unit_idx);
}
//0x000
if (unit_idx < MRP_MAX_UNITS) {
unit = &mrp_units[unit_idx];
//0x000
if (unit->device_status & MRP_DEV_VALID) {
bid = readw(&unit->las2b->bid);
//0x000
if (bid & MRP_BID_PIO_MODE) {
//0x000
if (mrp_units[unit_idx].device_status & MRP_DEV_CPOPEN) {
//0x000
mrp_units[unit_idx].device_status &= ~MRP_DEV_CPOPEN;
//0x000
MOD_DEC_USE_COUNT;
}
}
else {
//0x000
if (mrp_units[unit_idx].device_status & MRP_DEV_OPENED) {
//0x000
mrp_units[unit_idx].device_status &= ~MRP_DEV_OPENED;
//0x000
MOD_DEC_USE_COUNT;
}
}
}
}
//0x0000164c
return 0;
}
int mrp_ioctl(struct inode *inode, struct file *file, unsigned int io_cmd, unsigned long io_arg)
{
uint16_t bid;
uint16_t unit_idx;
struct mrp_unit *unit;
int result;
//0x000
unit_idx = MINOR(inode->i_rdev) & 0x3;
//0x000
unit = &mrp_units[unit_idx];
//0x000
if (mrp_debug > 1) {
//0x000
printk("mrp_ioctl: cmd=0x%x arg=0x%lx\n", io_cmd, io_arg);
}
//0x000
if (unit_idx >= MRP_MAX_UNITS) {
//0x000
return -ENODEV;
}
//0x000
if (!(unit->device_status & MRP_DEV_VALID)) {
//0x000
return -ENODEV;
}
bid = readw(&unit->las2b->bid);
//0x000
if (bid & MRP_BID_PIO_MODE) {
//0x000
if (!(unit->device_status & MRP_DEV_CPOPEN)) {
//0x000
return -ENODEV;
}
else {
//0x000
return -EINVAL;
}
}
else {
//0x000
if (!(unit->device_status & MRP_DEV_OPENED)) {
//0x000
return -ENODEV;
}
}
switch (io_cmd) {
//0x000
case MRP_IOCTL_RECV:
//0x000
if (unit->device_status & MRP_DEV_EPIPE) {
//0x000
unit->device_status &= ~MRP_DEV_EPIPE;
//0x000
return -EPIPE;
}
//0x000
else if (unit->device_status & MRP_DEV_RDONE) {
result = unit->device_status;
return result;
}
//0x000
else {
return 0;
}
//0x000
case MRP_IOCTL_RESET:
//0x000
unit->driver_status |= MRP_DRV_IOC;
//0x000
result = mrp_reset(unit);
//0x000
if (result < 0) {
return result;
}
//0x000
while (unit->device_status & (MRP_DEV_RESET | MRP_DEV_OPENED)) {
//0x000
interruptible_sleep_on(&unit->send_wait);
//0x000
if (signal_pending(current)) {
//0x000
return -EINTR;
}
}
//0x000
return 0;
//0x000
case MRP_IOCTL_RESET_INFO:
//0x000
result = unit->driver_status;
//0x000
unit->driver_status = 0;
//0x000
return result;
//0x000
case MRP_IOCTL_POWEROFF:
//0x000
writew(MRP_RST_POFF_REQ, &unit->las2b->rst);
//0x000
unit->device_status |= MRP_DEV_POWEROFF;
//0x000
return 0;
//0x000
default:
//0x000
return -EINVAL;
}
}
int mrp_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
{
//TODO should probably respect start/offset/length values
mrp_unit *unit;
uint32_t i, j;
uint32_t buf_idx;
uint32_t device_status;
struct mrp_regs *regs;
uint16_t bid;
//0x000
buf_idx = sprintf(buffer,"MRP-DECI2 [ Version %s %s %s ]\n",BUILD_VER,BUILD_DATE,BUILD_TIME);
for(i = 0; i < MRP_MAX_UNITS; ++i)
{
unit = &mrp_units[i];
bid = readw(&unit->las2b->bid);
device_status = unit->device_status;
//0x000
if (device_status & MRP_DEV_VALID) {
//0x000
buf_idx += sprintf(buffer + buf_idx,"unit%d",(bid & 0x3));
//0x000
if (device_status & MRP_DEV_DETECT) {
//0x000
buf_idx += sprintf(buffer + buf_idx," DETECT");
}
//0x000
buf_idx += sprintf(buffer + buf_idx," VALID");
//0x000
if (device_status & MRP_DEV_RESET) {
//0x000
buf_idx += sprintf(buffer + buf_idx," RESET");
}
//0x000
if (device_status & MRP_DEV_OPENED) {
//0x000
buf_idx += sprintf(buffer + buf_idx," OPENED");
}
//0x000
if (device_status & MRP_DEV_SBUSY) {
//0x000
buf_idx += sprintf(buffer + buf_idx," SBUSY");
}
//0x000
if (device_status & MRP_DEV_RDONE) {
//0x000
buf_idx += sprintf(buffer + buf_idx," RDONE");
}
//0x000
if (device_status & MRP_DEV_REQTAG) {
//0x000
buf_idx += sprintf(buffer + buf_idx," REQTAG");
}
//0x000
if (device_status & MRP_DEV_CPOPEN) {
//0x000
buf_idx += sprintf(buffer + buf_idx," CPOPEN");
}
//0x000
if (device_status & MRP_DEV_CPBUSY) {
//0x000
buf_idx += sprintf(buffer + buf_idx," CPBUSY");
}
//0x000
if (device_status & MRP_DEV_EPIPE) {
//0x000
buf_idx += sprintf(buffer + buf_idx," EPIPE");
}
//0x000
if (device_status & MRP_DEV_WRESET) {
//0x000
buf_idx += sprintf(buffer + buf_idx," WRESET");
}
//0x000
if (device_status & MRP_DEV_WBOOT) {
//0x000
buf_idx += sprintf(buffer + buf_idx," WBOOT");
}
//0x000
if (device_status & MRP_DEV_B3) {
//0x000
buf_idx += sprintf(buffer + buf_idx," B3");
}
//0x000
else if (device_status & MRP_DEV_VALID) {
//0x000
buf_idx += sprintf(buffer + buf_idx," PIF");
}
//0x000
if (device_status & MRP_DEV_POWEROFF) {
//0x000
buf_idx += sprintf(buffer + buf_idx," POWEROFF");
}
//0x000
buf_idx += sprintf(buffer + buf_idx," (");
//0x000
if (unit->driver_status & MRP_DRV_LOAD) {
//0x000
buf_idx += sprintf(buffer + buf_idx," LOAD");
}
//0x000
if (unit->driver_status & MRP_DRV_IOC) {
//0x000
buf_idx += sprintf(buffer + buf_idx," IOC");
}
//0x000
if (unit->driver_status & MRP_DRV_BPR) {
//0x000
buf_idx += sprintf(buffer + buf_idx," BPR");
}
//0x000
if (unit->driver_status & MRP_DRV_PIF) {
//0x000
buf_idx += sprintf(buffer + buf_idx," PIF");
}
//0x000
buf_idx += sprintf(buffer + buf_idx," )");
//0x000
buf_idx += sprintf(buffer + buf_idx,"\n");
//0x000
buf_idx += sprintf(buffer + buf_idx," nintr=%d",unit->nintr);
//0x000
buf_idx += sprintf(buffer + buf_idx," nfixed=%d",unit->nfixed);
//0x000
buf_idx += sprintf(buffer + buf_idx," slen=%d",unit->send_len);
//0x000
buf_idx += sprintf(buffer + buf_idx," rlen=%d",unit->recv_len);
//0x000
buf_idx += sprintf(buffer + buf_idx," scq.len=%d",unit->scq_len);
//0x000
buf_idx += sprintf(buffer + buf_idx," rcq.len=%d",unit->rcq_len);
//0x000
buf_idx += sprintf(buffer + buf_idx,"\n");
//0x000
for (j = 0; j < 16; ++j) {
//0x000
if (unit->stats[j] != 0) { //TODO why is stats[0] skipped and CPR/SI0 maintained separately?
//0x000
buf_idx += sprintf(buffer + buf_idx," stat[%s]=%d\n",intr_names[j],unit->stats[j]);
}
}
//0x000
if (unit->stats_rcv_cp != 0) {
//0x000
buf_idx += sprintf(buffer + buf_idx," stat[RCV_CP]=%d\n",unit->stats_rcv_cp);
}
//0x000
regs = unit->las2b;
//0x000
buf_idx += sprintf(buffer + buf_idx," BID=%04x",(uint32_t)readw(&regs->bid));
//0x000
buf_idx += sprintf(buffer + buf_idx," RST=%04x",(uint32_t)readw(&regs->rst));
//0x000
buf_idx += sprintf(buffer + buf_idx," CPS=%04x",(uint32_t)readw(&regs->cps));
//0x000
buf_idx += sprintf(buffer + buf_idx," CPR=%04x",(uint32_t)readw(&regs->cpr));
//0x000
buf_idx += sprintf(buffer + buf_idx," FST=%04x",(uint32_t)readw(&regs->fst));
//0x000
buf_idx += sprintf(buffer + buf_idx," TXC=%04x",(uint32_t)readw(&regs->txc));
//0x000
buf_idx += sprintf(buffer + buf_idx," RXC=%04x",(uint32_t)readw(&regs->rxc));
//0x000
buf_idx += sprintf(buffer + buf_idx," F1C=%04x",(uint32_t)readw(&regs->f1c));
//0x000
buf_idx += sprintf(buffer + buf_idx,"\n");
//0x000
buf_idx += sprintf(buffer + buf_idx," IST=%04x",(uint32_t)readw(&regs->ist));
//0x000
buf_idx += sprintf(buffer + buf_idx," ISP=%04x",(uint32_t)readw(&regs->isp));
//0x000
buf_idx += sprintf(buffer + buf_idx," IER=%04x",(uint32_t)readw(&regs->ier));
//0x000
buf_idx += sprintf(buffer + buf_idx," CSI=%04x",(uint32_t)readw(&regs->csi));
//0x000
buf_idx += sprintf(buffer + buf_idx," FSI=%04x",(uint32_t)readw(&regs->fsi));
//0x000
buf_idx += sprintf(buffer + buf_idx," AEO=%04x",(uint32_t)readw(&regs->aeo));
//0x000
buf_idx += sprintf(buffer + buf_idx," AFO=%04x",(uint32_t)readw(&regs->afo));
//0x000
buf_idx += sprintf(buffer + buf_idx,"\n");
}
}
//0x000
return buf_idx;
}
uint32_t mrp_base(struct pci_dev *dev,uint8_t wher)
{
uint32_t rc;
uint32_t baridx;
uint32_t configval;
//TODO make more easily readable
//0x000
baridx = (wher-0x10) >> 2;
//0x000
rc = pci_read_config_dword(dev,wher & 0xFF,&configval);
//0x000
if (rc != 0) {
//0x000
printk("mrp: can\'t read config (BASE%d)\n",baridx);
//0x000
return 0;
}
//0x000
if ((configval & 7U) != 0) {
//0x000
printk("mrp: unsupported address type (BASE%d=0x%x)\n",baridx,configval);
//0x000
return 0;
}
//0x000
return configval & ~0xF;
}
void *mrp_remap(void *base)
{
void *newbase;
//0x000
if ((((uint32_t)base) >> PAGE_SHIFT) < (((uint32_t)high_memory) >> PAGE_SHIFT)) {
//0x000
printk("mrp: base < high_memory ?? (base=0x%x)\n",base);
//0x000
return NULL;
}
else {
//0x000
newbase = ioremap((uint32_t) base & PAGE_MASK, PAGE_SIZE);
//0x000
if (newbase != NULL) {
//0x000
return (void *)(((uint32_t)base & ~PAGE_MASK) + (uint32_t)newbase);
}
else {
//0x000
//0x000
printk("mrp: can\'t ioremap (base=0x%x)\n",base);
//0x000
return NULL;
}
}
}
int mrp_init(void)
{
int rc;
uint32_t bar0, bar1, bar2;
uint8_t i;
uint8_t unit_count = 0;
struct pci_dev *dev;
uint8_t irq;
struct mrp_unit *unit;
uint32_t *cntrl_ptr;
uint32_t cntrl_value_orig;
uint32_t cntrl_value_tmp;
uint32_t intcsr_value;
// Clear device_status in all units
//0x000
for (i = 0; i < MRP_MAX_UNITS; ++i) {
//0x000
mrp_units[i].device_status = 0;
}
//0x000
for (i = 0; i < MRP_MAX_UNITS; ++i) {
unit = &mrp_units[i];
//0x000
if (pci_find_device(PCI_VENDOR_ID_SONY, PCI_DEVICE_ID_SONY_MRP,dev)) {
//0x000
break;
}
//0x000
bar0 = mrp_base(dev, 0x10);
//0x000
if (!bar0) {
//0x000
continue;
}
//0x000
bar1 = mrp_base(dev, 0x18);
//0x000
if (!bar1) {
//0x000
continue;
}
//0x000
bar2 = mrp_base(dev, 0x1c);
//0x000
if (!bar2) {
//0x000
continue;
}
//0x000
unit->las0b = mrp_remap((void *)bar0);
//0x000
if (unit->las0b == NULL) {
//0x000
continue;
}
//0x000
unit->las1b = mrp_remap((void *)bar1);
//0x000
if (unit->las1b == NULL) {
//0x000
continue;
}
//0x000
unit->las2b = (struct mrp_regs *)mrp_remap((void *)bar2);
//0x000
if (unit->las2b == NULL) {
continue;
}
//0x000
if (pci_read_config_byte(dev, 0x3c, &irq)) {
//0x000
printk("mrp: can't read config (IRQ)\n");
//0x000
continue;
}
//0x000
unit->irq = irq;
//0x000
printk("mrp: unit %d at 0x%x,0x%x,0x%x (irq = %d)\n",
i, bar0, bar1, bar2, irq);
//0x000
unit->device_status |= MRP_DEV_DETECT;
//0x000
++unit_count;
}
//0x000
if (unit_count == 0) {
//0x000
return 0;
}
//0x000
mrp_major = register_chrdev(0, "mrp", &mrp_fops);
//0x000
if (mrp_major < 1) {
//0x000
printk("mrp: unable to get dynamic major\n");
//0x000
return 0;
}
//0x000
printk("mrp: registered character major %d\n", mrp_major);
//0x000
proc_register(&proc_root, &mrp_proc_de);
//0x000
for (i = 0; i < MRP_MAX_UNITS; ++i) {
unit = &mrp_units[i];
//0x000
if (!(unit->device_status & MRP_DEV_DETECT)) {
//0x000
continue;
}
//0x000
unit->send_buf = kmalloc(0xffff, GFP_KERNEL);
//0x000
if (unit->send_buf == NULL) {
//0x000
//0x000
printk("mrp%d: no space for send buffer\n", i);
//0x000
continue;
}
//0x000
unit->recv_buf = kmalloc(0xffff, GFP_KERNEL);
//0x000
if (unit->recv_buf == NULL) {
//0x000
//0x000
printk("mrp%d: no space for recv buffer\n", i);
//0x000
continue;
}
//0x000
rc = request_irq(unit->irq
,mrp_interrupt
,SA_INTERRUPT | SA_SHIRQ
,"MRP"
,unit);
//0x000
if (rc != 0) {
//0x000
printk("mrp%d: can't register irq\n", i);
//0x000
continue;
}
//0x000
rc = request_irq(unit->irq
,mrp_interrupt
,SA_NOMASK
,"MRP"
,unit);
//0x000
if (rc != 0) {
//0x000
printk("mrp%d: can't register irq\n", i);
//0x000
continue;
}
//0x000
cntrl_ptr = unit->las0b + PCI9050_CNTRL;
//0x000
cntrl_value_orig = readl(cntrl_ptr);
cntrl_value_tmp = cntrl_value_orig;
//0x000
cntrl_value_tmp &= ~PCI9050_CNTRL_USER1_SIG;
//0x000
cntrl_value_tmp |= PCI9050_CNTRL_SW_RESET;
writel(cntrl_value_tmp,cntrl_ptr);
//0x000
udelay(10);
//0x000
writel(cntrl_value_orig & ~(PCI9050_CNTRL_SW_RESET | PCI9050_CNTRL_USER1_SIG), cntrl_ptr);
//0x000
intcsr_value = readl(unit->las0b + PCI9050_INTCSR);
//0x000
intcsr_value |= PCI9050_INTCSR_PCI_INT_ENA;
//0x000
writel(intcsr_value, unit->las0b + PCI9050_INTCSR);
//0x000
writew(0, &unit->las2b->ier);
//0x000
unit->device_status |= MRP_DEV_VALID;
//0x000
unit->driver_status |= MRP_DRV_LOAD;
//0x000
mrp_reset(unit);
//0x000
init_timer(&unit->init_timer);
//0x000
unit->init_timer.function = (void (*)(unsigned long)) mrp_b3timer;
//0x000
unit->init_timer.data = 0;
//0x000
unit->init_timer.expires = jiffies + 100;
//0x000
add_timer(&unit->init_timer);
}
//0x000
return 0;
}
void init_module(void)
{
//0x000
mrp_init();
//0x000
return;
}
void cleanup_module(void)
{
uint8_t i;
mrp_unit *unit;
uint32_t cntrl_value, intcsr_value;
//0x000
if (0 < mrp_major) {
//0x000
for(i=0; i < MRP_MAX_UNITS; ++i) {
unit = &mrp_units[i];
//0x000
if (unit->device_status & MRP_DEV_VALID) {
//0x000
cntrl_value = readl(unit->las0b + PCI9050_CNTRL);
//0x000
cntrl_value |= PCI9050_CNTRL_USER1_SIG;
//0x000
writel(cntrl_value, unit->las0b + PCI9050_CNTRL);
//0x000
intcsr_value = readl(unit->las0b + PCI9050_INTCSR);
//0x000
intcsr_value &= ~PCI9050_INTCSR_PCI_INT_ENA;
//0x000
writel(intcsr_value, unit->las0b + PCI9050_INTCSR);
//0x000
free_irq(unit->irq,unit);
}
//0x000
if (unit->send_buf != NULL) {
//0x000
kfree(unit->send_buf);
}
//0x000
if (unit->recv_buf != NULL) {
//0x000
kfree(unit->recv_buf);
}
//0x000
if (unit->las0b != NULL) {
//0x000
vfree((void *)((uint32_t)unit->las0b & PAGE_MASK));
}
//0x000
if (unit->las1b != NULL) {
//0x000
vfree((void *)((uint32_t)unit->las1b & PAGE_MASK));
}
//0x000
if (unit->las2b != (mrp_regs *)0x0) {
//0x000
vfree((void *)((uint32_t)unit->las2b & PAGE_MASK));
}
//0x000
del_timer(&unit->init_timer);
}
//0x000
unregister_chrdev(mrp_major,"mrp");
//0x000
printk("mrp: unregistered character major %d\n",mrp_major);
//0x000
proc_unregister(&proc_root,&mrp_proc_de);
}
//0x000
return;
}
/*
* guesses at acronyms:
*
* MRP = MediaDirect Remote Processor (or Message Routing Processor or Mini-RA Processor)
* DRP = DECI2 Remote Processor (or Remote Protocol, Routing Processor)
* PIF = PlayStation Interface
* TIF = TCP/IP Interface (or Tool Interface)
* CP = Communication Port (or Command Port, Control Processor, Command Processor)
* BPR = Boot Protocol Ready (or Back Plane Ready, Back Plane Reset)
* B3 = Board revision 3 (or Backplane v3)
* LDE = Link Delay Enable (or (FIFO) Load Enable)
*/
#include <asm/io.h>
#include <asm/segment.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/wait.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/poll.h>
#define PCI_VENDOR_ID_SONY 0x104d
#define PCI_DEVICE_ID_SONY_MRP 0x8047
#define PCI9050_INTCSR 0x4C /* Interrupt Control/Status */
#define PCI9050_CNTRL 0x50 /* Control Register */
/*
* PCI 9050-1 Data Book, version 1.1, pg7-23/4 (pg115-116)
*
* CNTRL register
* 0 = User I/O 0 Function
* 1 = User I/O 0 Direction
* 2 = User I/O 0 Signal
* 3 = User I/O 1 Function
* 4 = User I/O 1 Direction
* 5 = User I/O 1 Signal
* 6 = User I/O 2 Function
* 7 = User I/O 2 Direction
* 8 = User I/O 2 Signal
* 9 = User I/O 3 Function
* 10 = User I/O 3 Direction
* 11 = User I/O 3 Signal
* 13:12 = PCIBAR0/1 enable/disable
* 14 = PCI2.1 Feature Enable
* 15 = PCI Read with Write Flush Mode
* 16 = PCI Read No Flush Mode
* 17 = Direct Slave Burst Read with PCI Write Release Bus Mode
* 18 = PCI Write Release Bus Mode Enable
* 19:22 = PCI Direct Slave Retry Delay Clocks
* 23 = Direct Slave LOCK# Enable Bit
* 24 = Serial EEPROM Clock for PCI Bus Reads or Writes to Serial EEPROM
* 25 = Serial EEPROM Chip Select
* 26 = Write Bit to Serial EEPROM
* 27 = Read Serial EEPROM Data Bit
* 28 = Serial EEPROM Present
* 29 = Reload Configuration Registers
* 30 = PCI Adapter Software Reset
*/
#define PCI9050_CNTRL_USER0_FUN (1<<0)
#define PCI9050_CNTRL_USER0_DIR (1<<1)
#define PCI9050_CNTRL_USER0_SIG (1<<2)
#define PCI9050_CNTRL_USER1_FUN (1<<3)
#define PCI9050_CNTRL_USER1_DIR (1<<4)
#define PCI9050_CNTRL_USER1_SIG (1<<5)
#define PCI9050_CNTRL_USER2_FUN (1<<6)
#define PCI9050_CNTRL_USER2_DIR (1<<7)
#define PCI9050_CNTRL_USER2_SIG (1<<8)
#define PCI9050_CNTRL_USER3_FUN (1<<9)
#define PCI9050_CNTRL_USER3_DIR (1<<10)
#define PCI9050_CNTRL_USER3_SIG (1<<11)
#define PCI9050_CNTRL_BAR0_ENA (1<<12)
#define PCI9050_CNTRL_BAR1_ENA (1<<13)
#define PCI9050_CNTRL_PCI21_FEAT_ENA (1<<14)
#define PCI9050_CNTRL_PCI_RD_W_WR_FLUSH (1<<15)
#define PCI9050_CNTRL_PCI_RD_NO_FLUSH (1<<16)
#define PCI9050_CNTRL_DRT_SLV_BURST_RD (1<<17)
#define PCI9050_CNTRL_PCI_WR_REL_BUS (1<<18)
#define PCI9050_CNTRL_DRT_SLV_LCK (1<<23)
#define PCI9050_CNTRL_EEP_CLK (1<<24)
#define PCI9050_CNTRL_EEP_CS (1<<25)
#define PCI9050_CNTRL_EEP_WR (1<<26)
#define PCI9050_CNTRL_EEP_RD (1<<27)
#define PCI9050_CNTRL_EEP_PR (1<<28)
#define PCI9050_CNTRL_RLD_CFG_REG (1<<29)
#define PCI9050_CNTRL_SW_RESET (1<<30)
#define PCI9050_INTCSR_LINT1_ENA (1<<0)
#define PCI9050_INTCSR_LINT1_SIG (1<<1)
#define PCI9050_INTCSR_LINT1_ACT (1<<2)
#define PCI9050_INTCSR_LINT2_ENA (1<<3)
#define PCI9050_INTCSR_LINT2_SIG (1<<4)
#define PCI9050_INTCSR_LINT2_ACT (1<<5)
#define PCI9050_INTCSR_PCI_INT_ENA (1<<6)
#define PCI9050_INTCSR_SW_INT_ENA (1<<7)
#define MRP_MAX_UNITS 4
// DTL-T10k and DTL-15k both identify as 4126
#define MRP_BID_PIF 0x4126
#define MRP_BID_B3 0x4127
#define MRP_BID_PIO_MODE 0x40
#define MRP_DEV_DETECT 0x0001 /* device detected */
#define MRP_DEV_VALID 0x0002
#define MRP_DEV_RESET 0x0004
#define MRP_DEV_OPENED 0x0008
#define MRP_DEV_SBUSY 0x0010
#define MRP_DEV_RDONE 0x0020
#define MRP_DEV_REQTAG 0x0040
#define MRP_DEV_CPOPEN 0x0080
#define MRP_DEV_CPBUSY 0x0100
#define MRP_DEV_EPIPE 0x0200 /* FIFO error */
#define MRP_DEV_WRESET 0x0400 /* wait for reset completion */
#define MRP_DEV_WBOOT 0x0800 /* wait for boot completion */
#define MRP_DEV_B3 0x1000
#define MRP_DEV_POWEROFF 0x2000
#define MRP_DEV_LOCKED 0x80000000
// 0x1000 (4096) - 0x0C01 (3073) = 0x03FF (1023)
// HWM = high water mark (queue almost full)
// LWM = low water mark (queue almost empty)
#define MRP_FIFO_MAX 4096
#define MRP_FIFO_HWM_FREE 1023
#define MRP_FIFO_HWM_FREE_B3 127
#define MRP_FIFO_HWM MRP_FIFO_MAX-MRP_FIFO_HWM_FREE
#define MRP_FIFO_HWM_B3 MRP_FIFO_MAX-MRP_FIFO_HWM_FREE_B3
#define MRP_FIFO_HALF MRP_FIFO_MAX/2
#define MRP_FIFO_LWM 1023
#define MRP_FIFO_LWM_B3 127
#define MRP_FIFO_LWM_FREE MRP_FIFO_MAX-MRP_FIFO_LWM
#define MRP_FIFO_LWM_FREE_B3 MRP_FIFO_MAX-MRP_FIFO_LWM_B3
#define MRP_BUF_SIZE 0x1000
#define MRP_BUF_MASK 0x0FFF
#define MRP_FIFO_OFFSET_SEL_RX (1 << 14)
#define MRP_FIFO_OFFSET_LOAD (1 << 15)
#define MRP_DRV_LOAD 0x01 /* driver loaded */
#define MRP_DRV_IOC 0x02 /* ioctl(chardev) available */
#define MRP_DRV_BPR 0x04 /* backplane ready */
#define MRP_DRV_PIF 0x08 /* PIF communication established */
#define MRP_INT_SI0 0x0001 /* SI0 / CPREAD */
#define MRP_INT_SI1 0x0002 /* SI1 / CPSEND */
#define MRP_INT_SI2 0x0004 /* SI2 / BOOT */
#define MRP_INT_RST 0x0008 /* RESET */
#define MRP_INT_RXC 0x0010 /* RX FIFO Control/Counter */
#define MRP_INT_TXC 0x0020 /* TX FIFO Control/Counter */
#define MRP_INT_REF 0x0040 /* RX FIFO Empty Flag */
#define MRP_INT_RAE 0x0080 /* RX FIFO Almost Empty */
#define MRP_INT_RHF 0x0100 /* RX FIFO Half Empty */
#define MRP_INT_RAF 0x0200 /* RX FIFO Almost Full */
#define MRP_INT_RFF 0x0400 /* RX FIFO Full Flag */
#define MRP_INT_TEF 0x0800 /* TX FIFO Empty Flag */
#define MRP_INT_TAE 0x1000 /* TX FIFO Almost Empty */
#define MRP_INT_THF 0x2000 /* TX FIFO Half Full */
#define MRP_INT_TAF 0x4000 /* TX FIFO Almost Full */
#define MRP_INT_TFF 0x8000 /* TX FIFO Full Flag */
#define MRP_INT_RX_MASK (MRP_INT_RXC | MRP_INT_RAE)
//TODO this makes it look like lower 10 bits are for FIFOS ?
#define MRP_INT_SOME 0x03FF
#define MRP_INT_ALL 0x7FFF
// Reset status register (RST) bit definitions for MRP (unit->las2b->rst)
#define MRP_RST_POFF_REQ 0x0800 /* see mrp_ioctl */
#define MRP_RST_POWEROFF 0x1000 /* Power-off condition */
#define MRP_RST_BOOT 0x2000 /* Boot request or completed boot */
#define MRP_RST_PIF 0x4000 /* Reset triggered */
#define MRP_RST_BACKPLANE 0x8000 /* Master reset (full reset of device) */
// shouldn't bit 31 be set for vendor-specific codes?
#define MRP_IOCTL_RESET 0x41260001 /* Reset the unit and optionally wait */
#define MRP_IOCTL_RECV 0x41260002 /* Special result logic (custom behavior) */
#define MRP_IOCTL_RESET_INFO 0x41260003 /* Return device status and clear it */
#define MRP_IOCTL_POWEROFF 0x41260004 /* Write 0x800 to FIFO control register */
// EF = empty flag
// AE = almost empty
// HF = half full
// AF = almost full
// FF = full flag
// OE = (guess)
// RST = (guess)
// see IDT72V245 datasheet
#define MRP_FIFO_RX_EF 0x0001
#define MRP_FIFO_RX_AE 0x0002
#define MRP_FIFO_RX_HF 0x0004
#define MRP_FIFO_RX_AF 0x0008
#define MRP_FIFO_RX_FF 0x0010
#define MRP_FIFO_RX_OE 0x0040
#define MRP_FIFO_RX_RST 0x0080
#define MRP_FIFO_TX_EF 0x0100
#define MRP_FIFO_TX_AE 0x0200
#define MRP_FIFO_TX_HF 0x0400
#define MRP_FIFO_TX_AF 0x0800
#define MRP_FIFO_TX_FF 0x1000
#define MRP_FIFO_TX_OE 0x4000
#define MRP_FIFO_TX_RST 0x8000
// missing 0x0020 and 0x2000 above
#define MRP_DECI1_MAGIC 0xA14C
// All registers are 16-bit wide but aligned to 32-bit boundaries.
// comments for struct are generated guesses that should not in any way be considered correct or reliable information
struct mrp_regs {
uint16_t bid; // 0x00 -- Board ID
// Lower 2 bits: unit ID (0-3), or total unit count
// Bit 2 (0x4): Mode flag -- possibly set when acting as Control Processor / Command Port (CP) side
// Values: 0x4126 = B2 mode (guess at name), 0x4127 = B3 mode
// the DTL-T10000 and DTL-T15000 are both 0x4126, at least when booting, based on YouTube videos
// ==> unit_idx = 2 and CP mode enabled always
uint16_t __pad00;
uint16_t rst; // 0x04 -- Reset Status / Acknowledge
// Bit 12 (0x1000): CP Reset Acknowledge
// Bit 13 (0x2000): Backplane Reset Acknowledge
// Bit 14 (0x4000): Power Off Acknowledge
// Write mask with same bits to acknowledge/clear reset
uint16_t __pad04;
uint16_t cps; // 0x08 -- Com Port Send
// Used in `mrp_cps` to initiate command transmission
// Likely used for small control message handling
uint16_t __pad08;
uint16_t cpr; // 0x0c -- Com Port Receive
// Used in `mrp_cpr` to acknowledge or fetch a received command
// Acts as a status gate for command buffer readiness
uint16_t __pad0c;
uint16_t fst; // 0x10 -- FIFO Status
uint16_t __pad10;
uint16_t txc; // 0x14 -- Transmit Control / Complete
// Writing 1 clears transmit status flags
// Set by software after writing or resetting transmit FIFO
uint16_t __pad14;
uint16_t rxc; // 0x18 -- Receive Control / Complete
// Writing 1 clears receive status flags
// Set by software after reading or resetting receive FIFO
uint16_t __pad18;
uint16_t f1c; // 0x1c -- FIFO #1 Control ... or just free register at offset 1c
// Not yet referenced in known code, possibly second channel control
uint16_t __pad1c;
uint16_t ist; // 0x20 -- Interrupt Status
// Shadow of interrupt causes: ANDed with ier to generate real interrupts
uint16_t __pad20;
uint16_t isp; // 0x24 -- Interrupt Status Polarity
uint16_t __pad24;
uint16_t ier; // 0x28 -- Interrupt Enable
uint16_t __pad28;
uint16_t csi; // 0x2c -- Com Port Status Interrupts
// Bit 1 (0x0002): CP Send Complete flag
// Used in `mrp_interrupt` to finalize sends via CPS
uint16_t __pad2c;
uint16_t fsi; // 0x30 -- FIFO Status Interrupts
// Used in function mrp_bootp, mrp_cpr, mrp_cps.
uint16_t __pad30;
uint16_t aeo; // 0x34 -- Almost Empty Offset
uint16_t __pad34;
uint16_t afo; // 0x38 -- Almost Full Offset
uint16_t __pad38;
};
typedef struct mrp_regs mrp_regs;
struct mrp_unit {
// status of the FPGA device, based on its registers
//TODO assembly uses this as 8-bit value when accessing bits in lowest byte
uint32_t device_status;
// interrupt assigned to this driver, 8-bit
uint8_t irq;
// PCI local address space 0 base address
uint8_t *las0b;
// PCI local address space 1 base address
// first lword is FPGA's FIFO
uint16_t *las1b;
// PCI local address space 2 base address
// access to each of the 16x 16-bit registers on the FPGA
volatile struct mrp_regs *las2b;
// data to send to MRP. first 2 bytes are current size
uint32_t *send_buf;
// data received from MRP. first 4 bytes are current size
uint32_t *recv_buf;
uint32_t *send_ptr;
uint32_t *recv_ptr;
//0x24
uint32_t send_len;
//0x28
uint32_t recv_len;
//0x2c
struct wait_queue *recv_wait;
//0x30
struct wait_queue *send_wait;
// number of interrupts received
uint32_t nintr;
// number of interrupts added to counter due to code intervention
uint32_t nfixed;
uint32_t nunused; // UNUSED
uint8_t scq[MRP_BUF_SIZE];
//0x1040
uint32_t scq_idx; // UNUSED
//0x1044
uint32_t scq_ptr;
//0x1048
uint32_t scq_len;
//0x104c
uint8_t rcq[MRP_BUF_SIZE];
//0x204c
uint32_t rcq_idx;
//0x2050
uint32_t rcq_ptr; // UNUSED
//0x2054
uint32_t rcq_len;
//0x2058
struct wait_queue *cpr_wait;
//0x205c
struct wait_queue *cps_wait;
uint32_t stats[16];
uint32_t stats_rcv_cp;
//0x20a4
struct timer_list init_timer;
// high level status of the system
uint8_t driver_status;
};
typedef struct mrp_unit mrp_unit;
void mrp_dump_regs(struct mrp_regs *fifo);
int mrp_send(mrp_unit *unit_ptr);
int mrp_recv(struct mrp_unit *unit, uint16_t status);
int mrp_reset(struct mrp_unit *unit_base);
int mrp_bootp(mrp_unit *mrp_unit);
void mrp_cpr(mrp_unit *unit_base);
void mrp_cps(mrp_unit *mrp_unit);
void mrp_b3timer(uint32_t unit_idx);
void mrp_interrupt(int irq, struct mrp_unit *unit, struct pt_regs *ptr);
ssize_t mrp_read (struct file *, char *, size_t, loff_t *);
ssize_t mrp_write (struct file *, const char *, size_t, loff_t *);
unsigned int mrp_poll (struct file *, struct poll_table_struct *);
int mrp_ioctl (struct inode *, struct file *, unsigned int, unsigned long);
int mrp_open (struct inode *, struct file *);
int mrp_release (struct inode *, struct file *);
int mrp_get_info(char *, char **, off_t, int, int);
uint32_t mrp_base(struct pci_dev *dev,uint8_t wher);
void * mrp_remap(void *base);
int mrp_init(void);
void init_module(void);
void cleanup_module(void);
KDIR := /usr/src/linux-2.2.14
CC := gcc
CFLAGS := -Wall -DMODULE -D__KERNEL__ -I$(KDIR)/include
CFLAGS += -m486
CFLAGS += -O1
CFLAGS += -fno-strength-reduce
all: mrp.o
mrp.o: mrp.c
$(CC) $(CFLAGS) -c mrp.c -o mrp.o
mrp.s: mrp.c
$(CC) $(CFLAGS) -S mrp.c -o mrp.s
clean:
rm -f mrp.o mrp.s
KDIR := /usr/src/linux-2.4.7-10
CC := gcc
CFLAGS := -Wall -DMODULE -D__KERNEL__ -I$(KDIR)/include
CFLAGS += -m486
CFLAGS += -O1
CFLAGS += -fno-strength-reduce
all: mrp.o
mrp.o: mrp.c
$(CC) $(CFLAGS) -c mrp.c -o mrp.o
mrp.s: mrp.c
$(CC) $(CFLAGS) -S mrp.c -o mrp.s
clean:
rm -f mrp.o
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment