📄 pcilynx.c
字号:
struct ti_lynx *lynx; /* shortcut to currently handled device */
struct ti_pcl pcl;
u32 *pcli;
int i;
int error;
error = -ENXIO;
if (pci_set_dma_mask(dev, DMA_32BIT_MASK))
FAIL("DMA address limits not supported for PCILynx hardware");
if (pci_enable_device(dev))
FAIL("failed to enable PCILynx hardware");
pci_set_master(dev);
error = -ENOMEM;
host = hpsb_alloc_host(&lynx_driver, sizeof(struct ti_lynx), &dev->dev);
if (!host) FAIL("failed to allocate control structure memory");
lynx = host->hostdata;
lynx->id = card_id++;
lynx->dev = dev;
lynx->state = clear;
lynx->host = host;
host->pdev = dev;
pci_set_drvdata(dev, lynx);
spin_lock_init(&lynx->lock);
spin_lock_init(&lynx->phy_reg_lock);
lynx->pcl_mem = pci_alloc_consistent(dev, LOCALRAM_SIZE,
&lynx->pcl_mem_dma);
if (lynx->pcl_mem != NULL) {
lynx->state = have_pcl_mem;
PRINT(KERN_INFO, lynx->id,
"allocated PCL memory %d Bytes @ 0x%p", LOCALRAM_SIZE,
lynx->pcl_mem);
} else {
FAIL("failed to allocate PCL memory area");
}
lynx->rcv_page = pci_alloc_consistent(dev, PAGE_SIZE,
&lynx->rcv_page_dma);
if (lynx->rcv_page == NULL) {
FAIL("failed to allocate receive buffer");
}
lynx->state = have_1394_buffers;
for (i = 0; i < ISORCV_PAGES; i++) {
lynx->iso_rcv.page[i] =
pci_alloc_consistent(dev, PAGE_SIZE,
&lynx->iso_rcv.page_dma[i]);
if (lynx->iso_rcv.page[i] == NULL) {
FAIL("failed to allocate iso receive buffers");
}
}
lynx->registers = ioremap_nocache(pci_resource_start(dev,0),
PCILYNX_MAX_REGISTER);
lynx->local_ram = ioremap(pci_resource_start(dev,1), PCILYNX_MAX_MEMORY);
lynx->aux_port = ioremap(pci_resource_start(dev,2), PCILYNX_MAX_MEMORY);
lynx->local_rom = ioremap(pci_resource_start(dev,PCI_ROM_RESOURCE),
PCILYNX_MAX_MEMORY);
lynx->state = have_iomappings;
if (lynx->registers == NULL) {
FAIL("failed to remap registers - card not accessible");
}
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);
sprintf (irq_buf, "%d", dev->irq);
if (!request_irq(dev->irq, lynx_irq_handler, IRQF_SHARED,
PCILYNX_DRIVER_NAME, lynx)) {
PRINT(KERN_INFO, lynx->id, "allocated interrupt %s", irq_buf);
lynx->state = have_intr;
} else {
FAIL("failed to allocate shared interrupt %s", irq_buf);
}
/* alloc_pcl return values are not checked, it is expected that the
* provided PCL space is sufficient for the initial allocations */
lynx->rcv_pcl = alloc_pcl(lynx);
lynx->rcv_pcl_start = alloc_pcl(lynx);
lynx->async.pcl = alloc_pcl(lynx);
lynx->async.pcl_start = alloc_pcl(lynx);
lynx->iso_send.pcl = alloc_pcl(lynx);
lynx->iso_send.pcl_start = alloc_pcl(lynx);
for (i = 0; i < NUM_ISORCV_PCL; i++) {
lynx->iso_rcv.pcl[i] = alloc_pcl(lynx);
}
lynx->iso_rcv.pcl_start = alloc_pcl(lynx);
/* all allocations successful - simple init stuff follows */
reg_write(lynx, PCI_INT_ENABLE, PCI_INT_DMA_ALL);
tasklet_init(&lynx->iso_rcv.tq, (void (*)(unsigned long))iso_rcv_bh,
(unsigned long)lynx);
spin_lock_init(&lynx->iso_rcv.lock);
spin_lock_init(&lynx->async.queue_lock);
lynx->async.channel = CHANNEL_ASYNC_SEND;
spin_lock_init(&lynx->iso_send.queue_lock);
lynx->iso_send.channel = CHANNEL_ISO_SEND;
PRINT(KERN_INFO, lynx->id, "remapped memory spaces reg 0x%p, rom 0x%p, "
"ram 0x%p, aux 0x%p", lynx->registers, lynx->local_rom,
lynx->local_ram, lynx->aux_port);
/* now, looking for PHY register set */
if ((get_phy_reg(lynx, 2) & 0xe0) == 0xe0) {
lynx->phyic.reg_1394a = 1;
PRINT(KERN_INFO, lynx->id,
"found 1394a conform PHY (using extended register set)");
lynx->phyic.vendor = get_phy_vendorid(lynx);
lynx->phyic.product = get_phy_productid(lynx);
} else {
lynx->phyic.reg_1394a = 0;
PRINT(KERN_INFO, lynx->id, "found old 1394 PHY");
}
lynx->selfid_size = -1;
lynx->phy_reg0 = -1;
INIT_LIST_HEAD(&lynx->async.queue);
INIT_LIST_HEAD(&lynx->async.pcl_queue);
INIT_LIST_HEAD(&lynx->iso_send.queue);
INIT_LIST_HEAD(&lynx->iso_send.pcl_queue);
pcl.next = pcl_bus(lynx, lynx->rcv_pcl);
put_pcl(lynx, lynx->rcv_pcl_start, &pcl);
pcl.next = PCL_NEXT_INVALID;
pcl.async_error_next = PCL_NEXT_INVALID;
pcl.buffer[0].control = PCL_CMD_RCV | 16;
#ifndef __BIG_ENDIAN
pcl.buffer[0].control |= PCL_BIGENDIAN;
#endif
pcl.buffer[1].control = PCL_LAST_BUFF | 4080;
pcl.buffer[0].pointer = lynx->rcv_page_dma;
pcl.buffer[1].pointer = lynx->rcv_page_dma + 16;
put_pcl(lynx, lynx->rcv_pcl, &pcl);
pcl.next = pcl_bus(lynx, lynx->async.pcl);
pcl.async_error_next = pcl_bus(lynx, lynx->async.pcl);
put_pcl(lynx, lynx->async.pcl_start, &pcl);
pcl.next = pcl_bus(lynx, lynx->iso_send.pcl);
pcl.async_error_next = PCL_NEXT_INVALID;
put_pcl(lynx, lynx->iso_send.pcl_start, &pcl);
pcl.next = PCL_NEXT_INVALID;
pcl.async_error_next = PCL_NEXT_INVALID;
pcl.buffer[0].control = PCL_CMD_RCV | 4;
#ifndef __BIG_ENDIAN
pcl.buffer[0].control |= PCL_BIGENDIAN;
#endif
pcl.buffer[1].control = PCL_LAST_BUFF | 2044;
for (i = 0; i < NUM_ISORCV_PCL; i++) {
int page = i / ISORCV_PER_PAGE;
int sec = i % ISORCV_PER_PAGE;
pcl.buffer[0].pointer = lynx->iso_rcv.page_dma[page]
+ sec * MAX_ISORCV_SIZE;
pcl.buffer[1].pointer = pcl.buffer[0].pointer + 4;
put_pcl(lynx, lynx->iso_rcv.pcl[i], &pcl);
}
pcli = (u32 *)&pcl;
for (i = 0; i < NUM_ISORCV_PCL; i++) {
pcli[i] = pcl_bus(lynx, lynx->iso_rcv.pcl[i]);
}
put_pcl(lynx, lynx->iso_rcv.pcl_start, &pcl);
/* FIFO sizes from left to right: ITF=48 ATF=48 GRF=160 */
reg_write(lynx, FIFO_SIZES, 0x003030a0);
/* 20 byte threshold before triggering PCI transfer */
reg_write(lynx, DMA_GLOBAL_REGISTER, 0x2<<24);
/* threshold on both send FIFOs before transmitting:
FIFO size - cache line size - 1 */
i = reg_read(lynx, PCI_LATENCY_CACHELINE) & 0xff;
i = 0x30 - i - 1;
reg_write(lynx, FIFO_XMIT_THRESHOLD, (i << 8) | i);
reg_set_bits(lynx, PCI_INT_ENABLE, PCI_INT_1394);
reg_write(lynx, LINK_INT_ENABLE, LINK_INT_PHY_TIMEOUT
| LINK_INT_PHY_REG_RCVD | LINK_INT_PHY_BUSRESET
| LINK_INT_ISO_STUCK | LINK_INT_ASYNC_STUCK
| LINK_INT_SENT_REJECT | LINK_INT_TX_INVALID_TC
| LINK_INT_GRF_OVERFLOW | LINK_INT_ITF_UNDERFLOW
| LINK_INT_ATF_UNDERFLOW);
reg_write(lynx, DMA_WORD0_CMP_VALUE(CHANNEL_ASYNC_RCV), 0);
reg_write(lynx, DMA_WORD0_CMP_ENABLE(CHANNEL_ASYNC_RCV), 0xa<<4);
reg_write(lynx, DMA_WORD1_CMP_VALUE(CHANNEL_ASYNC_RCV), 0);
reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ASYNC_RCV),
DMA_WORD1_CMP_MATCH_LOCAL_NODE | DMA_WORD1_CMP_MATCH_BROADCAST
| DMA_WORD1_CMP_MATCH_EXACT | DMA_WORD1_CMP_MATCH_BUS_BCAST
| DMA_WORD1_CMP_ENABLE_SELF_ID | DMA_WORD1_CMP_ENABLE_MASTER);
run_pcl(lynx, lynx->rcv_pcl_start, CHANNEL_ASYNC_RCV);
reg_write(lynx, DMA_WORD0_CMP_VALUE(CHANNEL_ISO_RCV), 0);
reg_write(lynx, DMA_WORD0_CMP_ENABLE(CHANNEL_ISO_RCV), 0x9<<4);
reg_write(lynx, DMA_WORD1_CMP_VALUE(CHANNEL_ISO_RCV), 0);
reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), 0);
run_sub_pcl(lynx, lynx->iso_rcv.pcl_start, 0, CHANNEL_ISO_RCV);
reg_write(lynx, LINK_CONTROL, LINK_CONTROL_RCV_CMP_VALID
| LINK_CONTROL_TX_ISO_EN | LINK_CONTROL_RX_ISO_EN
| LINK_CONTROL_TX_ASYNC_EN | LINK_CONTROL_RX_ASYNC_EN
| LINK_CONTROL_RESET_TX | LINK_CONTROL_RESET_RX);
if (!lynx->phyic.reg_1394a) {
if (!hpsb_disable_irm) {
/* attempt to enable contender bit -FIXME- would this
* work elsewhere? */
reg_set_bits(lynx, GPIO_CTRL_A, 0x1);
reg_write(lynx, GPIO_DATA_BASE + 0x3c, 0x1);
}
} else {
/* set the contender (if appropriate) and LCtrl bit in the
* extended PHY register set. (Should check that PHY_02_EXTENDED
* is set in register 2?)
*/
i = get_phy_reg(lynx, 4);
i |= PHY_04_LCTRL;
if (hpsb_disable_irm)
i &= ~PHY_04_CONTENDER;
else
i |= PHY_04_CONTENDER;
if (i != -1) set_phy_reg(lynx, 4, i);
}
if (!skip_eeprom)
{
/* needed for i2c communication with serial eeprom */
struct i2c_adapter *i2c_ad;
struct i2c_algo_bit_data i2c_adapter_data;
error = -ENOMEM;
i2c_ad = kmemdup(&bit_ops, sizeof(*i2c_ad), GFP_KERNEL);
if (!i2c_ad) FAIL("failed to allocate I2C adapter memory");
i2c_adapter_data = bit_data;
i2c_ad->algo_data = &i2c_adapter_data;
i2c_adapter_data.data = lynx;
i2c_ad->dev.parent = &dev->dev;
PRINTD(KERN_DEBUG, lynx->id,"original eeprom control: %d",
reg_read(lynx, SERIAL_EEPROM_CONTROL));
/* reset hardware to sane state */
lynx->i2c_driven_state = 0x00000070;
reg_write(lynx, SERIAL_EEPROM_CONTROL, lynx->i2c_driven_state);
if (i2c_bit_add_bus(i2c_ad) < 0)
{
kfree(i2c_ad);
error = -ENXIO;
FAIL("unable to register i2c");
}
else
{
/* do i2c stuff */
unsigned char i2c_cmd = 0x10;
struct i2c_msg msg[2] = { { 0x50, 0, 1, &i2c_cmd },
{ 0x50, I2C_M_RD, 20, (unsigned char*) lynx->bus_info_block }
};
/* we use i2c_transfer, because i2c_smbus_read_block_data does not work properly and we
do it more efficiently in one transaction rather then using several reads */
if (i2c_transfer(i2c_ad, msg, 2) < 0) {
PRINT(KERN_ERR, lynx->id, "unable to read bus info block from i2c");
} else {
int i;
PRINT(KERN_INFO, lynx->id, "got bus info block from serial eeprom");
/* FIXME: probably we shoud rewrite the max_rec, max_ROM(1394a),
* generation(1394a) and link_spd(1394a) field and recalculate
* the CRC */
for (i = 0; i < 5 ; i++)
PRINTD(KERN_DEBUG, lynx->id, "Businfo block quadlet %i: %08x",
i, be32_to_cpu(lynx->bus_info_block[i]));
/* info_length, crc_length and 1394 magic number to check, if it is really a bus info block */
if (((be32_to_cpu(lynx->bus_info_block[0]) & 0xffff0000) == 0x04040000) &&
(lynx->bus_info_block[1] == __constant_cpu_to_be32(0x31333934)))
{
PRINT(KERN_DEBUG, lynx->id, "read a valid bus info block from");
} else {
kfree(i2c_ad);
error = -ENXIO;
FAIL("read something from serial eeprom, but it does not seem to be a valid bus info block");
}
}
i2c_del_adapter(i2c_ad);
kfree(i2c_ad);
}
}
host->csr.guid_hi = be32_to_cpu(lynx->bus_info_block[3]);
host->csr.guid_lo = be32_to_cpu(lynx->bus_info_block[4]);
host->csr.cyc_clk_acc = (be32_to_cpu(lynx->bus_info_block[2]) >> 16) & 0xff;
host->csr.max_rec = (be32_to_cpu(lynx->bus_info_block[2]) >> 12) & 0xf;
if (!lynx->phyic.reg_1394a)
host->csr.lnk_spd = (get_phy_reg(lynx, 2) & 0xc0) >> 6;
else
host->csr.lnk_spd = be32_to_cpu(lynx->bus_info_block[2]) & 0x7;
if (hpsb_add_host(host)) {
error = -ENOMEM;
FAIL("Failed to register host with highlevel");
}
lynx->state = is_host;
return 0;
#undef FAIL
}
static struct pci_device_id pci_table[] = {
{
.vendor = PCI_VENDOR_ID_TI,
.device = PCI_DEVICE_ID_TI_PCILYNX,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{ } /* Terminating entry */
};
static struct pci_driver lynx_pci_driver = {
.name = PCILYNX_DRIVER_NAME,
.id_table = pci_table,
.probe = add_card,
.remove = remove_card,
};
static struct hpsb_host_driver lynx_driver = {
.owner = THIS_MODULE,
.name = PCILYNX_DRIVER_NAME,
.set_hw_config_rom = NULL,
.transmit_packet = lynx_transmit,
.devctl = lynx_devctl,
.isoctl = NULL,
};
MODULE_AUTHOR("Andreas E. Bombe <andreas.bombe@munich.netsurf.de>");
MODULE_DESCRIPTION("driver for Texas Instruments PCI Lynx IEEE-1394 controller");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("pcilynx");
MODULE_DEVICE_TABLE(pci, pci_table);
static int __init pcilynx_init(void)
{
int ret;
ret = pci_register_driver(&lynx_pci_driver);
if (ret < 0) {
PRINT_G(KERN_ERR, "PCI module init failed");
return ret;
}
return 0;
}
static void __exit pcilynx_cleanup(void)
{
pci_unregister_driver(&lynx_pci_driver);
}
module_init(pcilynx_init);
module_exit(pcilynx_cleanup);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -