Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/2217573/?format=api
{ "id": 2217573, "url": "http://patchwork.ozlabs.org/api/patches/2217573/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-aspeed/patch/20260330-upstream_i2c-v28-3-17bdae39c5cb@aspeedtech.com/", "project": { "id": 57, "url": "http://patchwork.ozlabs.org/api/projects/57/?format=api", "name": "Linux ASPEED SoC development", "link_name": "linux-aspeed", "list_id": "linux-aspeed.lists.ozlabs.org", "list_email": "linux-aspeed@lists.ozlabs.org", "web_url": "", "scm_url": "", "webscm_url": "", "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<20260330-upstream_i2c-v28-3-17bdae39c5cb@aspeedtech.com>", "list_archive_url": null, "date": "2026-03-30T08:21:48", "name": "[v28,3/4] i2c: ast2600: Add controller driver for AST2600 new register set", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "97e8338b6a5c66a86ee6ef7690fd13161180ff81", "submitter": { "id": 71489, "url": "http://patchwork.ozlabs.org/api/people/71489/?format=api", "name": "Ryan Chen", "email": "ryan_chen@aspeedtech.com" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/linux-aspeed/patch/20260330-upstream_i2c-v28-3-17bdae39c5cb@aspeedtech.com/mbox/", "series": [ { "id": 497972, "url": "http://patchwork.ozlabs.org/api/series/497972/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-aspeed/list/?series=497972", "date": "2026-03-30T08:21:45", "name": "Add ASPEED AST2600 I2C controller driver", "version": 28, "mbox": "http://patchwork.ozlabs.org/series/497972/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2217573/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2217573/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "\n <linux-aspeed+bounces-3805-incoming=patchwork.ozlabs.org@lists.ozlabs.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "linux-aspeed@lists.ozlabs.org" ], "Delivered-To": "patchwork-incoming@legolas.ozlabs.org", "Authentication-Results": [ "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=lists.ozlabs.org\n (client-ip=2404:9400:21b9:f100::1; helo=lists.ozlabs.org;\n envelope-from=linux-aspeed+bounces-3805-incoming=patchwork.ozlabs.org@lists.ozlabs.org;\n receiver=patchwork.ozlabs.org)", "lists.ozlabs.org;\n arc=none smtp.remote-ip=211.20.114.72", "lists.ozlabs.org;\n dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com", "lists.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=aspeedtech.com\n (client-ip=211.20.114.72; helo=twmbx01.aspeed.com;\n envelope-from=ryan_chen@aspeedtech.com; receiver=lists.ozlabs.org)" ], "Received": [ "from lists.ozlabs.org (lists.ozlabs.org\n [IPv6:2404:9400:21b9:f100::1])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fkklL1f4sz1yG8\n\tfor <incoming@patchwork.ozlabs.org>; Mon, 30 Mar 2026 19:22:54 +1100 (AEDT)", "from boromir.ozlabs.org (localhost [127.0.0.1])\n\tby lists.ozlabs.org (Postfix) with ESMTP id 4fkkkT1Nvrz2yhP;\n\tMon, 30 Mar 2026 19:22:09 +1100 (AEDT)", "from TWMBX01.aspeed.com (mail.aspeedtech.com [211.20.114.72])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby lists.ozlabs.org (Postfix) with ESMTPS id 4fkkkR0Pxnz2yj3;\n\tMon, 30 Mar 2026 19:22:07 +1100 (AEDT)", "from TWMBX01.aspeed.com (192.168.0.62) by TWMBX01.aspeed.com\n (192.168.0.62) with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Mon, 30 Mar\n 2026 16:21:47 +0800", "from [127.0.1.1] (192.168.10.13) by TWMBX01.aspeed.com\n (192.168.0.62) with Microsoft SMTP Server id 15.2.1748.10 via Frontend\n Transport; Mon, 30 Mar 2026 16:21:47 +0800" ], "ARC-Seal": "i=1; a=rsa-sha256; d=lists.ozlabs.org; s=201707; t=1774858929;\n\tcv=none;\n b=lq8EHiROpLvTssl8l5Cx47oYAr1JFIsD7VxOLfmNRlynELb9S2hoaKYiUqu1j014co2LY9mZs21w77CA/IPEW3aEvbg7Yq+hECBZIXSHYHJhd6oFHPzBwjNBpYmMoaa9xaeLg8Uw0saEsBKFEisYlklCHMjAvASveisIIO1pCdOS+TA9U6XqEPHCFqYEe1vLGw4Ylpj7Yml+oRwCdw7tEmU0RQKzqDy86mI42LM77V8OaOxrxVLsXTiEq38TQJ5gqk5SjskxwqUanJ3uuKL8KlJc2mSWxjhQElTY7SMbdoiAnK1BTQoF69mWn8jYt2HKayMk2bJu7NVgN76rwiGgMA==", "ARC-Message-Signature": "i=1; a=rsa-sha256; d=lists.ozlabs.org; s=201707;\n\tt=1774858929; c=relaxed/relaxed;\n\tbh=/1HwiBqIpesx9uF5Vv/LCe1oahuIoMipkPQ552Zk2GM=;\n\th=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References:\n\t In-Reply-To:To:CC;\n b=jB48wC0BUULWX2aL5rS1Yq25EOfp3m9yPICag7JaOGn8OGpyz4GizqR/YL7DDuA/Xwe5X4vFjh2avZkhCpx9kMlRMgKoJ52OOjvOHDtN14h7eHaGccotiB3SvCDGNXRWlTPAOqte5xJzfB1FBh8qjE7XN9EPmte5v4u/ITDY3M/wWhRXtI+hgVTZE3T2dSHJdZ40knpV398Mr/BQAmejYQcitMPpRv/Bid0etvfOBiBv5jZfQJR1MQNmkaOVDVyhnnssCOJxzIls7ReD3LMsnjhaLfTJsLh4IqNwrPu2u1B62K1fiDJ7wXYXLQ8gPAT5L9As2OQQREqka60rt8OQKQ==", "ARC-Authentication-Results": "i=1; lists.ozlabs.org;\n dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com;\n spf=pass (client-ip=211.20.114.72; helo=twmbx01.aspeed.com;\n envelope-from=ryan_chen@aspeedtech.com;\n receiver=lists.ozlabs.org) smtp.mailfrom=aspeedtech.com", "From": "Ryan Chen <ryan_chen@aspeedtech.com>", "Date": "Mon, 30 Mar 2026 16:21:48 +0800", "Subject": "[PATCH v28 3/4] i2c: ast2600: Add controller driver for AST2600\n new register set", "X-Mailing-List": "linux-aspeed@lists.ozlabs.org", "List-Id": "<linux-aspeed.lists.ozlabs.org>", "List-Help": "<mailto:linux-aspeed+help@lists.ozlabs.org>", "List-Owner": "<mailto:linux-aspeed+owner@lists.ozlabs.org>", "List-Post": "<mailto:linux-aspeed@lists.ozlabs.org>", "List-Archive": "<https://lore.kernel.org/linux-aspeed/>,\n <https://lists.ozlabs.org/pipermail/linux-aspeed/>", "List-Subscribe": "<mailto:linux-aspeed+subscribe@lists.ozlabs.org>,\n <mailto:linux-aspeed+subscribe-digest@lists.ozlabs.org>,\n <mailto:linux-aspeed+subscribe-nomail@lists.ozlabs.org>", "List-Unsubscribe": "<mailto:linux-aspeed+unsubscribe@lists.ozlabs.org>", "Precedence": "list", "MIME-Version": "1.0", "Content-Type": "text/plain; charset=\"utf-8\"", "Content-Transfer-Encoding": "7bit", "Message-ID": "<20260330-upstream_i2c-v28-3-17bdae39c5cb@aspeedtech.com>", "References": "<20260330-upstream_i2c-v28-0-17bdae39c5cb@aspeedtech.com>", "In-Reply-To": "<20260330-upstream_i2c-v28-0-17bdae39c5cb@aspeedtech.com>", "To": "<jk@codeconstruct.com.au>, <andriy.shevchenko@linux.intel.com>, Andi Shyti\n\t<andi.shyti@kernel.org>, Rob Herring <robh@kernel.org>, Krzysztof Kozlowski\n\t<krzk+dt@kernel.org>, Conor Dooley <conor+dt@kernel.org>, Joel Stanley\n\t<joel@jms.id.au>, Andrew Jeffery <andrew@codeconstruct.com.au>, \"Benjamin\n Herrenschmidt\" <benh@kernel.crashing.org>, Rayn Chen\n\t<rayn_chen@aspeedtech.com>, Philipp Zabel <p.zabel@pengutronix.de>", "CC": "<linux-i2c@vger.kernel.org>, <devicetree@vger.kernel.org>,\n\t<linux-arm-kernel@lists.infradead.org>, <linux-aspeed@lists.ozlabs.org>,\n\t<linux-kernel@vger.kernel.org>, <openbmc@lists.ozlabs.org>, Ryan Chen\n\t<ryan_chen@aspeedtech.com>", "X-Mailer": "b4 0.14.3", "X-Developer-Signature": "v=1; a=ed25519-sha256; t=1774858906; l=40481;\n i=ryan_chen@aspeedtech.com; s=20251126; h=from:subject:message-id;\n bh=cVF//4Im50k5fb4Tq1B2xo1LcWJgiSFw1Gq1Rm0z8vk=;\n b=XJSi2hVibTN04pO/KQePZZcpA9gz70IF+pSkDFxz21x6/jPmRtBhHm4QxdJoyOMlRA0j/SxIr\n Rs1krY0/9YdB6p2XQTJ0jmxO1k7aAPJn/YgKdO96eIijBqMKlx5A/x+", "X-Developer-Key": "i=ryan_chen@aspeedtech.com; a=ed25519;\n pk=Xe73xY6tcnkuRjjbVAB/oU30KdB3FvG4nuJuILj7ZVc=", "X-Spam-Status": "No, score=0.0 required=5.0 tests=SPF_HELO_FAIL,SPF_PASS\n\tautolearn=disabled version=4.0.1", "X-Spam-Checker-Version": "SpamAssassin 4.0.1 (2024-03-25) on lists.ozlabs.org" }, "content": "The AST2600 introduces a new I2C controller register layout, selectable\nat runtime via global control registers. Compared to the legacy layout\nused on AST2400/AST2500, the new layout separates controller (master)\nand target (slave) registers and adds support for packet-based transfers\n\nThe new register set extends the hardware capabilities with:\n\n- Enhanced clock divider configuration for improved timing precision\n- tCKHighMin timing control for SCL high pulse width\n- Dual pool buffer mode (separate Tx/Rx buffers)\n- Extended DMA support with larger buffer size and alignment handling\n- Dedicated DMA buffers for controller and target directions\n- Hardware-assisted bus recovery and timeout mechanisms\n\nThis patch adds an AST2600-specific I2C controller driver implementing\nthe new register layout, including support for packet-based transfers\nand byte, buffer and DMA transfer modes.\n\nThe legacy and new register layouts represent the same AST2600 I2C\ncontroller IP and therefore share the existing compatible string:\n\n \"aspeed,ast2600-i2c-bus\"\n\nTo preserve DT ABI compatibility, driver selection is performed at probe\ntime based on DT contents. In particular, the new binding requires the\n`aspeed,global-regs` phandle, which is absent from legacy DTBs:\n\n- The new driver only probes successfully when `aspeed,global-regs` is\n present.\n\n- The existing i2c-aspeed driver returns -ENODEV for AST2600 nodes that\n provide `aspeed,global-regs`, allowing the new driver to bind.\n\nSigned-off-by: Ryan Chen <ryan_chen@aspeedtech.com>\n\n---\nChanges in v28:\n- Separate xfer_mode_store into distinct parse and availability-check\n steps by introducing ast2600_i2c_xfer_mode_check()\n- fix tx dma memcpy source point address.\n- Use a temporary variable for devm_platform_get_and_ioremap_resource()\n to avoid storing an ERR_PTR in i2c_bus->buf_base; drop the redundant\n NULL assignment in the error path since i2c_bus is kzalloc()ed\n- Add ABI documentation file\n Documentation/ABI/testing/sysfs-driver-ast2600-i2c\nChanges in v27:\n- remove aspeed,transfer-mode selection instead aspeed,dma-mode.\n- add sysfs for xfer mode.\nChanges in v25:\n- Rename AST2600_I2CM_SMBUS_ALT to AST2600_I2CM_SMBUS_ALERT.\n- Refactor transfer mode handling using setup_tx/setup_rx helpers.\n- Rework DMA handling to use pre-allocated buffers and reduce\n mapping overhead in interrupt context.\n- Fix IRQ status checks to use consistent (sts & value) style.\n- Move device_property_read_bool() to probe().\n- Improve probe error handling.\n- Handle timeout condition in target_byte_irq().\n- Rename \"package\" to \"packet\".\n- Remove target reset when master wait_for_completion_timeout().\n---\n Documentation/ABI/testing/sysfs-driver-ast2600-i2c | 19 +\n drivers/i2c/busses/Makefile | 2 +-\n drivers/i2c/busses/i2c-aspeed.c | 5 +\n drivers/i2c/busses/i2c-ast2600.c | 1080 ++++++++++++++++++++\n 4 files changed, 1105 insertions(+), 1 deletion(-)", "diff": "diff --git a/Documentation/ABI/testing/sysfs-driver-ast2600-i2c b/Documentation/ABI/testing/sysfs-driver-ast2600-i2c\nnew file mode 100644\nindex 000000000000..7d2a69a7281a\n--- /dev/null\n+++ b/Documentation/ABI/testing/sysfs-driver-ast2600-i2c\n@@ -0,0 +1,19 @@\n+What:\t\t/sys/bus/platform/drivers/i2c-ast2600/.../xfer_mode\n+Date:\t\tMarch 2026\n+KernelVersion:\t7.x\n+Contact:\tRyan Chen <ryan_chen@aspeedtech.com>\n+Description:\tShows or sets the active transfer mode for an ASPEED AST2600\n+\t\tI2C controller instance.\n+\n+\t\tPossible values:\n+\n+\t\t========= =================================================\n+\t\tbyte Programmed I/O, one byte at a time.\n+\t\tbuffer Programmed I/O using the hardware FIFO buffer.\n+\t\t Only available if the controller has a buffer\n+\t\t resource defined in the device tree.\n+\t\tdma DMA transfer (if DMA is available for this\n+\t\t controller).\n+\t\t========= =================================================\n+\n+\t\tWriting an unsupported or unavailable mode returns -EINVAL.\ndiff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile\nindex 547123ab351f..ece201a67d41 100644\n--- a/drivers/i2c/busses/Makefile\n+++ b/drivers/i2c/busses/Makefile\n@@ -37,7 +37,7 @@ obj-$(CONFIG_I2C_POWERMAC)\t+= i2c-powermac.o\n obj-$(CONFIG_I2C_ALTERA)\t+= i2c-altera.o\n obj-$(CONFIG_I2C_AMD_MP2)\t+= i2c-amd-mp2-pci.o i2c-amd-mp2-plat.o\n obj-$(CONFIG_I2C_AMD_ASF)\t+= i2c-amd-asf-plat.o\n-obj-$(CONFIG_I2C_ASPEED)\t+= i2c-aspeed.o\n+obj-$(CONFIG_I2C_ASPEED)\t+= i2c-aspeed.o i2c-ast2600.o\n obj-$(CONFIG_I2C_AT91)\t\t+= i2c-at91.o\n i2c-at91-y\t\t\t:= i2c-at91-core.o i2c-at91-master.o\n i2c-at91-$(CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL)\t+= i2c-at91-slave.o\ndiff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c\nindex a26b74c71206..8286fd2cd130 100644\n--- a/drivers/i2c/busses/i2c-aspeed.c\n+++ b/drivers/i2c/busses/i2c-aspeed.c\n@@ -22,6 +22,7 @@\n #include <linux/of_irq.h>\n #include <linux/of_platform.h>\n #include <linux/platform_device.h>\n+#include <linux/property.h>\n #include <linux/reset.h>\n #include <linux/slab.h>\n \n@@ -1002,6 +1003,10 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)\n \tstruct clk *parent_clk;\n \tint irq, ret;\n \n+\tif (device_is_compatible(&pdev->dev, \"aspeed,ast2600-i2c-bus\") &&\n+\t device_property_present(&pdev->dev, \"aspeed,global-regs\"))\n+\t\treturn -ENODEV;\n+\n \tbus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);\n \tif (!bus)\n \t\treturn -ENOMEM;\ndiff --git a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c\nnew file mode 100644\nindex 000000000000..c2368ba309a7\n--- /dev/null\n+++ b/drivers/i2c/busses/i2c-ast2600.c\n@@ -0,0 +1,1080 @@\n+// SPDX-License-Identifier: GPL-2.0-only\n+/*\n+ * ASPEED AST2600 new register set I2C controller driver\n+ *\n+ * Copyright (C) 2026 ASPEED Technology Inc.\n+ */\n+#include <linux/array_size.h>\n+#include <linux/bits.h>\n+#include <linux/clk.h>\n+#include <linux/completion.h>\n+#include <linux/delay.h>\n+#include <linux/dma-mapping.h>\n+#include <linux/err.h>\n+#include <linux/i2c.h>\n+#include <linux/i2c-smbus.h>\n+#include <linux/interrupt.h>\n+#include <linux/io.h>\n+#include <linux/minmax.h>\n+#include <linux/mfd/syscon.h>\n+#include <linux/module.h>\n+#include <linux/of_device.h>\n+#include <linux/platform_device.h>\n+#include <linux/property.h>\n+#include <linux/regmap.h>\n+#include <linux/reset.h>\n+#include <linux/slab.h>\n+#include <linux/string_helpers.h>\n+#include <linux/unaligned.h>\n+\n+#define AST2600_I2CG_ISR\t\t\t0x00\n+#define AST2600_I2CG_SLAVE_ISR\t\t0x04\n+#define AST2600_I2CG_OWNER\t\t0x08\n+#define AST2600_I2CG_CTRL\t\t0x0C\n+#define AST2600_I2CG_CLK_DIV_CTRL\t0x10\n+\n+#define AST2600_I2CG_SLAVE_PKT_NAK\tBIT(4)\n+#define AST2600_I2CG_M_S_SEPARATE_INTR\tBIT(3)\n+#define AST2600_I2CG_CTRL_NEW_REG\tBIT(2)\n+#define AST2600_I2CG_CTRL_NEW_CLK_DIV\tBIT(1)\n+#define AST2600_GLOBAL_INIT\t\\\n+\t(AST2600_I2CG_CTRL_NEW_REG | AST2600_I2CG_CTRL_NEW_CLK_DIV)\n+/*\n+ * APB clk : 100Mhz\n+ * div\t: scl\t\t: baseclk [APB/((div/2) + 1)] : tBuf [1/bclk * 16]\n+ * I2CG10[31:24] base clk4 for i2c auto recovery timeout counter (0xC6)\n+ * I2CG10[23:16] base clk3 for Standard-mode (100Khz) min tBuf 4.7us\n+ * 0x3c : 100.8Khz\t: 3.225Mhz\t\t\t\t\t : 4.96us\n+ * 0x3d : 99.2Khz\t: 3.174Mhz\t\t\t\t\t : 5.04us\n+ * 0x3e : 97.65Khz\t: 3.125Mhz\t\t\t\t\t : 5.12us\n+ * 0x40 : 97.75Khz\t: 3.03Mhz\t\t\t\t\t : 5.28us\n+ * 0x41 : 99.5Khz\t: 2.98Mhz\t\t\t\t\t : 5.36us (default)\n+ * I2CG10[15:8] base clk2 for Fast-mode (400Khz) min tBuf 1.3us\n+ * 0x12 : 400Khz\t: 10Mhz\t\t\t\t\t\t : 1.6us\n+ * I2CG10[7:0] base clk1 for Fast-mode Plus (1Mhz) min tBuf 0.5us\n+ * 0x08 : 1Mhz\t\t: 20Mhz\t\t\t\t\t\t : 0.8us\n+ */\n+#define I2CCG_DIV_CTRL 0xC6411208\n+\n+/* 0x00 : I2CC Controller/Target Function Control Register */\n+#define AST2600_I2CC_FUN_CTRL\t\t0x00\n+#define AST2600_I2CC_SLAVE_ADDR_RX_EN\t\tBIT(20)\n+#define AST2600_I2CC_MASTER_RETRY_MASK\t\tGENMASK(19, 18)\n+#define AST2600_I2CC_MASTER_RETRY(x)\t\t(((x) & GENMASK(1, 0)) << 18)\n+#define AST2600_I2CC_BUS_AUTO_RELEASE\t\tBIT(17)\n+#define AST2600_I2CC_M_SDA_LOCK_EN\t\t\tBIT(16)\n+#define AST2600_I2CC_MULTI_MASTER_DIS\t\tBIT(15)\n+#define AST2600_I2CC_M_SCL_DRIVE_EN\t\t\tBIT(14)\n+#define AST2600_I2CC_MSB_STS\t\t\t\tBIT(9)\n+#define AST2600_I2CC_SDA_DRIVE_1T_EN\t\tBIT(8)\n+#define AST2600_I2CC_M_SDA_DRIVE_1T_EN\t\tBIT(7)\n+#define AST2600_I2CC_M_HIGH_SPEED_EN\t\tBIT(6)\n+/* reserver 5 : 2 */\n+#define AST2600_I2CC_SLAVE_EN\t\t\tBIT(1)\n+#define AST2600_I2CC_MASTER_EN\t\t\tBIT(0)\n+\n+/* 0x04 : I2CC Controller/Target Clock and AC Timing Control Register #1 */\n+#define AST2600_I2CC_AC_TIMING\t\t0x04\n+#define AST2600_I2CC_TTIMEOUT(x)\t\t\t(((x) & GENMASK(4, 0)) << 24)\n+#define AST2600_I2CC_TCKHIGHMIN(x)\t\t\t(((x) & GENMASK(3, 0)) << 20)\n+#define AST2600_I2CC_TCKHIGH(x)\t\t\t(((x) & GENMASK(3, 0)) << 16)\n+#define AST2600_I2CC_TCKLOW(x)\t\t\t(((x) & GENMASK(3, 0)) << 12)\n+#define AST2600_I2CC_THDDAT(x)\t\t\t(((x) & GENMASK(1, 0)) << 10)\n+#define AST2600_I2CC_TOUTBASECLK(x)\t\t\t(((x) & GENMASK(1, 0)) << 8)\n+#define AST2600_I2CC_TBASECLK(x)\t\t\t((x) & GENMASK(3, 0))\n+#define AST2600_I2CC_AC_TIMING_MASK\t\tGENMASK(23, 0)\n+\n+/* 0x08 : I2CC Controller/Target Transmit/Receive Byte Buffer Register */\n+#define AST2600_I2CC_STS_AND_BUFF\t\t0x08\n+#define AST2600_I2CC_TX_DIR_MASK\t\t\tGENMASK(31, 29)\n+#define AST2600_I2CC_SDA_OE\t\t\t\tBIT(28)\n+#define AST2600_I2CC_SDA_O\t\t\t\tBIT(27)\n+#define AST2600_I2CC_SCL_OE\t\t\t\tBIT(26)\n+#define AST2600_I2CC_SCL_O\t\t\t\tBIT(25)\n+\n+#define AST2600_I2CC_SCL_LINE_STS\t\t\tBIT(18)\n+#define AST2600_I2CC_SDA_LINE_STS\t\t\tBIT(17)\n+#define AST2600_I2CC_BUS_BUSY_STS\t\t\tBIT(16)\n+\n+#define AST2600_I2CC_GET_RX_BUFF(x)\t\t\t(((x) >> 8) & GENMASK(7, 0))\n+\n+/* 0x0C : I2CC Controller/Target Pool Buffer Control Register */\n+#define AST2600_I2CC_BUFF_CTRL\t\t0x0C\n+#define AST2600_I2CC_GET_RX_BUF_LEN(x) (((x) & GENMASK(29, 24)) >> 24)\n+#define AST2600_I2CC_SET_RX_BUF_LEN(x)\t\t(((((x) - 1) & GENMASK(4, 0)) << 16) | BIT(0))\n+#define AST2600_I2CC_SET_TX_BUF_LEN(x)\t\t(((((x) - 1) & GENMASK(4, 0)) << 8) | BIT(0))\n+#define AST2600_I2CC_GET_TX_BUF_LEN(x) ((((x) & GENMASK(12, 8)) >> 8) + 1)\n+\n+/* 0x10 : I2CM Controller Interrupt Control Register */\n+#define AST2600_I2CM_IER\t\t\t0x10\n+/* 0x14 : I2CM Controller Interrupt Status Register : WC */\n+#define AST2600_I2CM_ISR\t\t\t0x14\n+\n+#define AST2600_I2CM_PKT_TIMEOUT\t\t\tBIT(18)\n+#define AST2600_I2CM_PKT_ERROR\t\t\tBIT(17)\n+#define AST2600_I2CM_PKT_DONE\t\t\tBIT(16)\n+\n+#define AST2600_I2CM_BUS_RECOVER_FAIL\t\tBIT(15)\n+#define AST2600_I2CM_SDA_DL_TO\t\t\tBIT(14)\n+#define AST2600_I2CM_BUS_RECOVER\t\t\tBIT(13)\n+#define AST2600_I2CM_SMBUS_ALERT\t\t\tBIT(12)\n+\n+#define AST2600_I2CM_SCL_LOW_TO\t\t\tBIT(6)\n+#define AST2600_I2CM_ABNORMAL\t\t\tBIT(5)\n+#define AST2600_I2CM_NORMAL_STOP\t\t\tBIT(4)\n+#define AST2600_I2CM_ARBIT_LOSS\t\t\tBIT(3)\n+#define AST2600_I2CM_RX_DONE\t\t\tBIT(2)\n+#define AST2600_I2CM_TX_NAK\t\t\t\tBIT(1)\n+#define AST2600_I2CM_TX_ACK\t\t\t\tBIT(0)\n+\n+/* 0x18 : I2CM Controller Command/Status Register */\n+#define AST2600_I2CM_CMD_STS\t\t0x18\n+#define AST2600_I2CM_PKT_ADDR(x)\t\t\t(((x) & GENMASK(6, 0)) << 24)\n+#define AST2600_I2CM_PKT_EN\t\t\t\tBIT(16)\n+#define AST2600_I2CM_SDA_OE_OUT_DIR\t\t\tBIT(15)\n+#define AST2600_I2CM_SDA_O_OUT_DIR\t\t\tBIT(14)\n+#define AST2600_I2CM_SCL_OE_OUT_DIR\t\t\tBIT(13)\n+#define AST2600_I2CM_SCL_O_OUT_DIR\t\t\tBIT(12)\n+#define AST2600_I2CM_RECOVER_CMD_EN\t\t\tBIT(11)\n+\n+#define AST2600_I2CM_RX_DMA_EN\t\t\tBIT(9)\n+#define AST2600_I2CM_TX_DMA_EN\t\t\tBIT(8)\n+/* Command Bit */\n+#define AST2600_I2CM_RX_BUFF_EN\t\t\tBIT(7)\n+#define AST2600_I2CM_TX_BUFF_EN\t\t\tBIT(6)\n+#define AST2600_I2CM_STOP_CMD\t\t\tBIT(5)\n+#define AST2600_I2CM_RX_CMD_LAST\t\t\tBIT(4)\n+#define AST2600_I2CM_RX_CMD\t\t\t\tBIT(3)\n+\n+#define AST2600_I2CM_TX_CMD\t\t\t\tBIT(1)\n+#define AST2600_I2CM_START_CMD\t\t\tBIT(0)\n+\n+/* 0x1C : I2CM Controller DMA Transfer Length Register\t */\n+#define AST2600_I2CM_DMA_LEN\t\t0x1C\n+/* Tx Rx support length 1 ~ 4096 */\n+#define AST2600_I2CM_SET_RX_DMA_LEN(x)\t((((x) & GENMASK(11, 0)) << 16) | BIT(31))\n+#define AST2600_I2CM_SET_TX_DMA_LEN(x)\t(((x) & GENMASK(11, 0)) | BIT(15))\n+\n+/* 0x20 : I2CS Target Interrupt Control Register */\n+#define AST2600_I2CS_IER\t\t\t0x20\n+/* 0x24 : I2CS Target Interrupt Status Register\t */\n+#define AST2600_I2CS_ISR\t\t\t0x24\n+\n+#define AST2600_I2CS_ADDR_INDICATE_MASK\tGENMASK(31, 30)\n+#define AST2600_I2CS_SLAVE_PENDING\t\t\tBIT(29)\n+\n+#define AST2600_I2CS_WAIT_TX_DMA\t\t\tBIT(25)\n+#define AST2600_I2CS_WAIT_RX_DMA\t\t\tBIT(24)\n+\n+#define AST2600_I2CS_ADDR3_NAK\t\t\tBIT(22)\n+#define AST2600_I2CS_ADDR2_NAK\t\t\tBIT(21)\n+#define AST2600_I2CS_ADDR1_NAK\t\t\tBIT(20)\n+\n+#define AST2600_I2CS_ADDR_MASK\t\t\tGENMASK(19, 18)\n+#define AST2600_I2CS_PKT_ERROR\t\t\tBIT(17)\n+#define AST2600_I2CS_PKT_DONE\t\t\tBIT(16)\n+#define AST2600_I2CS_INACTIVE_TO\t\t\tBIT(15)\n+\n+#define AST2600_I2CS_SLAVE_MATCH\t\t\tBIT(7)\n+#define AST2600_I2CS_ABNOR_STOP\t\t\tBIT(5)\n+#define AST2600_I2CS_STOP\t\t\t\tBIT(4)\n+#define AST2600_I2CS_RX_DONE_NAK\t\t\tBIT(3)\n+#define AST2600_I2CS_RX_DONE\t\t\tBIT(2)\n+#define AST2600_I2CS_TX_NAK\t\t\t\tBIT(1)\n+#define AST2600_I2CS_TX_ACK\t\t\t\tBIT(0)\n+\n+/* 0x28 : I2CS Target CMD/Status Register */\n+#define AST2600_I2CS_CMD_STS\t\t0x28\n+#define AST2600_I2CS_ACTIVE_ALL\t\t\tGENMASK(18, 17)\n+#define AST2600_I2CS_PKT_MODE_EN\t\t\tBIT(16)\n+#define AST2600_I2CS_AUTO_NAK_NOADDR\t\tBIT(15)\n+#define AST2600_I2CS_AUTO_NAK_EN\t\t\tBIT(14)\n+\n+#define AST2600_I2CS_ALT_EN\t\t\t\tBIT(10)\n+#define AST2600_I2CS_RX_DMA_EN\t\t\tBIT(9)\n+#define AST2600_I2CS_TX_DMA_EN\t\t\tBIT(8)\n+#define AST2600_I2CS_RX_BUFF_EN\t\t\tBIT(7)\n+#define AST2600_I2CS_TX_BUFF_EN\t\t\tBIT(6)\n+#define AST2600_I2CS_RX_CMD_LAST\t\t\tBIT(4)\n+\n+#define AST2600_I2CS_TX_CMD\t\t\t\tBIT(2)\n+\n+#define AST2600_I2CS_DMA_LEN\t\t0x2C\n+#define AST2600_I2CS_SET_RX_DMA_LEN(x)\t(((((x) - 1) & GENMASK(11, 0)) << 16) | BIT(31))\n+#define AST2600_I2CS_SET_TX_DMA_LEN(x)\t((((x) - 1) & GENMASK(11, 0)) | BIT(15))\n+\n+/* I2CM Controller DMA Tx Buffer Register */\n+#define AST2600_I2CM_TX_DMA\t\t\t0x30\n+/* I2CM Controller DMA Rx Buffer Register\t*/\n+#define AST2600_I2CM_RX_DMA\t\t\t0x34\n+/* I2CS Target DMA Tx Buffer Register */\n+#define AST2600_I2CS_TX_DMA\t\t\t0x38\n+/* I2CS Target DMA Rx Buffer Register */\n+#define AST2600_I2CS_RX_DMA\t\t\t0x3C\n+\n+#define AST2600_I2CS_ADDR_CTRL\t\t0x40\n+\n+#define\tAST2600_I2CS_ADDR3_MASK\t\tGENMASK(22, 16)\n+#define\tAST2600_I2CS_ADDR2_MASK\t\tGENMASK(14, 8)\n+#define\tAST2600_I2CS_ADDR1_MASK\t\tGENMASK(6, 0)\n+\n+#define AST2600_I2CM_DMA_LEN_STS\t\t0x48\n+#define AST2600_I2CS_DMA_LEN_STS\t\t0x4C\n+\n+#define AST2600_I2C_GET_TX_DMA_LEN(x)\t\t((x) & GENMASK(12, 0))\n+#define AST2600_I2C_GET_RX_DMA_LEN(x) (((x) & GENMASK(28, 16)) >> 16)\n+\n+/* 0x40 : Target Device Address Register */\n+#define AST2600_I2CS_ADDR3_ENABLE\t\t\tBIT(23)\n+#define AST2600_I2CS_ADDR3(x)\t\t\t((x) << 16)\n+#define AST2600_I2CS_ADDR2_ENABLE\t\t\tBIT(15)\n+#define AST2600_I2CS_ADDR2(x)\t\t\t((x) << 8)\n+#define AST2600_I2CS_ADDR1_ENABLE\t\t\tBIT(7)\n+#define AST2600_I2CS_ADDR1(x)\t\t\t(x)\n+\n+#define I2C_TARGET_MSG_BUF_SIZE\t\t4096\n+\n+#define AST2600_I2C_DMA_SIZE\t\t4096\n+\n+#define CONTROLLER_TRIGGER_LAST_STOP\t(AST2600_I2CM_RX_CMD_LAST | AST2600_I2CM_STOP_CMD)\n+#define TARGET_TRIGGER_CMD\t(AST2600_I2CS_ACTIVE_ALL | AST2600_I2CS_PKT_MODE_EN)\n+\n+#define AST_I2C_TIMEOUT_CLK\t\t0x1\n+\n+enum xfer_mode {\n+\tBYTE_MODE,\n+\tBUFF_MODE,\n+\tDMA_MODE,\n+};\n+\n+struct ast2600_i2c_bus {\n+\tstruct i2c_adapter\tadap;\n+\tstruct device\t\t*dev;\n+\tvoid __iomem\t\t*reg_base;\n+\tstruct regmap\t\t*global_regs;\n+\tstruct clk\t\t*clk;\n+\tstruct i2c_timings\ttiming_info;\n+\tstruct completion\tcmd_complete;\n+\tstruct i2c_msg\t\t*msgs;\n+\tu8\t\t\t*controller_dma_buf;\n+\tdma_addr_t\t\tcontroller_dma_addr;\n+\tu32\t\t\tapb_clk;\n+\tu32\t\t\ttimeout;\n+\tint\t\t\tirq;\n+\tint\t\t\tcmd_err;\n+\tint\t\t\tmsgs_index;\n+\tint\t\t\tmsgs_count;\n+\tint\t\t\tcontroller_xfer_cnt;\n+\tsize_t\t\t\tbuf_index;\n+\tsize_t\t\t\tbuf_size;\n+\tenum xfer_mode\t\tmode;\n+\tbool\t\t\tdma_available;\n+\tbool\t\t\tmulti_master;\n+\t/* Buffer mode */\n+\tvoid __iomem\t\t*buf_base;\n+\tint (*setup_tx)(u32 cmd, struct ast2600_i2c_bus *i2c_bus);\n+\tint (*setup_rx)(u32 cmd, struct ast2600_i2c_bus *i2c_bus);\n+};\n+\n+static void ast2600_i2c_ac_timing_config(struct ast2600_i2c_bus *i2c_bus)\n+{\n+\tunsigned long base_clk[16];\n+\tint baseclk_idx = 0;\n+\tint divisor = 0;\n+\tu32 clk_div_reg;\n+\tu32 scl_low;\n+\tu32 scl_high;\n+\tu32 data;\n+\n+\tregmap_read(i2c_bus->global_regs, AST2600_I2CG_CLK_DIV_CTRL, &clk_div_reg);\n+\n+\tfor (int i = 0; i < ARRAY_SIZE(base_clk); i++) {\n+\t\tif (i == 0)\n+\t\t\tbase_clk[i] = i2c_bus->apb_clk;\n+\t\telse if (i < 5)\n+\t\t\tbase_clk[i] = (i2c_bus->apb_clk * 2) /\n+\t\t\t (((clk_div_reg >> ((i - 1) * 8)) & GENMASK(7, 0)) + 2);\n+\t\telse\n+\t\t\tbase_clk[i] = base_clk[4] >> (i - 4);\n+\n+\t\tif ((base_clk[i] / i2c_bus->timing_info.bus_freq_hz) <= 32) {\n+\t\t\tbaseclk_idx = i;\n+\t\t\tdivisor = DIV_ROUND_UP(base_clk[i], i2c_bus->timing_info.bus_freq_hz);\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\tbaseclk_idx = min(baseclk_idx, 15);\n+\tdivisor = min(divisor, 32);\n+\tscl_low = min(divisor * 9 / 16 - 1, 15);\n+\tscl_high = (divisor - scl_low - 2) & GENMASK(3, 0);\n+\tdata = (scl_high - 1) << 20 | scl_high << 16 | scl_low << 12 | baseclk_idx;\n+\tif (i2c_bus->timeout) {\n+\t\tdata |= AST2600_I2CC_TOUTBASECLK(AST_I2C_TIMEOUT_CLK);\n+\t\tdata |= AST2600_I2CC_TTIMEOUT(i2c_bus->timeout);\n+\t}\n+\n+\twritel(data, i2c_bus->reg_base + AST2600_I2CC_AC_TIMING);\n+}\n+\n+static int ast2600_i2c_recover_bus(struct ast2600_i2c_bus *i2c_bus)\n+{\n+\tu32 state = readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF);\n+\tint ret = 0;\n+\tu32 ctrl;\n+\tint r;\n+\n+\tdev_dbg(i2c_bus->dev, \"%d-bus recovery bus [%x]\\n\", i2c_bus->adap.nr, state);\n+\n+\t/* reset controller */\n+\tctrl = readl(i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);\n+\twritel(ctrl & ~AST2600_I2CC_MASTER_EN, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);\n+\twritel(ctrl, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);\n+\n+\treinit_completion(&i2c_bus->cmd_complete);\n+\ti2c_bus->cmd_err = 0;\n+\n+\t/* Check SDA/SCL status in the status register. */\n+\tstate = readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF);\n+\tif (!(state & AST2600_I2CC_SDA_LINE_STS) && (state & AST2600_I2CC_SCL_LINE_STS)) {\n+\t\twritel(AST2600_I2CM_RECOVER_CMD_EN, i2c_bus->reg_base + AST2600_I2CM_CMD_STS);\n+\t\tr = wait_for_completion_timeout(&i2c_bus->cmd_complete, i2c_bus->adap.timeout);\n+\t\tif (r == 0) {\n+\t\t\tdev_dbg(i2c_bus->dev, \"recovery timed out\\n\");\n+\t\t\treturn -ETIMEDOUT;\n+\t\t} else if (i2c_bus->cmd_err) {\n+\t\t\tdev_dbg(i2c_bus->dev, \"recovery error\\n\");\n+\t\t\tret = -EPROTO;\n+\t\t}\n+\t}\n+\n+\t/* Recovery done */\n+\tstate = readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF);\n+\tif (state & AST2600_I2CC_BUS_BUSY_STS) {\n+\t\tdev_dbg(i2c_bus->dev, \"Can't recover bus [%x]\\n\", state);\n+\t\tret = -EPROTO;\n+\t}\n+\n+\treturn ret;\n+}\n+\n+static int ast2600_i2c_setup_dma_tx(u32 cmd, struct ast2600_i2c_bus *i2c_bus)\n+{\n+\tstruct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];\n+\tint xfer_len = msg->len - i2c_bus->controller_xfer_cnt;\n+\n+\tcmd |= AST2600_I2CM_PKT_EN;\n+\n+\tif (xfer_len > AST2600_I2C_DMA_SIZE)\n+\t\txfer_len = AST2600_I2C_DMA_SIZE;\n+\telse if (i2c_bus->msgs_index + 1 == i2c_bus->msgs_count)\n+\t\tcmd |= AST2600_I2CM_STOP_CMD;\n+\n+\tif (cmd & AST2600_I2CM_START_CMD)\n+\t\tcmd |= AST2600_I2CM_PKT_ADDR(msg->addr);\n+\n+\tif (xfer_len) {\n+\t\tmemcpy(i2c_bus->controller_dma_buf,\n+\t\t msg->buf + i2c_bus->controller_xfer_cnt, xfer_len);\n+\t\tcmd |= AST2600_I2CM_TX_DMA_EN | AST2600_I2CM_TX_CMD;\n+\t\twritel(AST2600_I2CM_SET_TX_DMA_LEN(xfer_len - 1),\n+\t\t i2c_bus->reg_base + AST2600_I2CM_DMA_LEN);\n+\t}\n+\n+\twritel(cmd, i2c_bus->reg_base + AST2600_I2CM_CMD_STS);\n+\n+\treturn 0;\n+}\n+\n+static int ast2600_i2c_setup_buff_tx(u32 cmd, struct ast2600_i2c_bus *i2c_bus)\n+{\n+\tstruct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];\n+\tint xfer_len = msg->len - i2c_bus->controller_xfer_cnt;\n+\tu32 wbuf_dword;\n+\tint i;\n+\n+\tcmd |= AST2600_I2CM_PKT_EN;\n+\n+\tif (xfer_len > i2c_bus->buf_size)\n+\t\txfer_len = i2c_bus->buf_size;\n+\telse if (i2c_bus->msgs_index + 1 == i2c_bus->msgs_count)\n+\t\tcmd |= AST2600_I2CM_STOP_CMD;\n+\n+\tif (cmd & AST2600_I2CM_START_CMD)\n+\t\tcmd |= AST2600_I2CM_PKT_ADDR(msg->addr);\n+\n+\tif (xfer_len) {\n+\t\tcmd |= AST2600_I2CM_TX_BUFF_EN | AST2600_I2CM_TX_CMD;\n+\t\t/*\n+\t\t * The controller's buffer register supports dword writes only.\n+\t\t * Therefore, write dwords to the buffer register in a 4-byte aligned,\n+\t\t * and write the remaining unaligned data at the end.\n+\t\t */\n+\t\tfor (i = 0; i < xfer_len; i += 4) {\n+\t\t\tint xfer_cnt = i2c_bus->controller_xfer_cnt + i;\n+\n+\t\t\tswitch (min(xfer_len - i, 4) % 4) {\n+\t\t\tcase 1:\n+\t\t\t\twbuf_dword = msg->buf[xfer_cnt];\n+\t\t\t\tbreak;\n+\t\t\tcase 2:\n+\t\t\t\twbuf_dword = get_unaligned_le16(&msg->buf[xfer_cnt]);\n+\t\t\t\tbreak;\n+\t\t\tcase 3:\n+\t\t\t\twbuf_dword = get_unaligned_le24(&msg->buf[xfer_cnt]);\n+\t\t\t\tbreak;\n+\t\t\tdefault:\n+\t\t\t\twbuf_dword = get_unaligned_le32(&msg->buf[xfer_cnt]);\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\twritel(wbuf_dword, i2c_bus->buf_base + i);\n+\t\t}\n+\t\twritel(AST2600_I2CC_SET_TX_BUF_LEN(xfer_len),\n+\t\t i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);\n+\t}\n+\n+\twritel(cmd, i2c_bus->reg_base + AST2600_I2CM_CMD_STS);\n+\n+\treturn 0;\n+}\n+\n+static int ast2600_i2c_setup_byte_tx(u32 cmd, struct ast2600_i2c_bus *i2c_bus)\n+{\n+\tstruct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];\n+\tint xfer_len;\n+\n+\txfer_len = msg->len - i2c_bus->controller_xfer_cnt;\n+\n+\tcmd |= AST2600_I2CM_PKT_EN;\n+\n+\tif (cmd & AST2600_I2CM_START_CMD)\n+\t\tcmd |= AST2600_I2CM_PKT_ADDR(msg->addr);\n+\n+\tif ((i2c_bus->msgs_index + 1 == i2c_bus->msgs_count) &&\n+\t ((i2c_bus->controller_xfer_cnt + 1) == msg->len))\n+\t\tcmd |= AST2600_I2CM_STOP_CMD;\n+\n+\tif (xfer_len) {\n+\t\tcmd |= AST2600_I2CM_TX_CMD;\n+\t\twritel(msg->buf[i2c_bus->controller_xfer_cnt],\n+\t\t i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF);\n+\t}\n+\n+\twritel(cmd, i2c_bus->reg_base + AST2600_I2CM_CMD_STS);\n+\n+\treturn 0;\n+}\n+\n+static int ast2600_i2c_setup_dma_rx(u32 cmd, struct ast2600_i2c_bus *i2c_bus)\n+{\n+\tstruct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];\n+\tint xfer_len = msg->len - i2c_bus->controller_xfer_cnt;\n+\n+\tcmd |= AST2600_I2CM_PKT_EN | AST2600_I2CM_RX_DMA_EN | AST2600_I2CM_RX_CMD;\n+\n+\tif (msg->flags & I2C_M_RECV_LEN)\n+\t\txfer_len = 1;\n+\telse if (xfer_len > AST2600_I2C_DMA_SIZE)\n+\t\txfer_len = AST2600_I2C_DMA_SIZE;\n+\telse if (i2c_bus->msgs_index + 1 == i2c_bus->msgs_count)\n+\t\tcmd |= CONTROLLER_TRIGGER_LAST_STOP;\n+\n+\twritel(AST2600_I2CM_SET_RX_DMA_LEN(xfer_len - 1), i2c_bus->reg_base + AST2600_I2CM_DMA_LEN);\n+\n+\tif (cmd & AST2600_I2CM_START_CMD)\n+\t\tcmd |= AST2600_I2CM_PKT_ADDR(msg->addr);\n+\n+\twritel(cmd, i2c_bus->reg_base + AST2600_I2CM_CMD_STS);\n+\n+\treturn 0;\n+}\n+\n+static int ast2600_i2c_setup_buff_rx(u32 cmd, struct ast2600_i2c_bus *i2c_bus)\n+{\n+\tstruct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];\n+\tint xfer_len = msg->len - i2c_bus->controller_xfer_cnt;\n+\n+\tcmd |= AST2600_I2CM_PKT_EN | AST2600_I2CM_RX_BUFF_EN | AST2600_I2CM_RX_CMD;\n+\n+\tif (cmd & AST2600_I2CM_START_CMD)\n+\t\tcmd |= AST2600_I2CM_PKT_ADDR(msg->addr);\n+\n+\tif (msg->flags & I2C_M_RECV_LEN) {\n+\t\tdev_dbg(i2c_bus->dev, \"smbus read\\n\");\n+\t\txfer_len = 1;\n+\t} else if (xfer_len > i2c_bus->buf_size) {\n+\t\txfer_len = i2c_bus->buf_size;\n+\t} else if (i2c_bus->msgs_index + 1 == i2c_bus->msgs_count) {\n+\t\tcmd |= CONTROLLER_TRIGGER_LAST_STOP;\n+\t}\n+\twritel(AST2600_I2CC_SET_RX_BUF_LEN(xfer_len), i2c_bus->reg_base + AST2600_I2CC_BUFF_CTRL);\n+\n+\twritel(cmd, i2c_bus->reg_base + AST2600_I2CM_CMD_STS);\n+\n+\treturn 0;\n+}\n+\n+static int ast2600_i2c_setup_byte_rx(u32 cmd, struct ast2600_i2c_bus *i2c_bus)\n+{\n+\tstruct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];\n+\n+\tcmd |= AST2600_I2CM_PKT_EN | AST2600_I2CM_RX_CMD;\n+\n+\tif (cmd & AST2600_I2CM_START_CMD)\n+\t\tcmd |= AST2600_I2CM_PKT_ADDR(msg->addr);\n+\n+\tif (msg->flags & I2C_M_RECV_LEN) {\n+\t\tdev_dbg(i2c_bus->dev, \"smbus read\\n\");\n+\t} else if ((i2c_bus->msgs_index + 1 == i2c_bus->msgs_count) &&\n+\t\t ((i2c_bus->controller_xfer_cnt + 1) == msg->len)) {\n+\t\tcmd |= CONTROLLER_TRIGGER_LAST_STOP;\n+\t}\n+\n+\twritel(cmd, i2c_bus->reg_base + AST2600_I2CM_CMD_STS);\n+\n+\treturn 0;\n+}\n+\n+static int ast2600_i2c_do_start(struct ast2600_i2c_bus *i2c_bus)\n+{\n+\tstruct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];\n+\n+\t/* send start */\n+\tdev_dbg(i2c_bus->dev, \"[%d] %s %d byte%s %s 0x%02x\\n\",\n+\t\ti2c_bus->msgs_index, str_read_write(msg->flags & I2C_M_RD),\n+\t\tmsg->len, str_plural(msg->len),\n+\t\tmsg->flags & I2C_M_RD ? \"from\" : \"to\", msg->addr);\n+\n+\tif (!i2c_bus->setup_rx || !i2c_bus->setup_tx)\n+\t\treturn -EINVAL;\n+\n+\ti2c_bus->controller_xfer_cnt = 0;\n+\ti2c_bus->buf_index = 0;\n+\n+\tif (msg->flags & I2C_M_RD)\n+\t\treturn i2c_bus->setup_rx(AST2600_I2CM_START_CMD, i2c_bus);\n+\n+\treturn i2c_bus->setup_tx(AST2600_I2CM_START_CMD, i2c_bus);\n+}\n+\n+static int ast2600_i2c_irq_err_to_errno(u32 irq_status)\n+{\n+\tif (irq_status & AST2600_I2CM_ARBIT_LOSS)\n+\t\treturn -EAGAIN;\n+\tif (irq_status & (AST2600_I2CM_SDA_DL_TO | AST2600_I2CM_SCL_LOW_TO))\n+\t\treturn -ETIMEDOUT;\n+\tif (irq_status & (AST2600_I2CM_ABNORMAL))\n+\t\treturn -EPROTO;\n+\n+\treturn 0;\n+}\n+\n+static void ast2600_i2c_controller_packet_irq(struct ast2600_i2c_bus *i2c_bus, u32 sts)\n+{\n+\tstruct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];\n+\tint xfer_len;\n+\tint i;\n+\n+\tsts &= ~AST2600_I2CM_PKT_DONE;\n+\twritel(AST2600_I2CM_PKT_DONE, i2c_bus->reg_base + AST2600_I2CM_ISR);\n+\tswitch (sts) {\n+\tcase AST2600_I2CM_PKT_ERROR:\n+\t\ti2c_bus->cmd_err = -EAGAIN;\n+\t\tcomplete(&i2c_bus->cmd_complete);\n+\t\tbreak;\n+\tcase AST2600_I2CM_PKT_ERROR | AST2600_I2CM_TX_NAK: /* a0 fix for issue */\n+\t\tfallthrough;\n+\tcase AST2600_I2CM_PKT_ERROR | AST2600_I2CM_TX_NAK | AST2600_I2CM_NORMAL_STOP:\n+\t\ti2c_bus->cmd_err = -ENXIO;\n+\t\tcomplete(&i2c_bus->cmd_complete);\n+\t\tbreak;\n+\tcase AST2600_I2CM_NORMAL_STOP:\n+\t\t/* write 0 byte only have stop isr */\n+\t\ti2c_bus->msgs_index++;\n+\t\tif (i2c_bus->msgs_index < i2c_bus->msgs_count) {\n+\t\t\tif (ast2600_i2c_do_start(i2c_bus)) {\n+\t\t\t\ti2c_bus->cmd_err = -ENOMEM;\n+\t\t\t\tcomplete(&i2c_bus->cmd_complete);\n+\t\t\t}\n+\t\t} else {\n+\t\t\ti2c_bus->cmd_err = i2c_bus->msgs_index;\n+\t\t\tcomplete(&i2c_bus->cmd_complete);\n+\t\t}\n+\t\tbreak;\n+\tcase AST2600_I2CM_TX_ACK:\n+\tcase AST2600_I2CM_TX_ACK | AST2600_I2CM_NORMAL_STOP:\n+\t\tif (i2c_bus->mode == DMA_MODE)\n+\t\t\txfer_len = AST2600_I2C_GET_TX_DMA_LEN(readl(i2c_bus->reg_base +\n+\t\t\t\t\t\t\t AST2600_I2CM_DMA_LEN_STS));\n+\t\telse if (i2c_bus->mode == BUFF_MODE)\n+\t\t\txfer_len = AST2600_I2CC_GET_TX_BUF_LEN(readl(i2c_bus->reg_base +\n+\t\t\t\t\t\t\t AST2600_I2CC_BUFF_CTRL));\n+\t\telse\n+\t\t\txfer_len = 1;\n+\n+\t\ti2c_bus->controller_xfer_cnt += xfer_len;\n+\n+\t\tif (i2c_bus->controller_xfer_cnt == msg->len) {\n+\t\t\ti2c_bus->msgs_index++;\n+\t\t\tif (i2c_bus->msgs_index == i2c_bus->msgs_count) {\n+\t\t\t\ti2c_bus->cmd_err = i2c_bus->msgs_index;\n+\t\t\t\tcomplete(&i2c_bus->cmd_complete);\n+\t\t\t} else {\n+\t\t\t\tif (ast2600_i2c_do_start(i2c_bus)) {\n+\t\t\t\t\ti2c_bus->cmd_err = -ENOMEM;\n+\t\t\t\t\tcomplete(&i2c_bus->cmd_complete);\n+\t\t\t\t}\n+\t\t\t}\n+\t\t} else {\n+\t\t\ti2c_bus->setup_tx(0, i2c_bus);\n+\t\t}\n+\t\tbreak;\n+\tcase AST2600_I2CM_RX_DONE:\n+\tcase AST2600_I2CM_RX_DONE | AST2600_I2CM_NORMAL_STOP:\n+\t\t/* do next rx */\n+\t\tif (i2c_bus->mode == DMA_MODE) {\n+\t\t\txfer_len = AST2600_I2C_GET_RX_DMA_LEN(readl(i2c_bus->reg_base +\n+\t\t\t\t\t\t\t\t AST2600_I2CM_DMA_LEN_STS));\n+\t\t\tmemcpy(&msg->buf[i2c_bus->controller_xfer_cnt],\n+\t\t\t i2c_bus->controller_dma_buf, xfer_len);\n+\t\t} else if (i2c_bus->mode == BUFF_MODE) {\n+\t\t\txfer_len = AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +\n+\t\t\t\t\t\t\t\t AST2600_I2CC_BUFF_CTRL));\n+\t\t\tfor (i = 0; i < xfer_len; i++)\n+\t\t\t\tmsg->buf[i2c_bus->controller_xfer_cnt + i] =\n+\t\t\t\t\treadb(i2c_bus->buf_base + 0x10 + i);\n+\t\t} else {\n+\t\t\txfer_len = 1;\n+\t\t\tmsg->buf[i2c_bus->controller_xfer_cnt] =\n+\t\t\t\tAST2600_I2CC_GET_RX_BUFF(readl(i2c_bus->reg_base +\n+\t\t\t\t\t\t AST2600_I2CC_STS_AND_BUFF));\n+\t\t}\n+\n+\t\tif (msg->flags & I2C_M_RECV_LEN) {\n+\t\t\tu8 recv_len = AST2600_I2CC_GET_RX_BUFF(readl(i2c_bus->reg_base\n+\t\t\t\t\t\t + AST2600_I2CC_STS_AND_BUFF));\n+\t\t\tmsg->len = min_t(unsigned int, recv_len, I2C_SMBUS_BLOCK_MAX);\n+\t\t\tmsg->len += ((msg->flags & I2C_CLIENT_PEC) ? 2 : 1);\n+\t\t\tmsg->flags &= ~I2C_M_RECV_LEN;\n+\t\t\tif (!recv_len)\n+\t\t\t\ti2c_bus->controller_xfer_cnt = 0;\n+\t\t\telse\n+\t\t\t\ti2c_bus->controller_xfer_cnt = 1;\n+\t\t} else {\n+\t\t\ti2c_bus->controller_xfer_cnt += xfer_len;\n+\t\t}\n+\n+\t\tif (i2c_bus->controller_xfer_cnt == msg->len) {\n+\t\t\ti2c_bus->msgs_index++;\n+\t\t\tif (i2c_bus->msgs_index == i2c_bus->msgs_count) {\n+\t\t\t\ti2c_bus->cmd_err = i2c_bus->msgs_index;\n+\t\t\t\tcomplete(&i2c_bus->cmd_complete);\n+\t\t\t} else {\n+\t\t\t\tif (ast2600_i2c_do_start(i2c_bus)) {\n+\t\t\t\t\ti2c_bus->cmd_err = -ENOMEM;\n+\t\t\t\t\tcomplete(&i2c_bus->cmd_complete);\n+\t\t\t\t}\n+\t\t\t}\n+\t\t} else {\n+\t\t\ti2c_bus->setup_rx(0, i2c_bus);\n+\t\t}\n+\t\tbreak;\n+\tdefault:\n+\t\tdev_dbg(i2c_bus->dev, \"unhandled sts %x\\n\", sts);\n+\t\tbreak;\n+\t}\n+}\n+\n+static int ast2600_i2c_controller_irq(struct ast2600_i2c_bus *i2c_bus)\n+{\n+\tu32 sts = readl(i2c_bus->reg_base + AST2600_I2CM_ISR);\n+\tu32 ctrl;\n+\n+\tsts &= ~AST2600_I2CM_SMBUS_ALERT;\n+\n+\tif (sts & AST2600_I2CM_BUS_RECOVER_FAIL) {\n+\t\twritel(AST2600_I2CM_BUS_RECOVER_FAIL, i2c_bus->reg_base + AST2600_I2CM_ISR);\n+\t\tctrl = readl(i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);\n+\t\twritel(0, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);\n+\t\twritel(ctrl, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);\n+\t\ti2c_bus->cmd_err = -EPROTO;\n+\t\tcomplete(&i2c_bus->cmd_complete);\n+\t\treturn 1;\n+\t}\n+\n+\tif (sts & AST2600_I2CM_BUS_RECOVER) {\n+\t\twritel(AST2600_I2CM_BUS_RECOVER, i2c_bus->reg_base + AST2600_I2CM_ISR);\n+\t\ti2c_bus->cmd_err = 0;\n+\t\tcomplete(&i2c_bus->cmd_complete);\n+\t\treturn 1;\n+\t}\n+\n+\ti2c_bus->cmd_err = ast2600_i2c_irq_err_to_errno(sts);\n+\tif (i2c_bus->cmd_err) {\n+\t\twritel(AST2600_I2CM_PKT_DONE, i2c_bus->reg_base + AST2600_I2CM_ISR);\n+\t\tcomplete(&i2c_bus->cmd_complete);\n+\t\treturn 1;\n+\t}\n+\n+\tif (sts & AST2600_I2CM_PKT_DONE) {\n+\t\tast2600_i2c_controller_packet_irq(i2c_bus, sts);\n+\t\treturn 1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static irqreturn_t ast2600_i2c_bus_irq(int irq, void *dev_id)\n+{\n+\tstruct ast2600_i2c_bus *i2c_bus = dev_id;\n+\n+\treturn IRQ_RETVAL(ast2600_i2c_controller_irq(i2c_bus));\n+}\n+\n+static int ast2600_i2c_controller_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)\n+{\n+\tstruct ast2600_i2c_bus *i2c_bus = i2c_get_adapdata(adap);\n+\tunsigned long timeout;\n+\tint ret;\n+\n+\tif (!i2c_bus->multi_master &&\n+\t (readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF) & AST2600_I2CC_BUS_BUSY_STS)) {\n+\t\tret = ast2600_i2c_recover_bus(i2c_bus);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\ti2c_bus->cmd_err = 0;\n+\ti2c_bus->msgs = msgs;\n+\ti2c_bus->msgs_index = 0;\n+\ti2c_bus->msgs_count = num;\n+\treinit_completion(&i2c_bus->cmd_complete);\n+\tret = ast2600_i2c_do_start(i2c_bus);\n+\tif (ret)\n+\t\tgoto controller_out;\n+\ttimeout = wait_for_completion_timeout(&i2c_bus->cmd_complete, i2c_bus->adap.timeout);\n+\tif (timeout == 0) {\n+\t\tu32 ctrl = readl(i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);\n+\n+\t\tdev_dbg(i2c_bus->dev, \"timeout isr[%x], sts[%x]\\n\",\n+\t\t\treadl(i2c_bus->reg_base + AST2600_I2CM_ISR),\n+\t\t\treadl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF));\n+\t\twritel(ctrl & ~AST2600_I2CC_MASTER_EN, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);\n+\t\twritel(ctrl, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);\n+\n+\t\t/*\n+\t\t * A slave holding SCL low can stall the transfer and trigger\n+\t\t * a master timeout. In multi-master mode, attempt bus recovery\n+\t\t * if the bus is still busy.\n+\t\t */\n+\t\tif (i2c_bus->multi_master &&\n+\t\t (readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF) &\n+\t\t AST2600_I2CC_BUS_BUSY_STS))\n+\t\t\tast2600_i2c_recover_bus(i2c_bus);\n+\t\tret = -ETIMEDOUT;\n+\t} else {\n+\t\tret = i2c_bus->cmd_err;\n+\t}\n+\n+\tdev_dbg(i2c_bus->dev, \"bus%d-m: %d end\\n\", i2c_bus->adap.nr, i2c_bus->cmd_err);\n+\n+controller_out:\n+\treturn ret;\n+}\n+\n+static int ast2600_i2c_init(struct ast2600_i2c_bus *i2c_bus)\n+{\n+\tu32 fun_ctrl = AST2600_I2CC_BUS_AUTO_RELEASE | AST2600_I2CC_MASTER_EN;\n+\n+\t/* I2C Reset */\n+\twritel(0, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);\n+\n+\tif (!i2c_bus->multi_master)\n+\t\tfun_ctrl |= AST2600_I2CC_MULTI_MASTER_DIS;\n+\n+\t/* Enable Controller Mode */\n+\twritel(fun_ctrl, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);\n+\t/* disable target address */\n+\twritel(0, i2c_bus->reg_base + AST2600_I2CS_ADDR_CTRL);\n+\n+\t/* Set AC Timing */\n+\tast2600_i2c_ac_timing_config(i2c_bus);\n+\n+\tif (i2c_bus->dma_available) {\n+\t\ti2c_bus->controller_dma_buf =\n+\t\t\tdmam_alloc_coherent(i2c_bus->dev, AST2600_I2C_DMA_SIZE,\n+\t\t\t\t\t &i2c_bus->controller_dma_addr, GFP_KERNEL);\n+\t\tif (!i2c_bus->controller_dma_buf)\n+\t\t\treturn -ENOMEM;\n+\t\twritel(i2c_bus->controller_dma_addr,\n+\t\t i2c_bus->reg_base + AST2600_I2CM_TX_DMA);\n+\t\twritel(i2c_bus->controller_dma_addr,\n+\t\t i2c_bus->reg_base + AST2600_I2CM_RX_DMA);\n+\t}\n+\n+\t/* Clear Interrupt */\n+\twritel(GENMASK(27, 0), i2c_bus->reg_base + AST2600_I2CM_ISR);\n+\n+\treturn 0;\n+}\n+\n+static u32 ast2600_i2c_functionality(struct i2c_adapter *adap)\n+{\n+\treturn I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA;\n+}\n+\n+static const struct i2c_algorithm i2c_ast2600_algorithm = {\n+\t.xfer = ast2600_i2c_controller_xfer,\n+\t.functionality = ast2600_i2c_functionality,\n+};\n+\n+static void ast2600_i2c_set_xfer_mode(struct ast2600_i2c_bus *i2c_bus,\n+\t\t\t\t enum xfer_mode mode)\n+{\n+\ti2c_bus->mode = mode;\n+\n+\tswitch (mode) {\n+\tcase DMA_MODE:\n+\t\ti2c_bus->setup_tx = ast2600_i2c_setup_dma_tx;\n+\t\ti2c_bus->setup_rx = ast2600_i2c_setup_dma_rx;\n+\t\tbreak;\n+\tcase BYTE_MODE:\n+\t\ti2c_bus->setup_tx = ast2600_i2c_setup_byte_tx;\n+\t\ti2c_bus->setup_rx = ast2600_i2c_setup_byte_rx;\n+\t\tbreak;\n+\tcase BUFF_MODE:\n+\tdefault:\n+\t\ti2c_bus->setup_tx = ast2600_i2c_setup_buff_tx;\n+\t\ti2c_bus->setup_rx = ast2600_i2c_setup_buff_rx;\n+\t\tbreak;\n+\t}\n+}\n+\n+static int ast2600_i2c_xfer_mode_parse(const char *buf, enum xfer_mode *mode)\n+{\n+\tif (sysfs_streq(buf, \"byte\")) {\n+\t\t*mode = BYTE_MODE;\n+\t\treturn 0;\n+\t}\n+\n+\tif (sysfs_streq(buf, \"buffer\")) {\n+\t\t*mode = BUFF_MODE;\n+\t\treturn 0;\n+\t}\n+\n+\tif (sysfs_streq(buf, \"dma\")) {\n+\t\t*mode = DMA_MODE;\n+\t\treturn 0;\n+\t}\n+\n+\treturn -EINVAL;\n+}\n+\n+static int ast2600_i2c_xfer_mode_check(struct ast2600_i2c_bus *i2c_bus,\n+\t\t\t\t\tenum xfer_mode mode)\n+{\n+\tif (mode == BUFF_MODE && !i2c_bus->buf_base)\n+\t\treturn -EINVAL;\n+\tif (mode == DMA_MODE && !i2c_bus->dma_available)\n+\t\treturn -EINVAL;\n+\treturn 0;\n+}\n+\n+static const char *ast2600_i2c_xfer_mode_name(enum xfer_mode mode)\n+{\n+\tswitch (mode) {\n+\tcase BYTE_MODE:\n+\t\treturn \"byte\";\n+\tcase DMA_MODE:\n+\t\treturn \"dma\";\n+\tcase BUFF_MODE:\n+\tdefault:\n+\t\treturn \"buffer\";\n+\t}\n+}\n+\n+static ssize_t xfer_mode_show(struct device *dev, struct device_attribute *attr, char *buf)\n+{\n+\tstruct ast2600_i2c_bus *i2c_bus = dev_get_drvdata(dev);\n+\n+\treturn sysfs_emit(buf, \"%s\\n\", ast2600_i2c_xfer_mode_name(i2c_bus->mode));\n+}\n+\n+static ssize_t xfer_mode_store(struct device *dev,\n+\t\t\t struct device_attribute *attr,\n+\t\t\t const char *buf, size_t count)\n+{\n+\tstruct ast2600_i2c_bus *i2c_bus = dev_get_drvdata(dev);\n+\tenum xfer_mode mode;\n+\tint ret;\n+\n+\tret = ast2600_i2c_xfer_mode_parse(buf, &mode);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = ast2600_i2c_xfer_mode_check(i2c_bus, mode);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\ti2c_lock_bus(&i2c_bus->adap, I2C_LOCK_ROOT_ADAPTER);\n+\tast2600_i2c_set_xfer_mode(i2c_bus, mode);\n+\ti2c_unlock_bus(&i2c_bus->adap, I2C_LOCK_ROOT_ADAPTER);\n+\n+\treturn count;\n+}\n+\n+static DEVICE_ATTR_RW(xfer_mode);\n+\n+static int ast2600_i2c_probe(struct platform_device *pdev)\n+{\n+\tstruct device *dev = &pdev->dev;\n+\tstruct ast2600_i2c_bus *i2c_bus;\n+\tvoid __iomem *buf_base;\n+\tstruct reset_control *rst;\n+\tstruct resource *res;\n+\tu32 global_ctrl;\n+\tint ret;\n+\n+\tif (!device_property_present(dev, \"aspeed,global-regs\"))\n+\t\treturn -ENODEV;\n+\n+\ti2c_bus = devm_kzalloc(dev, sizeof(*i2c_bus), GFP_KERNEL);\n+\tif (!i2c_bus)\n+\t\treturn -ENOMEM;\n+\n+\ti2c_bus->reg_base = devm_platform_ioremap_resource(pdev, 0);\n+\tif (IS_ERR(i2c_bus->reg_base))\n+\t\treturn PTR_ERR(i2c_bus->reg_base);\n+\n+\trst = devm_reset_control_get_shared_deasserted(dev, NULL);\n+\tif (IS_ERR(rst))\n+\t\treturn dev_err_probe(dev, PTR_ERR(rst), \"Missing reset ctrl\\n\");\n+\n+\ti2c_bus->global_regs =\n+\t\tsyscon_regmap_lookup_by_phandle(dev_of_node(dev), \"aspeed,global-regs\");\n+\tif (IS_ERR(i2c_bus->global_regs))\n+\t\treturn PTR_ERR(i2c_bus->global_regs);\n+\n+\tregmap_read(i2c_bus->global_regs, AST2600_I2CG_CTRL, &global_ctrl);\n+\tif ((global_ctrl & AST2600_GLOBAL_INIT) != AST2600_GLOBAL_INIT) {\n+\t\tregmap_write(i2c_bus->global_regs, AST2600_I2CG_CTRL, AST2600_GLOBAL_INIT);\n+\t\tregmap_write(i2c_bus->global_regs, AST2600_I2CG_CLK_DIV_CTRL, I2CCG_DIV_CTRL);\n+\t}\n+\n+\ti2c_bus->dev = dev;\n+\ti2c_bus->multi_master = device_property_read_bool(dev, \"multi-master\");\n+\ti2c_bus->dma_available = device_property_read_bool(dev, \"aspeed,enable-dma\");\n+\n+\tbuf_base = devm_platform_get_and_ioremap_resource(pdev, 1, &res);\n+\tif (!IS_ERR(buf_base)) {\n+\t\ti2c_bus->buf_base = buf_base;\n+\t\ti2c_bus->buf_size = resource_size(res) / 2;\n+\t}\n+\n+\tenum xfer_mode mode;\n+\n+\tif (i2c_bus->dma_available)\n+\t\tmode = DMA_MODE;\n+\telse if (i2c_bus->buf_base)\n+\t\tmode = BUFF_MODE;\n+\telse\n+\t\tmode = BYTE_MODE;\n+\n+\tast2600_i2c_set_xfer_mode(i2c_bus, mode);\n+\n+\t/*\n+\t * i2c timeout counter: use base clk4 1Mhz,\n+\t * per unit: 1/(1000/1024) = 1024us\n+\t */\n+\tret = device_property_read_u32(dev, \"i2c-scl-clk-low-timeout-us\", &i2c_bus->timeout);\n+\tif (!ret)\n+\t\ti2c_bus->timeout = DIV_ROUND_UP(i2c_bus->timeout, 1024);\n+\n+\tinit_completion(&i2c_bus->cmd_complete);\n+\n+\ti2c_bus->irq = platform_get_irq(pdev, 0);\n+\tif (i2c_bus->irq < 0)\n+\t\treturn i2c_bus->irq;\n+\n+\tplatform_set_drvdata(pdev, i2c_bus);\n+\n+\ti2c_bus->clk = devm_clk_get(i2c_bus->dev, NULL);\n+\tif (IS_ERR(i2c_bus->clk))\n+\t\treturn dev_err_probe(i2c_bus->dev, PTR_ERR(i2c_bus->clk), \"Can't get clock\\n\");\n+\n+\ti2c_bus->apb_clk = clk_get_rate(i2c_bus->clk);\n+\n+\ti2c_parse_fw_timings(i2c_bus->dev, &i2c_bus->timing_info, true);\n+\n+\t/* Initialize the I2C adapter */\n+\ti2c_bus->adap.owner = THIS_MODULE;\n+\ti2c_bus->adap.algo = &i2c_ast2600_algorithm;\n+\ti2c_bus->adap.retries = 0;\n+\ti2c_bus->adap.dev.parent = i2c_bus->dev;\n+\tdevice_set_node(&i2c_bus->adap.dev, dev_fwnode(dev));\n+\ti2c_bus->adap.algo_data = i2c_bus;\n+\tstrscpy(i2c_bus->adap.name, pdev->name);\n+\ti2c_set_adapdata(&i2c_bus->adap, i2c_bus);\n+\n+\tret = ast2600_i2c_init(i2c_bus);\n+\tif (ret < 0)\n+\t\treturn dev_err_probe(dev, ret, \"Unable to initialize i2c %d\\n\", ret);\n+\n+\tret = devm_request_irq(dev, i2c_bus->irq, ast2600_i2c_bus_irq, 0,\n+\t\t\t dev_name(dev), i2c_bus);\n+\tif (ret < 0) {\n+\t\tret = dev_err_probe(dev, ret, \"Unable to request irq %d\\n\",\n+\t\t\t\t i2c_bus->irq);\n+\t\tgoto err;\n+\t}\n+\n+\twritel(AST2600_I2CM_PKT_DONE | AST2600_I2CM_BUS_RECOVER,\n+\t i2c_bus->reg_base + AST2600_I2CM_IER);\n+\n+\tret = sysfs_create_file(&dev->kobj, &dev_attr_xfer_mode.attr);\n+\tif (ret)\n+\t\tgoto err;\n+\n+\tret = devm_i2c_add_adapter(dev, &i2c_bus->adap);\n+\tif (ret) {\n+\t\tsysfs_remove_file(&dev->kobj, &dev_attr_xfer_mode.attr);\n+\t\tgoto err;\n+\t}\n+\n+\treturn 0;\n+\n+err:\n+\twritel(0, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);\n+\twritel(0, i2c_bus->reg_base + AST2600_I2CM_IER);\n+\treturn ret;\n+}\n+\n+static void ast2600_i2c_remove(struct platform_device *pdev)\n+{\n+\tstruct ast2600_i2c_bus *i2c_bus = platform_get_drvdata(pdev);\n+\n+\tsysfs_remove_file(&pdev->dev.kobj, &dev_attr_xfer_mode.attr);\n+\n+\t/* Disable everything. */\n+\twritel(0, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);\n+\twritel(0, i2c_bus->reg_base + AST2600_I2CM_IER);\n+}\n+\n+static const struct of_device_id ast2600_i2c_of_match[] = {\n+\t{ .compatible = \"aspeed,ast2600-i2c-bus\" },\n+\t{ }\n+};\n+MODULE_DEVICE_TABLE(of, ast2600_i2c_of_match);\n+\n+static struct platform_driver ast2600_i2c_driver = {\n+\t.probe\t\t= ast2600_i2c_probe,\n+\t.remove\t\t= ast2600_i2c_remove,\n+\t.driver\t\t= {\n+\t\t.name\t\t= \"ast2600-i2c\",\n+\t\t.of_match_table\t= ast2600_i2c_of_match,\n+\t},\n+};\n+module_platform_driver(ast2600_i2c_driver);\n+\n+MODULE_AUTHOR(\"Ryan Chen <ryan_chen@aspeedtech.com>\");\n+MODULE_DESCRIPTION(\"ASPEED AST2600 I2C Controller Driver\");\n+MODULE_LICENSE(\"GPL\");\n", "prefixes": [ "v28", "3/4" ] }