/*
* 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 struct i2c_algo_bit_data bit_data = {
.setsda = bit_setsda,
.setscl = bit_setscl,
.getsda = bit_getsda,
.getscl = bit_getscl,
.udelay = 5,
.timeout = 100,
};
/*
* 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",
__func__, addr);
ret
|