Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/806928/?format=api
{ "id": 806928, "url": "http://patchwork.ozlabs.org/api/patches/806928/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-i2c/patch/6a85f5be4a3c30d598c8635da09ac3e30af00550.1503977439.git.baolin.wang@spreadtrum.com/", "project": { "id": 35, "url": "http://patchwork.ozlabs.org/api/projects/35/?format=api", "name": "Linux I2C development", "link_name": "linux-i2c", "list_id": "linux-i2c.vger.kernel.org", "list_email": "linux-i2c@vger.kernel.org", "web_url": "", "scm_url": "", "webscm_url": "", "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<6a85f5be4a3c30d598c8635da09ac3e30af00550.1503977439.git.baolin.wang@spreadtrum.com>", "list_archive_url": null, "date": "2017-08-29T03:35:04", "name": "[v5,2/2] i2c: Add Spreadtrum I2C controller driver", "commit_ref": null, "pull_url": null, "state": "accepted", "archived": false, "hash": "67b06f3f6655916b51929fce7c55c0a7de6584c4", "submitter": { "id": 71631, "url": "http://patchwork.ozlabs.org/api/people/71631/?format=api", "name": "Baolin Wang", "email": "baolin.wang@spreadtrum.com" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/linux-i2c/patch/6a85f5be4a3c30d598c8635da09ac3e30af00550.1503977439.git.baolin.wang@spreadtrum.com/mbox/", "series": [ { "id": 292, "url": "http://patchwork.ozlabs.org/api/series/292/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-i2c/list/?series=292", "date": "2017-08-29T03:35:03", "name": "[v5,1/2] dt-bindings: i2c: Add Spreadtrum I2C controller documentation", "version": 5, "mbox": "http://patchwork.ozlabs.org/series/292/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/806928/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/806928/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "<linux-i2c-owner@vger.kernel.org>", "X-Original-To": "incoming@patchwork.ozlabs.org", "Delivered-To": "patchwork-incoming@bilbo.ozlabs.org", "Authentication-Results": "ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=vger.kernel.org\n\t(client-ip=209.132.180.67; helo=vger.kernel.org;\n\tenvelope-from=linux-i2c-owner@vger.kernel.org;\n\treceiver=<UNKNOWN>)", "Received": [ "from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3xhDwq55dpz9s7h\n\tfor <incoming@patchwork.ozlabs.org>;\n\tTue, 29 Aug 2017 13:42:35 +1000 (AEST)", "(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S1751284AbdH2DmM (ORCPT <rfc822;incoming@patchwork.ozlabs.org>);\n\tMon, 28 Aug 2017 23:42:12 -0400", "from sci-ig2.spreadtrum.com ([222.66.158.135]:24577 \"EHLO\n\tSHSQR01.spreadtrum.com\" rhost-flags-OK-FAIL-OK-OK) by vger.kernel.org\n\twith ESMTP id S1751249AbdH2DmL (ORCPT\n\t<rfc822; linux-i2c@vger.kernel.org>); Mon, 28 Aug 2017 23:42:11 -0400", "from ig2.spreadtrum.com (shmbx01.spreadtrum.com [10.0.1.203])\n\tby SHSQR01.spreadtrum.com with ESMTP id v7T3eFs4091168\n\t(version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO);\n\tTue, 29 Aug 2017 11:40:15 +0800 (CST)\n\t(envelope-from Orson.Zhai@spreadtrum.com)", "from SHCAS02.spreadtrum.com (10.0.1.202) by SHMBX01.spreadtrum.com\n\t(10.0.1.203) with Microsoft SMTP Server (TLS) id 15.0.847.32;\n\tTue, 29 Aug 2017 11:40:01 +0800", "from localhost (10.0.73.143) by SHCAS02.spreadtrum.com (10.0.1.250)\n\twith Microsoft SMTP Server (TLS) id 15.0.847.32 via Frontend\n\tTransport; Tue, 29 Aug 2017 11:40:00 +0800" ], "From": "Baolin Wang <baolin.wang@spreadtrum.com>", "To": "<wsa@the-dreams.de>, <mark.rutland@arm.com>, <robh+dt@kernel.org>", "CC": "<andriy.shevchenko@linux.intel.com>, <linux-i2c@vger.kernel.org>,\n\t<devicetree@vger.kernel.org>, <linux-kernel@vger.kernel.org>,\n\t<broonie@kernel.org>, <baolin.wang@linaro.org>,\n\t<baolin.wang@spreadtrum.com>", "Subject": "[PATCH v5 2/2] i2c: Add Spreadtrum I2C controller driver", "Date": "Tue, 29 Aug 2017 11:35:04 +0800", "Message-ID": "<6a85f5be4a3c30d598c8635da09ac3e30af00550.1503977439.git.baolin.wang@spreadtrum.com>", "X-Mailer": "git-send-email 2.12.2", "In-Reply-To": "<8677c8e20e767b27a53468531c1af03016cf22bb.1503977439.git.baolin.wang@spreadtrum.com>", "References": "<8677c8e20e767b27a53468531c1af03016cf22bb.1503977439.git.baolin.wang@spreadtrum.com>", "MIME-Version": "1.0", "Content-Type": "text/plain", "X-MAIL": "SHSQR01.spreadtrum.com v7T3eFs4091168", "Sender": "linux-i2c-owner@vger.kernel.org", "Precedence": "bulk", "List-ID": "<linux-i2c.vger.kernel.org>", "X-Mailing-List": "linux-i2c@vger.kernel.org" }, "content": "This patch adds the I2C controller driver for Spreadtrum SC9860 platform.\n\nSigned-off-by: Baolin Wang <baolin.wang@spreadtrum.com>\nReviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>\n---\nChanges since v4:\n - Remove dump registers function.\n - Change 'unsigned int' to 'u32' type.\n - Invert ack logic to make it clear.\n - Modify some comments and error message.\n\nChanges since v3:\n - Use SPDX-License-Identifier tag instead.\n\nChanges since v2:\n - Remove some redundant comments and parens.\n - Define macros instead of magic number.\n - Add some comments to explain clock formula.\n - Change of_clk_get_by_name() to devm_clk_get().\n - Deal with other frequency.\n - Change register definiton to low case.\n - Change is_last_msg to boolean.\n - Other optimization.\n\nChanges sice v1:\n - Power on I2C device in probe().\n - Remove redundant macros and usb __maybe_unused.\n - Remove redundant 'of_match_ptr'.\n - Modify return values and check the return value for 'clk_prepare_enable'.\n---\n drivers/i2c/busses/Kconfig | 7 +\n drivers/i2c/busses/Makefile | 1 +\n drivers/i2c/busses/i2c-sprd.c | 646 +++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 654 insertions(+)\n create mode 100644 drivers/i2c/busses/i2c-sprd.c", "diff": "diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig\nindex 1006b23..64729ac 100644\n--- a/drivers/i2c/busses/Kconfig\n+++ b/drivers/i2c/busses/Kconfig\n@@ -900,6 +900,13 @@ config I2C_SIRF\n \t This driver can also be built as a module. If so, the module\n \t will be called i2c-sirf.\n \n+config I2C_SPRD\n+\tbool \"Spreadtrum I2C interface\"\n+\tdepends on ARCH_SPRD\n+\thelp\n+\t If you say yes to this option, support will be included for the\n+\t Spreadtrum I2C interface.\n+\n config I2C_ST\n \ttristate \"STMicroelectronics SSC I2C support\"\n \tdepends on ARCH_STI\ndiff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile\nindex 1b2fc81..505f74a 100644\n--- a/drivers/i2c/busses/Makefile\n+++ b/drivers/i2c/busses/Makefile\n@@ -89,6 +89,7 @@ obj-$(CONFIG_I2C_SH7760)\t+= i2c-sh7760.o\n obj-$(CONFIG_I2C_SH_MOBILE)\t+= i2c-sh_mobile.o\n obj-$(CONFIG_I2C_SIMTEC)\t+= i2c-simtec.o\n obj-$(CONFIG_I2C_SIRF)\t\t+= i2c-sirf.o\n+obj-$(CONFIG_I2C_SPRD)\t\t+= i2c-sprd.o\n obj-$(CONFIG_I2C_ST)\t\t+= i2c-st.o\n obj-$(CONFIG_I2C_STM32F4)\t+= i2c-stm32f4.o\n obj-$(CONFIG_I2C_STU300)\t+= i2c-stu300.o\ndiff --git a/drivers/i2c/busses/i2c-sprd.c b/drivers/i2c/busses/i2c-sprd.c\nnew file mode 100644\nindex 0000000..22e08ae\n--- /dev/null\n+++ b/drivers/i2c/busses/i2c-sprd.c\n@@ -0,0 +1,646 @@\n+/*\n+ * Copyright (C) 2017 Spreadtrum Communications Inc.\n+ *\n+ * SPDX-License-Identifier: (GPL-2.0+ OR MIT)\n+ */\n+\n+#include <linux/clk.h>\n+#include <linux/delay.h>\n+#include <linux/err.h>\n+#include <linux/io.h>\n+#include <linux/i2c.h>\n+#include <linux/init.h>\n+#include <linux/interrupt.h>\n+#include <linux/kernel.h>\n+#include <linux/of.h>\n+#include <linux/of_device.h>\n+#include <linux/platform_device.h>\n+#include <linux/pm_runtime.h>\n+\n+#define I2C_CTL\t\t\t0x00\n+#define I2C_ADDR_CFG\t\t0x04\n+#define I2C_COUNT\t\t0x08\n+#define I2C_RX\t\t\t0x0c\n+#define I2C_TX\t\t\t0x10\n+#define I2C_STATUS\t\t0x14\n+#define I2C_HSMODE_CFG\t\t0x18\n+#define I2C_VERSION\t\t0x1c\n+#define ADDR_DVD0\t\t0x20\n+#define ADDR_DVD1\t\t0x24\n+#define ADDR_STA0_DVD\t\t0x28\n+#define ADDR_RST\t\t0x2c\n+\n+/* I2C_CTL */\n+#define STP_EN\t\t\tBIT(20)\n+#define FIFO_AF_LVL_MASK\tGENMASK(19, 16)\n+#define FIFO_AF_LVL\t\t16\n+#define FIFO_AE_LVL_MASK\tGENMASK(15, 12)\n+#define FIFO_AE_LVL\t\t12\n+#define I2C_DMA_EN\t\tBIT(11)\n+#define FULL_INTEN\t\tBIT(10)\n+#define EMPTY_INTEN\t\tBIT(9)\n+#define I2C_DVD_OPT\t\tBIT(8)\n+#define I2C_OUT_OPT\t\tBIT(7)\n+#define I2C_TRIM_OPT\t\tBIT(6)\n+#define I2C_HS_MODE\t\tBIT(4)\n+#define I2C_MODE\t\tBIT(3)\n+#define I2C_EN\t\t\tBIT(2)\n+#define I2C_INT_EN\t\tBIT(1)\n+#define I2C_START\t\tBIT(0)\n+\n+/* I2C_STATUS */\n+#define SDA_IN\t\t\tBIT(21)\n+#define SCL_IN\t\t\tBIT(20)\n+#define FIFO_FULL\t\tBIT(4)\n+#define FIFO_EMPTY\t\tBIT(3)\n+#define I2C_INT\t\t\tBIT(2)\n+#define I2C_RX_ACK\t\tBIT(1)\n+#define I2C_BUSY\t\tBIT(0)\n+\n+/* ADDR_RST */\n+#define I2C_RST\t\t\tBIT(0)\n+\n+#define I2C_FIFO_DEEP\t\t12\n+#define I2C_FIFO_FULL_THLD\t15\n+#define I2C_FIFO_EMPTY_THLD\t4\n+#define I2C_DATA_STEP\t\t8\n+#define I2C_ADDR_DVD0_CALC(high, low)\t\\\n+\t((((high) & GENMASK(15, 0)) << 16) | ((low) & GENMASK(15, 0)))\n+#define I2C_ADDR_DVD1_CALC(high, low)\t\\\n+\t(((high) & GENMASK(31, 16)) | (((low) & GENMASK(31, 16)) >> 16))\n+\n+/* timeout (ms) for pm runtime autosuspend */\n+#define SPRD_I2C_PM_TIMEOUT\t1000\n+\n+/* SPRD i2c data structure */\n+struct sprd_i2c {\n+\tstruct i2c_adapter adap;\n+\tstruct device *dev;\n+\tvoid __iomem *base;\n+\tstruct i2c_msg *msg;\n+\tstruct clk *clk;\n+\tu32 src_clk;\n+\tu32 bus_freq;\n+\tstruct completion complete;\n+\tu8 *buf;\n+\tu32 count;\n+\tint irq;\n+\tint err;\n+};\n+\n+static void sprd_i2c_set_count(struct sprd_i2c *i2c_dev, u32 count)\n+{\n+\twritel(count, i2c_dev->base + I2C_COUNT);\n+}\n+\n+static void sprd_i2c_send_stop(struct sprd_i2c *i2c_dev, int stop)\n+{\n+\tu32 tmp = readl(i2c_dev->base + I2C_CTL);\n+\n+\tif (stop)\n+\t\twritel(tmp & ~STP_EN, i2c_dev->base + I2C_CTL);\n+\telse\n+\t\twritel(tmp | STP_EN, i2c_dev->base + I2C_CTL);\n+}\n+\n+static void sprd_i2c_clear_start(struct sprd_i2c *i2c_dev)\n+{\n+\tu32 tmp = readl(i2c_dev->base + I2C_CTL);\n+\n+\twritel(tmp & ~I2C_START, i2c_dev->base + I2C_CTL);\n+}\n+\n+static void sprd_i2c_clear_ack(struct sprd_i2c *i2c_dev)\n+{\n+\tu32 tmp = readl(i2c_dev->base + I2C_STATUS);\n+\n+\twritel(tmp & ~I2C_RX_ACK, i2c_dev->base + I2C_STATUS);\n+}\n+\n+static void sprd_i2c_clear_irq(struct sprd_i2c *i2c_dev)\n+{\n+\tu32 tmp = readl(i2c_dev->base + I2C_STATUS);\n+\n+\twritel(tmp & ~I2C_INT, i2c_dev->base + I2C_STATUS);\n+}\n+\n+static void sprd_i2c_reset_fifo(struct sprd_i2c *i2c_dev)\n+{\n+\twritel(I2C_RST, i2c_dev->base + ADDR_RST);\n+}\n+\n+static void sprd_i2c_set_devaddr(struct sprd_i2c *i2c_dev, struct i2c_msg *m)\n+{\n+\twritel(m->addr << 1, i2c_dev->base + I2C_ADDR_CFG);\n+}\n+\n+static void sprd_i2c_write_bytes(struct sprd_i2c *i2c_dev, u8 *buf, u32 len)\n+{\n+\tu32 i;\n+\n+\tfor (i = 0; i < len; i++)\n+\t\twriteb(buf[i], i2c_dev->base + I2C_TX);\n+}\n+\n+static void sprd_i2c_read_bytes(struct sprd_i2c *i2c_dev, u8 *buf, u32 len)\n+{\n+\tu32 i;\n+\n+\tfor (i = 0; i < len; i++)\n+\t\tbuf[i] = readb(i2c_dev->base + I2C_RX);\n+}\n+\n+static void sprd_i2c_set_full_thld(struct sprd_i2c *i2c_dev, u32 full_thld)\n+{\n+\tu32 tmp = readl(i2c_dev->base + I2C_CTL);\n+\n+\ttmp &= ~FIFO_AF_LVL_MASK;\n+\ttmp |= full_thld << FIFO_AF_LVL;\n+\twritel(tmp, i2c_dev->base + I2C_CTL);\n+};\n+\n+static void sprd_i2c_set_empty_thld(struct sprd_i2c *i2c_dev, u32 empty_thld)\n+{\n+\tu32 tmp = readl(i2c_dev->base + I2C_CTL);\n+\n+\ttmp &= ~FIFO_AE_LVL_MASK;\n+\ttmp |= empty_thld << FIFO_AE_LVL;\n+\twritel(tmp, i2c_dev->base + I2C_CTL);\n+};\n+\n+static void sprd_i2c_set_fifo_full_int(struct sprd_i2c *i2c_dev, int enable)\n+{\n+\tu32 tmp = readl(i2c_dev->base + I2C_CTL);\n+\n+\tif (enable)\n+\t\ttmp |= FULL_INTEN;\n+\telse\n+\t\ttmp &= ~FULL_INTEN;\n+\n+\twritel(tmp, i2c_dev->base + I2C_CTL);\n+};\n+\n+static void sprd_i2c_set_fifo_empty_int(struct sprd_i2c *i2c_dev, int enable)\n+{\n+\tu32 tmp = readl(i2c_dev->base + I2C_CTL);\n+\n+\tif (enable)\n+\t\ttmp |= EMPTY_INTEN;\n+\telse\n+\t\ttmp &= ~EMPTY_INTEN;\n+\n+\twritel(tmp, i2c_dev->base + I2C_CTL);\n+};\n+\n+static void sprd_i2c_opt_start(struct sprd_i2c *i2c_dev)\n+{\n+\tu32 tmp = readl(i2c_dev->base + I2C_CTL);\n+\n+\twritel(tmp | I2C_START, i2c_dev->base + I2C_CTL);\n+}\n+\n+static void sprd_i2c_opt_mode(struct sprd_i2c *i2c_dev, int rw)\n+{\n+\tu32 cmd = readl(i2c_dev->base + I2C_CTL) & ~I2C_MODE;\n+\n+\twritel(cmd | rw << 3, i2c_dev->base + I2C_CTL);\n+}\n+\n+static void sprd_i2c_data_transfer(struct sprd_i2c *i2c_dev)\n+{\n+\tu32 i2c_count = i2c_dev->count;\n+\tu32 need_tran = i2c_count <= I2C_FIFO_DEEP ? i2c_count : I2C_FIFO_DEEP;\n+\tstruct i2c_msg *msg = i2c_dev->msg;\n+\n+\tif (msg->flags & I2C_M_RD) {\n+\t\tsprd_i2c_read_bytes(i2c_dev, i2c_dev->buf, I2C_FIFO_FULL_THLD);\n+\t\ti2c_dev->count -= I2C_FIFO_FULL_THLD;\n+\t\ti2c_dev->buf += I2C_FIFO_FULL_THLD;\n+\n+\t\t/*\n+\t\t * If the read data count is larger than rx fifo full threshold,\n+\t\t * we should enable the rx fifo full interrupt to read data\n+\t\t * again.\n+\t\t */\n+\t\tif (i2c_dev->count >= I2C_FIFO_FULL_THLD)\n+\t\t\tsprd_i2c_set_fifo_full_int(i2c_dev, 1);\n+\t} else {\n+\t\tsprd_i2c_write_bytes(i2c_dev, i2c_dev->buf, need_tran);\n+\t\ti2c_dev->buf += need_tran;\n+\t\ti2c_dev->count -= need_tran;\n+\n+\t\t/*\n+\t\t * If the write data count is arger than tx fifo depth which\n+\t\t * means we can not write all data in one time, then we should\n+\t\t * enable the tx fifo empty interrupt to write again.\n+\t\t */\n+\t\tif (i2c_count > I2C_FIFO_DEEP)\n+\t\t\tsprd_i2c_set_fifo_empty_int(i2c_dev, 1);\n+\t}\n+}\n+\n+static int sprd_i2c_handle_msg(struct i2c_adapter *i2c_adap,\n+\t\t\t struct i2c_msg *msg, bool is_last_msg)\n+{\n+\tstruct sprd_i2c *i2c_dev = i2c_adap->algo_data;\n+\n+\ti2c_dev->msg = msg;\n+\ti2c_dev->buf = msg->buf;\n+\ti2c_dev->count = msg->len;\n+\n+\treinit_completion(&i2c_dev->complete);\n+\tsprd_i2c_reset_fifo(i2c_dev);\n+\tsprd_i2c_set_devaddr(i2c_dev, msg);\n+\tsprd_i2c_set_count(i2c_dev, msg->len);\n+\n+\tif (msg->flags & I2C_M_RD) {\n+\t\tsprd_i2c_opt_mode(i2c_dev, 1);\n+\t\tsprd_i2c_send_stop(i2c_dev, 1);\n+\t} else {\n+\t\tsprd_i2c_opt_mode(i2c_dev, 0);\n+\t\tsprd_i2c_send_stop(i2c_dev, !!is_last_msg);\n+\t}\n+\n+\t/*\n+\t * We should enable rx fifo full interrupt to get data when receiving\n+\t * full data.\n+\t */\n+\tif (msg->flags & I2C_M_RD)\n+\t\tsprd_i2c_set_fifo_full_int(i2c_dev, 1);\n+\telse\n+\t\tsprd_i2c_data_transfer(i2c_dev);\n+\n+\tsprd_i2c_opt_start(i2c_dev);\n+\n+\twait_for_completion(&i2c_dev->complete);\n+\n+\treturn i2c_dev->err;\n+}\n+\n+static int sprd_i2c_master_xfer(struct i2c_adapter *i2c_adap,\n+\t\t\t\tstruct i2c_msg *msgs, int num)\n+{\n+\tstruct sprd_i2c *i2c_dev = i2c_adap->algo_data;\n+\tint im, ret;\n+\n+\tret = pm_runtime_get_sync(i2c_dev->dev);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tfor (im = 0; im < num - 1; im++) {\n+\t\tret = sprd_i2c_handle_msg(i2c_adap, &msgs[im], 0);\n+\t\tif (ret)\n+\t\t\tgoto err_msg;\n+\t}\n+\n+\tret = sprd_i2c_handle_msg(i2c_adap, &msgs[im++], 1);\n+\n+err_msg:\n+\tpm_runtime_mark_last_busy(i2c_dev->dev);\n+\tpm_runtime_put_autosuspend(i2c_dev->dev);\n+\n+\treturn ret < 0 ? ret : im;\n+}\n+\n+static u32 sprd_i2c_func(struct i2c_adapter *adap)\n+{\n+\treturn I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;\n+}\n+\n+static const struct i2c_algorithm sprd_i2c_algo = {\n+\t.master_xfer = sprd_i2c_master_xfer,\n+\t.functionality = sprd_i2c_func,\n+};\n+\n+static void sprd_i2c_set_clk(struct sprd_i2c *i2c_dev, u32 freq)\n+{\n+\tu32 apb_clk = i2c_dev->src_clk;\n+\t/*\n+\t * From I2C databook, the prescale calculation formula:\n+\t * prescale = freq_i2c / (4 * freq_scl) - 1;\n+\t */\n+\tu32 i2c_dvd = apb_clk / (4 * freq) - 1;\n+\t/*\n+\t * From I2C databook, the high period of SCL clock is recommended as\n+\t * 40% (2/5), and the low period of SCL clock is recommended as 60%\n+\t * (3/5), then the formula should be:\n+\t * high = (prescale * 2 * 2) / 5\n+\t * low = (prescale * 2 * 3) / 5\n+\t */\n+\tu32 high = ((i2c_dvd << 1) * 2) / 5;\n+\tu32 low = ((i2c_dvd << 1) * 3) / 5;\n+\tu32 div0 = I2C_ADDR_DVD0_CALC(high, low);\n+\tu32 div1 = I2C_ADDR_DVD1_CALC(high, low);\n+\n+\twritel(div0, i2c_dev->base + ADDR_DVD0);\n+\twritel(div1, i2c_dev->base + ADDR_DVD1);\n+\n+\t/* Start hold timing = hold time(us) * source clock */\n+\tif (freq == 400000)\n+\t\twritel((6 * apb_clk) / 10000000, i2c_dev->base + ADDR_STA0_DVD);\n+\telse if (freq == 100000)\n+\t\twritel((4 * apb_clk) / 1000000, i2c_dev->base + ADDR_STA0_DVD);\n+}\n+\n+static void sprd_i2c_enable(struct sprd_i2c *i2c_dev)\n+{\n+\tu32 tmp = I2C_DVD_OPT;\n+\n+\twritel(tmp, i2c_dev->base + I2C_CTL);\n+\n+\tsprd_i2c_set_full_thld(i2c_dev, I2C_FIFO_FULL_THLD);\n+\tsprd_i2c_set_empty_thld(i2c_dev, I2C_FIFO_EMPTY_THLD);\n+\n+\tsprd_i2c_set_clk(i2c_dev, i2c_dev->bus_freq);\n+\tsprd_i2c_reset_fifo(i2c_dev);\n+\tsprd_i2c_clear_irq(i2c_dev);\n+\n+\ttmp = readl(i2c_dev->base + I2C_CTL);\n+\twritel(tmp | I2C_EN | I2C_INT_EN, i2c_dev->base + I2C_CTL);\n+}\n+\n+static irqreturn_t sprd_i2c_isr_thread(int irq, void *dev_id)\n+{\n+\tstruct sprd_i2c *i2c_dev = dev_id;\n+\tstruct i2c_msg *msg = i2c_dev->msg;\n+\tbool ack = !(readl(i2c_dev->base + I2C_STATUS) & I2C_RX_ACK);\n+\tu32 i2c_count = readl(i2c_dev->base + I2C_COUNT);\n+\tu32 i2c_tran;\n+\n+\tif (msg->flags & I2C_M_RD)\n+\t\ti2c_tran = i2c_dev->count >= I2C_FIFO_FULL_THLD;\n+\telse\n+\t\ti2c_tran = i2c_count;\n+\n+\t/*\n+\t * If we got one ACK from slave when writing data, and we did not\n+\t * finish this transmission (i2c_tran is not zero), then we should\n+\t * continue to write data.\n+\t *\n+\t * For reading data, ack is always true, if i2c_tran is not 0 which\n+\t * means we still need to contine to read data from slave.\n+\t */\n+\tif (i2c_tran && ack) {\n+\t\tsprd_i2c_data_transfer(i2c_dev);\n+\t\treturn IRQ_HANDLED;\n+\t}\n+\n+\ti2c_dev->err = 0;\n+\n+\t/*\n+\t * If we did not get one ACK from slave when writing data, we should\n+\t * return -EIO to notify users.\n+\t */\n+\tif (!ack)\n+\t\ti2c_dev->err = -EIO;\n+\telse if (msg->flags & I2C_M_RD && i2c_dev->count)\n+\t\tsprd_i2c_read_bytes(i2c_dev, i2c_dev->buf, i2c_dev->count);\n+\n+\t/* Transmission is done and clear ack and start operation */\n+\tsprd_i2c_clear_ack(i2c_dev);\n+\tsprd_i2c_clear_start(i2c_dev);\n+\tcomplete(&i2c_dev->complete);\n+\n+\treturn IRQ_HANDLED;\n+}\n+\n+static irqreturn_t sprd_i2c_isr(int irq, void *dev_id)\n+{\n+\tstruct sprd_i2c *i2c_dev = dev_id;\n+\tstruct i2c_msg *msg = i2c_dev->msg;\n+\tu32 i2c_count = readl(i2c_dev->base + I2C_COUNT);\n+\tbool ack = !(readl(i2c_dev->base + I2C_STATUS) & I2C_RX_ACK);\n+\tu32 i2c_tran;\n+\n+\tif (msg->flags & I2C_M_RD)\n+\t\ti2c_tran = i2c_dev->count >= I2C_FIFO_FULL_THLD;\n+\telse\n+\t\ti2c_tran = i2c_count;\n+\n+\t/*\n+\t * If we did not get one ACK from slave when writing data, then we\n+\t * should finish this transmission since we got some errors.\n+\t *\n+\t * When writing data, if i2c_tran == 0 which means we have writen\n+\t * done all data, then we can finish this transmission.\n+\t *\n+\t * When reading data, if conut < rx fifo full threshold, which\n+\t * means we can read all data in one time, then we can finish this\n+\t * transmission too.\n+\t */\n+\tif (!i2c_tran || !ack) {\n+\t\tsprd_i2c_clear_start(i2c_dev);\n+\t\tsprd_i2c_clear_irq(i2c_dev);\n+\t}\n+\n+\tsprd_i2c_set_fifo_empty_int(i2c_dev, 0);\n+\tsprd_i2c_set_fifo_full_int(i2c_dev, 0);\n+\n+\treturn IRQ_WAKE_THREAD;\n+}\n+\n+static int sprd_i2c_clk_init(struct sprd_i2c *i2c_dev)\n+{\n+\tstruct clk *clk_i2c, *clk_parent;\n+\n+\tclk_i2c = devm_clk_get(i2c_dev->dev, \"i2c\");\n+\tif (IS_ERR(clk_i2c)) {\n+\t\tdev_warn(i2c_dev->dev, \"i2c%d can't get the i2c clock\\n\",\n+\t\t\t i2c_dev->adap.nr);\n+\t\tclk_i2c = NULL;\n+\t}\n+\n+\tclk_parent = devm_clk_get(i2c_dev->dev, \"source\");\n+\tif (IS_ERR(clk_parent)) {\n+\t\tdev_warn(i2c_dev->dev, \"i2c%d can't get the source clock\\n\",\n+\t\t\t i2c_dev->adap.nr);\n+\t\tclk_parent = NULL;\n+\t}\n+\n+\tif (clk_set_parent(clk_i2c, clk_parent))\n+\t\ti2c_dev->src_clk = clk_get_rate(clk_i2c);\n+\telse\n+\t\ti2c_dev->src_clk = 26000000;\n+\n+\tdev_dbg(i2c_dev->dev, \"i2c%d set source clock is %d\\n\",\n+\t\ti2c_dev->adap.nr, i2c_dev->src_clk);\n+\n+\ti2c_dev->clk = devm_clk_get(i2c_dev->dev, \"enable\");\n+\tif (IS_ERR(i2c_dev->clk)) {\n+\t\tdev_warn(i2c_dev->dev, \"i2c%d can't get the enable clock\\n\",\n+\t\t\t i2c_dev->adap.nr);\n+\t\ti2c_dev->clk = NULL;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int sprd_i2c_probe(struct platform_device *pdev)\n+{\n+\tstruct device *dev = &pdev->dev;\n+\tstruct sprd_i2c *i2c_dev;\n+\tstruct resource *res;\n+\tu32 prop;\n+\tint ret;\n+\n+\tpdev->id = of_alias_get_id(dev->of_node, \"i2c\");\n+\n+\ti2c_dev = devm_kzalloc(dev, sizeof(struct sprd_i2c), GFP_KERNEL);\n+\tif (!i2c_dev)\n+\t\treturn -ENOMEM;\n+\n+\tres = platform_get_resource(pdev, IORESOURCE_MEM, 0);\n+\ti2c_dev->base = devm_ioremap_resource(dev, res);\n+\tif (IS_ERR(i2c_dev->base))\n+\t\treturn PTR_ERR(i2c_dev->base);\n+\n+\ti2c_dev->irq = platform_get_irq(pdev, 0);\n+\tif (i2c_dev->irq < 0) {\n+\t\tdev_err(&pdev->dev, \"failed to get irq resource\\n\");\n+\t\treturn i2c_dev->irq;\n+\t}\n+\n+\ti2c_set_adapdata(&i2c_dev->adap, i2c_dev);\n+\tinit_completion(&i2c_dev->complete);\n+\tsnprintf(i2c_dev->adap.name, sizeof(i2c_dev->adap.name),\n+\t\t \"%s\", \"sprd-i2c\");\n+\n+\ti2c_dev->bus_freq = 100000;\n+\ti2c_dev->adap.owner = THIS_MODULE;\n+\ti2c_dev->dev = dev;\n+\ti2c_dev->adap.retries = 3;\n+\ti2c_dev->adap.algo = &sprd_i2c_algo;\n+\ti2c_dev->adap.algo_data = i2c_dev;\n+\ti2c_dev->adap.dev.parent = dev;\n+\ti2c_dev->adap.nr = pdev->id;\n+\ti2c_dev->adap.dev.of_node = dev->of_node;\n+\n+\tif (!of_property_read_u32(dev->of_node, \"clock-frequency\", &prop))\n+\t\ti2c_dev->bus_freq = prop;\n+\n+\t/* We only support 100k and 400k now, otherwise will return error. */\n+\tif (i2c_dev->bus_freq != 100000 && i2c_dev->bus_freq != 400000)\n+\t\treturn -EINVAL;\n+\n+\tsprd_i2c_clk_init(i2c_dev);\n+\tplatform_set_drvdata(pdev, i2c_dev);\n+\n+\tret = clk_prepare_enable(i2c_dev->clk);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tsprd_i2c_enable(i2c_dev);\n+\n+\tpm_runtime_set_autosuspend_delay(i2c_dev->dev, SPRD_I2C_PM_TIMEOUT);\n+\tpm_runtime_use_autosuspend(i2c_dev->dev);\n+\tpm_runtime_set_active(i2c_dev->dev);\n+\tpm_runtime_enable(i2c_dev->dev);\n+\n+\tret = pm_runtime_get_sync(i2c_dev->dev);\n+\tif (ret < 0)\n+\t\tgoto err_rpm_put;\n+\n+\tret = devm_request_threaded_irq(dev, i2c_dev->irq,\n+\t\tsprd_i2c_isr, sprd_i2c_isr_thread,\n+\t\tIRQF_NO_SUSPEND | IRQF_ONESHOT,\n+\t\tpdev->name, i2c_dev);\n+\tif (ret) {\n+\t\tdev_err(&pdev->dev, \"failed to request irq %d\\n\", i2c_dev->irq);\n+\t\tgoto err_rpm_put;\n+\t}\n+\n+\tret = i2c_add_numbered_adapter(&i2c_dev->adap);\n+\tif (ret) {\n+\t\tdev_err(&pdev->dev, \"add adapter failed\\n\");\n+\t\tgoto err_rpm_put;\n+\t}\n+\n+\tpm_runtime_mark_last_busy(i2c_dev->dev);\n+\tpm_runtime_put_autosuspend(i2c_dev->dev);\n+\treturn 0;\n+\n+err_rpm_put:\n+\tpm_runtime_put_noidle(i2c_dev->dev);\n+\tpm_runtime_disable(i2c_dev->dev);\n+\tclk_disable_unprepare(i2c_dev->clk);\n+\treturn ret;\n+}\n+\n+static int sprd_i2c_remove(struct platform_device *pdev)\n+{\n+\tstruct sprd_i2c *i2c_dev = platform_get_drvdata(pdev);\n+\tint ret;\n+\n+\tret = pm_runtime_get_sync(i2c_dev->dev);\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\ti2c_del_adapter(&i2c_dev->adap);\n+\tclk_disable_unprepare(i2c_dev->clk);\n+\n+\tpm_runtime_put_noidle(i2c_dev->dev);\n+\tpm_runtime_disable(i2c_dev->dev);\n+\n+\treturn 0;\n+}\n+\n+static int __maybe_unused sprd_i2c_suspend_noirq(struct device *pdev)\n+{\n+\treturn pm_runtime_force_suspend(pdev);\n+}\n+\n+static int __maybe_unused sprd_i2c_resume_noirq(struct device *pdev)\n+{\n+\treturn pm_runtime_force_resume(pdev);\n+}\n+\n+static int __maybe_unused sprd_i2c_runtime_suspend(struct device *pdev)\n+{\n+\tstruct sprd_i2c *i2c_dev = dev_get_drvdata(pdev);\n+\n+\tclk_disable_unprepare(i2c_dev->clk);\n+\n+\treturn 0;\n+}\n+\n+static int __maybe_unused sprd_i2c_runtime_resume(struct device *pdev)\n+{\n+\tstruct sprd_i2c *i2c_dev = dev_get_drvdata(pdev);\n+\tint ret;\n+\n+\tret = clk_prepare_enable(i2c_dev->clk);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tsprd_i2c_enable(i2c_dev);\n+\n+\treturn 0;\n+}\n+\n+static const struct dev_pm_ops sprd_i2c_pm_ops = {\n+\tSET_RUNTIME_PM_OPS(sprd_i2c_runtime_suspend,\n+\t\t\t sprd_i2c_runtime_resume, NULL)\n+\n+\tSET_NOIRQ_SYSTEM_SLEEP_PM_OPS(sprd_i2c_suspend_noirq,\n+\t\t\t\t sprd_i2c_resume_noirq)\n+};\n+\n+static const struct of_device_id sprd_i2c_of_match[] = {\n+\t{ .compatible = \"sprd,sc9860-i2c\", },\n+};\n+\n+static struct platform_driver sprd_i2c_driver = {\n+\t.probe = sprd_i2c_probe,\n+\t.remove = sprd_i2c_remove,\n+\t.driver = {\n+\t\t .name = \"sprd-i2c\",\n+\t\t .of_match_table = sprd_i2c_of_match,\n+\t\t .pm = &sprd_i2c_pm_ops,\n+\t},\n+};\n+\n+static int sprd_i2c_init(void)\n+{\n+\treturn platform_driver_register(&sprd_i2c_driver);\n+}\n+arch_initcall_sync(sprd_i2c_init);\n", "prefixes": [ "v5", "2/2" ] }