⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pcilynx.c

📁 Ieee1394驱动实现
💻 C
📖 第 1 页 / 共 4 页
字号:
        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 + -