devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/2] i2c: Add support for Qualcomm Camera Control Interface
@ 2018-07-17 12:57 Vinod Koul
  2018-07-17 12:57 ` [PATCH v2 1/2] dt-bindings: i2c: Add binding document for Qualcomm CCI Vinod Koul
  2018-07-17 12:57 ` [PATCH v2 2/2] i2c: Add Qualcomm Camera Control Interface driver Vinod Koul
  0 siblings, 2 replies; 6+ messages in thread
From: Vinod Koul @ 2018-07-17 12:57 UTC (permalink / raw)
  To: linux-i2c
  Cc: Bjorn Andersson, linux-arm-msm, Rob Herring, Wolfram Sang,
	devicetree, Vinod Koul

Hi,

This series adds support for Qualcomm Camera Control Interface (CCI). This
contains two I2C masters and registers two adapters for each of the masters.

Changes in v2:
 - Add support for two adapters

Todor Tomov (2):
  dt-bindings: i2c: Add binding document for Qualcomm CCI
  i2c: Add Qualcomm Camera Control Interface driver

 .../devicetree/bindings/i2c/i2c-qcom-cci.txt       |  46 ++
 drivers/i2c/busses/Kconfig                         |  10 +
 drivers/i2c/busses/Makefile                        |   1 +
 drivers/i2c/busses/i2c-qcom-cci.c                  | 793 +++++++++++++++++++++
 4 files changed, 850 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/i2c-qcom-cci.txt
 create mode 100644 drivers/i2c/busses/i2c-qcom-cci.c

-- 
2.14.4

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v2 1/2] dt-bindings: i2c: Add binding document for Qualcomm CCI
  2018-07-17 12:57 [PATCH v2 0/2] i2c: Add support for Qualcomm Camera Control Interface Vinod Koul
@ 2018-07-17 12:57 ` Vinod Koul
  2018-07-20 18:03   ` Rob Herring
  2018-07-17 12:57 ` [PATCH v2 2/2] i2c: Add Qualcomm Camera Control Interface driver Vinod Koul
  1 sibling, 1 reply; 6+ messages in thread
From: Vinod Koul @ 2018-07-17 12:57 UTC (permalink / raw)
  To: linux-i2c
  Cc: Bjorn Andersson, linux-arm-msm, Rob Herring, Wolfram Sang,
	devicetree, Todor Tomov, Vinod Koul

From: Todor Tomov <todor.tomov@linaro.org>

Add DT binding document for Qualcomm Camera Control Interface device

Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
 .../devicetree/bindings/i2c/i2c-qcom-cci.txt       | 46 ++++++++++++++++++++++
 1 file changed, 46 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/i2c-qcom-cci.txt

diff --git a/Documentation/devicetree/bindings/i2c/i2c-qcom-cci.txt b/Documentation/devicetree/bindings/i2c/i2c-qcom-cci.txt
new file mode 100644
index 000000000000..cb1fe158fba3
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-qcom-cci.txt
@@ -0,0 +1,46 @@
+Qualcomm Camera Control Interface controller
+
+Required properties:
+ - compatible: Should be one of:
+   - "qcom,cci-v1.0.8" for 8916;
+   - "qcom,cci-v1.4.0" for 8996.
+ - #address-cells: Should be <1>.
+ - #size-cells: Should be <0>.
+ - reg: Base address of the controller and length of memory mapped region.
+ - interrupts: Specifier for CCI interrupt.
+ - clocks: List of clock specifiers, one for each entry in clock-names.
+ - clock-names: Should contain:
+   - "mmss_mmagic_ahb" - on 8996 only;
+   - "camss_top_ahb";
+   - "cci_ahb";
+   - "cci";
+   - "camss_ahb".
+
+Required properties on 8996:
+ - power-domains: Power domain specifier.
+
+Optional:
+ - clock-frequency: Desired I2C bus clock frequency in Hz, defaults to 100 kHz
+   if omitted.
+
+Example:
+
+                cci@a0c000 {
+                        compatible = "qcom,cci-v1.4.0";
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+                        reg = <0xa0c000 0x1000>;
+                        interrupts = <GIC_SPI 295 IRQ_TYPE_EDGE_RISING>;
+                        power-domains = <&mmcc CAMSS_GDSC>;
+                        clocks = <&mmcc MMSS_MMAGIC_AHB_CLK>,
+                                        <&mmcc CAMSS_TOP_AHB_CLK>,
+                                        <&mmcc CAMSS_CCI_AHB_CLK>,
+                                        <&mmcc CAMSS_CCI_CLK>,
+                                        <&mmcc CAMSS_AHB_CLK>;
+                        clock-names = "mmss_mmagic_ahb",
+                                        "camss_top_ahb",
+                                        "cci_ahb",
+                                        "cci",
+                                        "camss_ahb";
+                        clock-frequency = <400000>;
+                };
-- 
2.14.4

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v2 2/2] i2c: Add Qualcomm Camera Control Interface driver
  2018-07-17 12:57 [PATCH v2 0/2] i2c: Add support for Qualcomm Camera Control Interface Vinod Koul
  2018-07-17 12:57 ` [PATCH v2 1/2] dt-bindings: i2c: Add binding document for Qualcomm CCI Vinod Koul
