Last active
May 19, 2026 00:34
-
-
Save leftbrainstrain/fcd1e6ebae85ae10f69b7e2eaacd3682 to your computer and use it in GitHub Desktop.
PS2-TOOL MRP Linux 2.2 dsnet driver untested RE prototype
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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(®s->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(®s->ier); | |
| ier &= ~(MRP_INT_RXC | MRP_INT_RAE); | |
| writew(ier,®s->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,®s->rxc); | |
| //0x000 | |
| ier = readw(®s->ier); | |
| //0x000 | |
| ier |= (MRP_INT_RXC | MRP_INT_RAE); | |
| //0x000 | |
| writew(ier,®s->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, ®s->fst); | |
| //0x000 | |
| udelay(10); | |
| //0x000 | |
| writew(0,®s->fst); | |
| //0x000 | |
| writew(1,®s->rxc); | |
| //0x000 | |
| ier = readw(®s->ier); | |
| //0x000004d3 | |
| ier |= (MRP_INT_RXC | MRP_INT_RAE | MRP_INT_SI2); | |
| //0x000 | |
| writew(ier, ®s->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,®s->rxc); | |
| //0x000 | |
| unit->device_status |= MRP_DEV_RDONE; | |
| //0x000 | |
| wake_up(unit->recv_wait); | |
| } | |
| else { | |
| //0x000 | |
| ier = readw(®s->ier); | |
| //0x000 | |
| ier |= (MRP_INT_RXC | MRP_INT_RAE); | |
| //0x000 | |
| writew(ier,®s->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(®s->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, ®s->ier); // disable interrupts | |
| //0x0000071e | |
| writew(MRP_RST_BACKPLANE, ®s->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), ®s->fst); | |
| //0x00000746 | |
| udelay(10); | |
| //0x00000760 | |
| writew(0, ®s->fst); | |
| // configure FIFO devices | |
| //0x000 | |
| if (!(unit->device_status & MRP_DEV_B3)) { | |
| //0x000 | |
| aeo = MRP_FIFO_LWM & MRP_BUF_MASK; | |
| writew(aeo, ®s->aeo); | |
| //0x000 | |
| afo = MRP_FIFO_HWM_FREE & MRP_BUF_MASK; | |
| writew(afo, ®s->afo); | |
| //0x000 | |
| aeo &= ~MRP_FIFO_OFFSET_SEL_RX; // set output to TX FIFO | |
| //0x000 | |
| writew(aeo, ®s->aeo); | |
| //0x000 | |
| aeo |= MRP_FIFO_OFFSET_LOAD; // strobe FPGA to configure offset | |
| //0x000 | |
| writew(aeo, ®s->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(®s->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, ®s->aeo); | |
| //0x000 | |
| aeo |= MRP_FIFO_OFFSET_LOAD; // strobe FPGA to configure offset | |
| //0x000 | |
| writew(aeo, ®s->aeo); | |
| //0x000 | |
| delay_count = 0; | |
| do { | |
| // wait for signal to flip to indicate state change completed | |
| //0x000 | |
| udelay(10); | |
| //0x000 | |
| if (!((aeo = readw(®s->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), ®s->rst); | |
| //0x000 | |
| writew(0, ®s->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), ®s->isp); | |
| // set up initial com port interrupt mask | |
| //0x000 | |
| writew((MRP_INT_SI2 | MRP_INT_SI1 | MRP_INT_SI0), ®s->csi); | |
| // set up initial interrupt mask | |
| //0x000 | |
| writew((MRP_INT_RAE | MRP_INT_RXC | MRP_INT_RST | MRP_INT_SI2 | MRP_INT_SI0), ®s->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(®s->cpr); | |
| //0x000 | |
| if (cpr == 0x1300) { | |
| //0x000 | |
| tmp = 0x1300; | |
| } | |
| else { | |
| //0x000 | |
| rc = -1; | |
| } | |
| //0x000 | |
| writew(tmp, ®s->cps); | |
| //0x000 | |
| writew(MRP_INT_SI2, ®s->csi); | |
| //0x000 | |
| //0x000 | |
| writew(MRP_INT_SI2, ®s->fsi); | |
| //0x000 | |
| ier = readw(®s->ier); | |
| //0x000 | |
| ier |= MRP_INT_SI2; | |
| //0x000 | |
| writew(ier, ®s->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(®s->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, ®s->csi); | |
| writew(MRP_INT_SI1, ®s->fsi); | |
| //0x000 | |
| ier = readw(®s->ier); | |
| //0x000 | |
| ier |= MRP_INT_SI0; | |
| //0x000 | |
| writew(ier, ®s->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], ®s->cps); | |
| //0x000 | |
| ++unit->scq_ptr; | |
| //0x000 | |
| --unit->scq_len; | |
| //0x000 | |
| writew(MRP_INT_SI0, ®s->fsi); | |
| //0x000 | |
| ier = readw(®s->ier); | |
| //0x000 | |
| ier |= MRP_INT_SI1; | |
| //0x000 | |
| writew(ier, ®s->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(®s->ist); | |
| ier = readw(®s->ier); | |
| rxc = readw(®s->rxc); | |
| //0x000 | |
| rst = readw(®s->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, ®s->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, ®s->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,®s->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, ®s->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, ®s->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, ®s->ier); | |
| //0x000 | |
| unit->stats_rcv_cp++; | |
| //0x000 | |
| mrp_cpr(unit); | |
| //0x000 | |
| udelay(10); | |
| //0x000 | |
| ist = readw(®s->ist); | |
| //0x000 | |
| ier = readw(®s->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, ®s->ier); | |
| //0x000 | |
| writew(MRP_INT_SI1, ®s->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(®s->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, ®s->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(®s->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(®s->ier); | |
| //0x000 | |
| ier |= MRP_INT_SI0; | |
| //0x000 | |
| writew(ier,®s->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(®s->ier); | |
| // Re-enable receive interrupt | |
| //0x000 | |
| ier |= (MRP_INT_RXC | MRP_INT_RAE); | |
| //0x000 | |
| writew(ier, ®s->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(®s->bid)); | |
| //0x000 | |
| buf_idx += sprintf(buffer + buf_idx," RST=%04x",(uint32_t)readw(®s->rst)); | |
| //0x000 | |
| buf_idx += sprintf(buffer + buf_idx," CPS=%04x",(uint32_t)readw(®s->cps)); | |
| //0x000 | |
| buf_idx += sprintf(buffer + buf_idx," CPR=%04x",(uint32_t)readw(®s->cpr)); | |
| //0x000 | |
| buf_idx += sprintf(buffer + buf_idx," FST=%04x",(uint32_t)readw(®s->fst)); | |
| //0x000 | |
| buf_idx += sprintf(buffer + buf_idx," TXC=%04x",(uint32_t)readw(®s->txc)); | |
| //0x000 | |
| buf_idx += sprintf(buffer + buf_idx," RXC=%04x",(uint32_t)readw(®s->rxc)); | |
| //0x000 | |
| buf_idx += sprintf(buffer + buf_idx," F1C=%04x",(uint32_t)readw(®s->f1c)); | |
| //0x000 | |
| buf_idx += sprintf(buffer + buf_idx,"\n"); | |
| //0x000 | |
| buf_idx += sprintf(buffer + buf_idx," IST=%04x",(uint32_t)readw(®s->ist)); | |
| //0x000 | |
| buf_idx += sprintf(buffer + buf_idx," ISP=%04x",(uint32_t)readw(®s->isp)); | |
| //0x000 | |
| buf_idx += sprintf(buffer + buf_idx," IER=%04x",(uint32_t)readw(®s->ier)); | |
| //0x000 | |
| buf_idx += sprintf(buffer + buf_idx," CSI=%04x",(uint32_t)readw(®s->csi)); | |
| //0x000 | |
| buf_idx += sprintf(buffer + buf_idx," FSI=%04x",(uint32_t)readw(®s->fsi)); | |
| //0x000 | |
| buf_idx += sprintf(buffer + buf_idx," AEO=%04x",(uint32_t)readw(®s->aeo)); | |
| //0x000 | |
| buf_idx += sprintf(buffer + buf_idx," AFO=%04x",(uint32_t)readw(®s->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; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * 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); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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