From nobody Thu Dec 25 01:29:29 2025 Received: from EUR04-VI1-obe.outbound.protection.outlook.com (mail-vi1eur04on2044.outbound.protection.outlook.com [40.107.8.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5C6484F215; Tue, 23 Jan 2024 21:26:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.8.44 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706045194; cv=fail; b=XFtvxXT6txMXsLHIftgcLgYDM8wVhsiTkZ8UoiviTriBdAAi+IX47AHmEeP7PFoEOHa3vbBUSGRXCANs2fsrER5UKnacXWpRPqktnN6gd9srGnDUwJQPbxYYzOPgqw7y92XbNtNE2hpYCSTQ4xs0so4+vj2BNVuGwYSAG1mwmYQ= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706045194; c=relaxed/simple; bh=aFPOfC8o6TVPkgK9tJDSz3OHkxkHBGqRxh2jknv+gHs=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: Content-Type:MIME-Version; b=VlbHCcBzIWTL7mwYuDLHvUJ5yr/19zjYI7rUWSKZkstnotfwXVJFrD82GpRaTaWq1+yc3vjFwUmopqFkmN7mqD8ogr/LCj0tMNpfhi24ZC0c99K2Whh4opLzgUWissk1+KVx5uygi0eMNdA8SfhV+oN1buGB52gZMKo5XLkvJTc= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com; spf=pass smtp.mailfrom=nxp.com; dkim=pass (1024-bit key) header.d=nxp.com header.i=@nxp.com header.b=UC/9zRjG; arc=fail smtp.client-ip=40.107.8.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=nxp.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=nxp.com header.i=@nxp.com header.b="UC/9zRjG" ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=YulzftrDHJay0q7oxkXvx2yOE7y7HqKQeXe7bsjf15mNuoGAPQNF3Kk5fTHVHvDBEPmPuY4MWuywzXaOQKP2BYIRVagaNYbsakOGGZMcdFQLh2khF83J/KCzrNctS8KKUCLCVqGoTZW9nvcBIsZUELHnchX/3CuTw7ejcpWalw6QmZtKjUTXs4fkhH4O5hCPY3bU19m1vGCV6b8s6yp4YZeOGxvpLTDao+CXmKX/wjYLAdN7qdmp67L05Fvq8N5a12LYbbEYDOb4X7Y98Cbw2urangpQSWCr8oRRnxX777R+daH4h5KJohO95D3YjEqvLrF3vfznAzTZpQAJJ7l/wA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=Jv63bzvfpmi9X4WF+c0Nny0z07mV+I5l+14mQv4nxik=; b=RIf68xbrU0sRfxNIsgJXjjq/BYBO9gWKdRMWNR9jELTiLxVrQe2HTM3p120ujI9kTUr4jf04SDgVkg9gWl11yt62maMyrlHxsPka79Aqc3h2Ogmhp7Wdgm7HNXR2jsSI0nSH1NoSSh4Z+2upylKvnGxfGTHUm4EECM0LzRpWIUZn0mY0OPfRSUZbvjB+BK3nJXYvfICOQNGN4OjkK1uUfP88LWUMX9f3ujBa8H8rgryTIbpl7ozYE59rODkRHbRiaX1ABEuXpkk1YGHXhSplYHp0iR1ge5jUFU1OnUDdszKLVvmLZDiQnGYuNtNChrAGZBe5DnVn7v5jRaVWH7GtRQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nxp.com; dmarc=pass action=none header.from=nxp.com; dkim=pass header.d=nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nxp.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=Jv63bzvfpmi9X4WF+c0Nny0z07mV+I5l+14mQv4nxik=; b=UC/9zRjG9VzJCFfAkGeATtd4lgYg95DRSbr60pAGcMPGL8ic9P3qur3WqhVohgfqJ4lAd1nixcFTdeZx9rALgkt2wmhv+wudc2H9ePmMBlFY2vcVukdRzrrZ1Du+g44l7TK2umcYz5XxGRv0Ud3Geo2nZDvx4qezghbkYLAN2Uk= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from PAXPR04MB9642.eurprd04.prod.outlook.com (2603:10a6:102:240::14) by PA4PR04MB7616.eurprd04.prod.outlook.com (2603:10a6:102:e7::5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7202.33; Tue, 23 Jan 2024 21:26:27 +0000 Received: from PAXPR04MB9642.eurprd04.prod.outlook.com ([fe80::c8b4:5648:8948:e85c]) by PAXPR04MB9642.eurprd04.prod.outlook.com ([fe80::c8b4:5648:8948:e85c%3]) with mapi id 15.20.7202.035; Tue, 23 Jan 2024 21:26:27 +0000 From: Frank Li To: frank.li@nxp.com Cc: alexandre.belloni@bootlin.com, conor.culhane@silvaco.com, devicetree@vger.kernel.org, gregkh@linuxfoundation.org, imx@lists.linux.dev, jirislaby@kernel.org, joe@perches.com, krzysztof.kozlowski+dt@linaro.org, krzysztof.kozlowski@linaro.org, linux-i3c@lists.infradead.org, linux-kernel@vger.kernel.org, linux-serial@vger.kernel.org, miquel.raynal@bootlin.com, robh@kernel.org, zbigniew.lukwinski@linux.intel.com Subject: [PATCH v3 5/8] i3c: target: add svc target controller support Date: Tue, 23 Jan 2024 16:25:46 -0500 Message-Id: <20240123212549.3858137-6-Frank.Li@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240123212549.3858137-1-Frank.Li@nxp.com> References: <20240123212549.3858137-1-Frank.Li@nxp.com> Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: SJ0PR13CA0081.namprd13.prod.outlook.com (2603:10b6:a03:2c4::26) To PAXPR04MB9642.eurprd04.prod.outlook.com (2603:10a6:102:240::14) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PAXPR04MB9642:EE_|PA4PR04MB7616:EE_ X-MS-Office365-Filtering-Correlation-Id: dd319862-28d0-472c-93e1-08dc1c59f502 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: i9byK9W0uRYP2/1ok94v1bhjSHYnilkXdCVFHWZPUmGOirrsURBTCiuHajF68sJr/HXiFxbu3D1VCW05lHXtBk4FSW5X9YIwhVPo21u+mmeBqWSoCjVxZ45oj4nfAnnNgjptOA8d1dQ2Ga85UEZmcwkqwb/SqRRD0Mv7pPyrogK9eRV/AQnpFN5DGSKobE7z2O2O8ZwscLlCmID3gWXz2+VK2YtBAIB/z73dd+w/rVB3FVQD4yPotU+vs+nHt9E2UKHaGIqEiByN0rCDLhZRZhJF7Gs6JcGKkJsLsXse5JFMX2F/Oo9xYPnqmAwVgq/jbBB/DFVqBeJ1RY63QzE5z7oMZBWNjw7CqmuHOK5dTA2p+5ocEhE435hgrMK/uNVAIJkvWWxv07lnCGI5OzoHJtY+7fOklrKTXiJ2Y53QPnWxEPnTrDnBT9Ff1fKgxLP3lR7pzDNxZxi2YYG7aFpfVxO556GjBzrqHg3TjbiImLevfxR+LpWhYIoAHD0LMbLIRrlHDp/41nhB5HMzRHLMR13xF5xnKZD64UaziPO/BDQhFcfHxbyvKYHovw17TcyhBPmI02z8inXkjcMOIt7NKfobummLzHxx2T0EzIfaORVzbpG/eTT99jjlbJPpjsst3kNOd9knc0M7Q0hOpS7ZFX65GXDArqqpOZvpqZ5sBqI= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PAXPR04MB9642.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230031)(396003)(39860400002)(136003)(346002)(366004)(376002)(230173577357003)(230273577357003)(230922051799003)(451199024)(186009)(1800799012)(64100799003)(1076003)(26005)(2616005)(478600001)(52116002)(6666004)(6506007)(6512007)(83380400001)(5660300002)(30864003)(7416002)(41300700001)(2906002)(6486002)(4326008)(8676002)(37006003)(34206002)(66946007)(66476007)(66556008)(316002)(8936002)(36756003)(86362001)(38100700002)(38350700005);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?FgLpYtt6iVbrHVjKQ8EnWOjZMACo3bf0SxAZPDrw/aDUSpusUNQUQzcF0nYV?= =?us-ascii?Q?JvvgpurBDn2xjQmbBzk0KCTkvTWoF+I1qKq44+0Qh6eQCva7mfxoJGPBAdKK?= =?us-ascii?Q?YS16z9ucURUTAuKPJTkNANpNZpbvDZzNXdoF+JHvzibg0srXu/F0QRFTAl2o?= =?us-ascii?Q?G3i4PvoXzIh7MqLjN7lP02eHeU53BvOiDDvkYkjznInto9rU1KjksSD4r+hO?= =?us-ascii?Q?c/9eRcn2HNXcFx2GShX1I5ahvemUjwkkG56Bp0aky8RMgr7I9mY9A0jEwxgU?= =?us-ascii?Q?lb63SFQd4yqo/zezUp0xdcjXP+gjQn4DP4xMi4qf6bLG78s8w83C36R6ZEK9?= =?us-ascii?Q?bxxP9q9iYZCryARQCi5gztxFOGcggbQgHBmIZTRg2xfg3d+W+wJosiAfQG39?= =?us-ascii?Q?FlYKTU/++/fvuNoiLpG/VmZbrbuHblESeG2XtHSx+nISZjbXDXliK8moUzhJ?= =?us-ascii?Q?dFV1ANhcsOg7NpXX/rsHnuFbuJPOdOFBrJ6YKwOzH6JeT04j/tsp+khV17Jd?= =?us-ascii?Q?uHOgahz0WK7wogkYAMsmu33tHK10y7cGjJatS3FQnW3G+ym+Ifxr3qzApOe4?= =?us-ascii?Q?SWYMe6OniwiwPJINs/ZoYgv3rmxlpt8QGznk0kqHFd9bDSANCzUh3DDrGUmn?= =?us-ascii?Q?Y8/uEMoDPe44JVqm+L+gk/R55SXZtSTSODNogQSvSb2t3j2ehaJfV/dKbDHd?= =?us-ascii?Q?fxt/HiAxG0GhE2aVWoKCrw4QAy+3r/haZw6MlM/wFIsNYvjgnOAlsW2PF9AK?= =?us-ascii?Q?kO9h2guPZrkb3qQwbrJ1BZcyWBaiC5xgdgMoFcLY6YJv1kgl4qsOZWrsXOKU?= =?us-ascii?Q?5PT6jVXqeJp6TGwElOSxpZxebfwvQ3tVaxFzs90tmpDbnYbTobPXDK6Y0s/T?= =?us-ascii?Q?zvyGiw3lylAnhgJUFZFsQSRvx4Q/YYa3ZifTi/YJOteDi6VqVLPsSHygdN3x?= =?us-ascii?Q?5UdzhgpFlZ5Q2lYIRW4AKrREZ4+x30PG5yS0xGPDYDU1PeQojof9o6ZhjUWR?= =?us-ascii?Q?Shy+gt674g0zekv0jiOHduLQqmNGukpYEBgIwXZndR7bUR9PK9QuC8utuotq?= =?us-ascii?Q?BiuVeZ0ZCKMMXOUsfbb9agIXiy/Odxr4QI7iK0sHvrEr5TUU9LEfH1NI3emP?= =?us-ascii?Q?2sjx7cmuoxwjYXTSYwasBabnHT7EPHVg2UCWl4d3NYjzNa0owFZ6uSp0XOwi?= =?us-ascii?Q?qIdO8cAvX8mi9jUHgk12Xg9epVHaguW1h1eh4VYA/KF/f946aOyujHb+/oEF?= =?us-ascii?Q?2H1SNr+wx0Dmkd1HxeUg70vXx0QkV5LyD/dC+9ON3njol8hAlhcUFMAFMabl?= =?us-ascii?Q?cfI7ujWzisRRubo3OvbKyD2flbfpk6bDLMXq7X+nKcQydW7RDmsRJyhmm9WV?= =?us-ascii?Q?eRJU7N4a5eyWxX2PkLxHzYl3DdbiZeRK/5H5dZv0bFuJ0fIak0sBp6K/rR+i?= =?us-ascii?Q?9h+iNQBJwz715T3MYE1N/DUPaHErssP+gOOwaThABxdgDJ7t6wvfFRi0XYis?= =?us-ascii?Q?kVUCCpN30TGZeRmyvfZEqjWsvzELxgF8g3CeJAUwowNvcCvJ3NGUhBIQlfli?= =?us-ascii?Q?EptqkMJpq9MdPh5q/ahu4lGQjjCz/bV7og2vaOIZ?= X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: dd319862-28d0-472c-93e1-08dc1c59f502 X-MS-Exchange-CrossTenant-AuthSource: PAXPR04MB9642.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 23 Jan 2024 21:26:27.0698 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 686ea1d3-bc2b-4c6f-a92c-d99c5c301635 X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: yWo2R178LgVO+89YkspTrr0jNFzlhSRrapSJPzbj5RYUmibZzOb+va7DXthhOwZq1VOs4JOH3E3uyaAW740yAQ== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PA4PR04MB7616 Content-Type: text/plain; charset="utf-8" Add Silvaco I3C target controller support Signed-off-by: Frank Li --- Notes: Change from v2 to v3 - fix build warning drivers/i3c/master/Makefile | 2 +- drivers/i3c/master/svc-i3c-main.c | 37 +- drivers/i3c/master/svc-i3c-target.c | 776 ++++++++++++++++++++++++++++ drivers/i3c/master/svc-i3c.h | 3 + 4 files changed, 812 insertions(+), 6 deletions(-) create mode 100644 drivers/i3c/master/svc-i3c-target.c diff --git a/drivers/i3c/master/Makefile b/drivers/i3c/master/Makefile index 484cb81f45821..b9ed6c9e7be13 100644 --- a/drivers/i3c/master/Makefile +++ b/drivers/i3c/master/Makefile @@ -2,6 +2,6 @@ obj-$(CONFIG_CDNS_I3C_MASTER) +=3D i3c-master-cdns.o obj-$(CONFIG_DW_I3C_MASTER) +=3D dw-i3c-master.o obj-$(CONFIG_AST2600_I3C_MASTER) +=3D ast2600-i3c-master.o -svc-i3c-objs +=3D svc-i3c-main.o svc-i3c-master.o +svc-i3c-objs +=3D svc-i3c-main.o svc-i3c-master.o svc-i3c-target.o obj-$(CONFIG_SVC_I3C_MASTER) +=3D svc-i3c.o obj-$(CONFIG_MIPI_I3C_HCI) +=3D mipi-i3c-hci/ diff --git a/drivers/i3c/master/svc-i3c-main.c b/drivers/i3c/master/svc-i3c= -main.c index 053b2bd9d8317..34d9f1e64b1c0 100644 --- a/drivers/i3c/master/svc-i3c-main.c +++ b/drivers/i3c/master/svc-i3c-main.c @@ -7,24 +7,51 @@ =20 #include "svc-i3c.h" =20 +static bool svc_i3c_is_master(struct device *dev) +{ + const char *mode =3D NULL; + + device_property_read_string(dev, "mode", &mode); + + if (!mode) + return true; + + if (strncmp(mode, "target", 6) =3D=3D 0) + return false; + + return true; +} + static int svc_i3c_probe(struct platform_device *pdev) { - return svc_i3c_master_probe(pdev); + if (svc_i3c_is_master(&pdev->dev)) + return svc_i3c_master_probe(pdev); + + return svc_i3c_target_probe(pdev); } =20 static void svc_i3c_remove(struct platform_device *pdev) { - svc_i3c_master_remove(pdev); + if (svc_i3c_is_master(&pdev->dev)) + return svc_i3c_master_remove(pdev); + + svc_i3c_target_remove(pdev); } =20 static int __maybe_unused svc_i3c_runtime_suspend(struct device *dev) { - return svc_i3c_master_runtime_suspend(dev); + if (svc_i3c_is_master(dev)) + return svc_i3c_master_runtime_suspend(dev); + + return -EINVAL; } =20 static int __maybe_unused svc_i3c_runtime_resume(struct device *dev) { - return svc_i3c_master_runtime_resume(dev); + if (svc_i3c_is_master(dev)) + return svc_i3c_master_runtime_resume(dev); + + return -EINVAL; } =20 static const struct dev_pm_ops svc_i3c_pm_ops =3D { @@ -49,4 +76,4 @@ static struct platform_driver svc_i3c_master =3D { .pm =3D &svc_i3c_pm_ops, }, }; -module_platform_driver(svc_i3c_master); \ No newline at end of file +module_platform_driver(svc_i3c_master); diff --git a/drivers/i3c/master/svc-i3c-target.c b/drivers/i3c/master/svc-i= 3c-target.c new file mode 100644 index 0000000000000..094efb60cb404 --- /dev/null +++ b/drivers/i3c/master/svc-i3c-target.c @@ -0,0 +1,776 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2023 NXP. + * + * Author: Frank Li + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "svc-i3c.h" + +enum i3c_clks { + PCLK, + FCLK, + SCLK, + MAXCLK, +}; + +struct svc_i3c_target { + struct device *dev; + void __iomem *regs; + int irq; + struct clk_bulk_data clks[MAXCLK]; + + struct list_head txq; + spinlock_t txq_lock; /* protect tx queue */ + struct list_head rxq; + spinlock_t rxq_lock; /* protect rx queue */ + struct list_head cq; + spinlock_t cq_lock; /* protect complete queue */ + + struct work_struct work; + struct workqueue_struct *workqueue; + + struct completion dacomplete; + struct i3c_target_ctrl_features features; + + spinlock_t ctrl_lock; /* protext access SCTRL register */ +}; + +#define I3C_SCONFIG 0x4 +#define I3C_SCONFIG_SLVENA_MASK BIT(0) +#define I3C_SCONFIG_OFFLINE_MASK BIT(9) +#define I3C_SCONFIG_SADDR_MASK GENMASK(31, 25) + +#define I3C_SSTATUS 0x8 +#define I3C_SSTATUS_STNOTSTOP_MASK BIT(0) +#define I3C_SSTATUS_STOP_MASK BIT(10) +#define I3C_SSTATUS_RX_PEND_MASK BIT(11) +#define I3C_SSTATUS_TXNOTFULL_MASK BIT(12) +#define I3C_SSTATUS_DACHG_MASK BIT(13) +#define I3C_SSTATUS_EVDET_MASK GENMASK(21, 20) +#define I3C_SSTATUS_EVDET_ACKED 0x3 +#define I3C_SSTATUS_IBIDIS_MASK BIT(24) +#define I3C_SSTATUS_HJDIS_MASK BIT(27) + +#define I3C_SCTRL 0xc +#define I3C_SCTRL_EVENT_MASK GENMASK(1, 0) +#define I3C_SCTRL_EVENT_IBI 0x1 +#define I3C_SCTRL_EVENT_HOTJOIN 0x3 +#define I3C_SCTRL_EXTDATA_MASK BIT(3) +#define I3C_SCTRL_IBIDATA_MASK GENMASK(15, 8) + +#define I3C_SINTSET 0x10 +#define I3C_SINTCLR 0x14 +#define I3C_SINT_START BIT(8) +#define I3C_SINT_MATCHED BIT(9) +#define I3C_SINT_STOP BIT(10) +#define I3C_SINT_RXPEND BIT(11) +#define I3C_SINT_TXSEND BIT(12) +#define I3C_SINT_DACHG BIT(13) +#define I3C_SINT_CCC BIT(14) +#define I3C_SINT_ERRWARN BIT(15) +#define I3C_SINT_DDRMAATCHED BIT(16) +#define I3C_SINT_CHANDLED BIT(17) +#define I3C_SINT_EVENT BIT(18) +#define I3C_SINT_SLVRST BIT(19) + +#define I3C_SDATACTRL 0x2c +#define I3C_SDATACTRL_RXEMPTY_MASK BIT(31) +#define I3C_SDATACTRL_TXFULL_MASK BIT(30) +#define I3C_SDATACTRL_RXCOUNT_MASK GENMASK(28, 24) +#define I3C_SDATACTRL_TXCOUNT_MASK GENMASK(20, 16) +#define I3C_SDATACTRL_FLUSHFB_MASK BIT(1) +#define I3C_SDATACTRL_FLUSHTB_MASK BIT(0) + +#define I3C_SWDATAB 0x30 +#define I3C_SWDATAB_END_ALSO_MASK BIT(16) +#define I3C_SWDATAB_END_MASK BIT(8) + +#define I3C_SWDATAE 0x34 +#define I3C_SRDATAB 0x40 + +#define I3C_SCAPABILITIES 0x60 +#define I3C_SCAPABILITIES_FIFOTX_MASK GENMASK(27, 26) +#define I3C_SCAPABILITIES_FIFORX_MASK GENMASK(29, 28) + +#define I3C_SMAXLIMITS 0x68 +#define I3C_SMAXLIMITS_MAXRD_MASK GENMASK(11, 0) +#define I3C_SMAXLIMITS_MAXWR_MASK GENMASK(27, 16) + +#define I3C_SIDPARTNO 0x6c + +#define I3C_SIDEXT 0x70 +#define I3C_SIDEXT_BCR_MASK GENMASK(23, 16) +#define I3C_SIDEXT_DCR_MASK GENMASK(15, 8) +#define I3C_SVENDORID 0x74 + +#define I3C_SMAPCTRL0 0x11c +#define I3C_SMAPCTRL0_ENA_MASK BIT(0) +#define I3C_SMAPCTRL0_DA_MASK GENMASK(7, 1) + +#define I3C_IBIEXT1 0x140 +#define I3C_IBIEXT1_CNT_MASK GEN_MASK(2, 0) +#define I3C_IBIEXT1_MAX_MASK GEN_MASK(4, 6) +#define I3C_IBIEXT1_EXT1_SHIFT 8 +#define I3C_IBIEXT1_EXT2_SHIFT 16 +#define I3C_IBIEXT1_EXT3_SHIFT 24 + +#define I3C_IBIEXT2 0x144 +#define I3C_IBIEXT2_EXT4_SHIFT 0 +#define I3C_IBIEXT2_EXT5_SHIFT 8 +#define I3C_IBIEXT2_EXT6_SHIFT 16 +#define I3C_IBIEXT2_EXT7_SHIFT 24 + +static int svc_i3c_target_enable(struct i3c_target_ctrl *ctrl) +{ + struct svc_i3c_target *svc; + u32 val; + + svc =3D dev_get_drvdata(&ctrl->dev); + + val =3D readl_relaxed(svc->regs + I3C_SCONFIG); + val |=3D I3C_SCONFIG_SLVENA_MASK; + writel_relaxed(val, svc->regs + I3C_SCONFIG); + + return 0; +} + +static int svc_i3c_target_disable(struct i3c_target_ctrl *ctrl) +{ + struct svc_i3c_target *svc; + u32 val; + + svc =3D dev_get_drvdata(&ctrl->dev); + + val =3D readl_relaxed(svc->regs + I3C_SCONFIG); + val &=3D ~I3C_SCONFIG_SLVENA_MASK; + writel_relaxed(val, svc->regs + I3C_SCONFIG); + + return 0; +} + +static int svc_i3c_target_set_config(struct i3c_target_ctrl *ctrl, struct = i3c_target_func *func) +{ + struct svc_i3c_target *svc; + u32 val; + u32 wm, rm; + + svc =3D dev_get_drvdata(&ctrl->dev); + + if (func->static_addr > 0x7F) + return -EINVAL; + + val =3D readl_relaxed(svc->regs + I3C_SCONFIG); + val &=3D ~I3C_SCONFIG_SLVENA_MASK; + val |=3D FIELD_PREP(I3C_SCONFIG_SADDR_MASK, func->static_addr); + writel_relaxed(val, svc->regs + I3C_SCONFIG); + + if (func->part_id) + writel_relaxed((func->part_id << 16) | + ((func->instance_id << 12) & GENMASK(15, 12)) | + (func->ext_id & GENMASK(11, 0)), svc->regs + I3C_SIDPARTNO); + + writel_relaxed(FIELD_PREP(I3C_SIDEXT_BCR_MASK, func->bcr) | + FIELD_PREP(I3C_SIDEXT_DCR_MASK, func->dcr), + svc->regs + I3C_SIDEXT); + + wm =3D func->max_write_len =3D=3D 0 ? + FIELD_GET(I3C_SMAXLIMITS_MAXWR_MASK, I3C_SMAXLIMITS_MAXWR_MASK) : fu= nc->max_write_len; + + wm =3D max_t(u32, val, 8); + + rm =3D func->max_read_len =3D=3D 0 ? + FIELD_GET(I3C_SMAXLIMITS_MAXRD_MASK, I3C_SMAXLIMITS_MAXRD_MASK) : fu= nc->max_read_len; + rm =3D max_t(u32, val, 16); + + val =3D FIELD_PREP(I3C_SMAXLIMITS_MAXRD_MASK, rm) | FIELD_PREP(I3C_SMAXLI= MITS_MAXWR_MASK, wm); + writel_relaxed(val, svc->regs + I3C_SMAXLIMITS); + + writel_relaxed(func->vendor_id, svc->regs + I3C_SVENDORID); + return 0; +} + +static const struct i3c_target_ctrl_features *svc_i3c_get_features(struct = i3c_target_ctrl *ctrl) +{ + struct svc_i3c_target *svc; + + svc =3D dev_get_drvdata(&ctrl->dev); + + if (!svc) + return NULL; + + return &svc->features; +} + +static void svc_i3c_queue_complete(struct svc_i3c_target *svc, struct i3c_= request *complete) +{ + unsigned long flags; + + spin_lock_irqsave(&svc->cq_lock, flags); + list_add_tail(&complete->list, &svc->cq); + spin_unlock_irqrestore(&svc->cq_lock, flags); + queue_work(svc->workqueue, &svc->work); +} + +static void svc_i3c_fill_txfifo(struct svc_i3c_target *svc) +{ + struct i3c_request *req, *complete =3D NULL; + unsigned long flags; + int val; + + spin_lock_irqsave(&svc->txq_lock, flags); + while ((!!(req =3D list_first_entry_or_null(&svc->txq, struct i3c_request= , list))) && + !((readl_relaxed(svc->regs + I3C_SDATACTRL) & I3C_SDATACTRL_TXFULL= _MASK))) { + while (!(readl_relaxed(svc->regs + I3C_SDATACTRL) + & I3C_SDATACTRL_TXFULL_MASK)) { + val =3D *(u8 *)(req->buf + req->actual); + + if (req->actual + 1 =3D=3D req->length) + writel_relaxed(val, svc->regs + I3C_SWDATAE); + else + writel_relaxed(val, svc->regs + I3C_SWDATAB); + + req->actual++; + + if (req->actual =3D=3D req->length) { + list_del(&req->list); + complete =3D req; + spin_unlock_irqrestore(&svc->txq_lock, flags); + + svc_i3c_queue_complete(svc, complete); + return; + + spin_lock_irqsave(&svc->txq_lock, flags); + break; + } + } + } + spin_unlock_irqrestore(&svc->txq_lock, flags); +} + +static int svc_i3c_target_queue(struct i3c_request *req, gfp_t gfp) +{ + struct svc_i3c_target *svc; + struct list_head *q; + unsigned long flags; + spinlock_t *lk; + + svc =3D dev_get_drvdata(&req->ctrl->dev); + if (!svc) + return -EINVAL; + + if (req->tx) { + q =3D &svc->txq; + lk =3D &svc->txq_lock; + } else { + q =3D &svc->rxq; + lk =3D &svc->rxq_lock; + } + + spin_lock_irqsave(lk, flags); + list_add_tail(&req->list, q); + spin_unlock_irqrestore(lk, flags); + + if (req->tx) + svc_i3c_fill_txfifo(svc); + + if (req->tx) + writel_relaxed(I3C_SINT_TXSEND, svc->regs + I3C_SINTSET); + else + writel_relaxed(I3C_SINT_RXPEND, svc->regs + I3C_SINTSET); + + return 0; +} + +static int svc_i3c_dequeue(struct i3c_request *req) +{ + struct svc_i3c_target *svc; + unsigned long flags; + spinlock_t *lk; + + svc =3D dev_get_drvdata(&req->ctrl->dev); + if (!svc) + return -EINVAL; + + if (req->tx) + lk =3D &svc->txq_lock; + else + lk =3D &svc->rxq_lock; + + spin_lock_irqsave(lk, flags); + list_del(&req->list); + spin_unlock_irqrestore(lk, flags); + + return 0; +} + +static void svc_i3c_target_fifo_flush(struct i3c_target_ctrl *ctrl, bool t= x) +{ + struct svc_i3c_target *svc; + u32 val; + + svc =3D dev_get_drvdata(&ctrl->dev); + + val =3D readl_relaxed(svc->regs + I3C_SDATACTRL); + + val |=3D tx ? I3C_SDATACTRL_FLUSHTB_MASK : I3C_SDATACTRL_FLUSHFB_MASK; + + writel_relaxed(val, svc->regs + I3C_SDATACTRL); +} + +static int +svc_i3c_target_raise_ibi(struct i3c_target_ctrl *ctrl, void *p, u8 size) +{ + struct svc_i3c_target *svc; + unsigned long flags; + u8 *ibidata =3D p; + u32 ext1 =3D 0, ext2 =3D 0; + u32 val; + int ret; + + svc =3D dev_get_drvdata(&ctrl->dev); + + if (size && !p) + return -EINVAL; + + if (size > 8) + return -EINVAL; + + val =3D readl_relaxed(svc->regs + I3C_SSTATUS); + if (val & I3C_SSTATUS_IBIDIS_MASK) + return -EINVAL; + + ret =3D readl_relaxed_poll_timeout(svc->regs + I3C_SCTRL, val, + !(val & I3C_SCTRL_EVENT_MASK), 0, 10000); + if (ret) { + dev_err(&ctrl->dev, "Timeout when polling for NO event pending"); + val &=3D ~I3C_SCTRL_EVENT_MASK; + writel_relaxed(val, svc->regs + I3C_SCTRL); + return -ENAVAIL; + } + + spin_lock_irqsave(&svc->ctrl_lock, flags); + + val =3D readl_relaxed(svc->regs + I3C_SCTRL); + + val &=3D ~I3C_SCTRL_EVENT_MASK | I3C_SCTRL_IBIDATA_MASK; + val |=3D FIELD_PREP(I3C_SCTRL_EVENT_MASK, I3C_SCTRL_EVENT_IBI); + + if (size) { + val |=3D FIELD_PREP(I3C_SCTRL_IBIDATA_MASK, *ibidata); + ibidata++; + + if (size > 1) + val |=3D I3C_SCTRL_EXTDATA_MASK; + + size--; + if (size > 0) { + ext1 |=3D (size + 2); + ext1 |=3D (*ibidata++) << I3C_IBIEXT1_EXT1_SHIFT; + size--; + } + + if (size > 0) { + ext1 |=3D (*ibidata++) << I3C_IBIEXT1_EXT2_SHIFT; + size--; + } + + if (size > 0) { + ext1 |=3D (*ibidata++) << I3C_IBIEXT1_EXT3_SHIFT; + size--; + } + + writel_relaxed(ext1, svc->regs + I3C_IBIEXT1); + + if (size > 0) { + ext2 |=3D (*ibidata++) << I3C_IBIEXT2_EXT4_SHIFT; + size--; + } + + if (size > 0) { + ext2 |=3D (*ibidata++) << I3C_IBIEXT2_EXT5_SHIFT; + size--; + } + + if (size > 0) { + ext2 |=3D (*ibidata++) << I3C_IBIEXT2_EXT6_SHIFT; + size--; + } + + if (size > 0) { + ext2 |=3D (*ibidata++) << I3C_IBIEXT2_EXT7_SHIFT; + size--; + } + + writeb_relaxed(ext2, svc->regs + I3C_IBIEXT2); + } + + /* Issue IBI*/ + writel_relaxed(val, svc->regs + I3C_SCTRL); + spin_unlock_irqrestore(&svc->ctrl_lock, flags); + + ret =3D readl_relaxed_poll_timeout(svc->regs + I3C_SCTRL, val, + !(val & I3C_SCTRL_EVENT_MASK), 0, 1000000); + if (ret) { + dev_err(&ctrl->dev, "Timeout when polling for IBI finish\n"); + + //clear event to above hang bus + spin_lock_irqsave(&svc->ctrl_lock, flags); + val =3D readl_relaxed(svc->regs + I3C_SCTRL); + val &=3D ~I3C_SCTRL_EVENT_MASK; + writel_relaxed(val, svc->regs + I3C_SCTRL); + spin_unlock_irqrestore(&svc->ctrl_lock, flags); + + return -ENAVAIL; + } + + return 0; +} + +static void svc_i3c_target_complete(struct work_struct *work) +{ + struct svc_i3c_target *svc =3D container_of(work, struct svc_i3c_target, = work); + struct i3c_request *req; + unsigned long flags; + + spin_lock_irqsave(&svc->cq_lock, flags); + while (!list_empty(&svc->cq)) { + req =3D list_first_entry(&svc->cq, struct i3c_request, list); + list_del(&req->list); + spin_unlock_irqrestore(&svc->cq_lock, flags); + req->complete(req); + + spin_lock_irqsave(&svc->cq_lock, flags); + } + spin_unlock_irqrestore(&svc->cq_lock, flags); +} + +static irqreturn_t svc_i3c_target_irq_handler(int irq, void *dev_id) +{ + struct i3c_request *req, *complete =3D NULL; + struct svc_i3c_target *svc =3D dev_id; + unsigned long flags; + u32 statusFlags; + + statusFlags =3D readl(svc->regs + I3C_SSTATUS); + writel(statusFlags, svc->regs + I3C_SSTATUS); + + if (statusFlags & I3C_SSTATUS_DACHG_MASK) + complete_all(&svc->dacomplete); + + if (statusFlags & I3C_SSTATUS_RX_PEND_MASK) { + spin_lock_irqsave(&svc->rxq_lock, flags); + req =3D list_first_entry_or_null(&svc->rxq, struct i3c_request, list); + + if (!req) { + writel_relaxed(I3C_SINT_RXPEND, svc->regs + I3C_SINTCLR); + } else { + while (!(readl_relaxed(svc->regs + I3C_SDATACTRL) & + I3C_SDATACTRL_RXEMPTY_MASK)) { + *(u8 *)(req->buf + req->actual) =3D + readl_relaxed(svc->regs + I3C_SRDATAB); + req->actual++; + + if (req->actual =3D=3D req->length) { + complete =3D req; + list_del(&req->list); + break; + } + } + + if (req->actual !=3D req->length && (statusFlags & I3C_SSTATUS_STOP_MAS= K)) { + complete =3D req; + list_del(&req->list); + } + } + spin_unlock_irqrestore(&svc->rxq_lock, flags); + + if (complete) { + spin_lock_irqsave(&svc->cq_lock, flags); + list_add_tail(&complete->list, &svc->cq); + spin_unlock_irqrestore(&svc->cq_lock, flags); + queue_work(svc->workqueue, &svc->work); + complete =3D NULL; + } + } + + if (statusFlags & I3C_SSTATUS_TXNOTFULL_MASK) { + svc_i3c_fill_txfifo(svc); + spin_lock_irqsave(&svc->txq_lock, flags); + if (list_empty(&svc->txq)) + writel_relaxed(I3C_SINT_TXSEND, svc->regs + I3C_SINTCLR); + spin_unlock_irqrestore(&svc->txq_lock, flags); + } + + return IRQ_HANDLED; +} + +static void svc_i3c_cancel_all_reqs(struct i3c_target_ctrl *ctrl, bool tx) +{ + struct svc_i3c_target *svc; + struct i3c_request *req; + struct list_head *q; + unsigned long flags; + spinlock_t *lk; + + svc =3D dev_get_drvdata(&ctrl->dev); + if (!svc) + return; + + if (tx) { + q =3D &svc->txq; + lk =3D &svc->txq_lock; + } else { + q =3D &svc->rxq; + lk =3D &svc->rxq_lock; + } + + spin_lock_irqsave(lk, flags); + while (!list_empty(q)) { + req =3D list_first_entry(q, struct i3c_request, list); + list_del(&req->list); + spin_unlock_irqrestore(lk, flags); + + req->status =3D I3C_REQUEST_CANCEL; + req->complete(req); + spin_lock_irqsave(lk, flags); + } + spin_unlock_irqrestore(lk, flags); +} + +static int svc_i3c_hotjoin(struct i3c_target_ctrl *ctrl) +{ + struct svc_i3c_target *svc; + int ret; + u32 val; + u32 cfg; + + svc =3D dev_get_drvdata(&ctrl->dev); + if (!svc) + return -EINVAL; + + reinit_completion(&svc->dacomplete); + + val =3D readl_relaxed(svc->regs + I3C_SSTATUS); + if (val & I3C_SSTATUS_HJDIS_MASK) { + dev_err(&ctrl->dev, "Hotjoin disabled by i3c master\n"); + return -EINVAL; + } + + ret =3D readl_relaxed_poll_timeout(svc->regs + I3C_SCTRL, val, + !(val & I3C_SCTRL_EVENT_MASK), 0, 10000); + if (ret) { + dev_err(&ctrl->dev, "Timeout when polling for none event pending"); + return -ENAVAIL; + } + + cfg =3D readl_relaxed(svc->regs + I3C_SCONFIG); + cfg |=3D I3C_SCONFIG_OFFLINE_MASK; + writel_relaxed(cfg, svc->regs + I3C_SCONFIG); + + val &=3D ~(I3C_SCTRL_EVENT_MASK | I3C_SCTRL_IBIDATA_MASK); + val |=3D FIELD_PREP(I3C_SCTRL_EVENT_MASK, I3C_SCTRL_EVENT_HOTJOIN); + /* Issue hotjoin*/ + writel_relaxed(val, svc->regs + I3C_SCTRL); + + ret =3D readl_relaxed_poll_timeout(svc->regs + I3C_SCTRL, val, + !(val & I3C_SCTRL_EVENT_MASK), 0, 100000); + if (ret) { + val &=3D ~FIELD_PREP(I3C_SCTRL_EVENT_MASK, I3C_SCTRL_EVENT_MASK); + writel_relaxed(val, svc->regs + I3C_SCTRL); + dev_err(&ctrl->dev, "Timeout when polling for HOTJOIN finish\n"); + return -EINVAL; + } + + val =3D readl_relaxed(svc->regs + I3C_SSTATUS); + val =3D FIELD_GET(I3C_SSTATUS_EVDET_MASK, val); + if (val !=3D I3C_SSTATUS_EVDET_ACKED) { + dev_err(&ctrl->dev, "Master NACKED hotjoin request\n"); + return -EINVAL; + } + + writel_relaxed(I3C_SINT_DACHG, svc->regs + I3C_SINTSET); + ret =3D wait_for_completion_timeout(&svc->dacomplete, msecs_to_jiffies(10= 0)); + writel_relaxed(I3C_SINT_DACHG, svc->regs + I3C_SINTCLR); + if (!ret) { + dev_err(&ctrl->dev, "wait for da assignment timeout\n"); + return -EIO; + } + + val =3D readl_relaxed(svc->regs + I3C_SMAPCTRL0); + val =3D FIELD_GET(I3C_SMAPCTRL0_DA_MASK, val); + dev_info(&ctrl->dev, "Get dynamtic address 0x%x\n", val); + return 0; +} + +static int svc_i3c_set_status_format1(struct i3c_target_ctrl *ctrl, u16 st= atus) +{ + struct svc_i3c_target *svc; + unsigned long flags; + u32 val; + + svc =3D dev_get_drvdata(&ctrl->dev); + + spin_lock_irqsave(&svc->ctrl_lock, flags); + val =3D readl_relaxed(svc->regs + I3C_SCTRL); + val &=3D 0xFFFF; + val |=3D status << 16; + writel_relaxed(val, svc->regs + I3C_SCTRL); + spin_unlock_irqrestore(&svc->ctrl_lock, flags); + + return 0; +} + +static u16 svc_i3c_get_status_format1(struct i3c_target_ctrl *ctrl) +{ + struct svc_i3c_target *svc; + + svc =3D dev_get_drvdata(&ctrl->dev); + + return readl_relaxed(svc->regs + I3C_SCTRL) >> 16; +} + +static u8 svc_i3c_get_addr(struct i3c_target_ctrl *ctrl) +{ + struct svc_i3c_target *svc; + int val; + + svc =3D dev_get_drvdata(&ctrl->dev); + + val =3D readl_relaxed(svc->regs + I3C_SMAPCTRL0); + + if (val & I3C_SMAPCTRL0_ENA_MASK) + return FIELD_GET(I3C_SMAPCTRL0_DA_MASK, val); + + return 0; +} + +static int svc_i3c_fifo_status(struct i3c_target_ctrl *ctrl, bool tx) +{ + struct svc_i3c_target *svc; + int val; + + svc =3D dev_get_drvdata(&ctrl->dev); + + val =3D readl_relaxed(svc->regs + I3C_SDATACTRL); + + if (tx) + return FIELD_GET(I3C_SDATACTRL_TXCOUNT_MASK, val); + else + return FIELD_GET(I3C_SDATACTRL_RXCOUNT_MASK, val); +} + +static struct i3c_target_ctrl_ops svc_i3c_target_ops =3D { + .set_config =3D svc_i3c_target_set_config, + .enable =3D svc_i3c_target_enable, + .disable =3D svc_i3c_target_disable, + .queue =3D svc_i3c_target_queue, + .dequeue =3D svc_i3c_dequeue, + .raise_ibi =3D svc_i3c_target_raise_ibi, + .fifo_flush =3D svc_i3c_target_fifo_flush, + .cancel_all_reqs =3D svc_i3c_cancel_all_reqs, + .get_features =3D svc_i3c_get_features, + .hotjoin =3D svc_i3c_hotjoin, + .fifo_status =3D svc_i3c_fifo_status, + .set_status_format1 =3D svc_i3c_set_status_format1, + .get_status_format1 =3D svc_i3c_get_status_format1, + .get_addr =3D svc_i3c_get_addr, +}; + +int svc_i3c_target_probe(struct platform_device *pdev) +{ + struct device *dev =3D &pdev->dev; + struct i3c_target_ctrl *target; + struct svc_i3c_target *svc; + int ret; + u32 val; + + svc =3D devm_kzalloc(dev, sizeof(*svc), GFP_KERNEL); + if (!svc) + return -ENOMEM; + + svc->regs =3D devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(svc->regs)) + return PTR_ERR(svc->regs); + + svc->clks[PCLK].id =3D "pclk"; + svc->clks[FCLK].id =3D "fast_clk"; + svc->clks[SCLK].id =3D "slow_clk"; + + ret =3D devm_clk_bulk_get(dev, MAXCLK, svc->clks); + if (ret < 0) { + dev_err(dev, "fail get clks: %d\n", ret); + return ret; + } + + ret =3D clk_bulk_prepare_enable(MAXCLK, svc->clks); + if (ret < 0) { + dev_err(dev, "fail enable clks: %d\n", ret); + return ret; + } + + svc->irq =3D platform_get_irq(pdev, 0); + if (svc->irq < 0) + return svc->irq; + + INIT_LIST_HEAD(&svc->txq); + INIT_LIST_HEAD(&svc->rxq); + INIT_LIST_HEAD(&svc->cq); + spin_lock_init(&svc->txq_lock); + spin_lock_init(&svc->rxq_lock); + spin_lock_init(&svc->cq_lock); + spin_lock_init(&svc->ctrl_lock); + + init_completion(&svc->dacomplete); + + INIT_WORK(&svc->work, svc_i3c_target_complete); + svc->workqueue =3D alloc_workqueue("%s-cq", 0, 0, dev_name(dev)); + if (!svc->workqueue) + return -ENOMEM; + + /* Disable all IRQ */ + writel_relaxed(0xFFFFFFFF, svc->regs + I3C_SINTCLR); + + val =3D readl_relaxed(svc->regs + I3C_SCAPABILITIES); + svc->features.tx_fifo_sz =3D FIELD_GET(I3C_SCAPABILITIES_FIFOTX_MASK, va= l); + svc->features.tx_fifo_sz =3D 2 << svc->features.tx_fifo_sz; + + svc->features.rx_fifo_sz =3D FIELD_GET(I3C_SCAPABILITIES_FIFORX_MASK, val= ); + svc->features.rx_fifo_sz =3D 2 << svc->features.rx_fifo_sz; + + ret =3D devm_request_irq(dev, svc->irq, svc_i3c_target_irq_handler, 0, "s= vc-i3c-irq", svc); + if (ret) + return -ENOENT; + + target =3D devm_i3c_target_ctrl_create(dev, &svc_i3c_target_ops); + if (!target) + return -ENOMEM; + + dev_set_drvdata(&target->dev, svc); + + return 0; +} + +void svc_i3c_target_remove(struct platform_device *pdev) +{ +} diff --git a/drivers/i3c/master/svc-i3c.h b/drivers/i3c/master/svc-i3c.h index 0bd1f0112a071..634837f146b2e 100644 --- a/drivers/i3c/master/svc-i3c.h +++ b/drivers/i3c/master/svc-i3c.h @@ -8,4 +8,7 @@ void svc_i3c_master_remove(struct platform_device *pdev); int svc_i3c_master_runtime_suspend(struct device *dev); int svc_i3c_master_runtime_resume(struct device *dev); =20 +int svc_i3c_target_probe(struct platform_device *pdev); +void svc_i3c_target_remove(struct platform_device *pdev); + #endif \ No newline at end of file --=20 2.34.1