root/drivers/media/pci/mantis/mantis_i2c.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);