/* SPDX-License-Identifier: LGPL-2.1-only OR BSD-3-Clause */
/******************************************************************************
 *
 * This file is provided under a dual license.  When you use or
 * distribute this software, you may choose to be licensed under
 * version 2.1 of the GNU Lesser General Public License
 * ("LGPLv2.1 License") or BSD License.
 *
 * LGPLv2.1 License
 *
 * Copyright(C) 2019 MediaTek Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of version 2.1 of the GNU Lesser General Public
 * License 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 https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html for more
 * details.
 *
 * BSD LICENSE
 *
 * Copyright(C) 2019 MediaTek Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *  * Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *****************************************************************************/

/*-----------------------------------------------------------------------------
 *
 * $Author: dtvbm11 $
 * $Date: 2015/11/10 $
 * $RCSfile: i2c-mt53xx.c,v $
 * $Revision: #1 $
 *
 *---------------------------------------------------------------------------*/

/** @file i2c-mt53xx
 *  MTK MT53xx I2C driver.
 */

//-----------------------------------------------------------------------------
// Include files
//-----------------------------------------------------------------------------

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include <linux/ioport.h>
#include <linux/errno.h>
#include <linux/sched.h>

#include <linux/i2c.h>

#include <x_typedef.h>
//-----------------------------------------------------------------------------
// Configurations
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Constant definitions
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Type definitions
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Macro definitions
//-----------------------------------------------------------------------------
#define MODULE_NAME "MTK I2C"

//#define	I2C_MTK_DEBUG
#ifdef I2C_MTK_DEBUG
static int i2c_debug = 0;
#define DEB0(format, arg...)	printk(KERN_DEBUG MODULE_NAME " DEBUG: " format "\n",  ## arg )
#define DEB1(format, arg...)	\
	if (i2c_debug>=1) {	\
		printk(KERN_DEBUG MODULE_NAME " DEBUG: " format "\n",  ## arg ); \
	}
#define DEB2(format, arg...)	\
	if (i2c_debug>=2) {	\
		printk(KERN_DEBUG MODULE_NAME " DEBUG: " format "\n",  ## arg ); \
	}
#define DEB3(format, arg...)	\
	if (i2c_debug>=3) {	\
		printk(KERN_DEBUG MODULE_NAME " DEBUG: " format "\n",  ## arg ); \
	}
#define DEB9(format, arg...)	\
	/* debug the protocol by showing transferred bits */	\
	if (i2c_debug>=9) {	\
		printk(KERN_DEBUG MODULE_NAME " DEBUG: " format "\n",  ## arg ); \
	}
#else
#define DEB0(fmt, args...)
#define DEB1(fmt, args...)
#define DEB2(fmt, args...)
#define DEB3(fmt, args...)
#define DEB9(fmt, args...)
#endif

#define err(format, arg...) printk(KERN_ERR MODULE_NAME " ERROR: " format "\n",  ## arg )
#define info(format, arg...) printk(KERN_INFO MODULE_NAME ": " format "\n",  ## arg )
#define warn(format, arg...) printk(KERN_WARNING MODULE_NAME " WARNING: " format "\n",  ## arg )
#define emerg(format, arg...) printk(KERN_EMERG MODULE_NAME " EMERGENCY: " format "\n",  ## arg )

#define MAX_MESSAGES	65536	/* max number of messages */

//---------------------------------------------------------------------------
// Imported variables
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// Imported functions
//---------------------------------------------------------------------------
extern UINT16 SIF_ReadNoSubAddr(UINT16 u2ClkDiv, UINT8 u1DevAddr, UINT8 *pu1Buf,
			UINT16 u2ByteCnt);
extern UINT16 SIF_WriteNoSubAddr(UINT16 u2ClkDiv, UINT8 u1DevAddr, UINT8 *pu1Buf,
                 UINT16 u2ByteCnt);

//---------------------------------------------------------------------------
// Static function forward declarations
//---------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Static variables
//-----------------------------------------------------------------------------
static unsigned short _i4MTK_i2c_div = 0x100;		/* Default: Fast Mode = 400 KHz, Standard Mode = 100 KHz */

//-----------------------------------------------------------------------------
// Static functions
//-----------------------------------------------------------------------------
static int
mtk_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg)
{
        u8 zero_byte = 0;
	int r;
	u8 *buf;
	size_t buf_len;

	DEB2("addr: 0x%04x, len: %d, flags: 0x%x",
	     msg->addr, msg->len, msg->flags);

	/* Sigh, seems we can't do zero length transactions. Thus, we
	 * can't probe for devices w/o actually sending/receiving at least
	 * a single byte. So we'll set count to 1 for the zero length
	 * transaction case and hope we don't cause grief for some
	 * arbitrary device due to random byte write/read during
	 * probes.
	 */
	if (msg->len == 0) {
		buf = &zero_byte;
                buf_len = 1;
	} else {
		buf = msg->buf;
		buf_len = msg->len;
	}

	if (msg->flags & I2C_M_TEN)
	{
	    return -EIO;
        }

	if (!(msg->flags & I2C_M_RD))
	{
            r = SIF_WriteNoSubAddr(_i4MTK_i2c_div, (msg->addr << 1), buf, buf_len);
	}
        else
        {
            r = SIF_ReadNoSubAddr(_i4MTK_i2c_div, (msg->addr << 1), buf, buf_len);
        }

	if (r != buf_len)
	{
	    return -EIO;
	}

        return msg->len;
}

/*
 * Prepare controller for a transaction and call mtk_i2c_rxbytes
 * to do the work.
 */
 static int
mtk_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
{
	int i;
	int r = 0;

	DEB1("msgs: %d", num);

	if (num < 1 || num > MAX_MESSAGES)
		return -EINVAL;

	/* Check for valid parameters in messages */
	for (i = 0; i < num; i++)
		if (msgs[i].buf == NULL)
			return -EINVAL;

	for (i = 0; i < num; i++) {
		DEB2("msg: %d, addr: 0x%04x, len: %d, flags: 0x%x",
		     i, msgs[i].addr, msgs[i].len, msgs[i].flags);

		r = mtk_i2c_xfer_msg(adap, &msgs[i]);

		DEB2("r: %d", r);

		if (r != msgs[i].len)
			break;
	}

	if (r >= 0 && num > 1)
		r = num;

	DEB1("r: %d", r);

	return r;
}

static u32
mtk_i2c_func(struct i2c_adapter *adap)
{
	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}

static int mtk_i2c_remove(struct device *dev)
{
        return 0;
}

static void mtk_i2c_device_release(struct device *dev)
{
        /* Nothing */
}

static struct i2c_algorithm mtk_i2c_algo = {
	.name = "MTK I2C algorithm",
	.id = I2C_ALGO_EXP,
	.master_xfer = mtk_i2c_xfer,
	.functionality = mtk_i2c_func,
};

static struct i2c_adapter mtk_i2c_adap = {
	.owner = THIS_MODULE,
	.name = "MTK I2C adapter",
	.id = I2C_ALGO_EXP,	/* REVISIT: register for id */
	.algo = &mtk_i2c_algo,
};

static struct device_driver mtk_i2c_driver = {
        .name           = "mtk_i2c",
        .bus            = &platform_bus_type,
        .remove         = mtk_i2c_remove,
};

static struct platform_device mtk_i2c_device = {
        .name       = "i2c",
        .id             = -1,
        .dev = {
                .driver         = &mtk_i2c_driver,
                .release        = mtk_i2c_device_release,
        },
};

int __init
mtk_i2c_init(void)
{
	int r = 0;

	//info("Driver ver. 1.0");
	DEB0("%s %s", __TIME__, __DATE__);

	r = i2c_add_adapter(&mtk_i2c_adap);
	if (r) {
		err("failed to add adapter");
		return r;
	}

	if(driver_register(&mtk_i2c_driver) != 0)
		printk(KERN_ERR "Driver register failed for mtk_i2c\n");

	if(platform_device_register(&mtk_i2c_device) != 0) {
		printk(KERN_ERR "Device register failed for i2c\n");
		driver_unregister(&mtk_i2c_driver);
	}

        return r;
}

static void __exit
mtk_i2c_exit(void)
{
	i2c_del_adapter(&mtk_i2c_adap);
        driver_unregister(&mtk_i2c_driver);
        platform_device_unregister(&mtk_i2c_device);
}

MODULE_AUTHOR("Max Liao, MTK Inc.");
MODULE_DESCRIPTION("MTK MT53XX I2C bus adapter");
MODULE_LICENSE("GPL");

#ifdef I2C_MTK_DEBUG
module_param(i2c_debug, int, 0);
MODULE_PARM_DESC(i2c_debug,
		 "debug level - 0 off; 1 normal; 2,3 more verbose; "
		 "9 mtk-protocol");
#endif

/* i2c may be needed to bring up other drivers */
//subsys_initcall(mtk_i2c_init);
//module_exit(mtk_i2c_exit);
