📄 ohci1394.c
字号:
int ret = -ENOMEM;
xmit = kmalloc(sizeof(*xmit), GFP_KERNEL);
if (!xmit)
return -ENOMEM;
iso->hostdata = xmit;
xmit->ohci = iso->host->hostdata;
xmit->task_active = 0;
dma_prog_region_init(&xmit->prog);
prog_size = sizeof(struct iso_xmit_cmd) * iso->buf_packets;
if (dma_prog_region_alloc(&xmit->prog, prog_size, xmit->ohci->dev))
goto err;
ohci1394_init_iso_tasklet(&xmit->task, OHCI_ISO_TRANSMIT,
ohci_iso_xmit_task, (unsigned long) iso);
if (ohci1394_register_iso_tasklet(xmit->ohci, &xmit->task) < 0) {
ret = -EBUSY;
goto err;
}
xmit->task_active = 1;
/* xmit context registers are spaced 16 bytes apart */
ctx = xmit->task.context;
xmit->ContextControlSet = OHCI1394_IsoXmitContextControlSet + 16 * ctx;
xmit->ContextControlClear = OHCI1394_IsoXmitContextControlClear + 16 * ctx;
xmit->CommandPtr = OHCI1394_IsoXmitCommandPtr + 16 * ctx;
return 0;
err:
ohci_iso_xmit_shutdown(iso);
return ret;
}
static void ohci_iso_xmit_stop(struct hpsb_iso *iso)
{
struct ohci_iso_xmit *xmit = iso->hostdata;
struct ti_ohci *ohci = xmit->ohci;
/* disable interrupts */
reg_write(xmit->ohci, OHCI1394_IsoXmitIntMaskClear, 1 << xmit->task.context);
/* halt DMA */
if (ohci1394_stop_context(xmit->ohci, xmit->ContextControlClear, NULL)) {
/* XXX the DMA context will lock up if you try to send too much data! */
PRINT(KERN_ERR,
"you probably exceeded the OHCI card's bandwidth limit - "
"reload the module and reduce xmit bandwidth");
}
}
static void ohci_iso_xmit_shutdown(struct hpsb_iso *iso)
{
struct ohci_iso_xmit *xmit = iso->hostdata;
if (xmit->task_active) {
ohci_iso_xmit_stop(iso);
ohci1394_unregister_iso_tasklet(xmit->ohci, &xmit->task);
xmit->task_active = 0;
}
dma_prog_region_free(&xmit->prog);
kfree(xmit);
iso->hostdata = NULL;
}
static void ohci_iso_xmit_task(unsigned long data)
{
struct hpsb_iso *iso = (struct hpsb_iso*) data;
struct ohci_iso_xmit *xmit = iso->hostdata;
struct ti_ohci *ohci = xmit->ohci;
int wake = 0;
int count;
/* check the whole buffer if necessary, starting at pkt_dma */
for (count = 0; count < iso->buf_packets; count++) {
int cycle;
/* DMA descriptor */
struct iso_xmit_cmd *cmd = dma_region_i(&xmit->prog, struct iso_xmit_cmd, iso->pkt_dma);
/* check for new writes to xferStatus */
u16 xferstatus = le32_to_cpu(cmd->output_last.status) >> 16;
u8 event = xferstatus & 0x1F;
if (!event) {
/* packet hasn't been sent yet; we are done for now */
break;
}
if (event != 0x11)
PRINT(KERN_ERR,
"IT DMA error - OHCI error code 0x%02x\n", event);
/* at least one packet went out, so wake up the writer */
wake = 1;
/* parse cycle */
cycle = le32_to_cpu(cmd->output_last.status) & 0x1FFF;
/* tell the subsystem the packet has gone out */
hpsb_iso_packet_sent(iso, cycle, event != 0x11);
/* reset the DMA descriptor for next time */
cmd->output_last.status = 0;
}
if (wake)
hpsb_iso_wake(iso);
}
static int ohci_iso_xmit_queue(struct hpsb_iso *iso, struct hpsb_iso_packet_info *info)
{
struct ohci_iso_xmit *xmit = iso->hostdata;
struct ti_ohci *ohci = xmit->ohci;
int next_i, prev_i;
struct iso_xmit_cmd *next, *prev;
unsigned int offset;
unsigned short len;
unsigned char tag, sy;
/* check that the packet doesn't cross a page boundary
(we could allow this if we added OUTPUT_MORE descriptor support) */
if (cross_bound(info->offset, info->len)) {
PRINT(KERN_ERR,
"rawiso xmit: packet %u crosses a page boundary",
iso->first_packet);
return -EINVAL;
}
offset = info->offset;
len = info->len;
tag = info->tag;
sy = info->sy;
/* sync up the card's view of the buffer */
dma_region_sync_for_device(&iso->data_buf, offset, len);
/* append first_packet to the DMA chain */
/* by linking the previous descriptor to it */
/* (next will become the new end of the DMA chain) */
next_i = iso->first_packet;
prev_i = (next_i == 0) ? (iso->buf_packets - 1) : (next_i - 1);
next = dma_region_i(&xmit->prog, struct iso_xmit_cmd, next_i);
prev = dma_region_i(&xmit->prog, struct iso_xmit_cmd, prev_i);
/* set up the OUTPUT_MORE_IMMEDIATE descriptor */
memset(next, 0, sizeof(struct iso_xmit_cmd));
next->output_more_immediate.control = cpu_to_le32(0x02000008);
/* ISO packet header is embedded in the OUTPUT_MORE_IMMEDIATE */
/* tcode = 0xA, and sy */
next->iso_hdr[0] = 0xA0 | (sy & 0xF);
/* tag and channel number */
next->iso_hdr[1] = (tag << 6) | (iso->channel & 0x3F);
/* transmission speed */
next->iso_hdr[2] = iso->speed & 0x7;
/* payload size */
next->iso_hdr[6] = len & 0xFF;
next->iso_hdr[7] = len >> 8;
/* set up the OUTPUT_LAST */
next->output_last.control = cpu_to_le32(1 << 28);
next->output_last.control |= cpu_to_le32(1 << 27); /* update timeStamp */
next->output_last.control |= cpu_to_le32(3 << 20); /* want interrupt */
next->output_last.control |= cpu_to_le32(3 << 18); /* enable branch */
next->output_last.control |= cpu_to_le32(len);
/* payload bus address */
next->output_last.address = cpu_to_le32(dma_region_offset_to_bus(&iso->data_buf, offset));
/* leave branchAddress at zero for now */
/* re-write the previous DMA descriptor to chain to this one */
/* set prev branch address to point to next (Z=3) */
prev->output_last.branchAddress = cpu_to_le32(
dma_prog_region_offset_to_bus(&xmit->prog, sizeof(struct iso_xmit_cmd) * next_i) | 3);
/* disable interrupt, unless required by the IRQ interval */
if (prev_i % iso->irq_interval) {
prev->output_last.control &= cpu_to_le32(~(3 << 20)); /* no interrupt */
} else {
prev->output_last.control |= cpu_to_le32(3 << 20); /* enable interrupt */
}
wmb();
/* wake DMA in case it is sleeping */
reg_write(xmit->ohci, xmit->ContextControlSet, 1 << 12);
/* issue a dummy read of the cycle timer to force all PCI
writes to be posted immediately */
mb();
reg_read(xmit->ohci, OHCI1394_IsochronousCycleTimer);
return 0;
}
static int ohci_iso_xmit_start(struct hpsb_iso *iso, int cycle)
{
struct ohci_iso_xmit *xmit = iso->hostdata;
struct ti_ohci *ohci = xmit->ohci;
/* clear out the control register */
reg_write(xmit->ohci, xmit->ContextControlClear, 0xFFFFFFFF);
wmb();
/* address and length of first descriptor block (Z=3) */
reg_write(xmit->ohci, xmit->CommandPtr,
dma_prog_region_offset_to_bus(&xmit->prog, iso->pkt_dma * sizeof(struct iso_xmit_cmd)) | 3);
/* cycle match */
if (cycle != -1) {
u32 start = cycle & 0x1FFF;
/* 'cycle' is only mod 8000, but we also need two 'seconds' bits -
just snarf them from the current time */
u32 seconds = reg_read(xmit->ohci, OHCI1394_IsochronousCycleTimer) >> 25;
/* advance one second to give some extra time for DMA to start */
seconds += 1;
start |= (seconds & 3) << 13;
reg_write(xmit->ohci, xmit->ContextControlSet, 0x80000000 | (start << 16));
}
/* enable interrupts */
reg_write(xmit->ohci, OHCI1394_IsoXmitIntMaskSet, 1 << xmit->task.context);
/* run */
reg_write(xmit->ohci, xmit->ContextControlSet, 0x8000);
mb();
/* wait 100 usec to give the card time to go active */
udelay(100);
/* check the RUN bit */
if (!(reg_read(xmit->ohci, xmit->ContextControlSet) & 0x8000)) {
PRINT(KERN_ERR, "Error starting IT DMA (ContextControl 0x%08x)\n",
reg_read(xmit->ohci, xmit->ContextControlSet));
return -1;
}
return 0;
}
static int ohci_isoctl(struct hpsb_iso *iso, enum isoctl_cmd cmd, unsigned long arg)
{
switch(cmd) {
case XMIT_INIT:
return ohci_iso_xmit_init(iso);
case XMIT_START:
return ohci_iso_xmit_start(iso, arg);
case XMIT_STOP:
ohci_iso_xmit_stop(iso);
return 0;
case XMIT_QUEUE:
return ohci_iso_xmit_queue(iso, (struct hpsb_iso_packet_info*) arg);
case XMIT_SHUTDOWN:
ohci_iso_xmit_shutdown(iso);
return 0;
case RECV_INIT:
return ohci_iso_recv_init(iso);
case RECV_START: {
int *args = (int*) arg;
return ohci_iso_recv_start(iso, args[0], args[1], args[2]);
}
case RECV_STOP:
ohci_iso_recv_stop(iso);
return 0;
case RECV_RELEASE:
ohci_iso_recv_release(iso, (struct hpsb_iso_packet_info*) arg);
return 0;
case RECV_FLUSH:
ohci_iso_recv_task((unsigned long) iso);
return 0;
case RECV_SHUTDOWN:
ohci_iso_recv_shutdown(iso);
return 0;
case RECV_LISTEN_CHANNEL:
ohci_iso_recv_change_channel(iso, arg, 1);
return 0;
case RECV_UNLISTEN_CHANNEL:
ohci_iso_recv_change_channel(iso, arg, 0);
return 0;
case RECV_SET_CHANNEL_MASK:
ohci_iso_recv_set_channel_mask(iso, *((u64*) arg));
return 0;
default:
PRINT_G(KERN_ERR, "ohci_isoctl cmd %d not implemented yet",
cmd);
break;
}
return -EINVAL;
}
/***************************************
* IEEE-1394 functionality section END *
***************************************/
/********************************************************
* Global stuff (interrupt handler, init/shutdown code) *
********************************************************/
static void dma_trm_reset(struct dma_trm_ctx *d)
{
unsigned long flags;
LIST_HEAD(packet_list);
struct ti_ohci *ohci = d->ohci;
struct hpsb_packet *packet, *ptmp;
ohci1394_stop_context(ohci, d->ctrlClear, NULL);
/* Lock the context, reset it and release it. Move the packets
* that were pending in the context to packet_list and free
* them after releasing the lock. */
spin_lock_irqsave(&d->lock, flags);
list_splice(&d->fifo_list, &packet_list);
list_splice(&d->pending_list, &packet_list);
INIT_LIST_HEAD(&d->fifo_list);
INIT_LIST_HEAD(&d->pending_list);
d->branchAddrPtr = NULL;
d->sent_ind = d->prg_ind;
d->free_prgs = d->num_desc;
spin_unlock_irqrestore(&d->lock, flags);
if (list_empty(&packet_list))
return;
PRINT(KERN_INFO, "AT dma reset ctx=%d, aborting transmission", d->ctx);
/* Now process subsystem callbacks for the packets from this
* context. */
list_for_each_entry_safe(packet, ptmp, &packet_list, driver_list) {
list_del_init(&packet->driver_list);
hpsb_packet_sent(ohci->host, packet, ACKX_ABORTED);
}
}
static void ohci_schedule_iso_tasklets(struct ti_ohci *ohci,
quadlet_t rx_event,
quadlet_t tx_event)
{
struct ohci1394_iso_tasklet *t;
unsigned long mask;
unsigned long flags;
spin_lock_irqsave(&ohci->iso_tasklet_list_lock, flags);
list_for_each_entry(t, &ohci->iso_tasklet_list, link) {
mask = 1 << t->context;
if (t->type == OHCI_ISO_TRANSMIT && tx_event & mask)
tasklet_schedule(&t->tasklet);
else if (rx_event & mask)
tasklet_schedule(&t->tasklet);
}
spin_unlock_irqrestore(&ohci->iso_tasklet_list_lock, flags);
}
static irqreturn_t ohci_irq_handler(int irq, void *dev_id)
{
quadlet_t event, node_id;
struct ti_ohci *ohci = (struct ti_ohci *)dev_id;
struct hpsb_host *host = ohci->host;
int phyid = -1, isroot = 0;
unsigned long flags;
/* Read and clear the interrupt event register. Don't clear
* the busReset event, though. This is done when we get the
* selfIDComplete interrupt. */
spin_lock_irqsave(&ohci->event_lock, flags);
event = reg_read(ohci, OHCI1394_IntEventClear);
reg_write(ohci, OHCI1394_IntEventClear, event & ~OHCI1394_busReset);
spin_unlock_irqrestore(&ohci->event_lock, flags);
if (!event)
return IRQ_NONE;
/* If event is ~(u32)0 cardbus card was ejected. In this case
* we just return, and clean up in the ohci1394_pci_remove
* function. */
if (event == ~(u32) 0) {
DBGMSG("Device removed.");
return IRQ_NONE;
}
DBGMSG("IntEvent: %08x", event);
if (event & OHCI1394_unrecoverableError) {
int ctx;
PRINT(KERN_ERR, "Unrecoverable error!");
if (reg_read(ohci, OHCI1394_AsReqTrContextControlSet) & 0x800)
PRINT(KERN_ERR, "Async Req Tx Context died: "
"ctrl[%08x] cmdptr[%08x]",
reg_read(ohci, OHCI1394_AsReqTrContextControlSet),
reg_read(ohci, OHCI1394_AsReqTrCommandPtr));
if (reg_read(ohci, OHCI1394_AsRspTrContextControlSet) & 0x800)
PRINT(KERN_ERR, "Async Rsp Tx Context died: "
"ctrl[%08x] cmdptr[%08x]",
reg_read(ohci, OHCI1394_AsRspTrContextControlSet),
reg_read(ohci, OHCI1394_AsRspTrCommandPtr));
if (reg_read(ohci, OHCI1394_AsReqRcvContextControlSet) & 0x800)
PRINT(KERN_ERR, "Async Req Rcv Context died: "
"ctrl[%08x] cmdptr[%08x]",
reg_read(ohci, OHCI1394_AsReqRcvContextControlSet),
reg_read(ohci, OHCI1394_AsReqRcvCommandPtr));
if (reg_read(ohci, OHCI1394_AsRspRcvContextControlS
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -