162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ddbridge-i2c.c: Digital Devices bridge i2c driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2010-2017 Digital Devices GmbH
662306a36Sopenharmony_ci *                         Ralph Metzler <rjkm@metzlerbros.de>
762306a36Sopenharmony_ci *                         Marcus Metzler <mocm@metzlerbros.de>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/delay.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/poll.h>
1662306a36Sopenharmony_ci#include <linux/io.h>
1762306a36Sopenharmony_ci#include <linux/pci.h>
1862306a36Sopenharmony_ci#include <linux/pci_ids.h>
1962306a36Sopenharmony_ci#include <linux/timer.h>
2062306a36Sopenharmony_ci#include <linux/i2c.h>
2162306a36Sopenharmony_ci#include <linux/swab.h>
2262306a36Sopenharmony_ci#include <linux/vmalloc.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include "ddbridge.h"
2562306a36Sopenharmony_ci#include "ddbridge-i2c.h"
2662306a36Sopenharmony_ci#include "ddbridge-regs.h"
2762306a36Sopenharmony_ci#include "ddbridge-io.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/******************************************************************************/
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic int ddb_i2c_cmd(struct ddb_i2c *i2c, u32 adr, u32 cmd)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	struct ddb *dev = i2c->dev;
3462306a36Sopenharmony_ci	unsigned long stat;
3562306a36Sopenharmony_ci	u32 val;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	ddbwritel(dev, (adr << 9) | cmd, i2c->regs + I2C_COMMAND);
3862306a36Sopenharmony_ci	stat = wait_for_completion_timeout(&i2c->completion, HZ);
3962306a36Sopenharmony_ci	val = ddbreadl(dev, i2c->regs + I2C_COMMAND);
4062306a36Sopenharmony_ci	if (stat == 0) {
4162306a36Sopenharmony_ci		dev_err(dev->dev, "I2C timeout, card %d, port %d, link %u\n",
4262306a36Sopenharmony_ci			dev->nr, i2c->nr, i2c->link);
4362306a36Sopenharmony_ci		{
4462306a36Sopenharmony_ci			u32 istat = ddbreadl(dev, INTERRUPT_STATUS);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci			dev_err(dev->dev, "DDBridge IRS %08x\n", istat);
4762306a36Sopenharmony_ci			if (i2c->link) {
4862306a36Sopenharmony_ci				u32 listat = ddbreadl(dev,
4962306a36Sopenharmony_ci					DDB_LINK_TAG(i2c->link) |
5062306a36Sopenharmony_ci					INTERRUPT_STATUS);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci				dev_err(dev->dev, "DDBridge link %u IRS %08x\n",
5362306a36Sopenharmony_ci					i2c->link, listat);
5462306a36Sopenharmony_ci			}
5562306a36Sopenharmony_ci			if (istat & 1) {
5662306a36Sopenharmony_ci				ddbwritel(dev, istat & 1, INTERRUPT_ACK);
5762306a36Sopenharmony_ci			} else {
5862306a36Sopenharmony_ci				u32 mon = ddbreadl(dev,
5962306a36Sopenharmony_ci					i2c->regs + I2C_MONITOR);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci				dev_err(dev->dev, "I2C cmd=%08x mon=%08x\n",
6262306a36Sopenharmony_ci					val, mon);
6362306a36Sopenharmony_ci			}
6462306a36Sopenharmony_ci		}
6562306a36Sopenharmony_ci		return -EIO;
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci	val &= 0x70000;
6862306a36Sopenharmony_ci	if (val == 0x20000)
6962306a36Sopenharmony_ci		dev_err(dev->dev, "I2C bus error\n");
7062306a36Sopenharmony_ci	if (val)
7162306a36Sopenharmony_ci		return -EIO;
7262306a36Sopenharmony_ci	return 0;
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic int ddb_i2c_master_xfer(struct i2c_adapter *adapter,
7662306a36Sopenharmony_ci			       struct i2c_msg msg[], int num)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	struct ddb_i2c *i2c = (struct ddb_i2c *)i2c_get_adapdata(adapter);
7962306a36Sopenharmony_ci	struct ddb *dev = i2c->dev;
8062306a36Sopenharmony_ci	u8 addr = 0;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	addr = msg[0].addr;
8362306a36Sopenharmony_ci	if (msg[0].len > i2c->bsize)
8462306a36Sopenharmony_ci		return -EIO;
8562306a36Sopenharmony_ci	switch (num) {
8662306a36Sopenharmony_ci	case 1:
8762306a36Sopenharmony_ci		if (msg[0].flags & I2C_M_RD) {
8862306a36Sopenharmony_ci			ddbwritel(dev, msg[0].len << 16,
8962306a36Sopenharmony_ci				  i2c->regs + I2C_TASKLENGTH);
9062306a36Sopenharmony_ci			if (ddb_i2c_cmd(i2c, addr, 3))
9162306a36Sopenharmony_ci				break;
9262306a36Sopenharmony_ci			ddbcpyfrom(dev, msg[0].buf,
9362306a36Sopenharmony_ci				   i2c->rbuf, msg[0].len);
9462306a36Sopenharmony_ci			return num;
9562306a36Sopenharmony_ci		}
9662306a36Sopenharmony_ci		ddbcpyto(dev, i2c->wbuf, msg[0].buf, msg[0].len);
9762306a36Sopenharmony_ci		ddbwritel(dev, msg[0].len, i2c->regs + I2C_TASKLENGTH);
9862306a36Sopenharmony_ci		if (ddb_i2c_cmd(i2c, addr, 2))
9962306a36Sopenharmony_ci			break;
10062306a36Sopenharmony_ci		return num;
10162306a36Sopenharmony_ci	case 2:
10262306a36Sopenharmony_ci		if ((msg[0].flags & I2C_M_RD) == I2C_M_RD)
10362306a36Sopenharmony_ci			break;
10462306a36Sopenharmony_ci		if ((msg[1].flags & I2C_M_RD) != I2C_M_RD)
10562306a36Sopenharmony_ci			break;
10662306a36Sopenharmony_ci		if (msg[1].len > i2c->bsize)
10762306a36Sopenharmony_ci			break;
10862306a36Sopenharmony_ci		ddbcpyto(dev, i2c->wbuf, msg[0].buf, msg[0].len);
10962306a36Sopenharmony_ci		ddbwritel(dev, msg[0].len | (msg[1].len << 16),
11062306a36Sopenharmony_ci			  i2c->regs + I2C_TASKLENGTH);
11162306a36Sopenharmony_ci		if (ddb_i2c_cmd(i2c, addr, 1))
11262306a36Sopenharmony_ci			break;
11362306a36Sopenharmony_ci		ddbcpyfrom(dev, msg[1].buf,
11462306a36Sopenharmony_ci			   i2c->rbuf,
11562306a36Sopenharmony_ci			   msg[1].len);
11662306a36Sopenharmony_ci		return num;
11762306a36Sopenharmony_ci	default:
11862306a36Sopenharmony_ci		break;
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci	return -EIO;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic u32 ddb_i2c_functionality(struct i2c_adapter *adap)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic const struct i2c_algorithm ddb_i2c_algo = {
12962306a36Sopenharmony_ci	.master_xfer   = ddb_i2c_master_xfer,
13062306a36Sopenharmony_ci	.functionality = ddb_i2c_functionality,
13162306a36Sopenharmony_ci};
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_civoid ddb_i2c_release(struct ddb *dev)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	int i;
13662306a36Sopenharmony_ci	struct ddb_i2c *i2c;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	for (i = 0; i < dev->i2c_num; i++) {
13962306a36Sopenharmony_ci		i2c = &dev->i2c[i];
14062306a36Sopenharmony_ci		i2c_del_adapter(&i2c->adap);
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic void i2c_handler(void *priv)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	struct ddb_i2c *i2c = (struct ddb_i2c *)priv;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	complete(&i2c->completion);
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic int ddb_i2c_add(struct ddb *dev, struct ddb_i2c *i2c,
15262306a36Sopenharmony_ci		       const struct ddb_regmap *regmap, int link,
15362306a36Sopenharmony_ci		       int i, int num)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct i2c_adapter *adap;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	i2c->nr = i;
15862306a36Sopenharmony_ci	i2c->dev = dev;
15962306a36Sopenharmony_ci	i2c->link = link;
16062306a36Sopenharmony_ci	i2c->bsize = regmap->i2c_buf->size;
16162306a36Sopenharmony_ci	i2c->wbuf = DDB_LINK_TAG(link) |
16262306a36Sopenharmony_ci		(regmap->i2c_buf->base + i2c->bsize * i);
16362306a36Sopenharmony_ci	i2c->rbuf = i2c->wbuf; /* + i2c->bsize / 2 */
16462306a36Sopenharmony_ci	i2c->regs = DDB_LINK_TAG(link) |
16562306a36Sopenharmony_ci		(regmap->i2c->base + regmap->i2c->size * i);
16662306a36Sopenharmony_ci	ddbwritel(dev, I2C_SPEED_100, i2c->regs + I2C_TIMING);
16762306a36Sopenharmony_ci	ddbwritel(dev, ((i2c->rbuf & 0xffff) << 16) | (i2c->wbuf & 0xffff),
16862306a36Sopenharmony_ci		  i2c->regs + I2C_TASKADDRESS);
16962306a36Sopenharmony_ci	init_completion(&i2c->completion);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	adap = &i2c->adap;
17262306a36Sopenharmony_ci	i2c_set_adapdata(adap, i2c);
17362306a36Sopenharmony_ci#ifdef I2C_ADAP_CLASS_TV_DIGITAL
17462306a36Sopenharmony_ci	adap->class = I2C_ADAP_CLASS_TV_DIGITAL | I2C_CLASS_TV_ANALOG;
17562306a36Sopenharmony_ci#else
17662306a36Sopenharmony_ci#ifdef I2C_CLASS_TV_ANALOG
17762306a36Sopenharmony_ci	adap->class = I2C_CLASS_TV_ANALOG;
17862306a36Sopenharmony_ci#endif
17962306a36Sopenharmony_ci#endif
18062306a36Sopenharmony_ci	snprintf(adap->name, I2C_NAME_SIZE, "ddbridge_%02x.%x.%x",
18162306a36Sopenharmony_ci		 dev->nr, i2c->link, i);
18262306a36Sopenharmony_ci	adap->algo = &ddb_i2c_algo;
18362306a36Sopenharmony_ci	adap->algo_data = (void *)i2c;
18462306a36Sopenharmony_ci	adap->dev.parent = dev->dev;
18562306a36Sopenharmony_ci	return i2c_add_adapter(adap);
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ciint ddb_i2c_init(struct ddb *dev)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	int stat = 0;
19162306a36Sopenharmony_ci	u32 i, j, num = 0, l, base;
19262306a36Sopenharmony_ci	struct ddb_i2c *i2c;
19362306a36Sopenharmony_ci	struct i2c_adapter *adap;
19462306a36Sopenharmony_ci	const struct ddb_regmap *regmap;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	for (l = 0; l < DDB_MAX_LINK; l++) {
19762306a36Sopenharmony_ci		if (!dev->link[l].info)
19862306a36Sopenharmony_ci			continue;
19962306a36Sopenharmony_ci		regmap = dev->link[l].info->regmap;
20062306a36Sopenharmony_ci		if (!regmap || !regmap->i2c)
20162306a36Sopenharmony_ci			continue;
20262306a36Sopenharmony_ci		base = regmap->irq_base_i2c;
20362306a36Sopenharmony_ci		for (i = 0; i < regmap->i2c->num; i++) {
20462306a36Sopenharmony_ci			if (!(dev->link[l].info->i2c_mask & (1 << i)))
20562306a36Sopenharmony_ci				continue;
20662306a36Sopenharmony_ci			i2c = &dev->i2c[num];
20762306a36Sopenharmony_ci			ddb_irq_set(dev, l, i + base, i2c_handler, i2c);
20862306a36Sopenharmony_ci			stat = ddb_i2c_add(dev, i2c, regmap, l, i, num);
20962306a36Sopenharmony_ci			if (stat)
21062306a36Sopenharmony_ci				break;
21162306a36Sopenharmony_ci			num++;
21262306a36Sopenharmony_ci		}
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci	if (stat) {
21562306a36Sopenharmony_ci		for (j = 0; j < num; j++) {
21662306a36Sopenharmony_ci			i2c = &dev->i2c[j];
21762306a36Sopenharmony_ci			adap = &i2c->adap;
21862306a36Sopenharmony_ci			i2c_del_adapter(adap);
21962306a36Sopenharmony_ci		}
22062306a36Sopenharmony_ci	} else {
22162306a36Sopenharmony_ci		dev->i2c_num = num;
22262306a36Sopenharmony_ci	}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	return stat;
22562306a36Sopenharmony_ci}
226