{"id":2175602,"url":"http://patchwork.ozlabs.org/api/1.0/patches/2175602/?format=json","project":{"id":35,"url":"http://patchwork.ozlabs.org/api/1.0/projects/35/?format=json","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":""},"msgid":"<20251218151509.361617-6-heikki.krogerus@linux.intel.com>","date":"2025-12-18T15:15:04","name":"[v2,5/6] i2c: designware: Enable mode swapping","commit_ref":null,"pull_url":null,"state":"accepted","archived":false,"hash":"e67aba1adc8d9d295b44288547c207292b5229cb","submitter":{"id":23674,"url":"http://patchwork.ozlabs.org/api/1.0/people/23674/?format=json","name":"Heikki Krogerus","email":"heikki.krogerus@linux.intel.com"},"delegate":{"id":149066,"url":"http://patchwork.ozlabs.org/api/1.0/users/149066/?format=json","username":"cazzacarna","first_name":"Andi","last_name":"Shyti","email":"andi.shyti@kernel.org"},"mbox":"http://patchwork.ozlabs.org/project/linux-i2c/patch/20251218151509.361617-6-heikki.krogerus@linux.intel.com/mbox/","series":[{"id":485867,"url":"http://patchwork.ozlabs.org/api/1.0/series/485867/?format=json","date":"2025-12-18T15:14:59","name":"i2c: designware: Enable mode swapping","version":2,"mbox":"http://patchwork.ozlabs.org/series/485867/mbox/"}],"check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2175602/checks/","tags":{},"headers":{"Return-Path":"\n <linux-i2c+bounces-14647-incoming=patchwork.ozlabs.org@vger.kernel.org>","X-Original-To":["incoming@patchwork.ozlabs.org","linux-i2c@vger.kernel.org"],"Delivered-To":"patchwork-incoming@legolas.ozlabs.org","Authentication-Results":["legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256\n header.s=Intel header.b=aKDLHEi9;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=2600:3c0a:e001:db::12fc:5321; helo=sea.lore.kernel.org;\n envelope-from=linux-i2c+bounces-14647-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)","smtp.subspace.kernel.org;\n\tdkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com\n header.b=\"aKDLHEi9\"","smtp.subspace.kernel.org;\n arc=none smtp.client-ip=192.198.163.9","smtp.subspace.kernel.org;\n dmarc=pass (p=none dis=none) header.from=linux.intel.com","smtp.subspace.kernel.org;\n spf=pass smtp.mailfrom=linux.intel.com"],"Received":["from sea.lore.kernel.org (sea.lore.kernel.org\n [IPv6:2600:3c0a:e001:db::12fc:5321])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4dXDnJ3JcZz1y2F\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 19 Dec 2025 02:17:56 +1100 (AEDT)","from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby sea.lore.kernel.org (Postfix) with ESMTP id E30BD30B01AF\n\tfor <incoming@patchwork.ozlabs.org>; Thu, 18 Dec 2025 15:15:43 +0000 (UTC)","from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id A8CA334C821;\n\tThu, 18 Dec 2025 15:15:33 +0000 (UTC)","from mgamail.intel.com (mgamail.intel.com [192.198.163.9])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby smtp.subspace.kernel.org (Postfix) with ESMTPS id 059C434B697;\n\tThu, 18 Dec 2025 15:15:24 +0000 (UTC)","from orviesa010.jf.intel.com ([10.64.159.150])\n  by fmvoesa103.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384;\n 18 Dec 2025 07:15:23 -0800","from black.igk.intel.com ([10.91.253.5])\n  by orviesa010.jf.intel.com with ESMTP; 18 Dec 2025 07:15:22 -0800"],"ARC-Seal":"i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1766070929; cv=none;\n b=XoD2OfwpGih0qaxvxusy556CfzW0cgIJdBBIpEQ+dkLmok9jASyQ/+66ReFEZOZwjHP1F4UCvjrTOiyR9kCmhqnX2PJKuoO9VxL91ysSnkxhIx9a434rElCyBM4TFmXYq9w4KGft7KhVo0mSJHjZCdjry5hieTzEVr8os3q0y/Y=","ARC-Message-Signature":"i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1766070929; c=relaxed/simple;\n\tbh=KRPlm8c8KvM5gAcqF35x0wKKB1qLV06f2T+q0/pi1Z8=;\n\th=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References:\n\t MIME-Version;\n b=KsoLpsqHfG6Yl+ekFfilb/BRfKQ+V57lPQneiINdBzSfUg+o09li5lCwAWNyeDG9k9VTvOXJfMViNy2vTC5k6ghhWH1MnlFz9y3lh1p6+dVBaYZeiniTIuc76nRtbphMo6BhBqvt+TOXZvpSK9AGYKwl89wqgYyTmRa1XyAky0E=","ARC-Authentication-Results":"i=1; smtp.subspace.kernel.org;\n dmarc=pass (p=none dis=none) header.from=linux.intel.com;\n spf=pass smtp.mailfrom=linux.intel.com;\n dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com\n header.b=aKDLHEi9; arc=none smtp.client-ip=192.198.163.9","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple;\n  d=intel.com; i=@intel.com; q=dns/txt; s=Intel;\n  t=1766070926; x=1797606926;\n  h=from:to:cc:subject:date:message-id:in-reply-to:\n   references:mime-version:content-transfer-encoding;\n  bh=KRPlm8c8KvM5gAcqF35x0wKKB1qLV06f2T+q0/pi1Z8=;\n  b=aKDLHEi9IW1VSuQsFVENl0gZXxJnLkoV0bABcNSwIpN8o8FHdfeUjvB6\n   L6wLe8Se8s6BFxs/PSoYDXTXMU2fEKAld60bTAIBZ9VOOt11zDaRdo6Nv\n   aKYu6bu5EYavt5g53qn2zaSTedLfWZImEZmATW1bqO6yvdYjLLQ6QLVt8\n   GaC2jQHfcbXiQw/aSO26AzXvkvyyTqzAwUr19dcL1X25PsqwfF/GJziSj\n   IFRQJJuFpTH+QVu4Hr1FOg+C0XLHy3VSOjrP1yfYQiOklPHlCGKbuqz+f\n   pi6qLz4f3r5sipcZtDedq9d4IEBo9bdcqikyToV2y8j6LzI9NYeDxY95/\n   w==;","X-CSE-ConnectionGUID":["LE9+kgkAR/CjheJSAQQ1fA==","kW8pg7rzQBmzGZRvp5RFTw=="],"X-CSE-MsgGUID":["sW2EQWtqShanGklK2sMONA==","HT2ugBo5SjmBDpNpCxV9iQ=="],"X-IronPort-AV":["E=McAfee;i=\"6800,10657,11646\"; a=\"78739557\"","E=Sophos;i=\"6.21,158,1763452800\";\n   d=\"scan'208\";a=\"78739557\"","E=Sophos;i=\"6.21,158,1763452800\";\n   d=\"scan'208\";a=\"197857512\""],"X-ExtLoop1":"1","From":"Heikki Krogerus <heikki.krogerus@linux.intel.com>","To":"Andi Shyti <andi.shyti@kernel.org>,\n\tMika Westerberg <mika.westerberg@linux.intel.com>","Cc":"Andy Shevchenko <andriy.shevchenko@linux.intel.com>,\n\tJan Dabros <jsd@semihalf.com>,\n\tRaag Jadav <raag.jadav@intel.com>,\n\tlinux-i2c@vger.kernel.org,\n\tlinux-kernel@vger.kernel.org","Subject":"[PATCH v2 5/6] i2c: designware: Enable mode swapping","Date":"Thu, 18 Dec 2025 16:15:04 +0100","Message-ID":"<20251218151509.361617-6-heikki.krogerus@linux.intel.com>","X-Mailer":"git-send-email 2.50.1","In-Reply-To":"<20251218151509.361617-1-heikki.krogerus@linux.intel.com>","References":"<20251218151509.361617-1-heikki.krogerus@linux.intel.com>","Precedence":"bulk","X-Mailing-List":"linux-i2c@vger.kernel.org","List-Id":"<linux-i2c.vger.kernel.org>","List-Subscribe":"<mailto:linux-i2c+subscribe@vger.kernel.org>","List-Unsubscribe":"<mailto:linux-i2c+unsubscribe@vger.kernel.org>","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit"},"content":"The DesignWare I2C can not be operated as I2C master and\nI2C slave simultaneously, but that does not actually mean\nmaster and slave modes can not be supported at the same\ntime. It just means an explicit mode swap needs to be\nexecuted when the mode is changed. The DesignWare I2C\ndocumentation actually describes a couple of cases where the\nmode is excepted to be changed.\n\nThe I2C master will now always be supported. Both modes are\nnow always configured in i2c_dw_configure(), but the slave\nmode will continue to be available only when the Kconfig\noption I2C_SLAVE is enabled.\n\nThe driver will now start in master mode and then swap to\nslave mode when a slave device is registered. After a slave\ndevice is registered, the controller is swapped to master\nmode when a transfer in master mode is started and then back\nto slave mode again after the transfer is completed.\n\nThe DesignWare I2C can now be used with protocols such as\nMCTP (drivers/net/mctp/mctp-i2c.c) and IPMI\n(drivers/char/ipmi/) that require support for both I2C\nmaster and I2C slave. It is now also possible to support the\nSMBus Host Notification Protocol as I2C master if needed.\n\nSigned-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>\n---\n drivers/i2c/busses/i2c-designware-common.c | 50 +++++++++++++++-------\n drivers/i2c/busses/i2c-designware-core.h   |  9 ++--\n drivers/i2c/busses/i2c-designware-master.c |  6 ++-\n drivers/i2c/busses/i2c-designware-slave.c  | 35 +++++++--------\n 4 files changed, 57 insertions(+), 43 deletions(-)","diff":"diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c\nindex 8e99549b37a3..aac7b1f4710f 100644\n--- a/drivers/i2c/busses/i2c-designware-common.c\n+++ b/drivers/i2c/busses/i2c-designware-common.c\n@@ -358,21 +358,25 @@ static inline u32 i2c_dw_acpi_round_bus_speed(struct device *device) { return 0;\n \n #endif\t/* CONFIG_ACPI */\n \n-static void i2c_dw_configure_mode(struct dw_i2c_dev *dev)\n+static void i2c_dw_configure_mode(struct dw_i2c_dev *dev, int mode)\n {\n-\tswitch (dev->mode) {\n+\tswitch (mode) {\n \tcase DW_IC_MASTER:\n \t\tregmap_write(dev->map, DW_IC_TX_TL, dev->tx_fifo_depth / 2);\n \t\tregmap_write(dev->map, DW_IC_RX_TL, 0);\n \t\tregmap_write(dev->map, DW_IC_CON, dev->master_cfg);\n \t\tbreak;\n \tcase DW_IC_SLAVE:\n+\t\tdev->status = 0;\n \t\tregmap_write(dev->map, DW_IC_TX_TL, 0);\n \t\tregmap_write(dev->map, DW_IC_RX_TL, 0);\n \t\tregmap_write(dev->map, DW_IC_CON, dev->slave_cfg);\n+\t\tregmap_write(dev->map, DW_IC_SAR, dev->slave->addr);\n \t\tregmap_write(dev->map, DW_IC_INTR_MASK, DW_IC_INTR_SLAVE_MASK);\n+\t\t__i2c_dw_enable(dev);\n \t\tbreak;\n \tdefault:\n+\t\tWARN(1, \"Invalid mode %d\\n\", mode);\n \t\treturn;\n \t}\n }\n@@ -394,6 +398,31 @@ static void i2c_dw_write_timings(struct dw_i2c_dev *dev)\n \t}\n }\n \n+/**\n+ * i2c_dw_set_mode() - Select the controller mode of operation - master or slave\n+ * @dev: device private data\n+ * @mode: I2C mode of operation\n+ *\n+ * Configures the controller to operate in @mode. This function needs to be\n+ * called when ever a mode swap is required.\n+ *\n+ * Setting the slave mode does not have an effect before a slave device is\n+ * registered. So before the slave device is registered, the controller is kept\n+ * in master mode regardless of @mode.\n+ *\n+ * The controller must be disabled before this function is called.\n+ */\n+void i2c_dw_set_mode(struct dw_i2c_dev *dev, int mode)\n+{\n+\tif (mode == DW_IC_SLAVE && !dev->slave)\n+\t\tmode = DW_IC_MASTER;\n+\tif (dev->mode == mode)\n+\t\treturn;\n+\n+\ti2c_dw_configure_mode(dev, mode);\n+\tdev->mode = mode;\n+}\n+\n /**\n  * i2c_dw_init() - Initialize the DesignWare I2C hardware\n  * @dev: device private data\n@@ -420,14 +449,13 @@ int i2c_dw_init(struct dw_i2c_dev *dev)\n \t */\n \tregmap_write(dev->map, DW_IC_SMBUS_INTR_MASK, 0);\n \n-\tif (dev->mode == DW_IC_MASTER)\n-\t\ti2c_dw_write_timings(dev);\n+\ti2c_dw_write_timings(dev);\n \n \t/* Write SDA hold time if supported */\n \tif (dev->sda_hold_time)\n \t\tregmap_write(dev->map, DW_IC_SDA_HOLD, dev->sda_hold_time);\n \n-\ti2c_dw_configure_mode(dev);\n+\ti2c_dw_configure_mode(dev, dev->mode);\n \n \ti2c_dw_release_lock(dev);\n \n@@ -861,17 +889,7 @@ int i2c_dw_probe(struct dw_i2c_dev *dev)\n \tif (ret)\n \t\treturn ret;\n \n-\tswitch (dev->mode) {\n-\tcase DW_IC_SLAVE:\n-\t\tret = i2c_dw_probe_slave(dev);\n-\t\tbreak;\n-\tcase DW_IC_MASTER:\n-\t\tret = i2c_dw_probe_master(dev);\n-\t\tbreak;\n-\tdefault:\n-\t\tret = -EINVAL;\n-\t\tbreak;\n-\t}\n+\tret = i2c_dw_probe_master(dev);\n \tif (ret)\n \t\treturn ret;\n \ndiff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h\nindex 82465b134c34..5d783d585406 100644\n--- a/drivers/i2c/busses/i2c-designware-core.h\n+++ b/drivers/i2c/busses/i2c-designware-core.h\n@@ -387,26 +387,23 @@ int i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);\n \n #if IS_ENABLED(CONFIG_I2C_SLAVE)\n extern void i2c_dw_configure_slave(struct dw_i2c_dev *dev);\n-extern int i2c_dw_probe_slave(struct dw_i2c_dev *dev);\n irqreturn_t i2c_dw_isr_slave(struct dw_i2c_dev *dev);\n int i2c_dw_reg_slave(struct i2c_client *client);\n int i2c_dw_unreg_slave(struct i2c_client *client);\n #else\n static inline void i2c_dw_configure_slave(struct dw_i2c_dev *dev) { }\n-static inline int i2c_dw_probe_slave(struct dw_i2c_dev *dev) { return -EINVAL; }\n static inline irqreturn_t i2c_dw_isr_slave(struct dw_i2c_dev *dev) { return IRQ_NONE; }\n #endif\n \n static inline void i2c_dw_configure(struct dw_i2c_dev *dev)\n {\n-\tif (i2c_detect_slave_mode(dev->dev))\n-\t\ti2c_dw_configure_slave(dev);\n-\telse\n-\t\ti2c_dw_configure_master(dev);\n+\ti2c_dw_configure_slave(dev);\n+\ti2c_dw_configure_master(dev);\n }\n \n int i2c_dw_probe(struct dw_i2c_dev *dev);\n int i2c_dw_init(struct dw_i2c_dev *dev);\n+void i2c_dw_set_mode(struct dw_i2c_dev *dev, int mode);\n \n #if IS_ENABLED(CONFIG_I2C_DESIGNWARE_BAYTRAIL)\n int i2c_dw_baytrail_probe_lock_support(struct dw_i2c_dev *dev);\ndiff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c\nindex 33432bbaec1f..ba2ee526ecc6 100644\n--- a/drivers/i2c/busses/i2c-designware-master.c\n+++ b/drivers/i2c/busses/i2c-designware-master.c\n@@ -194,6 +194,8 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)\n \t/* Disable the adapter */\n \t__i2c_dw_disable(dev);\n \n+\ti2c_dw_set_mode(dev, DW_IC_MASTER);\n+\n \t/* If the slave address is ten bit address, enable 10BITADDR */\n \tif (msgs[dev->msg_write_idx].flags & I2C_M_TEN) {\n \t\tic_con = DW_IC_CON_10BITADDR_MASTER;\n@@ -831,6 +833,8 @@ i2c_dw_xfer_common(struct dw_i2c_dev *dev, struct i2c_msg msgs[], int num)\n \tret = -EIO;\n \n done:\n+\ti2c_dw_set_mode(dev, DW_IC_SLAVE);\n+\n \ti2c_dw_release_lock(dev);\n \n done_nolock:\n@@ -853,7 +857,7 @@ void i2c_dw_configure_master(struct dw_i2c_dev *dev)\n {\n \tstruct i2c_timings *t = &dev->timings;\n \n-\tdev->functionality = I2C_FUNC_10BIT_ADDR | DW_IC_DEFAULT_FUNCTIONALITY;\n+\tdev->functionality |= I2C_FUNC_10BIT_ADDR | DW_IC_DEFAULT_FUNCTIONALITY;\n \n \tdev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |\n \t\t\t  DW_IC_CON_RESTART_EN;\ndiff --git a/drivers/i2c/busses/i2c-designware-slave.c b/drivers/i2c/busses/i2c-designware-slave.c\nindex 9fc8faa33735..ad0d5fbfa6d5 100644\n--- a/drivers/i2c/busses/i2c-designware-slave.c\n+++ b/drivers/i2c/busses/i2c-designware-slave.c\n@@ -24,24 +24,25 @@\n int i2c_dw_reg_slave(struct i2c_client *slave)\n {\n \tstruct dw_i2c_dev *dev = i2c_get_adapdata(slave->adapter);\n+\tint ret;\n \n+\tif (!i2c_check_functionality(slave->adapter, I2C_FUNC_SLAVE))\n+\t\treturn -EOPNOTSUPP;\n \tif (dev->slave)\n \t\treturn -EBUSY;\n \tif (slave->flags & I2C_CLIENT_TEN)\n \t\treturn -EAFNOSUPPORT;\n-\tpm_runtime_get_sync(dev->dev);\n \n-\t/*\n-\t * Set slave address in the IC_SAR register,\n-\t * the address to which the DW_apb_i2c responds.\n-\t */\n+\tret = i2c_dw_acquire_lock(dev);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tpm_runtime_get_sync(dev->dev);\n \t__i2c_dw_disable_nowait(dev);\n-\tregmap_write(dev->map, DW_IC_SAR, slave->addr);\n \tdev->slave = slave;\n+\ti2c_dw_set_mode(dev, DW_IC_SLAVE);\n \n-\t__i2c_dw_enable(dev);\n-\n-\tdev->status = 0;\n+\ti2c_dw_release_lock(dev);\n \n \treturn 0;\n }\n@@ -54,6 +55,7 @@ int i2c_dw_unreg_slave(struct i2c_client *slave)\n \ti2c_dw_disable(dev);\n \tsynchronize_irq(dev->irq);\n \tdev->slave = NULL;\n+\ti2c_dw_set_mode(dev, DW_IC_MASTER);\n \tpm_runtime_put_sync_suspend(dev->dev);\n \n \treturn 0;\n@@ -176,23 +178,16 @@ irqreturn_t i2c_dw_isr_slave(struct dw_i2c_dev *dev)\n \n void i2c_dw_configure_slave(struct dw_i2c_dev *dev)\n {\n-\tdev->functionality = I2C_FUNC_SLAVE;\n+\tif (dev->flags & ACCESS_POLLING)\n+\t\treturn;\n+\n+\tdev->functionality |= I2C_FUNC_SLAVE;\n \n \tdev->slave_cfg = DW_IC_CON_RX_FIFO_FULL_HLD_CTRL |\n \t\t\t DW_IC_CON_RESTART_EN | DW_IC_CON_STOP_DET_IFADDRESSED;\n-\n-\tdev->mode = DW_IC_SLAVE;\n }\n EXPORT_SYMBOL_GPL(i2c_dw_configure_slave);\n \n-int i2c_dw_probe_slave(struct dw_i2c_dev *dev)\n-{\n-\tif (dev->flags & ACCESS_POLLING)\n-\t\treturn -EOPNOTSUPP;\n-\n-\treturn 0;\n-}\n-\n MODULE_AUTHOR(\"Luis Oliveira <lolivei@synopsys.com>\");\n MODULE_DESCRIPTION(\"Synopsys DesignWare I2C bus slave adapter\");\n MODULE_LICENSE(\"GPL v2\");\n","prefixes":["v2","5/6"]}