18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci	Mantis PCI bridge driver
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci*/
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <asm/io.h>
108c2ecf20Sopenharmony_ci#include <linux/ioport.h>
118c2ecf20Sopenharmony_ci#include <linux/pci.h>
128c2ecf20Sopenharmony_ci#include <linux/i2c.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <media/dmxdev.h>
158c2ecf20Sopenharmony_ci#include <media/dvbdev.h>
168c2ecf20Sopenharmony_ci#include <media/dvb_demux.h>
178c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h>
188c2ecf20Sopenharmony_ci#include <media/dvb_net.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include "mantis_common.h"
218c2ecf20Sopenharmony_ci#include "mantis_reg.h"
228c2ecf20Sopenharmony_ci#include "mantis_i2c.h"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define TRIALS			10000
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic int mantis_i2c_read(struct mantis_pci *mantis, const struct i2c_msg *msg)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	u32 rxd, i, stat, trials;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	dprintk(MANTIS_INFO, 0, "        %s:  Address=[0x%02x] <R>[ ",
318c2ecf20Sopenharmony_ci		__func__, msg->addr);
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	for (i = 0; i < msg->len; i++) {
348c2ecf20Sopenharmony_ci		rxd = (msg->addr << 25) | (1 << 24)
358c2ecf20Sopenharmony_ci					| MANTIS_I2C_RATE_3
368c2ecf20Sopenharmony_ci					| MANTIS_I2C_STOP
378c2ecf20Sopenharmony_ci					| MANTIS_I2C_PGMODE;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci		if (i == (msg->len - 1))
408c2ecf20Sopenharmony_ci			rxd &= ~MANTIS_I2C_STOP;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci		mmwrite(MANTIS_INT_I2CDONE, MANTIS_INT_STAT);
438c2ecf20Sopenharmony_ci		mmwrite(rxd, MANTIS_I2CDATA_CTL);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci		/* wait for xfer completion */
468c2ecf20Sopenharmony_ci		for (trials = 0; trials < TRIALS; trials++) {
478c2ecf20Sopenharmony_ci			stat = mmread(MANTIS_INT_STAT);
488c2ecf20Sopenharmony_ci			if (stat & MANTIS_INT_I2CDONE)
498c2ecf20Sopenharmony_ci				break;
508c2ecf20Sopenharmony_ci		}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci		dprintk(MANTIS_TMG, 0, "I2CDONE: trials=%d\n", trials);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci		/* wait for xfer completion */
558c2ecf20Sopenharmony_ci		for (trials = 0; trials < TRIALS; trials++) {
568c2ecf20Sopenharmony_ci			stat = mmread(MANTIS_INT_STAT);
578c2ecf20Sopenharmony_ci			if (stat & MANTIS_INT_I2CRACK)
588c2ecf20Sopenharmony_ci				break;
598c2ecf20Sopenharmony_ci		}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci		dprintk(MANTIS_TMG, 0, "I2CRACK: trials=%d\n", trials);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci		rxd = mmread(MANTIS_I2CDATA_CTL);
648c2ecf20Sopenharmony_ci		msg->buf[i] = (u8)((rxd >> 8) & 0xFF);
658c2ecf20Sopenharmony_ci		dprintk(MANTIS_INFO, 0, "%02x ", msg->buf[i]);
668c2ecf20Sopenharmony_ci	}
678c2ecf20Sopenharmony_ci	dprintk(MANTIS_INFO, 0, "]\n");
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	return 0;
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic int mantis_i2c_write(struct mantis_pci *mantis, const struct i2c_msg *msg)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	int i;
758c2ecf20Sopenharmony_ci	u32 txd = 0, stat, trials;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	dprintk(MANTIS_INFO, 0, "        %s: Address=[0x%02x] <W>[ ",
788c2ecf20Sopenharmony_ci		__func__, msg->addr);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	for (i = 0; i < msg->len; i++) {
818c2ecf20Sopenharmony_ci		dprintk(MANTIS_INFO, 0, "%02x ", msg->buf[i]);
828c2ecf20Sopenharmony_ci		txd = (msg->addr << 25) | (msg->buf[i] << 8)
838c2ecf20Sopenharmony_ci					| MANTIS_I2C_RATE_3
848c2ecf20Sopenharmony_ci					| MANTIS_I2C_STOP
858c2ecf20Sopenharmony_ci					| MANTIS_I2C_PGMODE;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci		if (i == (msg->len - 1))
888c2ecf20Sopenharmony_ci			txd &= ~MANTIS_I2C_STOP;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci		mmwrite(MANTIS_INT_I2CDONE, MANTIS_INT_STAT);
918c2ecf20Sopenharmony_ci		mmwrite(txd, MANTIS_I2CDATA_CTL);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci		/* wait for xfer completion */
948c2ecf20Sopenharmony_ci		for (trials = 0; trials < TRIALS; trials++) {
958c2ecf20Sopenharmony_ci			stat = mmread(MANTIS_INT_STAT);
968c2ecf20Sopenharmony_ci			if (stat & MANTIS_INT_I2CDONE)
978c2ecf20Sopenharmony_ci				break;
988c2ecf20Sopenharmony_ci		}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci		dprintk(MANTIS_TMG, 0, "I2CDONE: trials=%d\n", trials);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci		/* wait for xfer completion */
1038c2ecf20Sopenharmony_ci		for (trials = 0; trials < TRIALS; trials++) {
1048c2ecf20Sopenharmony_ci			stat = mmread(MANTIS_INT_STAT);
1058c2ecf20Sopenharmony_ci			if (stat & MANTIS_INT_I2CRACK)
1068c2ecf20Sopenharmony_ci				break;
1078c2ecf20Sopenharmony_ci		}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci		dprintk(MANTIS_TMG, 0, "I2CRACK: trials=%d\n", trials);
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci	dprintk(MANTIS_INFO, 0, "]\n");
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	return 0;
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic int mantis_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	int ret = 0, i = 0, trials;
1198c2ecf20Sopenharmony_ci	u32 stat, data, txd;
1208c2ecf20Sopenharmony_ci	struct mantis_pci *mantis;
1218c2ecf20Sopenharmony_ci	struct mantis_hwconfig *config;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	mantis = i2c_get_adapdata(adapter);
1248c2ecf20Sopenharmony_ci	BUG_ON(!mantis);
1258c2ecf20Sopenharmony_ci	config = mantis->hwconfig;
1268c2ecf20Sopenharmony_ci	BUG_ON(!config);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	dprintk(MANTIS_DEBUG, 1, "Messages:%d", num);
1298c2ecf20Sopenharmony_ci	mutex_lock(&mantis->i2c_lock);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	while (i < num) {
1328c2ecf20Sopenharmony_ci		/* Byte MODE */
1338c2ecf20Sopenharmony_ci		if ((config->i2c_mode & MANTIS_BYTE_MODE) &&
1348c2ecf20Sopenharmony_ci		    ((i + 1) < num)			&&
1358c2ecf20Sopenharmony_ci		    (msgs[i].len < 2)			&&
1368c2ecf20Sopenharmony_ci		    (msgs[i + 1].len < 2)		&&
1378c2ecf20Sopenharmony_ci		    (msgs[i + 1].flags & I2C_M_RD)) {
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci			dprintk(MANTIS_DEBUG, 0, "        Byte MODE:\n");
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci			/* Read operation */
1428c2ecf20Sopenharmony_ci			txd = msgs[i].addr << 25 | (0x1 << 24)
1438c2ecf20Sopenharmony_ci						 | (msgs[i].buf[0] << 16)
1448c2ecf20Sopenharmony_ci						 | MANTIS_I2C_RATE_3;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci			mmwrite(txd, MANTIS_I2CDATA_CTL);
1478c2ecf20Sopenharmony_ci			/* wait for xfer completion */
1488c2ecf20Sopenharmony_ci			for (trials = 0; trials < TRIALS; trials++) {
1498c2ecf20Sopenharmony_ci				stat = mmread(MANTIS_INT_STAT);
1508c2ecf20Sopenharmony_ci				if (stat & MANTIS_INT_I2CDONE)
1518c2ecf20Sopenharmony_ci					break;
1528c2ecf20Sopenharmony_ci			}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci			/* check for xfer completion */
1558c2ecf20Sopenharmony_ci			if (stat & MANTIS_INT_I2CDONE) {
1568c2ecf20Sopenharmony_ci				/* check xfer was acknowledged */
1578c2ecf20Sopenharmony_ci				if (stat & MANTIS_INT_I2CRACK) {
1588c2ecf20Sopenharmony_ci					data = mmread(MANTIS_I2CDATA_CTL);
1598c2ecf20Sopenharmony_ci					msgs[i + 1].buf[0] = (data >> 8) & 0xff;
1608c2ecf20Sopenharmony_ci					dprintk(MANTIS_DEBUG, 0, "        Byte <%d> RXD=0x%02x  [%02x]\n", 0x0, data, msgs[i + 1].buf[0]);
1618c2ecf20Sopenharmony_ci				} else {
1628c2ecf20Sopenharmony_ci					/* I/O error */
1638c2ecf20Sopenharmony_ci					dprintk(MANTIS_ERROR, 1, "        I/O error, LINE:%d", __LINE__);
1648c2ecf20Sopenharmony_ci					ret = -EIO;
1658c2ecf20Sopenharmony_ci					break;
1668c2ecf20Sopenharmony_ci				}
1678c2ecf20Sopenharmony_ci			} else {
1688c2ecf20Sopenharmony_ci				/* I/O error */
1698c2ecf20Sopenharmony_ci				dprintk(MANTIS_ERROR, 1, "        I/O error, LINE:%d", __LINE__);
1708c2ecf20Sopenharmony_ci				ret = -EIO;
1718c2ecf20Sopenharmony_ci				break;
1728c2ecf20Sopenharmony_ci			}
1738c2ecf20Sopenharmony_ci			i += 2; /* Write/Read operation in one go */
1748c2ecf20Sopenharmony_ci		}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci		if (i < num) {
1778c2ecf20Sopenharmony_ci			if (msgs[i].flags & I2C_M_RD)
1788c2ecf20Sopenharmony_ci				ret = mantis_i2c_read(mantis, &msgs[i]);
1798c2ecf20Sopenharmony_ci			else
1808c2ecf20Sopenharmony_ci				ret = mantis_i2c_write(mantis, &msgs[i]);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci			i++;
1838c2ecf20Sopenharmony_ci			if (ret < 0)
1848c2ecf20Sopenharmony_ci				goto bail_out;
1858c2ecf20Sopenharmony_ci		}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	mutex_unlock(&mantis->i2c_lock);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	return num;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cibail_out:
1948c2ecf20Sopenharmony_ci	mutex_unlock(&mantis->i2c_lock);
1958c2ecf20Sopenharmony_ci	return ret;
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic u32 mantis_i2c_func(struct i2c_adapter *adapter)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	return I2C_FUNC_SMBUS_EMUL;
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic const struct i2c_algorithm mantis_algo = {
2048c2ecf20Sopenharmony_ci	.master_xfer		= mantis_i2c_xfer,
2058c2ecf20Sopenharmony_ci	.functionality		= mantis_i2c_func,
2068c2ecf20Sopenharmony_ci};
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ciint mantis_i2c_init(struct mantis_pci *mantis)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	u32 intstat;
2118c2ecf20Sopenharmony_ci	struct i2c_adapter *i2c_adapter = &mantis->adapter;
2128c2ecf20Sopenharmony_ci	struct pci_dev *pdev		= mantis->pdev;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	init_waitqueue_head(&mantis->i2c_wq);
2158c2ecf20Sopenharmony_ci	mutex_init(&mantis->i2c_lock);
2168c2ecf20Sopenharmony_ci	strscpy(i2c_adapter->name, "Mantis I2C", sizeof(i2c_adapter->name));
2178c2ecf20Sopenharmony_ci	i2c_set_adapdata(i2c_adapter, mantis);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	i2c_adapter->owner	= THIS_MODULE;
2208c2ecf20Sopenharmony_ci	i2c_adapter->algo	= &mantis_algo;
2218c2ecf20Sopenharmony_ci	i2c_adapter->algo_data	= NULL;
2228c2ecf20Sopenharmony_ci	i2c_adapter->timeout	= 500;
2238c2ecf20Sopenharmony_ci	i2c_adapter->retries	= 3;
2248c2ecf20Sopenharmony_ci	i2c_adapter->dev.parent	= &pdev->dev;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	mantis->i2c_rc		= i2c_add_adapter(i2c_adapter);
2278c2ecf20Sopenharmony_ci	if (mantis->i2c_rc < 0)
2288c2ecf20Sopenharmony_ci		return mantis->i2c_rc;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	dprintk(MANTIS_DEBUG, 1, "Initializing I2C ..");
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	intstat = mmread(MANTIS_INT_STAT);
2338c2ecf20Sopenharmony_ci	mmread(MANTIS_INT_MASK);
2348c2ecf20Sopenharmony_ci	mmwrite(intstat, MANTIS_INT_STAT);
2358c2ecf20Sopenharmony_ci	dprintk(MANTIS_DEBUG, 1, "Disabling I2C interrupt");
2368c2ecf20Sopenharmony_ci	mantis_mask_ints(mantis, MANTIS_INT_I2CDONE);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	return 0;
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mantis_i2c_init);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ciint mantis_i2c_exit(struct mantis_pci *mantis)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	dprintk(MANTIS_DEBUG, 1, "Disabling I2C interrupt");
2458c2ecf20Sopenharmony_ci	mantis_mask_ints(mantis, MANTIS_INT_I2CDONE);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	dprintk(MANTIS_DEBUG, 1, "Removing I2C adapter");
2488c2ecf20Sopenharmony_ci	i2c_del_adapter(&mantis->adapter);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	return 0;
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mantis_i2c_exit);
253