@ 2018-07-17 12:57 ` Vinod Koul
  1 sibling, 0 replies; 6+ messages in thread
From: Vinod Koul @ 2018-07-17 12:57 UTC (permalink / raw)
  To: linux-i2c
  Cc: Bjorn Andersson, linux-arm-msm, Rob Herring, Wolfram Sang,
	devicetree, Todor Tomov, Vinod Koul

From: Todor Tomov <todor.tomov@linaro.org>

This commit adds I2C bus support for the Camera Control Interface
(CCI) controller found on the Qualcomm SoC processors.

CCI versions supported:
 - 1.0.8 - found on MSM8916/APQ8016
 - 1.4.0 - found on MSM8996/APQ8096 I2C bus.

Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
Signed-off-by: Vinod Koul <vkoul@kernel.org>
---
 drivers/i2c/busses/Kconfig        |  10 +
 drivers/i2c/busses/Makefile       |   1 +
 drivers/i2c/busses/i2c-qcom-cci.c | 793 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 804 insertions(+)
 create mode 100644 drivers/i2c/busses/i2c-qcom-cci.c

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 4f8df2ec87b1..033958bcdd4b 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -828,6 +828,16 @@ config I2C_PXA_SLAVE
 	  is necessary for systems where the PXA may be a target on the
 	  I2C bus.
 
+config I2C_QCOM_CCI
+	tristate "Qualcomm Camera Control Interface"
+	depends on ARCH_QCOM
+	help
+	  If you say yes to this option, support will be included for the
+	  built-in camera control interface on the Qualcomm SoCs.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called i2c-qcom-cci.
+
 config I2C_QUP
 	tristate "Qualcomm QUP based I2C controller"
 	depends on ARCH_QCOM
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 5a869144a0c5..d6e48ec3372a 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -83,6 +83,7 @@ obj-$(CONFIG_I2C_PNX)		+= i2c-pnx.o
 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_QCOM_CCI)	+= i2c-qcom-cci.o
 obj-$(CONFIG_I2C_QUP)		+= i2c-qup.o
 obj-$(CONFIG_I2C_RIIC)		+= i2c-riic.o
 obj-$(CONFIG_I2C_RK3X)		+= i2c-rk3x.o
diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c
new file mode 100644
index 000000000000..972e3dcc4556
--- /dev/null
+++ b/drivers/i2c/busses/i2c-qcom-cci.c
@@ -0,0 +1,793 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+// Copyright (c) 2017-18 Linaro Limited.
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#define CCI_HW_VERSION				0x0
+#define CCI_RESET_CMD				0x004
+#define CCI_RESET_CMD_MASK			0x0f73f3f7
+#define CCI_RESET_CMD_M0_MASK			0x000003f1
+#define CCI_RESET_CMD_M1_MASK			0x0003f001
+#define CCI_QUEUE_START				0x008
+#define CCI_HALT_REQ				0x034
+#define CCI_HALT_REQ_I2C_M0_Q0Q1		BIT(0)
+#define CCI_HALT_REQ_I2C_M1_Q0Q1		BIT(1)
+
+#define CCI_I2C_Mm_SCL_CTL(m)			(0x100 + 0x100 * (m))
+#define CCI_I2C_Mm_SDA_CTL_0(m)			(0x104 + 0x100 * (m))
+#define CCI_I2C_Mm_SDA_CTL_1(m)			(0x108 + 0x100 * (m))
+#define CCI_I2C_Mm_SDA_CTL_2(m)			(0x10c + 0x100 * (m))
+#define CCI_I2C_Mm_MISC_CTL(m)			(0x110 + 0x100 * (m))
+
+#define CCI_I2C_Mm_READ_DATA(m)			(0x118 + 0x100 * (m))
+#define CCI_I2C_Mm_READ_BUF_LEVEL(m)		(0x11c + 0x100 * (m))
+#define CCI_I2C_Mm_Qn_EXEC_WORD_CNT(m, n)	(0x300 + 0x200 * (m) + 0x100 * (n))
+#define CCI_I2C_Mm_Qn_CUR_WORD_CNT(m, n)	(0x304 + 0x200 * (m) + 0x100 * (n))
+#define CCI_I2C_Mm_Qn_CUR_CMD(m, n)		(0x308 + 0x200 * (m) + 0x100 * (n))
+#define CCI_I2C_Mm_Qn_REPORT_STATUS(m, n)	(0x30c + 0x200 * (m) + 0x100 * (n))
+#define CCI_I2C_Mm_Qn_LOAD_DATA(m, n)		(0x310 + 0x200 * (m) + 0x100 * (n))
+
+#define CCI_IRQ_GLOBAL_CLEAR_CMD		0xc00
+#define CCI_IRQ_MASK_0				0xc04
+#define CCI_IRQ_MASK_0_I2C_M0_RD_DONE		BIT(0)
+#define CCI_IRQ_MASK_0_I2C_M0_Q0_REPORT		BIT(4)
+#define CCI_IRQ_MASK_0_I2C_M0_Q1_REPORT		BIT(8)
+#define CCI_IRQ_MASK_0_I2C_M1_RD_DONE		BIT(12)
+#define CCI_IRQ_MASK_0_I2C_M1_Q0_REPORT		BIT(16)
+#define CCI_IRQ_MASK_0_I2C_M1_Q1_REPORT		BIT(20)
+#define CCI_IRQ_MASK_0_RST_DONE_ACK		BIT(24)
+#define CCI_IRQ_MASK_0_I2C_M0_Q0Q1_HALT_ACK	BIT(25)
+#define CCI_IRQ_MASK_0_I2C_M1_Q0Q1_HALT_ACK	BIT(26)
+#define CCI_IRQ_MASK_0_I2C_M0_ERROR		0x18000ee6
+#define CCI_IRQ_MASK_0_I2C_M1_ERROR		0x60ee6000
+#define CCI_IRQ_CLEAR_0				0xc08
+#define CCI_IRQ_STATUS_0			0xc0c
+#define CCI_IRQ_STATUS_0_I2C_M0_RD_DONE		BIT(0)
+#define CCI_IRQ_STATUS_0_I2C_M0_Q0_REPORT	BIT(4)
+#define CCI_IRQ_STATUS_0_I2C_M0_Q1_REPORT	BIT(8)
+#define CCI_IRQ_STATUS_0_I2C_M1_RD_DONE		BIT(12)
+#define CCI_IRQ_STATUS_0_I2C_M1_Q0_REPORT	BIT(16)
+#define CCI_IRQ_STATUS_0_I2C_M1_Q1_REPORT	BIT(20)
+#define CCI_IRQ_STATUS_0_RST_DONE_ACK		BIT(24)
+#define CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK	BIT(25)
+#define CCI_IRQ_STATUS_0_I2C_M1_Q0Q1_HALT_ACK	BIT(26)
+#define CCI_IRQ_STATUS_0_I2C_M0_ERROR		0x18000ee6
+#define CCI_IRQ_STATUS_0_I2C_M1_ERROR		0x60ee6000
+
+#define CCI_TIMEOUT_MS 100
+#define NUM_MASTERS 2
+#define NUM_QUEUES 2
+
+/* Max number of resources + 1 for a NULL terminator */
+#define CCI_RES_MAX 6
+
+enum cci_i2c_cmd {
+	CCI_I2C_SET_PARAM = 1,
+	CCI_I2C_WAIT,
+	CCI_I2C_WAIT_SYNC,
+	CCI_I2C_WAIT_GPIO_EVENT,
+	CCI_I2C_TRIG_I2C_EVENT,
+	CCI_I2C_LOCK,
+	CCI_I2C_UNLOCK,
+	CCI_I2C_REPORT,
+	CCI_I2C_WRITE,
+	CCI_I2C_READ,
+	CCI_I2C_WRITE_DISABLE_P,
+	CCI_I2C_READ_DISABLE_P,
+};
+
+enum {
+	I2C_MODE_STANDARD,
+	I2C_MODE_FAST,
+	I2C_MODE_FAST_PLUS,
+};
+
+enum cci_i2c_queue_t {
+	QUEUE_0,
+	QUEUE_1
+};
+
+struct cci_res {
+	char *clock[CCI_RES_MAX];
+	u32 clock_rate[CCI_RES_MAX];
+};
+
+struct hw_params {
+	u16 thigh;
+	u16 tlow;
+	u16 tsu_sto;
+	u16 tsu_sta;
+	u16 thd_dat;
+	u16 thd_sta;
+	u16 tbuf;
+	u8 scl_stretch_en;
+	u16 trdhld;
+	u16 tsp;
+};
+
+struct cci_clock {
+	struct clk *clk;
+	const char *name;
+	u32 freq;
+};
+
+struct cci;
+
+struct cci_master {
+	struct i2c_adapter adap;
+	u16 master;
+	int status;
+	bool complete_pending;
+	struct completion irq_complete;
+	struct cci *cci;
+};
+
+struct cci {
+	struct device *dev;
+	void __iomem *base;
+	u32 irq;
+	struct clk_bulk_data *clock;
+	u32 *clock_freq;
+	int nclocks;
+	u16 queue_size[NUM_QUEUES];
+	struct cci_master master[NUM_MASTERS];
+};
+
+static const struct cci_res res_v1_0_8 = {
+	.clock = {
+		"camss_top_ahb",
+		"cci_ahb",
+		"camss_ahb",
+		"cci"
+	},
+	.clock_rate = {
+		0,
+		80000000,
+		0,
+		19200000
+	}
+};
+
+static const struct cci_res res_v1_4_0 = {
+	.clock = { "camss_top_ahb",
+		   "cci_ahb",
+		   "camss_ahb",
+		   "cci" },
+	.clock_rate = { 0,
+			0,
+			0,
+			37500000 }
+};
+
+static const struct hw_params hw_params_v1_0_8[3] = {
+	{
+		/* I2C_MODE_STANDARD */
+		.thigh = 78,
+		.tlow = 114,
+		.tsu_sto = 28,
+		.tsu_sta = 28,
+		.thd_dat = 10,
+		.thd_sta = 77,
+		.tbuf = 118,
+		.scl_stretch_en = 0,
+		.trdhld = 6,
+		.tsp = 1
+	},
+	{
+		/* I2C_MODE_FAST */
+		.thigh = 20,
+		.tlow = 28,
+		.tsu_sto = 21,
+		.tsu_sta = 21,
+		.thd_dat = 13,
+		.thd_sta = 18,
+		.tbuf = 32,
+		.scl_stretch_en = 0,
+		.trdhld = 6,
+		.tsp = 3
+	}
+};
+
+static const struct hw_params hw_params_v1_4_0[3] = {
+	{
+		/* I2C_MODE_STANDARD */
+		.thigh = 201,
+		.tlow = 174,
+		.tsu_sto = 204,
+		.tsu_sta = 231,
+		.thd_dat = 22,
+		.thd_sta = 162,
+		.tbuf = 227,
+		.scl_stretch_en = 0,
+		.trdhld = 6,
+		.tsp = 3
+	},
+	{
+		/* I2C_MODE_FAST */
+		.thigh = 38,
+		.tlow = 56,
+		.tsu_sto = 40,
+		.tsu_sta = 40,
+		.thd_dat = 22,
+		.thd_sta = 35,
+		.tbuf = 62,
+		.scl_stretch_en = 0,
+		.trdhld = 6,
+		.tsp = 3
+	},
+	{
+		/* I2C_MODE_FAST_PLUS */
+		.thigh = 16,
+		.tlow = 22,
+		.tsu_sto = 17,
+		.tsu_sta = 18,
+		.thd_dat = 16,
+		.thd_sta = 15,
+		.tbuf = 24,
+		.scl_stretch_en = 0,
+		.trdhld = 3,
+		.tsp = 3
+	}
+};
+
+static const u16 queue_0_size_v1_0_8 = 64;
+static const u16 queue_1_size_v1_0_8 = 16;
+
+static const u16 queue_0_size_v1_4_0 = 64;
+static const u16 queue_1_size_v1_4_0 = 16;
+
+/**
+ * cci_clock_set_rate() - Set clock frequency rates
+ * @nclocks: Number of clocks
+ * @clock: Clock array
+ * @clock_freq: Clock frequency rate array
+ * @dev: Device
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int cci_clock_set_rate(int nclocks, struct clk_bulk_data *clock,
+			      u32 *clock_freq, struct device *dev)
+{
+	int i, ret;
+	long rate;
+
+	for (i = 0; i < nclocks; i++)
+		if (clock_freq[i]) {
+			rate = clk_round_rate(clock[i].clk, clock_freq[i]);
+			if (rate < 0) {
+				dev_err(dev, "clk round rate failed: %ld\n",
+					rate);
+				return rate;
+			}
+
+			ret = clk_set_rate(clock[i].clk, clock_freq[i]);
+			if (ret < 0) {
+				dev_err(dev, "clk set rate failed: %d\n", ret);
+				return ret;
+			}
+		}
+
+	return 0;
+}
+
+static irqreturn_t cci_isr(int irq, void *dev)
+{
+	struct cci *cci = dev;
+	u32 val, reset = 0;
+
+	val = readl(cci->base + CCI_IRQ_STATUS_0);
+	writel(val, cci->base + CCI_IRQ_CLEAR_0);
+	writel(0x1, cci->base + CCI_IRQ_GLOBAL_CLEAR_CMD);
+
+	if (val & CCI_IRQ_STATUS_0_RST_DONE_ACK) {
+		if (cci->master[0].complete_pending) {
+			cci->master[0].complete_pending = false;
+			complete(&cci->master[0].irq_complete);
+		}
+
+		if (cci->master[1].complete_pending) {
+			cci->master[1].complete_pending = false;
+			complete(&cci->master[1].irq_complete);
+		}
+	}
+
+	if (val & CCI_IRQ_STATUS_0_I2C_M0_RD_DONE ||
+			val & CCI_IRQ_STATUS_0_I2C_M0_Q0_REPORT ||
+			val & CCI_IRQ_STATUS_0_I2C_M0_Q1_REPORT) {
+		cci->master[0].status = 0;
+		complete(&cci->master[0].irq_complete);
+	}
+
+	if (val & CCI_IRQ_STATUS_0_I2C_M1_RD_DONE ||
+			val & CCI_IRQ_STATUS_0_I2C_M1_Q0_REPORT ||
+			val & CCI_IRQ_STATUS_0_I2C_M1_Q1_REPORT) {
+		cci->master[1].status = 0;
+		complete(&cci->master[1].irq_complete);
+	}
+
+	if (unlikely(val & CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK)) {
+		cci->master[0].complete_pending = true;
+		reset = CCI_RESET_CMD_M0_MASK;
+	}
+
+	if (unlikely(val & CCI_IRQ_STATUS_0_I2C_M1_Q0Q1_HALT_ACK)) {
+		cci->master[1].complete_pending = true;
+		reset = CCI_RESET_CMD_M1_MASK;
+	}
+
+	if (unlikely(reset))
+		writel(reset, cci->base + CCI_RESET_CMD);
+
+	if (unlikely(val & CCI_IRQ_STATUS_0_I2C_M0_ERROR)) {
+		dev_err_ratelimited(cci->dev, "Master 0 error 0x%08x\n", val);
+		cci->master[0].status = -EIO;
+		writel(CCI_HALT_REQ_I2C_M0_Q0Q1, cci->base + CCI_HALT_REQ);
+	}
+
+	if (unlikely(val & CCI_IRQ_STATUS_0_I2C_M1_ERROR)) {
+		dev_err_ratelimited(cci->dev, "Master 1 error 0x%08x\n", val);
+		cci->master[1].status = -EIO;
+		writel(CCI_HALT_REQ_I2C_M1_Q0Q1, cci->base + CCI_HALT_REQ);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void cci_halt(struct cci *cci)
+{
+	unsigned long time;
+	u32 val = CCI_HALT_REQ_I2C_M0_Q0Q1 | CCI_HALT_REQ_I2C_M1_Q0Q1;
+
+	cci->master[0].complete_pending = true;
+	writel(val, cci->base + CCI_HALT_REQ);
+
+	time = wait_for_completion_timeout
+		(&cci->master[0].irq_complete,
+		 msecs_to_jiffies(CCI_TIMEOUT_MS));
+	if (!time)
+		dev_err(cci->dev, "CCI halt timeout\n");
+}
+
+static int cci_reset(struct cci *cci)
+{
+	unsigned long time;
+
+	cci->master[0].complete_pending = true;
+	writel(CCI_RESET_CMD_MASK, cci->base + CCI_RESET_CMD);
+
+	time = wait_for_completion_timeout
+		(&cci->master[0].irq_complete,
+		 msecs_to_jiffies(CCI_TIMEOUT_MS));
+	if (!time) {
+		dev_err(cci->dev, "CCI reset timeout\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int cci_init(struct cci *cci, const struct hw_params *hw)
+{
+	u32 val = CCI_IRQ_MASK_0_I2C_M0_RD_DONE |
+			CCI_IRQ_MASK_0_I2C_M0_Q0_REPORT |
+			CCI_IRQ_MASK_0_I2C_M0_Q1_REPORT |
+			CCI_IRQ_MASK_0_I2C_M1_RD_DONE |
+			CCI_IRQ_MASK_0_I2C_M1_Q0_REPORT |
+			CCI_IRQ_MASK_0_I2C_M1_Q1_REPORT |
+			CCI_IRQ_MASK_0_RST_DONE_ACK |
+			CCI_IRQ_MASK_0_I2C_M0_Q0Q1_HALT_ACK |
+			CCI_IRQ_MASK_0_I2C_M1_Q0Q1_HALT_ACK |
+			CCI_IRQ_MASK_0_I2C_M0_ERROR |
+			CCI_IRQ_MASK_0_I2C_M1_ERROR;
+	int i;
+
+	writel(val, cci->base + CCI_IRQ_MASK_0);
+
+	for (i = 0; i < NUM_MASTERS; i++) {
+		val = hw->thigh << 16 | hw->tlow;
+		writel(val, cci->base + CCI_I2C_Mm_SCL_CTL(i));
+
+		val = hw->tsu_sto << 16 | hw->tsu_sta;
+		writel(val, cci->base + CCI_I2C_Mm_SDA_CTL_0(i));
+
+		val = hw->thd_dat << 16 | hw->thd_sta;
+		writel(val, cci->base + CCI_I2C_Mm_SDA_CTL_1(i));
+
+		val = hw->tbuf;
+		writel(val, cci->base + CCI_I2C_Mm_SDA_CTL_2(i));
+
+		val = hw->scl_stretch_en << 8 | hw->trdhld << 4 | hw->tsp;
+		writel(val, cci->base + CCI_I2C_Mm_MISC_CTL(i));
+	}
+
+	return 0;
+}
+
+static int cci_run_queue(struct cci *cci, u8 master, u8 queue)
+{
+	unsigned long time;
+	u32 val;
+	int ret;
+
+	val = readl(cci->base + CCI_I2C_Mm_Qn_CUR_WORD_CNT(master, queue));
+	writel(val, cci->base + CCI_I2C_Mm_Qn_EXEC_WORD_CNT(master, queue));
+
+	val = BIT(master * 2 + queue);
+	writel(val, cci->base + CCI_QUEUE_START);
+
+	time = wait_for_completion_timeout(&cci->master[master].irq_complete,
+					   msecs_to_jiffies(CCI_TIMEOUT_MS));
+	if (!time) {
+		dev_err(cci->dev, "master %d queue %d timeout\n",
+			master, queue);
+
+		cci_halt(cci);
+
+		return -ETIMEDOUT;
+	}
+
+	ret = cci->master[master].status;
+	if (ret < 0)
+		dev_err(cci->dev, "master %d queue %d error %d\n",
+			master, queue, ret);
+
+	return ret;
+}
+
+static int cci_validate_queue(struct cci *cci, u8 master, u8 queue)
+{
+	int ret = 0;
+	u32 val;
+
+	val = readl(cci->base + CCI_I2C_Mm_Qn_CUR_WORD_CNT(master, queue));
+
+	if (val == cci->queue_size[queue])
+		return -EINVAL;
+
+	if (val) {
+		val = CCI_I2C_REPORT | BIT(8);
+		writel(val, cci->base + CCI_I2C_Mm_Qn_LOAD_DATA(master, queue));
+
+		ret = cci_run_queue(cci, master, queue);
+	}
+
+	return ret;
+}
+
+static int cci_i2c_read(struct cci *cci, u16 master,
+			u16 addr, u8 *buf, u16 len)
+{
+	u32 val, words_read, words_exp;
+	u8 queue = QUEUE_1;
+	int i, index = 0, ret;
+	bool first = 0;
+
+	/*
+	 * Call validate queue to make sure queue is empty before starting.
+	 * This is to avoid overflow / underflow of queue.
+	 */
+	ret = cci_validate_queue(cci, master, queue);
+	if (ret < 0)
+		return ret;
+
+	val = CCI_I2C_SET_PARAM | (addr & 0x7f) << 4;
+	writel(val, cci->base + CCI_I2C_Mm_Qn_LOAD_DATA(master, queue));
+
+	val = CCI_I2C_READ | len << 4;
+	writel(val, cci->base + CCI_I2C_Mm_Qn_LOAD_DATA(master, queue));
+
+	ret = cci_run_queue(cci, master, queue);
+	if (ret < 0)
+		return ret;
+
+	words_read = readl(cci->base + CCI_I2C_Mm_READ_BUF_LEVEL(master));
+	words_exp = len / 4 + 1;
+	if (words_read != words_exp) {
+		dev_err(cci->dev, "words read = %d, words expected = %d\n",
+			words_read, words_exp);
+		return -EIO;
+	}
+
+	do {
+		val = readl(cci->base + CCI_I2C_Mm_READ_DATA(master));
+
+		for (i = 0; i < 4 && index < len; i++) {
+			if (first) {
+				first = false;
+				continue;
+			}
+			buf[index++] = (val >> (i * 8)) & 0xff;
+		}
+	} while (--words_read);
+
+	return 0;
+}
+
+static int cci_i2c_write(struct cci *cci, u16 master,
+			 u16 addr, u8 *buf, u16 len)
+{
+	u8 queue = QUEUE_0;
+	u8 load[12] = { 0 };
+	int i = 0, j, ret;
+	u32 val;
+
+	/*
+	 * Call validate queue to make sure queue is empty before starting.
+	 * This is to avoid overflow / underflow of queue.
+	 */
+	ret = cci_validate_queue(cci, master, queue);
+	if (ret < 0)
+		return ret;
+
+	val = CCI_I2C_SET_PARAM | (addr & 0x7f) << 4;
+	writel(val, cci->base + CCI_I2C_Mm_Qn_LOAD_DATA(master, queue));
+
+	load[i++] = CCI_I2C_WRITE | len << 4;
+
+	for (j = 0; j < len; j++)
+		load[i++] = buf[j];
+
+	for (j = 0; j < i; j += 4) {
+		val = load[j];
+		val |= load[j + 1] << 8;
+		val |= load[j + 2] << 16;
+		val |= load[j + 3] << 24;
+		writel(val, cci->base + CCI_I2C_Mm_Qn_LOAD_DATA(master, queue));
+	}
+
+	val = CCI_I2C_REPORT | BIT(8);
+	writel(val, cci->base + CCI_I2C_Mm_Qn_LOAD_DATA(master, queue));
+
+	return cci_run_queue(cci, master, queue);
+}
+
+static int cci_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+	struct cci_master *cci_master = i2c_get_adapdata(adap);
+	struct cci *cci = cci_master->cci;
+	int i, ret = 0;
+
+	for (i = 0; i < num; i++) {
+		if (msgs[i].flags & I2C_M_RD)
+			ret = cci_i2c_read(cci, cci_master->master,
+					   msgs[i].addr, msgs[i].buf,
+					   msgs[i].len);
+		else
+			ret = cci_i2c_write(cci, msgs[i].addr,
+					    cci_master->master,
+					    msgs[i].buf, msgs[i].len);
+
+		if (ret < 0) {
+			dev_err(cci->dev, "cci i2c xfer error %d", ret);
+			break;
+		}
+	}
+
+	if (!ret)
+		ret = num;
+
+	return ret;
+}
+
+static u32 cci_func(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C;
+}
+
+static const struct i2c_algorithm cci_algo = {
+	.master_xfer	= cci_xfer,
+	.functionality	= cci_func,
+};
+
+static const struct i2c_adapter_quirks cci_quirks_v1_0_8 = {
+	.max_write_len = 10,
+	.max_read_len = 12,
+};
+
+static const struct i2c_adapter_quirks cci_quirks_v1_4_0 = {
+	.max_write_len = 11,
+	.max_read_len = 12,
+};
+
+static int cci_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	const struct cci_res *res;
+	const struct hw_params *hw;
+	struct cci *cci;
+	struct resource *r;
+	int ret = 0, i, i2c_error;
+	u32 val;
+	u8 mode;
+
+	cci = devm_kzalloc(dev, sizeof(*cci), GFP_KERNEL);
+	if (!cci)
+		return -ENOMEM;
+
+	cci->dev = dev;
+	platform_set_drvdata(pdev, cci);
+
+	if (of_device_is_compatible(dev->of_node, "qcom,cci-v1.0.8")) {
+		res = &res_v1_0_8;
+		hw = hw_params_v1_0_8;
+		cci->queue_size[0] = queue_0_size_v1_0_8;
+		cci->queue_size[1] = queue_1_size_v1_0_8;
+
+		for (i = 0; i < NUM_MASTERS; i++)
+			cci->master[i].adap.quirks = &cci_quirks_v1_0_8;
+
+	} else if (of_device_is_compatible(dev->of_node, "qcom,cci-v1.4.0")) {
+		res = &res_v1_4_0;
+		hw = hw_params_v1_4_0;
+		cci->queue_size[0] = queue_0_size_v1_4_0;
+		cci->queue_size[1] = queue_1_size_v1_4_0;
+
+		for (i = 0; i < NUM_MASTERS; i++)
+			cci->master[i].adap.quirks = &cci_quirks_v1_4_0;
+
+	} else {
+		return -EINVAL;
+	}
+
+	for (i = 0; i < NUM_MASTERS; i++) {
+		cci->master[i].adap.algo = &cci_algo;
+		cci->master[i].adap.dev.parent = cci->dev;
+		cci->master[i].adap.dev.of_node = dev->of_node;
+		cci->master[i].master = i;
+		cci->master[i].cci = cci;
+		i2c_set_adapdata(&cci->master[i].adap, cci);
+		snprintf(cci->master[i].adap.name,
+			sizeof(cci->master[i].adap.name),
+			"Qualcomm Camera Control Interface: %d", i);
+	}
+
+	mode = I2C_MODE_STANDARD;
+	ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency", &val);
+	if (!ret) {
+		if (val == 400000)
+			mode = I2C_MODE_FAST;
+		else if (val == 1000000)
+			mode = I2C_MODE_FAST_PLUS;
+	}
+
+	/* Memory */
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	cci->base = devm_ioremap_resource(dev, r);
+	if (IS_ERR(cci->base)) {
+		dev_err(dev, "could not map memory\n");
+		return PTR_ERR(cci->base);
+	}
+
+	/* Interrupt */
+
+	ret = platform_get_irq(pdev, 0);
+	if (ret < 0) {
+		dev_err(dev, "missing IRQ: %d\n", ret);
+		return ret;
+	}
+	cci->irq = ret;
+
+	ret = devm_request_irq(dev, cci->irq, cci_isr,
+			       IRQF_TRIGGER_RISING, dev_name(dev), cci);
+	if (ret < 0) {
+		dev_err(dev, "request_irq failed, ret: %d\n", ret);
+		return ret;
+	}
+
+	disable_irq(cci->irq);
+
+	/* Clocks */
+
+	cci->nclocks = 0;
+	while (res->clock[cci->nclocks])
+		cci->nclocks++;
+
+	cci->clock = devm_kzalloc(dev, cci->nclocks *
+				  sizeof(*cci->clock), GFP_KERNEL);
+	if (!cci->clock)
+		return -ENOMEM;
+
+	cci->clock_freq = devm_kzalloc(dev, cci->nclocks *
+				       sizeof(*cci->clock_freq), GFP_KERNEL);
+	if (!cci->clock_freq)
+		return -ENOMEM;
+
+	for (i = 0; i < cci->nclocks; i++) {
+		struct clk_bulk_data *clock = &cci->clock[i];
+
+		clock->clk = devm_clk_get(dev, res->clock[i]);
+		if (IS_ERR(clock->clk))
+			return PTR_ERR(clock->clk);
+
+		clock->id = res->clock[i];
+		cci->clock_freq[i] = res->clock_rate[i];
+	}
+
+	ret = cci_clock_set_rate(cci->nclocks, cci->clock,
+				 cci->clock_freq, dev);
+	if (ret < 0)
+		return ret;
+
+	ret = clk_bulk_prepare_enable(cci->nclocks, cci->clock);
+	if (ret < 0)
+		return ret;
+
+	val = readl_relaxed(cci->base + CCI_HW_VERSION);
+	dev_dbg(dev, "%s: CCI HW version = 0x%08x", __func__, val);
+
+	init_completion(&cci->master[0].irq_complete);
+	init_completion(&cci->master[1].irq_complete);
+
+	enable_irq(cci->irq);
+
+	ret = cci_reset(cci);
+	if (ret < 0)
+		goto error;
+
+	ret = cci_init(cci, &hw[mode]);
+	if (ret < 0)
+		goto error;
+
+	for (i = 0; i < NUM_MASTERS; i++) {
+		ret = i2c_add_adapter(&cci->master[i].adap);
+		if (ret < 0) {
+			i2c_error = i;
+			goto error_i2c;
+		}
+	}
+
+	return 0;
+
+error_i2c:
+	for (i = 0; i < i2c_error; i++)
+		i2c_del_adapter(&cci->master[i].adap);
+error:
+	clk_bulk_disable_unprepare(cci->nclocks, cci->clock);
+
+	return ret;
+}
+
+static int cci_remove(struct platform_device *pdev)
+{
+	struct cci *cci = platform_get_drvdata(pdev);
+	int i;
+
+	disable_irq(cci->irq);
+	clk_bulk_disable_unprepare(cci->nclocks, cci->clock);
+
+	for (i = 0; i < NUM_MASTERS; i++)
+		i2c_del_adapter(&cci->master[i].adap);
+
+	return 0;
+}
+
+static const struct of_device_id cci_dt_match[] = {
+	{ .compatible = "qcom,cci-v1.0.8" },
+	{ .compatible = "qcom,cci-v1.4.0" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, cci_dt_match);
+
+static struct platform_driver qcom_cci_driver = {
+	.probe  = cci_probe,
+	.remove = cci_remove,
+	.driver = {
+		.name = "i2c-qcom-cci",
+		.of_match_table = cci_dt_match,
+	},
+};
+
+module_platform_driver(qcom_cci_driver);
+
+MODULE_DESCRIPTION("Qualcomm Camera Control Interface driver");
+MODULE_AUTHOR("Todor Tomov <todor.tomov@linaro.org>");
+MODULE_LICENSE("GPL v2");
-- 
2.14.4

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH v2 1/2] dt-bindings: i2c: Add binding document for Qualcomm CCI
  2018-07-17 12:57 ` [PATCH v2 1/2] dt-bindings: i2c: Add binding document for Qualcomm CCI Vinod Koul
