265 lines
6.5 KiB
C
265 lines
6.5 KiB
C
/*
|
|
Mantis PCI bridge driver
|
|
|
|
Copyright (C) Manu Abraham (abraham.manu@gmail.com)
|
|
|
|
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include <asm/io.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/i2c.h>
|
|
|
|
#include <media/dmxdev.h>
|
|
#include <media/dvbdev.h>
|
|
#include <media/dvb_demux.h>
|
|
#include <media/dvb_frontend.h>
|
|
#include <media/dvb_net.h>
|
|
|
|
#include "mantis_common.h"
|
|
#include "mantis_reg.h"
|
|
#include "mantis_i2c.h"
|
|
|
|
#define TRIALS 10000
|
|
|
|
static int mantis_i2c_read(struct mantis_pci *mantis, const struct i2c_msg *msg)
|
|
{
|
|
u32 rxd, i, stat, trials;
|
|
|
|
dprintk(MANTIS_INFO, 0, " %s: Address=[0x%02x] <R>[ ",
|
|
__func__, msg->addr);
|
|
|
|
for (i = 0; i < msg->len; i++) {
|
|
rxd = (msg->addr << 25) | (1 << 24)
|
|
| MANTIS_I2C_RATE_3
|
|
| MANTIS_I2C_STOP
|
|
| MANTIS_I2C_PGMODE;
|
|
|
|
if (i == (msg->len - 1))
|
|
rxd &= ~MANTIS_I2C_STOP;
|
|
|
|
mmwrite(MANTIS_INT_I2CDONE, MANTIS_INT_STAT);
|
|
mmwrite(rxd, MANTIS_I2CDATA_CTL);
|
|
|
|
/* wait for xfer completion */
|
|
for (trials = 0; trials < TRIALS; trials++) {
|
|
stat = mmread(MANTIS_INT_STAT);
|
|
if (stat & MANTIS_INT_I2CDONE)
|
|
break;
|
|
}
|
|
|
|
dprintk(MANTIS_TMG, 0, "I2CDONE: trials=%d\n", trials);
|
|
|
|
/* wait for xfer completion */
|
|
for (trials = 0; trials < TRIALS; trials++) {
|
|
stat = mmread(MANTIS_INT_STAT);
|
|
if (stat & MANTIS_INT_I2CRACK)
|
|
break;
|
|
}
|
|
|
|
dprintk(MANTIS_TMG, 0, "I2CRACK: trials=%d\n", trials);
|
|
|
|
rxd = mmread(MANTIS_I2CDATA_CTL);
|
|
msg->buf[i] = (u8)((rxd >> 8) & 0xFF);
|
|
dprintk(MANTIS_INFO, 0, "%02x ", msg->buf[i]);
|
|
}
|
|
dprintk(MANTIS_INFO, 0, "]\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mantis_i2c_write(struct mantis_pci *mantis, const struct i2c_msg *msg)
|
|
{
|
|
int i;
|
|
u32 txd = 0, stat, trials;
|
|
|
|
dprintk(MANTIS_INFO, 0, " %s: Address=[0x%02x] <W>[ ",
|
|
__func__, msg->addr);
|
|
|
|
for (i = 0; i < msg->len; i++) {
|
|
dprintk(MANTIS_INFO, 0, "%02x ", msg->buf[i]);
|
|
txd = (msg->addr << 25) | (msg->buf[i] << 8)
|
|
| MANTIS_I2C_RATE_3
|
|
| MANTIS_I2C_STOP
|
|
| MANTIS_I2C_PGMODE;
|
|
|
|
if (i == (msg->len - 1))
|
|
txd &= ~MANTIS_I2C_STOP;
|
|
|
|
mmwrite(MANTIS_INT_I2CDONE, MANTIS_INT_STAT);
|
|
mmwrite(txd, MANTIS_I2CDATA_CTL);
|
|
|
|
/* wait for xfer completion */
|
|
for (trials = 0; trials < TRIALS; trials++) {
|
|
stat = mmread(MANTIS_INT_STAT);
|
|
if (stat & MANTIS_INT_I2CDONE)
|
|
break;
|
|
}
|
|
|
|
dprintk(MANTIS_TMG, 0, "I2CDONE: trials=%d\n", trials);
|
|
|
|
/* wait for xfer completion */
|
|
for (trials = 0; trials < TRIALS; trials++) {
|
|
stat = mmread(MANTIS_INT_STAT);
|
|
if (stat & MANTIS_INT_I2CRACK)
|
|
break;
|
|
}
|
|
|
|
dprintk(MANTIS_TMG, 0, "I2CRACK: trials=%d\n", trials);
|
|
}
|
|
dprintk(MANTIS_INFO, 0, "]\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mantis_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
|
|
{
|
|
int ret = 0, i = 0, trials;
|
|
u32 stat, data, txd;
|
|
struct mantis_pci *mantis;
|
|
struct mantis_hwconfig *config;
|
|
|
|
mantis = i2c_get_adapdata(adapter);
|
|
BUG_ON(!mantis);
|
|
config = mantis->hwconfig;
|
|
BUG_ON(!config);
|
|
|
|
dprintk(MANTIS_DEBUG, 1, "Messages:%d", num);
|
|
mutex_lock(&mantis->i2c_lock);
|
|
|
|
while (i < num) {
|
|
/* Byte MODE */
|
|
if ((config->i2c_mode & MANTIS_BYTE_MODE) &&
|
|
((i + 1) < num) &&
|
|
(msgs[i].len < 2) &&
|
|
(msgs[i + 1].len < 2) &&
|
|
(msgs[i + 1].flags & I2C_M_RD)) {
|
|
|
|
dprintk(MANTIS_DEBUG, 0, " Byte MODE:\n");
|
|
|
|
/* Read operation */
|
|
txd = msgs[i].addr << 25 | (0x1 << 24)
|
|
| (msgs[i].buf[0] << 16)
|
|
| MANTIS_I2C_RATE_3;
|
|
|
|
mmwrite(txd, MANTIS_I2CDATA_CTL);
|
|
/* wait for xfer completion */
|
|
for (trials = 0; trials < TRIALS; trials++) {
|
|
stat = mmread(MANTIS_INT_STAT);
|
|
if (stat & MANTIS_INT_I2CDONE)
|
|
break;
|
|
}
|
|
|
|
/* check for xfer completion */
|
|
if (stat & MANTIS_INT_I2CDONE) {
|
|
/* check xfer was acknowledged */
|
|
if (stat & MANTIS_INT_I2CRACK) {
|
|
data = mmread(MANTIS_I2CDATA_CTL);
|
|
msgs[i + 1].buf[0] = (data >> 8) & 0xff;
|
|
dprintk(MANTIS_DEBUG, 0, " Byte <%d> RXD=0x%02x [%02x]\n", 0x0, data, msgs[i + 1].buf[0]);
|
|
} else {
|
|
/* I/O error */
|
|
dprintk(MANTIS_ERROR, 1, " I/O error, LINE:%d", __LINE__);
|
|
ret = -EIO;
|
|
break;
|
|
}
|
|
} else {
|
|
/* I/O error */
|
|
dprintk(MANTIS_ERROR, 1, " I/O error, LINE:%d", __LINE__);
|
|
ret = -EIO;
|
|
break;
|
|
}
|
|
i += 2; /* Write/Read operation in one go */
|
|
}
|
|
|
|
if (i < num) {
|
|
if (msgs[i].flags & I2C_M_RD)
|
|
ret = mantis_i2c_read(mantis, &msgs[i]);
|
|
else
|
|
ret = mantis_i2c_write(mantis, &msgs[i]);
|
|
|
|
i++;
|
|
if (ret < 0)
|
|
goto bail_out;
|
|
}
|
|
|
|
}
|
|
|
|
mutex_unlock(&mantis->i2c_lock);
|
|
|
|
return num;
|
|
|
|
bail_out:
|
|
mutex_unlock(&mantis->i2c_lock);
|
|
return ret;
|
|
}
|
|
|
|
static u32 mantis_i2c_func(struct i2c_adapter *adapter)
|
|
{
|
|
return I2C_FUNC_SMBUS_EMUL;
|
|
}
|
|
|
|
static const struct i2c_algorithm mantis_algo = {
|
|
.master_xfer = mantis_i2c_xfer,
|
|
.functionality = mantis_i2c_func,
|
|
};
|
|
|
|
int mantis_i2c_init(struct mantis_pci *mantis)
|
|
{
|
|
u32 intstat;
|
|
struct i2c_adapter *i2c_adapter = &mantis->adapter;
|
|
struct pci_dev *pdev = mantis->pdev;
|
|
|
|
init_waitqueue_head(&mantis->i2c_wq);
|
|
mutex_init(&mantis->i2c_lock);
|
|
strscpy(i2c_adapter->name, "Mantis I2C", sizeof(i2c_adapter->name));
|
|
i2c_set_adapdata(i2c_adapter, mantis);
|
|
|
|
i2c_adapter->owner = THIS_MODULE;
|
|
i2c_adapter->algo = &mantis_algo;
|
|
i2c_adapter->algo_data = NULL;
|
|
i2c_adapter->timeout = 500;
|
|
i2c_adapter->retries = 3;
|
|
i2c_adapter->dev.parent = &pdev->dev;
|
|
|
|
mantis->i2c_rc = i2c_add_adapter(i2c_adapter);
|
|
if (mantis->i2c_rc < 0)
|
|
return mantis->i2c_rc;
|
|
|
|
dprintk(MANTIS_DEBUG, 1, "Initializing I2C ..");
|
|
|
|
intstat = mmread(MANTIS_INT_STAT);
|
|
mmread(MANTIS_INT_MASK);
|
|
mmwrite(intstat, MANTIS_INT_STAT);
|
|
dprintk(MANTIS_DEBUG, 1, "Disabling I2C interrupt");
|
|
mantis_mask_ints(mantis, MANTIS_INT_I2CDONE);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mantis_i2c_init);
|
|
|
|
int mantis_i2c_exit(struct mantis_pci *mantis)
|
|
{
|
|
dprintk(MANTIS_DEBUG, 1, "Disabling I2C interrupt");
|
|
mantis_mask_ints(mantis, MANTIS_INT_I2CDONE);
|
|
|
|
dprintk(MANTIS_DEBUG, 1, "Removing I2C adapter");
|
|
i2c_del_adapter(&mantis->adapter);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mantis_i2c_exit);
|