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

📄 pcilynx.c

📁 Ieee1394驱动实现
💻 C
📖 第 1 页 / 共 4 页
字号:
/*
 * pcilynx.c - Texas Instruments PCILynx driver
 * Copyright (C) 1999,2000 Andreas Bombe <andreas.bombe@munich.netsurf.de>,
 *                         Stephan Linz <linz@mazet.de>
 *                         Manfred Weihs <weihs@ict.tuwien.ac.at>
 *
 * 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.
 */

/*
 * Contributions:
 *
 * Manfred Weihs <weihs@ict.tuwien.ac.at>
 *        reading bus info block (containing GUID) from serial
 *            eeprom via i2c and storing it in config ROM
 *        Reworked code for initiating bus resets
 *            (long, short, with or without hold-off)
 *        Enhancements in async and iso send code
 */

#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/moduleparam.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/kdev_t.h>
#include <linux/dma-mapping.h>
#include <asm/byteorder.h>
#include <asm/atomic.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/irq.h>

#include "csr1212.h"
#include "ieee1394.h"
#include "ieee1394_types.h"
#include "hosts.h"
#include "ieee1394_core.h"
#include "highlevel.h"
#include "pcilynx.h"

#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>

/* 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)
#endif


/* Module Parameters */
static int skip_eeprom;
module_param(skip_eeprom, int, 0444);
MODULE_PARM_DESC(skip_eeprom, "Use generic bus info block instead of serial eeprom (default = 0).");


static struct hpsb_host_driver lynx_driver;
static unsigned int card_id;



/*
 * I2C stuff
 */

/* the i2c stuff was inspired by i2c-philips-par.c */

static void bit_setscl(void *data, int state)
{
	if (state) {
		  ((struct ti_lynx *) data)->i2c_driven_state |= 0x00000040;
	} else {
		  ((struct ti_lynx *) data)->i2c_driven_state &= ~0x00000040;
	}
	reg_write((struct ti_lynx *) data, SERIAL_EEPROM_CONTROL, ((struct ti_lynx *) data)->i2c_driven_state);
}

static void bit_setsda(void *data, int state)
{
	if (state) {
		  ((struct ti_lynx *) data)->i2c_driven_state |= 0x00000010;
	} else {
		  ((struct ti_lynx *) data)->i2c_driven_state &= ~0x00000010;
	}
	reg_write((struct ti_lynx *) data, SERIAL_EEPROM_CONTROL, ((struct ti_lynx *) data)->i2c_driven_state);
}

static int bit_getscl(void *data)
{
	return reg_read((struct ti_lynx *) data, SERIAL_EEPROM_CONTROL) & 0x00000040;
}

static int bit_getsda(void *data)
{
	return reg_read((struct ti_lynx *) data, SERIAL_EEPROM_CONTROL) & 0x00000010;
}

static int bit_reg(struct i2c_client *client)
{
	return 0;
}

static int bit_unreg(struct i2c_client *client)
{
	return 0;
}

static struct i2c_algo_bit_data bit_data = {
	.setsda			= bit_setsda,
	.setscl			= bit_setscl,
	.getsda			= bit_getsda,
	.getscl			= bit_getscl,
	.udelay			= 5,
	.timeout		= 100,
};

static struct i2c_adapter bit_ops = {
	.id 			= 0xAA, //FIXME: probably we should get an id in i2c-id.h
	.client_register	= bit_reg,
	.client_unregister	= bit_unreg,
	.name			= "PCILynx I2C",
};



/*
 * 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 0
static 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);
}
#endif



/***********************************
 * 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,
                      "%s: PHY register address %d out of range",
		      __FUNCTION__, 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, "%s: runaway loop, aborting",
			      __FUNCTION__);
                        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,
                      "%s: PHY register address %d out of range", __FUNCTION__, addr);
                return -1;
        }

        if (val > 0xff) {
                PRINT(KERN_ERR, lynx->id,
                      "%s: PHY register value %d out of range", __FUNCTION__, 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,
                      "%s: PHY page %d out of range", __FUNCTION__, 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,
                      "%s: PHY port %d out of range", __FUNCTION__, 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;
        }
}
#endif

static 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 */
	if (!hpsb_disable_irm)
		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 {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -