Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b3ed5ebe authored by Kyle Yan's avatar Kyle Yan Committed by Gerrit - the friendly Code Review server
Browse files

Merge "i2c: msm-geni: I2C controller driver for GENI based serial engine." into msm-4.9

parents 2064ec00 7c927c00
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
GENI based Qualcomm Technologies Inc Universal Peripheral version 3 (QUPv3)
								I2C controller

Required properties:
 - compatible: Should be:
   * "qcom,i2c-geni.
 - reg: Should contain QUP register address and length.
 - interrupts: Should contain I2C interrupt.
 - #address-cells: Should be <1> Address cells for i2c device address
 - #size-cells: Should be <0> as i2c addresses have no size component

Child nodes should conform to i2c bus binding.

Example:

i2c@a94000 {
	compatible = "qcom,i2c-geni";
	reg = <0xa94000 0x4000>;
	interrupts = <GIC_SPI 358 0>;
	#address-cells = <1>;
	#size-cells = <0>;
};
+10 −0
Original line number Diff line number Diff line
@@ -790,6 +790,16 @@ config I2C_QUP
	  This driver can also be built as a module.  If so, the module
	  will be called i2c-qup.

config I2C_QCOM_GENI
	tristate "Qualcomm Technologies Inc.'s GENI based I2C controller"
	depends on ARCH_QCOM
	help
	  If you say yes to this option, support will be included for the
	  built-in I2C interface on the Qualcomm Technologies Inc.'s SoCs.

	  This driver can also be built as a module.  If so, the module
	  will be called i2c-qcom-geni.

config I2C_RIIC
	tristate "Renesas RIIC adapter"
	depends on ARCH_RENESAS || COMPILE_TEST
+1 −0
Original line number Diff line number Diff line
@@ -76,6 +76,7 @@ obj-$(CONFIG_I2C_PUV3) += i2c-puv3.o
obj-$(CONFIG_I2C_PXA)		+= i2c-pxa.o
obj-$(CONFIG_I2C_PXA_PCI)	+= i2c-pxa-pci.o
obj-$(CONFIG_I2C_QUP)		+= i2c-qup.o
obj-$(CONFIG_I2C_QCOM_GENI)	+= i2c-qcom-geni.o
obj-$(CONFIG_I2C_RIIC)		+= i2c-riic.o
obj-$(CONFIG_I2C_RK3X)		+= i2c-rk3x.o
obj-$(CONFIG_I2C_S3C2410)	+= i2c-s3c2410.o
+321 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2017, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 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 the
 * GNU General Public License for more details.
 *
 */

#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/qcom-geni-se.h>

#define SE_I2C_TX_TRANS_LEN		(0x26C)
#define SE_I2C_RX_TRANS_LEN		(0x270)
#define SE_I2C_SCL_COUNTERS		(0x278)

#define SE_I2C_ERR  (M_CMD_OVERRUN_EN | M_ILLEGAL_CMD_EN | M_CMD_FAILURE_EN |\
			M_GP_IRQ_1_EN | M_GP_IRQ_3_EN | M_GP_IRQ_4_EN)
#define SE_I2C_ABORT (1U << 1)
/* M_CMD OP codes for I2C */
#define I2C_WRITE		(0x1)
#define I2C_READ		(0x2)
#define I2C_WRITE_READ		(0x3)
#define I2C_ADDR_ONLY		(0x4)
#define I2C_BUS_CLEAR		(0x6)
#define I2C_STOP_ON_BUS		(0x7)
/* M_CMD params for I2C */
#define PRE_CMD_DELAY		(BIT(0))
#define TIMESTAMP_BEFORE	(BIT(1))
#define STOP_STRETCH		(BIT(2))
#define TIMESTAMP_AFTER		(BIT(3))
#define POST_COMMAND_DELAY	(BIT(4))
#define IGNORE_ADD_NACK		(BIT(6))
#define READ_FINISHED_WITH_ACK	(BIT(7))
#define BYPASS_ADDR_PHASE	(BIT(8))
#define SLV_ADDR_MSK		(GENMASK(15, 9))
#define SLV_ADDR_SHFT		(9)

struct geni_i2c_dev {
	struct device *dev;
	void __iomem *base;
	int irq;
	int err;
	struct i2c_adapter adap;
	struct completion xfer;
	struct i2c_msg *cur;
	int cur_wr;
	int cur_rd;
};

static inline void qcom_geni_i2c_conf(void __iomem *base, int dfs, int div)
{
	geni_write_reg(dfs, base, SE_GENI_CLK_SEL);
	geni_write_reg((div << 4) | 1, base, GENI_SER_M_CLK_CFG);
	geni_write_reg(((5 << 20) | (0xC << 10) | 0x18),
				base, SE_I2C_SCL_COUNTERS);
	/*
	 * Ensure Clk config completes before return.
	 */
	mb();
}

static irqreturn_t geni_i2c_irq(int irq, void *dev)
{
	struct geni_i2c_dev *gi2c = dev;
	int i, j;
	u32 m_stat = readl_relaxed(gi2c->base + SE_GENI_M_IRQ_STATUS);
	u32 tx_stat = readl_relaxed(gi2c->base + SE_GENI_TX_FIFO_STATUS);
	u32 rx_stat = readl_relaxed(gi2c->base + SE_GENI_RX_FIFO_STATUS);
	struct i2c_msg *cur = gi2c->cur;

	dev_dbg(gi2c->dev,
		"got i2c irq:%d, stat:0x%x, tx stat:0x%x, rx stat:0x%x\n",
		irq, m_stat, tx_stat, rx_stat);
	if (!cur || m_stat & SE_I2C_ERR) {
		dev_err(gi2c->dev, "i2c txn err");
		writel_relaxed(0, (gi2c->base + SE_GENI_TX_WATERMARK_REG));
		gi2c->err = -EIO;
		goto irqret;
	}
	if (((m_stat & M_RX_FIFO_WATERMARK_EN) ||
		(m_stat & M_RX_FIFO_LAST_EN)) && (cur->flags & I2C_M_RD)) {
		u32 rxcnt = rx_stat & RX_FIFO_WC_MSK;

		for (j = 0; j < rxcnt; j++) {
			u32 temp;
			int p;

			temp = readl_relaxed(gi2c->base + SE_GENI_RX_FIFOn);
			for (i = gi2c->cur_rd, p = 0; (i < cur->len && p < 4);
				i++, p++)
				cur->buf[i] = (u8) ((temp >> (p * 8)) & 0xff);
			gi2c->cur_rd = i;
			if (gi2c->cur_rd == cur->len) {
				dev_dbg(gi2c->dev, "i:%d,read 0x%x\n", i, temp);
				break;
			}
			dev_dbg(gi2c->dev, "i: %d, read 0x%x\n", i, temp);
		}
	} else if ((m_stat & M_TX_FIFO_WATERMARK_EN) &&
					!(cur->flags & I2C_M_RD)) {
		for (j = 0; j < 0x1f; j++) {
			u32 temp = 0;
			int p;

			for (i = gi2c->cur_wr, p = 0; (i < cur->len && p < 4);
				i++, p++)
				temp |= (((u32)(cur->buf[i]) << (p * 8)));
			writel_relaxed(temp, gi2c->base + SE_GENI_TX_FIFOn);
			gi2c->cur_wr = i;
			dev_dbg(gi2c->dev, "i:%d,wrote 0x%x\n", i, temp);
			if (gi2c->cur_wr == cur->len) {
				dev_dbg(gi2c->dev, "i2c bytes done writing\n");
				writel_relaxed(0,
				(gi2c->base + SE_GENI_TX_WATERMARK_REG));
				break;
			}
		}
	}
irqret:
	writel_relaxed(m_stat, gi2c->base + SE_GENI_M_IRQ_CLEAR);
	/* Ensure all writes are done before returning from ISR. */
	wmb();
	/* if this is err with done-bit not set, handle that thr' timeout. */
	if (m_stat & M_CMD_DONE_EN) {
		dev_dbg(gi2c->dev, "i2c irq: err:%d, stat:0x%x\n",
							gi2c->err, m_stat);
		complete(&gi2c->xfer);
	}
	return IRQ_HANDLED;
}

static int geni_i2c_xfer(struct i2c_adapter *adap,
			 struct i2c_msg msgs[],
			 int num)
{
	struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap);
	int i, ret = 0, timeout = 0;

	gi2c->err = 0;
	gi2c->cur = &msgs[0];
	reinit_completion(&gi2c->xfer);
	enable_irq(gi2c->irq);
	qcom_geni_i2c_conf(gi2c->base, 0, 2);
	se_config_packing(gi2c->base, 8, 4, true);
	dev_dbg(gi2c->dev, "i2c xfer:num:%d, msgs:len:%d,flg:%d\n",
				num, msgs[0].len, msgs[0].flags);
	for (i = 0; i < num; i++) {
		int stretch = (i < (num - 1));
		u32 m_param = 0;
		u32 m_cmd = 0;

		m_param |= (stretch ? STOP_STRETCH : ~(STOP_STRETCH));
		m_param |= ((msgs[i].addr & 0x7F) << SLV_ADDR_SHFT);

		gi2c->cur = &msgs[i];
		if (msgs[i].flags & I2C_M_RD) {
			dev_dbg(gi2c->dev,
				"READ,n:%d,i:%d len:%d, stretch:%d\n",
					num, i, msgs[i].len, stretch);
			geni_write_reg(msgs[i].len,
				       gi2c->base, SE_I2C_RX_TRANS_LEN);
			m_cmd = I2C_READ;
			geni_setup_m_cmd(gi2c->base, m_cmd, m_param);
		} else {
			dev_dbg(gi2c->dev,
				"WRITE:n:%d,i%d len:%d, stretch:%d\n",
					num, i, msgs[i].len, stretch);
			geni_write_reg(msgs[i].len, gi2c->base,
						SE_I2C_TX_TRANS_LEN);
			m_cmd = I2C_WRITE;
			geni_setup_m_cmd(gi2c->base, m_cmd, m_param);
			/* Get FIFO IRQ */
			geni_write_reg(1, gi2c->base, SE_GENI_TX_WATERMARK_REG);
		}
		/* Ensure FIFO write go through before waiting for Done evet */
		mb();
		timeout = wait_for_completion_timeout(&gi2c->xfer, HZ);
		if (!timeout) {
			dev_err(gi2c->dev, "Timed out\n");
			gi2c->err = -ETIMEDOUT;
			gi2c->cur = NULL;
			geni_abort_m_cmd(gi2c->base);
			timeout = wait_for_completion_timeout(&gi2c->xfer, HZ);
		}
		gi2c->cur_wr = 0;
		gi2c->cur_rd = 0;
		if (gi2c->err) {
			dev_err(gi2c->dev, "i2c error :%d\n", gi2c->err);
			ret = gi2c->err;
			break;
		}
	}
	if (ret == 0)
		ret = i;
	disable_irq(gi2c->irq);
	gi2c->cur = NULL;
	gi2c->err = 0;
	dev_dbg(gi2c->dev, "i2c txn ret:%d\n", ret);
	return ret;
}

static u32 geni_i2c_func(struct i2c_adapter *adap)
{
	return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
}

static const struct i2c_algorithm geni_i2c_algo = {
	.master_xfer	= geni_i2c_xfer,
	.functionality	= geni_i2c_func,
};

static int geni_i2c_probe(struct platform_device *pdev)
{
	struct geni_i2c_dev *gi2c;
	struct resource *res;
	int ret;

	gi2c = devm_kzalloc(&pdev->dev, sizeof(*gi2c), GFP_KERNEL);
	if (!gi2c)
		return -ENOMEM;

	gi2c->dev = &pdev->dev;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!res)
		return -EINVAL;

	gi2c->base = devm_ioremap_resource(gi2c->dev, res);
	if (IS_ERR(gi2c->base))
		return PTR_ERR(gi2c->base);

	gi2c->irq = platform_get_irq(pdev, 0);
	if (gi2c->irq < 0) {
		dev_err(gi2c->dev, "IRQ error for i2c-geni\n");
		return gi2c->irq;
	}

	gi2c->adap.algo = &geni_i2c_algo;
	init_completion(&gi2c->xfer);
	platform_set_drvdata(pdev, gi2c);
	ret = devm_request_irq(gi2c->dev, gi2c->irq, geni_i2c_irq,
			       IRQF_TRIGGER_HIGH, "i2c_geni", gi2c);
	if (ret) {
		dev_err(gi2c->dev, "Request_irq failed:%d: err:%d\n",
				   gi2c->irq, ret);
		return ret;
	}
	disable_irq(gi2c->irq);
	i2c_set_adapdata(&gi2c->adap, gi2c);
	gi2c->adap.dev.parent = gi2c->dev;
	gi2c->adap.dev.of_node = pdev->dev.of_node;

	strlcpy(gi2c->adap.name, "Geni-I2C", sizeof(gi2c->adap.name));

	i2c_add_adapter(&gi2c->adap);
	geni_se_init(gi2c->base, FIFO_MODE, 0xF, 0x10);

	return 0;
}

static int geni_i2c_remove(struct platform_device *pdev)
{
	struct geni_i2c_dev *gi2c = platform_get_drvdata(pdev);

	disable_irq(gi2c->irq);
	i2c_del_adapter(&gi2c->adap);
	return 0;
}

#ifdef CONFIG_PM_SLEEP
static int geni_i2c_suspend(struct device *device)
{
	return 0;
}

static int geni_i2c_resume(struct device *device)
{
	return 0;
}
#endif

static const struct dev_pm_ops geni_i2c_pm_ops = {
	SET_SYSTEM_SLEEP_PM_OPS(
		geni_i2c_suspend,
		geni_i2c_resume)
};

static const struct of_device_id geni_i2c_dt_match[] = {
	{ .compatible = "qcom,i2c-geni" },
	{}
};
MODULE_DEVICE_TABLE(of, geni_i2c_dt_match);

static struct platform_driver geni_i2c_driver = {
	.probe  = geni_i2c_probe,
	.remove = geni_i2c_remove,
	.driver = {
		.name = "i2c_geni",
		.pm = &geni_i2c_pm_ops,
		.of_match_table = geni_i2c_dt_match,
	},
};

module_platform_driver(geni_i2c_driver);

MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:i2c_geni");
+452 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2017, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 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 the
 * GNU General Public License for more details.
 *
 */

#ifndef _LINUX_QCOM_GENI_SE
#define _LINUX_QCOM_GENI_SE
#include <linux/io.h>

enum se_xfer_mode {
	INVALID,
	FIFO_MODE,
	GSI_DMA,
};

enum se_protocol_types {
	NONE,
	SPI,
	UART,
	I2C,
	I3C
};

#define GENI_INIT_CFG_REVISION		(0x0)
#define GENI_S_INIT_CFG_REVISION	(0x4)
#define GENI_FORCE_DEFAULT_REG		(0x20)
#define GENI_OUTPUT_CTRL		(0x24)
#define GENI_CGC_CTRL			(0x28)
#define SE_GENI_STATUS			(0x40)
#define GENI_SER_M_CLK_CFG		(0x48)
#define GENI_SER_S_CLK_CFG		(0x4C)
#define GENI_CLK_CTRL_RO		(0x60)
#define GENI_IF_DISABLE_RO		(0x64)
#define GENI_FW_REVISION_RO		(0x68)
#define GENI_FW_S_REVISION_RO		(0x6C)
#define SE_GENI_CLK_SEL			(0x7C)
#define SE_GENI_DMA_MODE_EN		(0x258)
#define SE_GENI_TX_PACKING_CFG0		(0x260)
#define SE_GENI_TX_PACKING_CFG1		(0x264)
#define SE_GENI_RX_PACKING_CFG0		(0x284)
#define SE_GENI_RX_PACKING_CFG1		(0x288)
#define SE_GENI_M_CMD0			(0x600)
#define SE_GENI_M_CMD_CTRL_REG		(0x604)
#define SE_GENI_M_IRQ_STATUS		(0x610)
#define SE_GENI_M_IRQ_EN		(0x614)
#define SE_GENI_M_IRQ_CLEAR		(0x618)
#define SE_GENI_S_CMD0			(0x630)
#define SE_GENI_S_CMD_CTRL_REG		(0x634)
#define SE_GENI_S_IRQ_STATUS		(0x640)
#define SE_GENI_S_IRQ_EN		(0x644)
#define SE_GENI_S_IRQ_CLEAR		(0x648)
#define SE_GENI_TX_FIFOn		(0x700)
#define SE_GENI_RX_FIFOn		(0x780)
#define SE_GENI_TX_FIFO_STATUS		(0x800)
#define SE_GENI_RX_FIFO_STATUS		(0x804)
#define SE_GENI_TX_WATERMARK_REG	(0x80C)
#define SE_GENI_RX_WATERMARK_REG	(0x810)
#define SE_GENI_RX_RFR_WATERMARK_REG	(0x814)
#define SE_GENI_M_GP_LENGTH		(0x910)
#define SE_GENI_S_GP_LENGTH		(0x914)
#define SE_IRQ_EN			(0xE1C)
#define SE_HW_PARAM_0			(0xE24)
#define SE_HW_PARAM_1			(0xE28)
#define SE_DMA_GENERAL_CFG		(0xE30)

/* GENI_OUTPUT_CTRL fields */
#define DEFAULT_IO_OUTPUT_CTRL_MSK	(GENMASK(6, 0))

/* GENI_FORCE_DEFAULT_REG fields */
#define FORCE_DEFAULT	(BIT(0))

/* GENI_CGC_CTRL fields */
#define CFG_AHB_CLK_CGC_ON		(BIT(0))
#define CFG_AHB_WR_ACLK_CGC_ON		(BIT(1))
#define DATA_AHB_CLK_CGC_ON		(BIT(2))
#define SCLK_CGC_ON			(BIT(3))
#define TX_CLK_CGC_ON			(BIT(4))
#define RX_CLK_CGC_ON			(BIT(5))
#define EXT_CLK_CGC_ON			(BIT(6))
#define PROG_RAM_HCLK_OFF		(BIT(8))
#define PROG_RAM_SCLK_OFF		(BIT(9))
#define DEFAULT_CGC_EN			(GENMASK(6, 0))

/* GENI_STATUS fields */
#define M_GENI_CMD_ACTIVE		(BIT(0))
#define S_GENI_CMD_ACTIVE		(BIT(12))

/* GENI_SER_M_CLK_CFG/GENI_SER_S_CLK_CFG */
#define SER_CLK_EN			(BIT(0))
#define CLK_DIV_MSK			(GENMASK(15, 4))
#define CLK_DIV_SHFT			(4)

/* CLK_CTRL_RO fields */

/* IF_DISABLE_RO fields */

/* FW_REVISION_RO fields */
#define FW_REV_PROTOCOL_MSK	(GENMASK(15, 8))
#define FW_REV_PROTOCOL_SHFT	(8)

/* SE_GENI_DMA_MODE_EN */
#define GENI_DMA_MODE_EN	(BIT(0))

/* GENI_M_CMD0 fields */
#define M_OPCODE_MSK		(GENMASK(31, 27))
#define M_OPCODE_SHFT		(27)
#define M_PARAMS_MSK		(GENMASK(26, 0))

/* GENI_M_CMD_CTRL_REG */
#define M_GENI_CMD_CANCEL	BIT(2)
#define M_GENI_CMD_ABORT	BIT(1)
#define M_GENI_DISABLE		BIT(0)

/* GENI_S_CMD0 fields */
#define S_OPCODE_MSK		(GENMASK(31, 27))
#define S_OPCODE_SHFT		(27)
#define S_PARAMS_MSK		(GENMASK(26, 0))

/* GENI_S_CMD_CTRL_REG */
#define S_GENI_CMD_CANCEL	(BIT(2))
#define S_GENI_CMD_ABORT	(BIT(1))
#define S_GENI_DISABLE		(BIT(0))

/* GENI_M_IRQ_EN fields */
#define M_CMD_DONE_EN		(BIT(0))
#define M_CMD_OVERRUN_EN	(BIT(1))
#define M_ILLEGAL_CMD_EN	(BIT(2))
#define M_CMD_FAILURE_EN	(BIT(3))
#define M_CMD_CANCEL_EN		(BIT(4))
#define M_CMD_ABORT_EN		(BIT(5))
#define M_TIMESTAMP_EN		(BIT(6))
#define M_RX_IRQ_EN		(BIT(7))
#define M_GP_SYNC_IRQ_0_EN	(BIT(8))
#define M_GP_IRQ_0_EN		(BIT(9))
#define M_GP_IRQ_1_EN		(BIT(10))
#define M_GP_IRQ_2_EN		(BIT(11))
#define M_GP_IRQ_3_EN		(BIT(12))
#define M_GP_IRQ_4_EN		(BIT(13))
#define M_GP_IRQ_5_EN		(BIT(14))
#define M_IO_DATA_DEASSERT_EN	(BIT(22))
#define M_IO_DATA_ASSERT_EN	(BIT(23))
#define M_RX_FIFO_RD_ERR_EN	(BIT(24))
#define M_RX_FIFO_WR_ERR_EN	(BIT(25))
#define M_RX_FIFO_WATERMARK_EN	(BIT(26))
#define M_RX_FIFO_LAST_EN	(BIT(27))
#define M_TX_FIFO_RD_ERR_EN	(BIT(28))
#define M_TX_FIFO_WR_ERR_EN	(BIT(29))
#define M_TX_FIFO_WATERMARK_EN	(BIT(30))
#define M_SEC_IRQ_EN		(BIT(31))
#define M_COMMON_GENI_M_IRQ_EN	(GENMASK(3, 0) |  M_TIMESTAMP_EN | \
				GENMASK(14, 8) | M_IO_DATA_DEASSERT_EN | \
				M_IO_DATA_ASSERT_EN | M_RX_FIFO_RD_ERR_EN | \
				M_RX_FIFO_WR_ERR_EN | M_TX_FIFO_RD_ERR_EN | \
				M_TX_FIFO_WR_ERR_EN | M_SEC_IRQ_EN)

/* GENI_S_IRQ_EN fields */
#define S_CMD_DONE_EN		(BIT(0))
#define S_CMD_OVERRUN_EN	(BIT(1))
#define S_ILLEGAL_CMD_EN	(BIT(2))
#define S_CMD_FAILURE_EN	(BIT(3))
#define S_CMD_CANCEL_EN		(BIT(4))
#define S_CMD_ABORT_EN		(BIT(5))
#define S_GP_SYNC_IRQ_0_EN	(BIT(8))
#define S_GP_IRQ_0_EN		(BIT(9))
#define S_GP_IRQ_1_EN		(BIT(10))
#define S_GP_IRQ_2_EN		(BIT(11))
#define S_GP_IRQ_3_EN		(BIT(12))
#define S_GP_IRQ_4_EN		(BIT(13))
#define S_GP_IRQ_5_EN		(BIT(14))
#define S_IO_DATA_DEASSERT_EN	(BIT(22))
#define S_IO_DATA_ASSERT_EN	(BIT(23))
#define S_RX_FIFO_RD_ERR_EN	(BIT(24))
#define S_RX_FIFO_WR_ERR_EN	(BIT(25))
#define S_RX_FIFO_WATERMARK_EN	(BIT(26))
#define S_RX_FIFO_LAST_EN	(BIT(27))
#define S_COMMON_GENI_S_IRQ_EN	(GENMASK(3, 0) | GENMASK(14, 8) | \
				 S_RX_FIFO_RD_ERR_EN | S_RX_FIFO_WR_ERR_EN)

/*  GENI_/TX/RX/RX_RFR/_WATERMARK_REG fields */
#define WATERMARK_MSK		(GENMASK(5, 0))

/* GENI_TX_FIFO_STATUS fields */
#define TX_FIFO_WC		(GENMASK(27, 0))

/*  GENI_RX_FIFO_STATUS fields */
#define RX_LAST			(BIT(31))
#define RX_LAST_BYTE_VALID_MSK	(GENMASK(30, 28))
#define RX_LAST_BYTE_VALID_SHFT	(28)
#define RX_FIFO_WC_MSK		(GENMASK(24, 0))

/* SE_IRQ_EN fields */
#define DMA_RX_IRQ_EN		(BIT(0))
#define DMA_TX_IRQ_EN		(BIT(1))
#define GENI_M_IRQ_EN		(BIT(2))
#define GENI_S_IRQ_EN		(BIT(3))

/* SE_HW_PARAM_0 fields */
#define TX_FIFO_WIDTH_MSK	(GENMASK(29, 24))
#define TX_FIFO_WIDTH_SHFT	(24)
#define TX_FIFO_DEPTH_MSK	(GENMASK(21, 16))
#define TX_FIFO_DEPTH_SHFT	(16)

/* SE_HW_PARAM_1 fields */
#define RX_FIFO_WIDTH_MSK	(GENMASK(29, 24))
#define RX_FIFO_WIDTH_SHFT	(24)
#define RX_FIFO_DEPTH_MSK	(GENMASK(21, 16))
#define RX_FIFO_DEPTH_SHFT	(16)

/* SE_DMA_GENERAL_CFG */
#define DMA_RX_CLK_CGC_ON	(BIT(0))
#define DMA_TX_CLK_CGC_ON	(BIT(1))
#define DMA_AHB_SLV_CFG_ON	(BIT(2))
#define AHB_SEC_SLV_CLK_CGC_ON	(BIT(3))
#define DUMMY_RX_NON_BUFFERABLE	(BIT(4))
#define RX_DMA_ZERO_PADDING_EN	(BIT(5))
#define RX_DMA_IRQ_DELAY_MSK	(GENMASK(8, 6))
#define RX_DMA_IRQ_DELAY_SHFT	(6)

static inline unsigned int geni_read_reg(void __iomem *base, int offset)
{
	return readl_relaxed(base + offset);
}

static inline void geni_write_reg(unsigned int value, void __iomem *base,
				int offset)
{
	return writel_relaxed(value, (base + offset));
}

static inline int get_se_proto(void __iomem *base)
{
	int proto = 0;

	proto = ((geni_read_reg(base, GENI_FW_REVISION_RO)
			& FW_REV_PROTOCOL_MSK) >> FW_REV_PROTOCOL_SHFT);
	return proto;
}

static inline int se_geni_irq_en(void __iomem *base, int mode)
{
	int ret = 0;
	unsigned int common_geni_m_irq_en;
	unsigned int common_geni_s_irq_en;
	int proto = get_se_proto(base);

	common_geni_m_irq_en = geni_read_reg(base, SE_GENI_M_IRQ_EN);
	common_geni_s_irq_en = geni_read_reg(base, SE_GENI_S_IRQ_EN);
	/* Common to all modes */
	common_geni_m_irq_en |= M_COMMON_GENI_M_IRQ_EN;
	common_geni_s_irq_en |= S_COMMON_GENI_S_IRQ_EN;

	switch (mode) {
	case FIFO_MODE:
	{
		if (proto == I2C) {
			common_geni_m_irq_en |=
				(M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN);
			common_geni_s_irq_en |= S_CMD_DONE_EN;
		}
		break;
	}
	case GSI_DMA:
		break;
	default:
		pr_err("%s: Invalid mode %d\n", __func__, mode);
		ret = -ENXIO;
		goto exit_irq_en;
	}


	geni_write_reg(common_geni_m_irq_en, base, SE_GENI_M_IRQ_EN);
	geni_write_reg(common_geni_s_irq_en, base, SE_GENI_S_IRQ_EN);
exit_irq_en:
	return ret;
}


static inline void se_set_rx_rfr_wm(void __iomem *base, unsigned int rx_wm,
						unsigned int rx_rfr)
{
	geni_write_reg(rx_wm, base, SE_GENI_RX_WATERMARK_REG);
	geni_write_reg(rx_rfr, base, SE_GENI_RX_RFR_WATERMARK_REG);
}

static inline int se_io_set_mode(void __iomem *base, int mode)
{
	int ret = 0;
	unsigned int io_mode = 0;
	unsigned int geni_dma_mode = 0;

	io_mode = geni_read_reg(base, SE_IRQ_EN);
	geni_dma_mode = geni_read_reg(base, SE_GENI_DMA_MODE_EN);

	switch (mode) {
	case FIFO_MODE:
	{
		io_mode |= (GENI_M_IRQ_EN | GENI_S_IRQ_EN);
		io_mode |= (DMA_TX_IRQ_EN | DMA_RX_IRQ_EN);
		geni_dma_mode &= ~GENI_DMA_MODE_EN;
		break;

	}
	default:
		ret = -ENXIO;
		goto exit_set_mode;
	}
	geni_write_reg(io_mode, base, SE_IRQ_EN);
	geni_write_reg(geni_dma_mode, base, SE_GENI_DMA_MODE_EN);
exit_set_mode:
	return ret;
}

static inline void se_io_init(void __iomem *base)
{
	unsigned int io_op_ctrl = 0;
	unsigned int geni_cgc_ctrl;
	unsigned int dma_general_cfg;

	geni_cgc_ctrl = geni_read_reg(base, GENI_CGC_CTRL);
	dma_general_cfg = geni_read_reg(base, SE_DMA_GENERAL_CFG);
	geni_cgc_ctrl |= DEFAULT_CGC_EN;
	dma_general_cfg |= (AHB_SEC_SLV_CLK_CGC_ON | DMA_AHB_SLV_CFG_ON |
			DMA_TX_CLK_CGC_ON | DMA_RX_CLK_CGC_ON);
	io_op_ctrl |= DEFAULT_IO_OUTPUT_CTRL_MSK;
	geni_write_reg(geni_cgc_ctrl, base, GENI_CGC_CTRL);
	geni_write_reg(dma_general_cfg, base, SE_DMA_GENERAL_CFG);

	geni_write_reg(io_op_ctrl, base, GENI_OUTPUT_CTRL);
	geni_write_reg(FORCE_DEFAULT, base, GENI_FORCE_DEFAULT_REG);
}

static inline int geni_se_init(void __iomem *base, int mode,
		unsigned int rx_wm, unsigned int rx_rfr)
{
	int ret = 0;

	se_io_init(base);
	ret = se_io_set_mode(base, mode);
	if (ret)
		goto exit_geni_se_init;

	se_set_rx_rfr_wm(base, rx_wm, rx_rfr);
	ret = se_geni_irq_en(base, mode);
	if (ret)
		goto exit_geni_se_init;

exit_geni_se_init:
	return ret;
}

static inline void geni_setup_m_cmd(void __iomem *base, u32 cmd,
								u32 params)
{
	u32 m_cmd = geni_read_reg(base, SE_GENI_M_CMD0);

	m_cmd &= ~(M_OPCODE_MSK | M_PARAMS_MSK);
	m_cmd |= (cmd << M_OPCODE_SHFT);
	m_cmd |= (params & M_PARAMS_MSK);
	geni_write_reg(m_cmd, base, SE_GENI_M_CMD0);
}

static inline void geni_setup_s_cmd(void __iomem *base, u32 cmd,
								u32 params)
{
	u32 s_cmd = geni_read_reg(base, SE_GENI_S_CMD0);

	s_cmd &= ~(S_OPCODE_MSK | S_PARAMS_MSK);
	s_cmd |= (cmd << S_OPCODE_SHFT);
	s_cmd |= (params & S_PARAMS_MSK);
	geni_write_reg(s_cmd, base, SE_GENI_S_CMD0);
}

static inline void geni_cancel_m_cmd(void __iomem *base)
{
	geni_write_reg(M_GENI_CMD_CANCEL, base, SE_GENI_S_CMD_CTRL_REG);
}

static inline void geni_cancel_s_cmd(void __iomem *base)
{
	geni_write_reg(S_GENI_CMD_CANCEL, base, SE_GENI_S_CMD_CTRL_REG);
}

static inline void geni_abort_m_cmd(void __iomem *base)
{
	geni_write_reg(M_GENI_CMD_ABORT, base, SE_GENI_M_CMD_CTRL_REG);
}

static inline void qcom_geni_abort_s_cmd(void __iomem *base)
{
	geni_write_reg(S_GENI_CMD_ABORT, base, SE_GENI_S_CMD_CTRL_REG);
}

static inline int get_tx_fifo_depth(void __iomem *base)
{
	int tx_fifo_depth;

	tx_fifo_depth = ((geni_read_reg(base, SE_HW_PARAM_0)
			& TX_FIFO_DEPTH_MSK) >> TX_FIFO_DEPTH_SHFT);
	return tx_fifo_depth;
}

static inline int get_tx_fifo_width(void __iomem *base)
{
	int tx_fifo_width;

	tx_fifo_width = ((geni_read_reg(base, SE_HW_PARAM_0)
			& TX_FIFO_WIDTH_MSK) >> TX_FIFO_WIDTH_SHFT);
	return tx_fifo_width;
}

static inline int get_rx_fifo_depth(void __iomem *base)
{
	int rx_fifo_depth;

	rx_fifo_depth = ((geni_read_reg(base, SE_HW_PARAM_1)
			& RX_FIFO_DEPTH_MSK) >> RX_FIFO_DEPTH_SHFT);
	return rx_fifo_depth;
}

static inline void se_config_packing(void __iomem *base, int bpw,
				int pack_words, bool msb_to_lsb)
{
	u32 cfg[4] = {0};
	unsigned long cfg0, cfg1;
	int len = ((bpw < 8) ? (bpw - 1) : 7);
	int idx = ((msb_to_lsb == 1) ? len : 0);
	int iter = (bpw * pack_words) >> 3;
	int i;

	for (i = 0; i < iter; i++) {
		cfg[i] = ((idx << 5) | (msb_to_lsb << 4) | (len << 1));
		idx += (len + 1);
		if (i == iter - 1)
			cfg[i] |= 1;
	}
	cfg0 = cfg[0] | (cfg[1] << 10);
	cfg1 = cfg[2] | (cfg[3] << 10);
	geni_write_reg(cfg0, base, SE_GENI_TX_PACKING_CFG0);
	geni_write_reg(cfg1, base, SE_GENI_TX_PACKING_CFG1);
	geni_write_reg(cfg0, base, SE_GENI_RX_PACKING_CFG0);
	geni_write_reg(cfg1, base, SE_GENI_RX_PACKING_CFG1);
}
#endif