@ 2018-07-20 18:03   ` Rob Herring
  2018-07-23  3:54     ` Vinod
  0 siblings, 1 reply; 6+ messages in thread
From: Rob Herring @ 2018-07-20 18:03 UTC (permalink / raw)
  To: Vinod Koul
  Cc: linux-i2c, Bjorn Andersson, linux-arm-msm, Wolfram Sang,
	devicetree, Todor Tomov

On Tue, Jul 17, 2018 at 06:27:28PM +0530, Vinod Koul wrote:
> From: Todor Tomov <todor.tomov@linaro.org>
> 
> Add DT binding document for Qualcomm Camera Control Interface device
> 
> Signed-off-by: Todor Tomov <todor.tomov@linaro.org>
> Signed-off-by: Vinod Koul <vkoul@kernel.org>
> ---
>  .../devicetree/bindings/i2c/i2c-qcom-cci.txt       | 46 ++++++++++++++++++++++
>  1 file changed, 46 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/i2c/i2c-qcom-cci.txt
> 
> diff --git a/Documentation/devicetree/bindings/i2c/i2c-qcom-cci.txt b/Documentation/devicetree/bindings/i2c/i2c-qcom-cci.txt
> new file mode 100644
> index 000000000000..cb1fe158fba3
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/i2c/i2c-qcom-cci.txt
> @@ -0,0 +1,46 @@
> +Qualcomm Camera Control Interface controller
> +
> +Required properties:
> + - compatible: Should be one of:
> +   - "qcom,cci-v1.0.8" for 8916;
> +   - "qcom,cci-v1.4.0" for 8996.

It's not very convincing to use version numbers instead of normal SoC 
part# convention when there is a 1-1 mapping.

> + - #address-cells: Should be <1>.
> + - #size-cells: Should be <0>.
> + - reg: Base address of the controller and length of memory mapped region.
> + - interrupts: Specifier for CCI interrupt.
> + - clocks: List of clock specifiers, one for each entry in clock-names.
> + - clock-names: Should contain:
> +   - "mmss_mmagic_ahb" - on 8996 only;
> +   - "camss_top_ahb";
> +   - "cci_ahb";
> +   - "cci";
> +   - "camss_ahb".
> +
> +Required properties on 8996:

Rather than '8996', use the compatible string.

> + - power-domains: Power domain specifier.
> +
> +Optional:
> + - clock-frequency: Desired I2C bus clock frequency in Hz, defaults to 100 kHz
> +   if omitted.

This is the only place that says this block is or has a I2C controller. 
Please make that more explicit (including what ever else this block 
does). I'm guessing there's more given how many clocks and that it has a 
power domain.

> +
> +Example:
> +
> +                cci@a0c000 {
> +                        compatible = "qcom,cci-v1.4.0";
> +                        #address-cells = <1>;
> +                        #size-cells = <0>;
> +                        reg = <0xa0c000 0x1000>;
> +                        interrupts = <GIC_SPI 295 IRQ_TYPE_EDGE_RISING>;
> +                        power-domains = <&mmcc CAMSS_GDSC>;
> +                        clocks = <&mmcc MMSS_MMAGIC_AHB_CLK>,
> +                                        <&mmcc CAMSS_TOP_AHB_CLK>,
> +                                        <&mmcc CAMSS_CCI_AHB_CLK>,
> +                                        <&mmcc CAMSS_CCI_CLK>,
> +                                        <&mmcc CAMSS_AHB_CLK>;
> +                        clock-names = "mmss_mmagic_ahb",
> +                                        "camss_top_ahb",
> +                                        "cci_ahb",
> +                                        "cci",
> +                                        "camss_ahb";
> +                        clock-frequency = <400000>;
> +                };
> -- 
> 2.14.4
> 

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v2 1/2] dt-bindings: i2c: Add binding document for Qualcomm CCI
  2018-07-20 18:03   ` Rob Herring
@ 2018-07-23  3:54     ` Vinod
  2018-07-23 13:20       ` Rob Herring
  0 siblings, 1 reply; 6+ messages in thread
From: Vinod @ 2018-07-23  3:54 UTC (permalink / raw)
  To: Rob Herring
  Cc: linux-i2c, Bjorn Andersson, linux-arm-msm, Wolfram Sang,
	devicetree, Todor Tomov

On 20-07-18, 12:03, Rob Herring wrote:
> On Tue, Jul 17, 2018 at 06:27:28PM +0530, Vinod Koul wrote:
> > From: Todor Tomov <todor.tomov@linaro.org>

> > +Required properties:
> > + - compatible: Should be one of:
> > +   - "qcom,cci-v1.0.8" for 8916;
> > +   - "qcom,cci-v1.4.0" for 8996.
> 
> It's not very convincing to use version numbers instead of normal SoC 
> part# convention when there is a 1-1 mapping.

I was under the impression that we don't have 1-1 mapping. An IP version
is found in multiple SoCs

> > + - #address-cells: Should be <1>.
> > + - #size-cells: Should be <0>.
> > + - reg: Base address of the controller and length of memory mapped region.
> > + - interrupts: Specifier for CCI interrupt.
> > + - clocks: List of clock specifiers, one for each entry in clock-names.
> > + - clock-names: Should contain:
> > +   - "mmss_mmagic_ahb" - on 8996 only;
> > +   - "camss_top_ahb";
> > +   - "cci_ahb";
> > +   - "cci";
> > +   - "camss_ahb".
> > +
> > +Required properties on 8996:
> 
> Rather than '8996', use the compatible string.

Makes sense

> > + - power-domains: Power domain specifier.
> > +
> > +Optional:
> > + - clock-frequency: Desired I2C bus clock frequency in Hz, defaults to 100 kHz
> > +   if omitted.
> 
> This is the only place that says this block is or has a I2C controller. 
> Please make that more explicit (including what ever else this block 
> does). I'm guessing there's more given how many clocks and that it has a 
> power domain.

Yes this also goes GPIO which will be added after this.

-- 
~Vinod

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v2 1/2] dt-bindings: i2c: Add binding document for Qualcomm CCI
  2018-07-23  3:54     ` Vinod
@ 2018-07-23 13:20       ` Rob Herring
  0 siblings, 0 replies; 6+ messages in thread
From: Rob Herring @ 2018-07-23 13:20 UTC (permalink / raw)
  To: Vinod
  Cc: Linux I2C, Bjorn Andersson, linux-arm-msm, Wolfram Sang,
	devicetree, Todor Tomov

On Sun, Jul 22, 2018 at 9:54 PM Vinod <vkoul@kernel.org> wrote:
>
> On 20-07-18, 12:03, Rob Herring wrote:
> > On Tue, Jul 17, 2018 at 06:27:28PM +0530, Vinod Koul wrote:
> > > From: Todor Tomov <todor.tomov@linaro.org>
>
> > > +Required properties:
> > > + - compatible: Should be one of:
> > > +   - "qcom,cci-v1.0.8" for 8916;
> > > +   - "qcom,cci-v1.4.0" for 8996.
> >
> > It's not very convincing to use version numbers instead of normal SoC
> > part# convention when there is a 1-1 mapping.
>
> I was under the impression that we don't have 1-1 mapping. An IP version
> is found in multiple SoCs

Maybe so, but I'm only looking at what is getting upstream. We already
discussed this at length on the thermal binding[1].

As you've written it, we'll still have to update the doc to add other
chips for an already documented version.

Rob

[1] https://lkml.org/lkml/2018/7/5/830

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2018-07-23 13:20 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-07-17 12:57 [PATCH v2 0/2] i2c: Add support for Qualcomm Camera Control Interface Vinod Koul
2018-07-17 12:57 ` [PATCH v2 1/2] dt-bindings: i2c: Add binding document for Qualcomm CCI Vinod Koul
2018-07-20 18:03   ` Rob Herring
2018-07-23  3:54     ` Vinod
2018-07-23 13:20       ` Rob Herring
2018-07-17 12:57 ` [PATCH v2 2/2] i2c: Add Qualcomm Camera Control Interface driver Vinod Koul

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).