📄 pcilynx.c
字号:
/* * ti_pcilynx.c - Texas Instruments PCILynx driver * Copyright (C) 1999,2000 Andreas Bombe <andreas.bombe@munich.netsurf.de>, * Stephan Linz <linz@mazet.de> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#include <linux/config.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <linux/wait.h>#include <linux/errno.h>#include <linux/module.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/fs.h>#include <linux/poll.h>#include <asm/byteorder.h>#include <asm/atomic.h>#include <asm/io.h>#include <asm/uaccess.h>#include "ieee1394.h"#include "ieee1394_types.h"#include "hosts.h"#include "ieee1394_core.h"#include "highlevel.h"#include "pcilynx.h"#if MAX_PCILYNX_CARDS > PCILYNX_MINOR_ROM_START#error Max number of cards is bigger than PCILYNX_MINOR_ROM_START - this does not work.#endif/* print general (card independent) information */#define PRINT_G(level, fmt, args...) printk(level "pcilynx: " fmt "\n" , ## args)/* print card specific information */#define PRINT(level, card, fmt, args...) printk(level "pcilynx%d: " fmt "\n" , card , ## args)#ifdef CONFIG_IEEE1394_VERBOSEDEBUG#define PRINT_GD(level, fmt, args...) printk(level "pcilynx: " fmt "\n" , ## args)#define PRINTD(level, card, fmt, args...) printk(level "pcilynx%d: " fmt "\n" , card , ## args)#else#define PRINT_GD(level, fmt, args...) do {} while (0)#define PRINTD(level, card, fmt, args...) do {} while (0)#endifstatic struct ti_lynx cards[MAX_PCILYNX_CARDS];static int num_of_cards = 0;static struct hpsb_host_template lynx_template;/* * PCL handling functions. */static pcl_t alloc_pcl(struct ti_lynx *lynx){ u8 m; int i, j; spin_lock(&lynx->lock); /* FIXME - use ffz() to make this readable */ for (i = 0; i < (LOCALRAM_SIZE / 1024); i++) { m = lynx->pcl_bmap[i]; for (j = 0; j < 8; j++) { if (m & 1<<j) { continue; } m |= 1<<j; lynx->pcl_bmap[i] = m; spin_unlock(&lynx->lock); return 8 * i + j; } } spin_unlock(&lynx->lock); return -1;}#if 0static void free_pcl(struct ti_lynx *lynx, pcl_t pclid){ int off, bit; off = pclid / 8; bit = pclid % 8; if (pclid < 0) { return; } spin_lock(&lynx->lock); if (lynx->pcl_bmap[off] & 1<<bit) { lynx->pcl_bmap[off] &= ~(1<<bit); } else { PRINT(KERN_ERR, lynx->id, "attempted to free unallocated PCL %d", pclid); } spin_unlock(&lynx->lock);}/* functions useful for debugging */ static void pretty_print_pcl(const struct ti_pcl *pcl){ int i; printk("PCL next %08x, userdata %08x, status %08x, remtrans %08x, nextbuf %08x\n", pcl->next, pcl->user_data, pcl->pcl_status, pcl->remaining_transfer_count, pcl->next_data_buffer); printk("PCL"); for (i=0; i<13; i++) { printk(" c%x:%08x d%x:%08x", i, pcl->buffer[i].control, i, pcl->buffer[i].pointer); if (!(i & 0x3) && (i != 12)) printk("\nPCL"); } printk("\n");} static void print_pcl(const struct ti_lynx *lynx, pcl_t pclid){ struct ti_pcl pcl; get_pcl(lynx, pclid, &pcl); pretty_print_pcl(&pcl);}#endifstatic int add_card(struct pci_dev *dev, const struct pci_device_id *devid);static void remove_card(struct pci_dev *dev);/*********************************** * IEEE-1394 functionality section * ***********************************/static int get_phy_reg(struct ti_lynx *lynx, int addr){ int retval; int i = 0; unsigned long flags; if (addr > 15) { PRINT(KERN_ERR, lynx->id, __FUNCTION__ ": PHY register address %d out of range", addr); return -1; } spin_lock_irqsave(&lynx->phy_reg_lock, flags); reg_write(lynx, LINK_PHY, LINK_PHY_READ | LINK_PHY_ADDR(addr)); do { retval = reg_read(lynx, LINK_PHY); if (i > 10000) { PRINT(KERN_ERR, lynx->id, __FUNCTION__ ": runaway loop, aborting"); retval = -1; break; } i++; } while ((retval & 0xf00) != LINK_PHY_RADDR(addr)); reg_write(lynx, LINK_INT_STATUS, LINK_INT_PHY_REG_RCVD); spin_unlock_irqrestore(&lynx->phy_reg_lock, flags); if (retval != -1) { return retval & 0xff; } else { return -1; }}static int set_phy_reg(struct ti_lynx *lynx, int addr, int val){ unsigned long flags; if (addr > 15) { PRINT(KERN_ERR, lynx->id, __FUNCTION__ ": PHY register address %d out of range", addr); return -1; } if (val > 0xff) { PRINT(KERN_ERR, lynx->id, __FUNCTION__ ": PHY register value %d out of range", val); return -1; } spin_lock_irqsave(&lynx->phy_reg_lock, flags); reg_write(lynx, LINK_PHY, LINK_PHY_WRITE | LINK_PHY_ADDR(addr) | LINK_PHY_WDATA(val)); spin_unlock_irqrestore(&lynx->phy_reg_lock, flags); return 0;}static int sel_phy_reg_page(struct ti_lynx *lynx, int page){ int reg; if (page > 7) { PRINT(KERN_ERR, lynx->id, __FUNCTION__ ": PHY page %d out of range", page); return -1; } reg = get_phy_reg(lynx, 7); if (reg != -1) { reg &= 0x1f; reg |= (page << 5); set_phy_reg(lynx, 7, reg); return 0; } else { return -1; }}#if 0 /* not needed at this time */static int sel_phy_reg_port(struct ti_lynx *lynx, int port){ int reg; if (port > 15) { PRINT(KERN_ERR, lynx->id, __FUNCTION__ ": PHY port %d out of range", port); return -1; } reg = get_phy_reg(lynx, 7); if (reg != -1) { reg &= 0xf0; reg |= port; set_phy_reg(lynx, 7, reg); return 0; } else { return -1; }}#endifstatic u32 get_phy_vendorid(struct ti_lynx *lynx){ u32 pvid = 0; sel_phy_reg_page(lynx, 1); pvid |= (get_phy_reg(lynx, 10) << 16); pvid |= (get_phy_reg(lynx, 11) << 8); pvid |= get_phy_reg(lynx, 12); PRINT(KERN_INFO, lynx->id, "PHY vendor id 0x%06x", pvid); return pvid;}static u32 get_phy_productid(struct ti_lynx *lynx){ u32 id = 0; sel_phy_reg_page(lynx, 1); id |= (get_phy_reg(lynx, 13) << 16); id |= (get_phy_reg(lynx, 14) << 8); id |= get_phy_reg(lynx, 15); PRINT(KERN_INFO, lynx->id, "PHY product id 0x%06x", id); return id;}static quadlet_t generate_own_selfid(struct ti_lynx *lynx, struct hpsb_host *host){ quadlet_t lsid; char phyreg[7]; int i; phyreg[0] = lynx->phy_reg0; for (i = 1; i < 7; i++) { phyreg[i] = get_phy_reg(lynx, i); } /* FIXME? We assume a TSB21LV03A phy here. This code doesn't support more than 3 ports on the PHY anyway. */ lsid = 0x80400000 | ((phyreg[0] & 0xfc) << 22); lsid |= (phyreg[1] & 0x3f) << 16; /* gap count */ lsid |= (phyreg[2] & 0xc0) << 8; /* max speed */ lsid |= (phyreg[6] & 0x01) << 11; /* contender (phy dependent) */ /* lsid |= 1 << 11; *//* set contender (hack) */ lsid |= (phyreg[6] & 0x10) >> 3; /* initiated reset */ for (i = 0; i < (phyreg[2] & 0xf); i++) { /* ports */ if (phyreg[3 + i] & 0x4) { lsid |= (((phyreg[3 + i] & 0x8) | 0x10) >> 3) << (6 - i*2); } else { lsid |= 1 << (6 - i*2); } } cpu_to_be32s(&lsid); PRINT(KERN_DEBUG, lynx->id, "generated own selfid 0x%x", lsid); return lsid;}static void handle_selfid(struct ti_lynx *lynx, struct hpsb_host *host){ quadlet_t *q = lynx->rcv_page; int phyid, isroot, size; quadlet_t lsid = 0; int i; if (lynx->phy_reg0 == -1 || lynx->selfid_size == -1) return; size = lynx->selfid_size; phyid = lynx->phy_reg0; i = (size > 16 ? 16 : size) / 4 - 1; while (i >= 0) { cpu_to_be32s(&q[i]); i--; } if (!lynx->phyic.reg_1394a) { lsid = generate_own_selfid(lynx, host); } isroot = (phyid & 2) != 0; phyid >>= 2; PRINT(KERN_INFO, lynx->id, "SelfID process finished (phyid %d, %s)", phyid, (isroot ? "root" : "not root")); reg_write(lynx, LINK_ID, (0xffc0 | phyid) << 16); if (!lynx->phyic.reg_1394a && !size) { hpsb_selfid_received(host, lsid); } while (size > 0) { struct selfid *sid = (struct selfid *)q; if (!lynx->phyic.reg_1394a && !sid->extended && (sid->phy_id == (phyid + 1))) { hpsb_selfid_received(host, lsid); } if (q[0] == ~q[1]) { PRINT(KERN_DEBUG, lynx->id, "SelfID packet 0x%x rcvd", q[0]); hpsb_selfid_received(host, q[0]); } else { PRINT(KERN_INFO, lynx->id, "inconsistent selfid 0x%x/0x%x", q[0], q[1]); } q += 2; size -= 8; } if (!lynx->phyic.reg_1394a && isroot && phyid != 0) { hpsb_selfid_received(host, lsid); } hpsb_selfid_complete(host, phyid, isroot); if (host->in_bus_reset) return; if (isroot) reg_set_bits(lynx, LINK_CONTROL, LINK_CONTROL_CYCMASTER); reg_set_bits(lynx, LINK_CONTROL, LINK_CONTROL_RCV_CMP_VALID | LINK_CONTROL_TX_ASYNC_EN | LINK_CONTROL_RX_ASYNC_EN | LINK_CONTROL_CYCTIMEREN);}/* This must be called with the respective queue_lock held. */static void send_next(struct ti_lynx *lynx, int what){ struct ti_pcl pcl; struct lynx_send_data *d; struct hpsb_packet *packet; d = (what == hpsb_iso ? &lynx->iso_send : &lynx->async); packet = d->queue; d->header_dma = pci_map_single(lynx->dev, packet->header, packet->header_size, PCI_DMA_TODEVICE); if (packet->data_size) { d->data_dma = pci_map_single(lynx->dev, packet->data, packet->data_size, PCI_DMA_TODEVICE); } else { d->data_dma = 0; } pcl.next = PCL_NEXT_INVALID; pcl.async_error_next = PCL_NEXT_INVALID;#ifdef __BIG_ENDIAN pcl.buffer[0].control = packet->speed_code << 14 | packet->header_size;#else pcl.buffer[0].control = packet->speed_code << 14 | packet->header_size | PCL_BIGENDIAN;#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -