253 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			253 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0-or-later
 | |
| /*
 | |
| 	Mantis PCI bridge driver
 | |
| 
 | |
| 	Copyright (C) Manu Abraham (abraham.manu@gmail.com)
 | |
| 
 | |
| */
 | |
| 
 | |
| #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);
 |