📄 pcilynx.c
字号:
} else {
ack = (pcl.pcl_status >> 15) & 0xf;
}
} else {
PRINT(KERN_INFO, lynx->id, "async packet was not completed");
ack = ACKX_ABORTED;
}
hpsb_packet_sent(host, packet, ack);
}
while (!list_empty(&packet_list)) {
packet = driver_packet(packet_list.next);
list_del_init(&packet->driver_list);
hpsb_packet_sent(host, packet, ACKX_ABORTED);
}
break;
#if 0 /* has been removed from ieee1394 core */
case ISO_LISTEN_CHANNEL:
spin_lock_irqsave(&lynx->iso_rcv.lock, flags);
if (lynx->iso_rcv.chan_count++ == 0) {
reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV),
DMA_WORD1_CMP_ENABLE_MASTER);
}
spin_unlock_irqrestore(&lynx->iso_rcv.lock, flags);
break;
case ISO_UNLISTEN_CHANNEL:
spin_lock_irqsave(&lynx->iso_rcv.lock, flags);
if (--lynx->iso_rcv.chan_count == 0) {
reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV),
0);
}
spin_unlock_irqrestore(&lynx->iso_rcv.lock, flags);
break;
#endif
default:
PRINT(KERN_ERR, lynx->id, "unknown devctl command %d", cmd);
retval = -1;
}
return retval;
}
/***************************************
* IEEE-1394 functionality section END *
***************************************/
/********************************************************
* Global stuff (interrupt handler, init/shutdown code) *
********************************************************/
static irqreturn_t lynx_irq_handler(int irq, void *dev_id)
{
struct ti_lynx *lynx = (struct ti_lynx *)dev_id;
struct hpsb_host *host = lynx->host;
u32 intmask;
u32 linkint;
linkint = reg_read(lynx, LINK_INT_STATUS);
intmask = reg_read(lynx, PCI_INT_STATUS);
if (!(intmask & PCI_INT_INT_PEND))
return IRQ_NONE;
PRINTD(KERN_DEBUG, lynx->id, "interrupt: 0x%08x / 0x%08x", intmask,
linkint);
reg_write(lynx, LINK_INT_STATUS, linkint);
reg_write(lynx, PCI_INT_STATUS, intmask);
if (intmask & PCI_INT_1394) {
if (linkint & LINK_INT_PHY_TIMEOUT) {
PRINT(KERN_INFO, lynx->id, "PHY timeout occurred");
}
if (linkint & LINK_INT_PHY_BUSRESET) {
PRINT(KERN_INFO, lynx->id, "bus reset interrupt");
lynx->selfid_size = -1;
lynx->phy_reg0 = -1;
if (!host->in_bus_reset)
hpsb_bus_reset(host);
}
if (linkint & LINK_INT_PHY_REG_RCVD) {
u32 reg;
spin_lock(&lynx->phy_reg_lock);
reg = reg_read(lynx, LINK_PHY);
spin_unlock(&lynx->phy_reg_lock);
if (!host->in_bus_reset) {
PRINT(KERN_INFO, lynx->id,
"phy reg received without reset");
} else if (reg & 0xf00) {
PRINT(KERN_INFO, lynx->id,
"unsolicited phy reg %d received",
(reg >> 8) & 0xf);
} else {
lynx->phy_reg0 = reg & 0xff;
handle_selfid(lynx, host);
}
}
if (linkint & LINK_INT_ISO_STUCK) {
PRINT(KERN_INFO, lynx->id, "isochronous transmitter stuck");
}
if (linkint & LINK_INT_ASYNC_STUCK) {
PRINT(KERN_INFO, lynx->id, "asynchronous transmitter stuck");
}
if (linkint & LINK_INT_SENT_REJECT) {
PRINT(KERN_INFO, lynx->id, "sent reject");
}
if (linkint & LINK_INT_TX_INVALID_TC) {
PRINT(KERN_INFO, lynx->id, "invalid transaction code");
}
if (linkint & LINK_INT_GRF_OVERFLOW) {
/* flush FIFO if overflow happens during reset */
if (host->in_bus_reset)
reg_write(lynx, FIFO_CONTROL,
FIFO_CONTROL_GRF_FLUSH);
PRINT(KERN_INFO, lynx->id, "GRF overflow");
}
if (linkint & LINK_INT_ITF_UNDERFLOW) {
PRINT(KERN_INFO, lynx->id, "ITF underflow");
}
if (linkint & LINK_INT_ATF_UNDERFLOW) {
PRINT(KERN_INFO, lynx->id, "ATF underflow");
}
}
if (intmask & PCI_INT_DMA_HLT(CHANNEL_ISO_RCV)) {
PRINTD(KERN_DEBUG, lynx->id, "iso receive");
spin_lock(&lynx->iso_rcv.lock);
lynx->iso_rcv.stat[lynx->iso_rcv.next] =
reg_read(lynx, DMA_CHAN_STAT(CHANNEL_ISO_RCV));
lynx->iso_rcv.used++;
lynx->iso_rcv.next = (lynx->iso_rcv.next + 1) % NUM_ISORCV_PCL;
if ((lynx->iso_rcv.next == lynx->iso_rcv.last)
|| !lynx->iso_rcv.chan_count) {
PRINTD(KERN_DEBUG, lynx->id, "stopped");
reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), 0);
}
run_sub_pcl(lynx, lynx->iso_rcv.pcl_start, lynx->iso_rcv.next,
CHANNEL_ISO_RCV);
spin_unlock(&lynx->iso_rcv.lock);
tasklet_schedule(&lynx->iso_rcv.tq);
}
if (intmask & PCI_INT_DMA_HLT(CHANNEL_ASYNC_SEND)) {
PRINTD(KERN_DEBUG, lynx->id, "async sent");
spin_lock(&lynx->async.queue_lock);
if (list_empty(&lynx->async.pcl_queue)) {
spin_unlock(&lynx->async.queue_lock);
PRINT(KERN_WARNING, lynx->id, "async dma halted, but no queued packet (maybe it was cancelled)");
} else {
struct ti_pcl pcl;
u32 ack;
struct hpsb_packet *packet;
get_pcl(lynx, lynx->async.pcl, &pcl);
packet = driver_packet(lynx->async.pcl_queue.next);
list_del_init(&packet->driver_list);
pci_unmap_single(lynx->dev, lynx->async.header_dma,
packet->header_size, PCI_DMA_TODEVICE);
if (packet->data_size) {
pci_unmap_single(lynx->dev, lynx->async.data_dma,
packet->data_size, PCI_DMA_TODEVICE);
}
if (!list_empty(&lynx->async.queue)) {
send_next(lynx, hpsb_async);
}
spin_unlock(&lynx->async.queue_lock);
if (pcl.pcl_status & DMA_CHAN_STAT_PKTCMPL) {
if (pcl.pcl_status & DMA_CHAN_STAT_SPECIALACK) {
ack = (pcl.pcl_status >> 15) & 0xf;
PRINTD(KERN_INFO, lynx->id, "special ack %d", ack);
ack = (ack == 1 ? ACKX_TIMEOUT : ACKX_SEND_ERROR);
} else {
ack = (pcl.pcl_status >> 15) & 0xf;
}
} else {
PRINT(KERN_INFO, lynx->id, "async packet was not completed");
ack = ACKX_SEND_ERROR;
}
hpsb_packet_sent(host, packet, ack);
}
}
if (intmask & PCI_INT_DMA_HLT(CHANNEL_ISO_SEND)) {
PRINTD(KERN_DEBUG, lynx->id, "iso sent");
spin_lock(&lynx->iso_send.queue_lock);
if (list_empty(&lynx->iso_send.pcl_queue)) {
spin_unlock(&lynx->iso_send.queue_lock);
PRINT(KERN_ERR, lynx->id, "iso send dma halted, but no queued packet");
} else {
struct ti_pcl pcl;
u32 ack;
struct hpsb_packet *packet;
get_pcl(lynx, lynx->iso_send.pcl, &pcl);
packet = driver_packet(lynx->iso_send.pcl_queue.next);
list_del_init(&packet->driver_list);
pci_unmap_single(lynx->dev, lynx->iso_send.header_dma,
packet->header_size, PCI_DMA_TODEVICE);
if (packet->data_size) {
pci_unmap_single(lynx->dev, lynx->iso_send.data_dma,
packet->data_size, PCI_DMA_TODEVICE);
}
#if 0 /* has been removed from ieee1394 core */
if (!list_empty(&lynx->iso_send.queue)) {
send_next(lynx, hpsb_iso);
}
#endif
spin_unlock(&lynx->iso_send.queue_lock);
if (pcl.pcl_status & DMA_CHAN_STAT_PKTCMPL) {
if (pcl.pcl_status & DMA_CHAN_STAT_SPECIALACK) {
ack = (pcl.pcl_status >> 15) & 0xf;
PRINTD(KERN_INFO, lynx->id, "special ack %d", ack);
ack = (ack == 1 ? ACKX_TIMEOUT : ACKX_SEND_ERROR);
} else {
ack = (pcl.pcl_status >> 15) & 0xf;
}
} else {
PRINT(KERN_INFO, lynx->id, "iso send packet was not completed");
ack = ACKX_SEND_ERROR;
}
hpsb_packet_sent(host, packet, ack); //FIXME: maybe we should just use ACK_COMPLETE and ACKX_SEND_ERROR
}
}
if (intmask & PCI_INT_DMA_HLT(CHANNEL_ASYNC_RCV)) {
/* general receive DMA completed */
int stat = reg_read(lynx, DMA_CHAN_STAT(CHANNEL_ASYNC_RCV));
PRINTD(KERN_DEBUG, lynx->id, "received packet size %d",
stat & 0x1fff);
if (stat & DMA_CHAN_STAT_SELFID) {
lynx->selfid_size = stat & 0x1fff;
handle_selfid(lynx, host);
} else {
quadlet_t *q_data = lynx->rcv_page;
if ((*q_data >> 4 & 0xf) == TCODE_READQ_RESPONSE
|| (*q_data >> 4 & 0xf) == TCODE_WRITEQ) {
cpu_to_be32s(q_data + 3);
}
hpsb_packet_received(host, q_data, stat & 0x1fff, 0);
}
run_pcl(lynx, lynx->rcv_pcl_start, CHANNEL_ASYNC_RCV);
}
return IRQ_HANDLED;
}
static void iso_rcv_bh(struct ti_lynx *lynx)
{
unsigned int idx;
quadlet_t *data;
unsigned long flags;
spin_lock_irqsave(&lynx->iso_rcv.lock, flags);
while (lynx->iso_rcv.used) {
idx = lynx->iso_rcv.last;
spin_unlock_irqrestore(&lynx->iso_rcv.lock, flags);
data = lynx->iso_rcv.page[idx / ISORCV_PER_PAGE]
+ (idx % ISORCV_PER_PAGE) * MAX_ISORCV_SIZE;
if ((*data >> 16) + 4 != (lynx->iso_rcv.stat[idx] & 0x1fff)) {
PRINT(KERN_ERR, lynx->id,
"iso length mismatch 0x%08x/0x%08x", *data,
lynx->iso_rcv.stat[idx]);
}
if (lynx->iso_rcv.stat[idx]
& (DMA_CHAN_STAT_PCIERR | DMA_CHAN_STAT_PKTERR)) {
PRINT(KERN_INFO, lynx->id,
"iso receive error on %d to 0x%p", idx, data);
} else {
hpsb_packet_received(lynx->host, data,
lynx->iso_rcv.stat[idx] & 0x1fff,
0);
}
spin_lock_irqsave(&lynx->iso_rcv.lock, flags);
lynx->iso_rcv.last = (idx + 1) % NUM_ISORCV_PCL;
lynx->iso_rcv.used--;
}
if (lynx->iso_rcv.chan_count) {
reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV),
DMA_WORD1_CMP_ENABLE_MASTER);
}
spin_unlock_irqrestore(&lynx->iso_rcv.lock, flags);
}
static void remove_card(struct pci_dev *dev)
{
struct ti_lynx *lynx;
struct device *lynx_dev;
int i;
lynx = pci_get_drvdata(dev);
if (!lynx) return;
pci_set_drvdata(dev, NULL);
lynx_dev = get_device(&lynx->host->device);
switch (lynx->state) {
case is_host:
reg_write(lynx, PCI_INT_ENABLE, 0);
hpsb_remove_host(lynx->host);
case have_intr:
reg_write(lynx, PCI_INT_ENABLE, 0);
free_irq(lynx->dev->irq, lynx);
/* Disable IRM Contender and LCtrl */
if (lynx->phyic.reg_1394a)
set_phy_reg(lynx, 4, ~0xc0 & get_phy_reg(lynx, 4));
/* Let all other nodes know to ignore us */
lynx_devctl(lynx->host, RESET_BUS, LONG_RESET_NO_FORCE_ROOT);
case have_iomappings:
reg_set_bits(lynx, MISC_CONTROL, MISC_CONTROL_SWRESET);
/* Fix buggy cards with autoboot pin not tied low: */
reg_write(lynx, DMA0_CHAN_CTRL, 0);
iounmap(lynx->registers);
iounmap(lynx->local_rom);
iounmap(lynx->local_ram);
iounmap(lynx->aux_port);
case have_1394_buffers:
for (i = 0; i < ISORCV_PAGES; i++) {
if (lynx->iso_rcv.page[i]) {
pci_free_consistent(lynx->dev, PAGE_SIZE,
lynx->iso_rcv.page[i],
lynx->iso_rcv.page_dma[i]);
}
}
pci_free_consistent(lynx->dev, PAGE_SIZE, lynx->rcv_page,
lynx->rcv_page_dma);
case have_aux_buf:
case have_pcl_mem:
pci_free_consistent(lynx->dev, LOCALRAM_SIZE, lynx->pcl_mem,
lynx->pcl_mem_dma);
case clear:
/* do nothing - already freed */
;
}
tasklet_kill(&lynx->iso_rcv.tq);
if (lynx_dev)
put_device(lynx_dev);
}
static int __devinit add_card(struct pci_dev *dev,
const struct pci_device_id *devid_is_unused)
{
#define FAIL(fmt, args...) do { \
PRINT_G(KERN_ERR, fmt , ## args); \
remove_card(dev); \
return error; \
} while (0)
char irq_buf[16];
struct hpsb_host *host;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -