📄 ppc85xx_rio.c
字号:
/* Set number of entries */ out_be32((void *)&msg_regs->omr, in_be32((void *)&msg_regs->omr) | ((get_bitmask_order(entries) - 2) << 12)); /* Now enable the unit */ out_be32((void *)&msg_regs->omr, in_be32((void *)&msg_regs->omr) | 0x1); out: return rc; out_irq: dma_free_coherent(NULL, msg_tx_ring.size * RIO_MSG_DESC_SIZE, msg_tx_ring.virt, msg_tx_ring.phys); out_dma: for (i = 0; i < msg_tx_ring.size; i++) dma_free_coherent(NULL, RIO_MSG_BUFFER_SIZE, msg_tx_ring.virt_buffer[i], msg_tx_ring.phys_buffer[i]); return rc;}/** * rio_close_outb_mbox - Shut down MPC85xx outbound mailbox * @mport: Master port implementing the outbound message unit * @mbox: Mailbox to close * * Disables the outbound message unit, free all buffers, and * frees the outbound message interrupt. */void rio_close_outb_mbox(struct rio_mport *mport, int mbox){ /* Disable inbound message unit */ out_be32((void *)&msg_regs->omr, 0); /* Free ring */ dma_free_coherent(NULL, msg_tx_ring.size * RIO_MSG_DESC_SIZE, msg_tx_ring.virt, msg_tx_ring.phys); /* Free interrupt */ free_irq(MPC85xx_IRQ_RIO_TX, (void *)mport);}/** * mpc85xx_rio_rx_handler - MPC85xx inbound message interrupt handler * @irq: Linux interrupt number * @dev_instance: Pointer to interrupt-specific data * @regs: Register context * * Handles inbound message interrupts. Executes a registered inbound * mailbox event handler and acks the interrupt occurence. */static irqreturn_tmpc85xx_rio_rx_handler(int irq, void *dev_instance, struct pt_regs *regs){ int isr; struct rio_mport *port = (struct rio_mport *)dev_instance; isr = in_be32((void *)&msg_regs->isr); if (isr & RIO_MSG_ISR_TE) { pr_info("RIO: inbound message reception error\n"); out_be32((void *)&msg_regs->isr, RIO_MSG_ISR_TE); goto out; } /* XXX Need to check/dispatch until queue empty */ if (isr & RIO_MSG_ISR_DIQI) { /* * We implement *only* mailbox 0, but can receive messages * for any mailbox/letter to that mailbox destination. So, * make the callback with an unknown/invalid mailbox number * argument. */ port->inb_msg[0].mcback(port, msg_rx_ring.dev_id, -1, -1); /* Ack the queueing interrupt */ out_be32((void *)&msg_regs->isr, RIO_MSG_ISR_DIQI); } out: return IRQ_HANDLED;}/** * rio_open_inb_mbox - Initialize MPC85xx inbound mailbox * @mport: Master port implementing the inbound message unit * @dev_id: Device specific pointer to pass on event * @mbox: Mailbox to open * @entries: Number of entries in the inbound mailbox ring * * Initializes buffer ring, request the inbound message interrupt, * and enables the inbound message unit. Returns %0 on success * and %-EINVAL or %-ENOMEM on failure. */int rio_open_inb_mbox(struct rio_mport *mport, void *dev_id, int mbox, int entries){ int i, rc = 0; if ((entries < RIO_MIN_RX_RING_SIZE) || (entries > RIO_MAX_RX_RING_SIZE) || (!is_power_of_2(entries))) { rc = -EINVAL; goto out; } /* Initialize client buffer ring */ msg_rx_ring.dev_id = dev_id; msg_rx_ring.size = entries; msg_rx_ring.rx_slot = 0; for (i = 0; i < msg_rx_ring.size; i++) msg_rx_ring.virt_buffer[i] = NULL; /* Initialize inbound message ring */ if (!(msg_rx_ring.virt = dma_alloc_coherent(NULL, msg_rx_ring.size * RIO_MAX_MSG_SIZE, &msg_rx_ring.phys, GFP_KERNEL))) { rc = -ENOMEM; goto out; } /* Point dequeue/enqueue pointers at first entry in ring */ out_be32((void *)&msg_regs->ifqdpar, (u32) msg_rx_ring.phys); out_be32((void *)&msg_regs->ifqepar, (u32) msg_rx_ring.phys); /* Clear interrupt status */ out_be32((void *)&msg_regs->isr, 0x00000091); /* Hook up inbound message handler */ if ((rc = request_irq(MPC85xx_IRQ_RIO_RX, mpc85xx_rio_rx_handler, 0, "msg_rx", (void *)mport)) < 0) { dma_free_coherent(NULL, RIO_MSG_BUFFER_SIZE, msg_tx_ring.virt_buffer[i], msg_tx_ring.phys_buffer[i]); goto out; } /* * Configure inbound message unit: * Snooping * 4KB max message size * Unmask all interrupt sources * Disable */ out_be32((void *)&msg_regs->imr, 0x001b0060); /* Set number of queue entries */ out_be32((void *)&msg_regs->imr, in_be32((void *)&msg_regs->imr) | ((get_bitmask_order(entries) - 2) << 12)); /* Now enable the unit */ out_be32((void *)&msg_regs->imr, in_be32((void *)&msg_regs->imr) | 0x1); out: return rc;}/** * rio_close_inb_mbox - Shut down MPC85xx inbound mailbox * @mport: Master port implementing the inbound message unit * @mbox: Mailbox to close * * Disables the inbound message unit, free all buffers, and * frees the inbound message interrupt. */void rio_close_inb_mbox(struct rio_mport *mport, int mbox){ /* Disable inbound message unit */ out_be32((void *)&msg_regs->imr, 0); /* Free ring */ dma_free_coherent(NULL, msg_rx_ring.size * RIO_MAX_MSG_SIZE, msg_rx_ring.virt, msg_rx_ring.phys); /* Free interrupt */ free_irq(MPC85xx_IRQ_RIO_RX, (void *)mport);}/** * rio_hw_add_inb_buffer - Add buffer to the MPC85xx inbound message queue * @mport: Master port implementing the inbound message unit * @mbox: Inbound mailbox number * @buf: Buffer to add to inbound queue * * Adds the @buf buffer to the MPC85xx inbound message queue. Returns * %0 on success or %-EINVAL on failure. */int rio_hw_add_inb_buffer(struct rio_mport *mport, int mbox, void *buf){ int rc = 0; pr_debug("RIO: rio_hw_add_inb_buffer(), msg_rx_ring.rx_slot %d\n", msg_rx_ring.rx_slot); if (msg_rx_ring.virt_buffer[msg_rx_ring.rx_slot]) { printk(KERN_ERR "RIO: error adding inbound buffer %d, buffer exists\n", msg_rx_ring.rx_slot); rc = -EINVAL; goto out; } msg_rx_ring.virt_buffer[msg_rx_ring.rx_slot] = buf; if (++msg_rx_ring.rx_slot == msg_rx_ring.size) msg_rx_ring.rx_slot = 0; out: return rc;}EXPORT_SYMBOL_GPL(rio_hw_add_inb_buffer);/** * rio_hw_get_inb_message - Fetch inbound message from the MPC85xx message unit * @mport: Master port implementing the inbound message unit * @mbox: Inbound mailbox number * * Gets the next available inbound message from the inbound message queue. * A pointer to the message is returned on success or NULL on failure. */void *rio_hw_get_inb_message(struct rio_mport *mport, int mbox){ u32 imr; u32 phys_buf, virt_buf; void *buf = NULL; int buf_idx; phys_buf = in_be32((void *)&msg_regs->ifqdpar); /* If no more messages, then bail out */ if (phys_buf == in_be32((void *)&msg_regs->ifqepar)) goto out2; virt_buf = (u32) msg_rx_ring.virt + (phys_buf - msg_rx_ring.phys); buf_idx = (phys_buf - msg_rx_ring.phys) / RIO_MAX_MSG_SIZE; buf = msg_rx_ring.virt_buffer[buf_idx]; if (!buf) { printk(KERN_ERR "RIO: inbound message copy failed, no buffers\n"); goto out1; } /* Copy max message size, caller is expected to allocate that big */ memcpy(buf, (void *)virt_buf, RIO_MAX_MSG_SIZE); /* Clear the available buffer */ msg_rx_ring.virt_buffer[buf_idx] = NULL; out1: imr = in_be32((void *)&msg_regs->imr); out_be32((void *)&msg_regs->imr, imr | RIO_MSG_IMR_MI); out2: return buf;}EXPORT_SYMBOL_GPL(rio_hw_get_inb_message);/** * mpc85xx_rio_dbell_handler - MPC85xx doorbell interrupt handler * @irq: Linux interrupt number * @dev_instance: Pointer to interrupt-specific data * @regs: Register context * * Handles doorbell interrupts. Parses a list of registered * doorbell event handlers and executes a matching event handler. */static irqreturn_tmpc85xx_rio_dbell_handler(int irq, void *dev_instance, struct pt_regs *regs){ int dsr; struct rio_mport *port = (struct rio_mport *)dev_instance; dsr = in_be32((void *)&msg_regs->dsr); if (dsr & DOORBELL_DSR_TE) { pr_info("RIO: doorbell reception error\n"); out_be32((void *)&msg_regs->dsr, DOORBELL_DSR_TE); goto out; } if (dsr & DOORBELL_DSR_QFI) { pr_info("RIO: doorbell queue full\n"); out_be32((void *)&msg_regs->dsr, DOORBELL_DSR_QFI); goto out; } /* XXX Need to check/dispatch until queue empty */ if (dsr & DOORBELL_DSR_DIQI) { u32 dmsg = (u32) dbell_ring.virt + (in_be32((void *)&msg_regs->dqdpar) & 0xfff); u32 dmr; struct rio_dbell *dbell; int found = 0; pr_debug ("RIO: processing doorbell, sid %2.2x tid %2.2x info %4.4x\n", DBELL_SID(dmsg), DBELL_TID(dmsg), DBELL_INF(dmsg)); list_for_each_entry(dbell, &port->dbells, node) { if ((dbell->res->start <= DBELL_INF(dmsg)) && (dbell->res->end >= DBELL_INF(dmsg))) { found = 1; break; } } if (found) { dbell->dinb(port, dbell->dev_id, DBELL_SID(dmsg), DBELL_TID(dmsg), DBELL_INF(dmsg)); } else { pr_debug ("RIO: spurious doorbell, sid %2.2x tid %2.2x info %4.4x\n", DBELL_SID(dmsg), DBELL_TID(dmsg), DBELL_INF(dmsg)); } dmr = in_be32((void *)&msg_regs->dmr); out_be32((void *)&msg_regs->dmr, dmr | DOORBELL_DMR_DI); out_be32((void *)&msg_regs->dsr, DOORBELL_DSR_DIQI); } out: return IRQ_HANDLED;}/** * mpc85xx_rio_doorbell_init - MPC85xx doorbell interface init * @mport: Master port implementing the inbound doorbell unit * * Initializes doorbell unit hardware and inbound DMA buffer * ring. Called from mpc85xx_rio_setup(). Returns %0 on success * or %-ENOMEM on failure. */static int mpc85xx_rio_doorbell_init(struct rio_mport *mport){ int rc = 0; /* Map outbound doorbell window immediately after maintenance window */ if (!(dbell_win = (u32) ioremap(mport->iores.start + RIO_MAINT_WIN_SIZE, RIO_DBELL_WIN_SIZE))) { printk(KERN_ERR "RIO: unable to map outbound doorbell window\n"); rc = -ENOMEM; goto out; } /* Initialize inbound doorbells */ if (!(dbell_ring.virt = dma_alloc_coherent(NULL, 512 * DOORBELL_MESSAGE_SIZE, &dbell_ring.phys, GFP_KERNEL))) { printk(KERN_ERR "RIO: unable allocate inbound doorbell ring\n"); rc = -ENOMEM; iounmap((void *)dbell_win); goto out; } /* Point dequeue/enqueue pointers at first entry in ring */ out_be32((void *)&msg_regs->dqdpar, (u32) dbell_ring.phys); out_be32((void *)&msg_regs->dqepar, (u32) dbell_ring.phys); /* Clear interrupt status */ out_be32((void *)&msg_regs->dsr, 0x00000091); /* Hook up doorbell handler */ if ((rc = request_irq(MPC85xx_IRQ_RIO_BELL, mpc85xx_rio_dbell_handler, 0, "dbell_rx", (void *)mport) < 0)) { iounmap((void *)dbell_win); dma_free_coherent(NULL, 512 * DOORBELL_MESSAGE_SIZE, dbell_ring.virt, dbell_ring.phys); printk(KERN_ERR "MPC85xx RIO: unable to request inbound doorbell irq"); goto out; } /* Configure doorbells for snooping, 512 entries, and enable */ out_be32((void *)&msg_regs->dmr, 0x00108161); out: return rc;}static char *cmdline = NULL;static int mpc85xx_rio_get_hdid(int index){ /* XXX Need to parse multiple entries in some format */ if (!cmdline) return -1; return simple_strtol(cmdline, NULL, 0);}static int mpc85xx_rio_get_cmdline(char *s){ if (!s) return 0; cmdline = s; return 1;}__setup("riohdid=", mpc85xx_rio_get_cmdline);/** * mpc85xx_rio_setup - Setup MPC85xx RapidIO interface * @law_start: Starting physical address of RapidIO LAW * @law_size: Size of RapidIO LAW * * Initializes MPC85xx RapidIO hardware interface, configures * master port with system-specific info, and registers the * master port with the RapidIO subsystem. */void mpc85xx_rio_setup(int law_start, int law_size){ struct rio_ops *ops; struct rio_mport *port; ops = kmalloc(sizeof(struct rio_ops), GFP_KERNEL); ops->lcread = mpc85xx_local_config_read; ops->lcwrite = mpc85xx_local_config_write; ops->cread = mpc85xx_rio_config_read; ops->cwrite = mpc85xx_rio_config_write; ops->dsend = mpc85xx_rio_doorbell_send; port = kmalloc(sizeof(struct rio_mport), GFP_KERNEL); port->id = 0; port->index = 0; INIT_LIST_HEAD(&port->dbells); port->iores.start = law_start; port->iores.end = law_start + law_size; port->iores.flags = IORESOURCE_MEM; rio_init_dbell_res(&port->riores[RIO_DOORBELL_RESOURCE], 0, 0xffff); rio_init_mbox_res(&port->riores[RIO_INB_MBOX_RESOURCE], 0, 0); rio_init_mbox_res(&port->riores[RIO_OUTB_MBOX_RESOURCE], 0, 0); strcpy(port->name, "RIO0 mport"); port->ops = ops; port->host_deviceid = mpc85xx_rio_get_hdid(port->id); rio_register_mport(port); regs_win = (u32) ioremap(RIO_REGS_BASE, 0x20000); atmu_regs = (struct rio_atmu_regs *)(regs_win + RIO_ATMU_REGS_OFFSET); maint_atmu_regs = atmu_regs + 1; dbell_atmu_regs = atmu_regs + 2; msg_regs = (struct rio_msg_regs *)(regs_win + RIO_MSG_REGS_OFFSET); /* Configure maintenance transaction window */ out_be32((void *)&maint_atmu_regs->rowbar, 0x000c0000); out_be32((void *)&maint_atmu_regs->rowar, 0x80077015); maint_win = (u32) ioremap(law_start, RIO_MAINT_WIN_SIZE); /* Configure outbound doorbell window */ out_be32((void *)&dbell_atmu_regs->rowbar, 0x000c0400); out_be32((void *)&dbell_atmu_regs->rowar, 0x8004200b); mpc85xx_rio_doorbell_init(port);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -