summaryrefslogtreecommitdiffstats
path: root/drivers/media/pci/ddbridge/ddbridge-i2c.c
diff options
context:
space:
mode:
authorDaniel Scheller <d.scheller@gmx.net>2017-08-12 13:55:52 +0200
committerMauro Carvalho Chehab <mchehab@s-opensource.com>2017-08-20 13:01:04 +0200
commit22e743898dcd693cf587593781699db2fa888e23 (patch)
treee4863378b1aba6e54975a87cd0cf5f6fc1b8a1cf /drivers/media/pci/ddbridge/ddbridge-i2c.c
parentmedia: ddbridge: split code into multiple files (diff)
downloadlinux-22e743898dcd693cf587593781699db2fa888e23.tar.xz
linux-22e743898dcd693cf587593781699db2fa888e23.zip
media: ddbridge: bump ddbridge code to version 0.9.29
This huge patch bumps the ddbridge driver to version 0.9.29. Compared to the vendor driver package, DD OctoNET including GTL link support, and all DVB-C Modulator card support has been removed since this requires large changes to the underlying DVB core API, which should eventually be done separately, and, after that, the functionality/device support can be added back rather easy. While the diff is rather large, the bump is mostly a big refactor of all data structures. Yet, the MSI support (message signaled interrupts) is greatly improved, also all currently available CI single/duo bridge cards are fully supported. More changes compared to the upstream driver: - the DDB_USE_WORKER flag/define was removed, kernel worker functionality will be used. - coding style is properly fixed (zero complaints from checkpatch) - all (not much though) CamelCase has been fixed to kernel_case - (private) IOCTLs temporarily removed (which are mainly used to provide rarely-used FPGA update functionality) Great care has been taken to keep all previous changes and fixes (e.g. kernel logging via dev_*(), pointer annotations and such) intact. Permission to reuse and mainline the driver code was formally granted by Ralph Metzler <rjkm@metzlerbros.de>. Signed-off-by: Daniel Scheller <d.scheller@gmx.net> Tested-by: Richard Scobie <r.scobie@clear.net.nz> Tested-by: Jasmin Jessich <jasmin@anw.at> Tested-by: Dietmar Spingler <d_spingler@freenet.de> Tested-by: Manfred Knick <Manfred.Knick@t-online.de> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
Diffstat (limited to 'drivers/media/pci/ddbridge/ddbridge-i2c.c')
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-i2c.c217
1 files changed, 136 insertions, 81 deletions
diff --git a/drivers/media/pci/ddbridge/ddbridge-i2c.c b/drivers/media/pci/ddbridge/ddbridge-i2c.c
index 45287f98ee92..376d8a7ca0b9 100644
--- a/drivers/media/pci/ddbridge/ddbridge-i2c.c
+++ b/drivers/media/pci/ddbridge/ddbridge-i2c.c
@@ -1,24 +1,21 @@
/*
- * ddbridge.c: Digital Devices PCIe bridge driver
+ * ddbridge-i2c.c: Digital Devices bridge i2c driver
*
- * Copyright (C) 2010-2011 Digital Devices GmbH
+ * Copyright (C) 2010-2017 Digital Devices GmbH
+ * Ralph Metzler <rjkm@metzlerbros.de>
+ * Marcus Metzler <mocm@metzlerbros.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 only, as published by the Free Software Foundation.
*
- *
* 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.
*
- * To obtain the license, point your browser to
- * http://www.gnu.org/copyleft/gpl.html
*/
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
@@ -34,30 +31,47 @@
#include <linux/vmalloc.h>
#include "ddbridge.h"
-#include "ddbridge-regs.h"
#include "ddbridge-i2c.h"
+#include "ddbridge-regs.h"
/******************************************************************************/
static int ddb_i2c_cmd(struct ddb_i2c *i2c, u32 adr, u32 cmd)
{
struct ddb *dev = i2c->dev;
- long stat;
+ unsigned long stat;
u32 val;
- i2c->done = 0;
- ddbwritel((adr << 9) | cmd, i2c->regs + I2C_COMMAND);
- stat = wait_event_timeout(i2c->wq, i2c->done == 1, HZ);
+ ddbwritel(dev, (adr << 9) | cmd, i2c->regs + I2C_COMMAND);
+ stat = wait_for_completion_timeout(&i2c->completion, HZ);
+ val = ddbreadl(dev, i2c->regs + I2C_COMMAND);
if (stat == 0) {
- dev_err(&dev->pdev->dev, "I2C timeout\n");
- { /* MSI debugging*/
- u32 istat = ddbreadl(INTERRUPT_STATUS);
- dev_err(&dev->pdev->dev, "IRS %08x\n", istat);
- ddbwritel(istat, INTERRUPT_ACK);
+ dev_err(dev->dev, "I2C timeout, card %d, port %d, link %u\n",
+ dev->nr, i2c->nr, i2c->link);
+ {
+ u32 istat = ddbreadl(dev, INTERRUPT_STATUS);
+
+ dev_err(dev->dev, "DDBridge IRS %08x\n", istat);
+ if (i2c->link) {
+ u32 listat = ddbreadl(dev,
+ DDB_LINK_TAG(i2c->link) |
+ INTERRUPT_STATUS);
+
+ dev_err(dev->dev, "DDBridge link %u IRS %08x\n",
+ i2c->link, listat);
+ }
+ if (istat & 1) {
+ ddbwritel(dev, istat & 1, INTERRUPT_ACK);
+ } else {
+ u32 mon = ddbreadl(dev,
+ i2c->regs + I2C_MONITOR);
+
+ dev_err(dev->dev, "I2C cmd=%08x mon=%08x\n",
+ val, mon);
+ }
}
return -EIO;
}
- val = ddbreadl(i2c->regs+I2C_COMMAND);
if (val & 0x70000)
return -EIO;
return 0;
@@ -66,48 +80,54 @@ static int ddb_i2c_cmd(struct ddb_i2c *i2c, u32 adr, u32 cmd)
static int ddb_i2c_master_xfer(struct i2c_adapter *adapter,
struct i2c_msg msg[], int num)
{
- struct ddb_i2c *i2c = (struct ddb_i2c *)i2c_get_adapdata(adapter);
+ struct ddb_i2c *i2c = (struct ddb_i2c *) i2c_get_adapdata(adapter);
struct ddb *dev = i2c->dev;
u8 addr = 0;
- if (num)
- addr = msg[0].addr;
-
- if (num == 2 && msg[1].flags & I2C_M_RD &&
- !(msg[0].flags & I2C_M_RD)) {
- memcpy_toio(dev->regs + I2C_TASKMEM_BASE + i2c->wbuf,
- msg[0].buf, msg[0].len);
- ddbwritel(msg[0].len|(msg[1].len << 16),
- i2c->regs+I2C_TASKLENGTH);
- if (!ddb_i2c_cmd(i2c, addr, 1)) {
- memcpy_fromio(msg[1].buf,
- dev->regs + I2C_TASKMEM_BASE + i2c->rbuf,
- msg[1].len);
- return num;
- }
- }
-
- if (num == 1 && !(msg[0].flags & I2C_M_RD)) {
- ddbcpyto(I2C_TASKMEM_BASE + i2c->wbuf, msg[0].buf, msg[0].len);
- ddbwritel(msg[0].len, i2c->regs + I2C_TASKLENGTH);
- if (!ddb_i2c_cmd(i2c, addr, 2))
- return num;
- }
- if (num == 1 && (msg[0].flags & I2C_M_RD)) {
- ddbwritel(msg[0].len << 16, i2c->regs + I2C_TASKLENGTH);
- if (!ddb_i2c_cmd(i2c, addr, 3)) {
- ddbcpyfrom(msg[0].buf,
- I2C_TASKMEM_BASE + i2c->rbuf, msg[0].len);
+ addr = msg[0].addr;
+ if (msg[0].len > i2c->bsize)
+ return -EIO;
+ switch (num) {
+ case 1:
+ if (msg[0].flags & I2C_M_RD) {
+ ddbwritel(dev, msg[0].len << 16,
+ i2c->regs + I2C_TASKLENGTH);
+ if (ddb_i2c_cmd(i2c, addr, 3))
+ break;
+ ddbcpyfrom(dev, msg[0].buf,
+ i2c->rbuf, msg[0].len);
return num;
}
+ ddbcpyto(dev, i2c->wbuf, msg[0].buf, msg[0].len);
+ ddbwritel(dev, msg[0].len, i2c->regs + I2C_TASKLENGTH);
+ if (ddb_i2c_cmd(i2c, addr, 2))
+ break;
+ return num;
+ case 2:
+ if ((msg[0].flags & I2C_M_RD) == I2C_M_RD)
+ break;
+ if ((msg[1].flags & I2C_M_RD) != I2C_M_RD)
+ break;
+ if (msg[1].len > i2c->bsize)
+ break;
+ ddbcpyto(dev, i2c->wbuf, msg[0].buf, msg[0].len);
+ ddbwritel(dev, msg[0].len | (msg[1].len << 16),
+ i2c->regs + I2C_TASKLENGTH);
+ if (ddb_i2c_cmd(i2c, addr, 1))
+ break;
+ ddbcpyfrom(dev, msg[1].buf,
+ i2c->rbuf,
+ msg[1].len);
+ return num;
+ default:
+ break;
}
return -EIO;
}
-
static u32 ddb_i2c_functionality(struct i2c_adapter *adap)
{
- return I2C_FUNC_SMBUS_EMUL;
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
static const struct i2c_algorithm ddb_i2c_algo = {
@@ -119,55 +139,90 @@ void ddb_i2c_release(struct ddb *dev)
{
int i;
struct ddb_i2c *i2c;
- struct i2c_adapter *adap;
- for (i = 0; i < dev->info->port_num; i++) {
+ for (i = 0; i < dev->i2c_num; i++) {
i2c = &dev->i2c[i];
- adap = &i2c->adap;
- i2c_del_adapter(adap);
+ i2c_del_adapter(&i2c->adap);
}
}
-int ddb_i2c_init(struct ddb *dev)
+static void i2c_handler(unsigned long priv)
+{
+ struct ddb_i2c *i2c = (struct ddb_i2c *) priv;
+
+ complete(&i2c->completion);
+}
+
+static int ddb_i2c_add(struct ddb *dev, struct ddb_i2c *i2c,
+ struct ddb_regmap *regmap, int link, int i, int num)
{
- int i, j, stat = 0;
- struct ddb_i2c *i2c;
struct i2c_adapter *adap;
- for (i = 0; i < dev->info->port_num; i++) {
- i2c = &dev->i2c[i];
- i2c->dev = dev;
- i2c->nr = i;
- i2c->wbuf = i * (I2C_TASKMEM_SIZE / 4);
- i2c->rbuf = i2c->wbuf + (I2C_TASKMEM_SIZE / 8);
- i2c->regs = 0x80 + i * 0x20;
- ddbwritel(I2C_SPEED_100, i2c->regs + I2C_TIMING);
- ddbwritel((i2c->rbuf << 16) | i2c->wbuf,
- i2c->regs + I2C_TASKADDRESS);
- init_waitqueue_head(&i2c->wq);
-
- adap = &i2c->adap;
- i2c_set_adapdata(adap, i2c);
+ i2c->nr = i;
+ i2c->dev = dev;
+ i2c->link = link;
+ i2c->bsize = regmap->i2c_buf->size;
+ i2c->wbuf = DDB_LINK_TAG(link) |
+ (regmap->i2c_buf->base + i2c->bsize * i);
+ i2c->rbuf = i2c->wbuf; /* + i2c->bsize / 2 */
+ i2c->regs = DDB_LINK_TAG(link) |
+ (regmap->i2c->base + regmap->i2c->size * i);
+ ddbwritel(dev, I2C_SPEED_100, i2c->regs + I2C_TIMING);
+ ddbwritel(dev, ((i2c->rbuf & 0xffff) << 16) | (i2c->wbuf & 0xffff),
+ i2c->regs + I2C_TASKADDRESS);
+ init_completion(&i2c->completion);
+
+ adap = &i2c->adap;
+ i2c_set_adapdata(adap, i2c);
#ifdef I2C_ADAP_CLASS_TV_DIGITAL
- adap->class = I2C_ADAP_CLASS_TV_DIGITAL|I2C_CLASS_TV_ANALOG;
+ adap->class = I2C_ADAP_CLASS_TV_DIGITAL|I2C_CLASS_TV_ANALOG;
#else
#ifdef I2C_CLASS_TV_ANALOG
- adap->class = I2C_CLASS_TV_ANALOG;
+ adap->class = I2C_CLASS_TV_ANALOG;
#endif
#endif
- strcpy(adap->name, "ddbridge");
- adap->algo = &ddb_i2c_algo;
- adap->algo_data = (void *)i2c;
- adap->dev.parent = &dev->pdev->dev;
- stat = i2c_add_adapter(adap);
- if (stat)
- break;
+ snprintf(adap->name, I2C_NAME_SIZE, "ddbridge_%02x.%x.%x",
+ dev->nr, i2c->link, i);
+ adap->algo = &ddb_i2c_algo;
+ adap->algo_data = (void *)i2c;
+ adap->dev.parent = dev->dev;
+ return i2c_add_adapter(adap);
+}
+
+int ddb_i2c_init(struct ddb *dev)
+{
+ int stat = 0;
+ u32 i, j, num = 0, l, base;
+ struct ddb_i2c *i2c;
+ struct i2c_adapter *adap;
+ struct ddb_regmap *regmap;
+
+ for (l = 0; l < DDB_MAX_LINK; l++) {
+ if (!dev->link[l].info)
+ continue;
+ regmap = dev->link[l].info->regmap;
+ if (!regmap || !regmap->i2c)
+ continue;
+ base = regmap->irq_base_i2c;
+ for (i = 0; i < regmap->i2c->num; i++) {
+ if (!(dev->link[l].info->i2c_mask & (1 << i)))
+ continue;
+ i2c = &dev->i2c[num];
+ dev->handler_data[l][i + base] = (unsigned long) i2c;
+ dev->handler[l][i + base] = i2c_handler;
+ stat = ddb_i2c_add(dev, i2c, regmap, l, i, num);
+ if (stat)
+ break;
+ num++;
+ }
}
- if (stat)
- for (j = 0; j < i; j++) {
+ if (stat) {
+ for (j = 0; j < num; j++) {
i2c = &dev->i2c[j];
adap = &i2c->adap;
i2c_del_adapter(adap);
}
+ } else
+ dev->i2c_num = num;
return stat;
}