From patchwork Wed Oct 18 21:58:05 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Frank Li X-Patchwork-Id: 735912 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (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 45E5B3FB20; Wed, 18 Oct 2023 21:58:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=nxp.com header.i=@nxp.com header.b="EWwmfiYl" Received: from EUR02-VI1-obe.outbound.protection.outlook.com (mail-vi1eur02on2072.outbound.protection.outlook.com [40.107.241.72]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0DE77120; Wed, 18 Oct 2023 14:58:36 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=TLTywtyeALqPKu6AVLHI1o7RxUZhvrXVhDMRByhEs4r3oSGDnf6NWHEi7KK0B0W+rdvyW34bTI+36ZB6lgZ4r6q6wSiY5OCPYwPJ+G1tQkIWcIWVynFjS0eT1UJzE03JcCytt+TijVCFGlOZ3V04JKjvirg2YtwvfDLACeS4MCTnaU3cOOLkppt+XcemRCRtRuJlHqikDWKqHsPJiuX7tvFsDrHzXqswZMG5UhA3dLsXcQoDYqjeDFedKACoRkqHhr30/CEz/3sP1mJ0WtzAQpQTl/+CUaoWy17d8+IUS9p3dEn+1GvXOZPcIFGMsIj5NA2AClKsM3hEgwp26FYJtQ== 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=iE42CLXPcC9MBAr6uBFfjcOSAX6gKN3X9WxWNVV9KOE=; b=j8E5Hx+Alvm/rGhzoNIdbUgxSDPM7l3T4NANjbb7V+t9BBakuQdJ8GqkJK/DGibML5docpD86KNLMY6eoJC0TyXFHBmtCxs/gyGP5froyi2ulq8XmDESG2eVYUN/iOYV/ntEKkl4+1pi6aCXqI2OFoKUHrQEgOTOjP7HthefcGXa2H9Icwth/dBTmOmR/A/PUvDvzBeoHSy4otENZynw208FYe5ii5uT6gM/hiSuQAK6UowJtY8SunUzN3wy/LfMAu0n8XWC9DHzOjy7FizAZ3KfrzwN6Chun14Ex2xGoTN6YLuSmXkQYHor5ThgRdD9cnvifnQjTZ2LqyhGXWN49g== 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=iE42CLXPcC9MBAr6uBFfjcOSAX6gKN3X9WxWNVV9KOE=; b=EWwmfiYlnb6OVnPnV2gmdKEgO8AUIcF+jRiL46vWn9/mBW2jhRyA8QZA9YPlotPCLydYt5jXvYRN1/z7goMIdSvhoWamBTe8CoeXpRCBjsifcTnx4U4rKphAZdgQOuHqjCVKJxgyRJSAHY/9i2++QVzK7688PaDr4S49snBV+ek= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from AM6PR04MB4838.eurprd04.prod.outlook.com (2603:10a6:20b:4::16) by DB9PR04MB9750.eurprd04.prod.outlook.com (2603:10a6:10:4c5::8) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6907.25; Wed, 18 Oct 2023 21:58:33 +0000 Received: from AM6PR04MB4838.eurprd04.prod.outlook.com ([fe80::1774:e25f:f99:aca2]) by AM6PR04MB4838.eurprd04.prod.outlook.com ([fe80::1774:e25f:f99:aca2%4]) with mapi id 15.20.6907.022; Wed, 18 Oct 2023 21:58:33 +0000 From: Frank Li To: miquel.raynal@bootlin.com, conor.culhane@silvaco.com, alexandre.belloni@bootlin.com, robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, conor+dt@kernel.org, corbet@lwn.net, joe@perches.com, Frank.Li@nxp.com, linux-i3c@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org Cc: gregkh@linuxfoundation.org, imx@lists.linux.dev, jirislaby@kernel.org, linux-serial@vger.kernel.org Subject: [PATCH 1/5] i3c: add slave mode support Date: Wed, 18 Oct 2023 17:58:05 -0400 Message-Id: <20231018215809.3477437-2-Frank.Li@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231018215809.3477437-1-Frank.Li@nxp.com> References: <20231018215809.3477437-1-Frank.Li@nxp.com> X-ClientProxiedBy: SJ0PR03CA0225.namprd03.prod.outlook.com (2603:10b6:a03:39f::20) To AM6PR04MB4838.eurprd04.prod.outlook.com (2603:10a6:20b:4::16) Precedence: bulk X-Mailing-List: devicetree@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: AM6PR04MB4838:EE_|DB9PR04MB9750:EE_ X-MS-Office365-Filtering-Correlation-Id: e0ce8efa-7fcd-421b-bad0-08dbd0255ef1 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: evv4dQhobJsGELjkNMw5sBnjfp1IEpIuqu+M/xae1uHyIATmi03QCprZBCXb2fMkucTT03xXejvB4xrIqlr6VYjfPYN8H2scFaIfhIah1UMYjIXessZW243YBZZbm9knZJxyQW/qGr8VFTUWP2OCLz2UPUEHK+zB2mIl+6jiueJl+9pBX9duumn3g879UIm69FBvFAexPfKQRRUFX8wBARdZ8fHlHz0PqVeMtuz7E0RJ93Ql18N9+CojNY1J4tqgrmNg4um5fg4Muf06Rjng+OI9ges0ywHmT5FU4RsY2cmymub7PsHLlD3sqSVNaOOt/BlU3N0ldkCI9g4sSTnwiVWb6zj84QxbVX8Zzcgd2/djBY2feRDS5yKPIoB1DRC9S4ieUbaLl7sFGQ5aapF11bB1AGWdgxEiY/zrZynJjS6cAZiFsZjWfMOclMk3wDpDwQnJj/bk8Cdund+HmmLD3o75vyEKdtAR8pBnJ59sRw77nFgTBZ4yA5RcJsYnJCTkh2nCDNFUvTO8SHQAL3pTlEoWfVQhZfzGQ6Wxh9Huxia6IA8ug3h/7nWKZJBRh/tFi/ycuJdo3C1kkd06eZhaUuoV+Zk/HgY1QJrkpRgW2TaR27w/msXrDeBJCkmbhLlt+viCZUhj0ECXIkWeEGXrdA== X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:AM6PR04MB4838.eurprd04.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230031)(346002)(376002)(396003)(39860400002)(136003)(366004)(230922051799003)(1800799009)(451199024)(186009)(64100799003)(66946007)(66556008)(66476007)(316002)(6666004)(478600001)(6486002)(8936002)(8676002)(36756003)(5660300002)(41300700001)(4326008)(30864003)(2906002)(86362001)(7416002)(38100700002)(2616005)(52116002)(6512007)(1076003)(83380400001)(6506007)(26005)(921005)(38350700005)(579004); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: YGK8b2WP+elXsYezShrZhtLCMlPPNYzl9jSbDDYf3HIutVrVLjfCgwFGREu5tv+tAz7of+IiwoceaFNZCoOKqeLsDyN57pBcIwk6hqRpiy4xuxJSoYM3kE+OYMY1Hj2BBViZlzNxL6SY6DwKRIzxWGgHBOO1UL3bIdXk3cpQvzU5cHRz514ahs19O9UG+WnASW8Bq5WMKwn+pXWqVqiX5UknCdLcG2Vv3OB47po0EyTeIXx6T/JSu1nvg1MbBHDkpbQ17o62jvECzomo8b7z7VzoOfpHFEmRGaeJRpJ05/rInf6YmzHTcFP3mAMWGcbvcsJ8mCyl27ZGLc1ZNj+l6gzUk983zh49caULW8X+GtXV3aYR60rp5/7gSCexOszsprH6M6p0IZBAotxsT3RiPMt/SiK4wHLhzJvOoQI6o1YHfLlCN9OxmuBLpSFBI1IO3zHeRm5LKVWDOHT15x6Vw6/z8fCvzHBnsBvucBYIWO74fIAPqr56YgqnHAgGD008bvP+BcEMVN+hCEMfyTWhf6clTu1KSHoLFuqgaXx4b9FR+Eu02134h9SFVgI6y1xyDs8+De+RuFtZmOhRDhutc9wirMOUWStmpwUdoYYX40M8KY1TyoYQqUFt8r1/VrBAVlxYpQG4tdujfPfJx1IvQ/XUgTCO6431bik93Ha2P74L/i16I4AOkCIyogIKpt8FSCYCbYVCflnkmNFdzRxML2qVjJq4w9kntTG0sKF7PR3VVi95ydQIKo4ux6BRq84mTF8gJYkSezCpuRgcm10OsesQr5yo9SAZmqnnnuDvoORZpsYtdvfOpqC0uij0j1K8IQm6uINzMiu5i15zBsF5A0E1RBZgaH48qxoLcMZNu5OJWe/S4zM6gWKaVTs+yuvMm6BZbA+FFY/eliM2MHrqrd6EqjO8Em9eRWauoghpHEiLwAAovA1EFu89I/BnUN5QSXxG5fEWMUIssfFaK9PWeD2gRuorPdi94ufjTGkD+hLlPr2UMz45YphQiJ/fsy4r4DPV1sIJ4w5x3Pn5ewQsANv4fMGF3GHETK/kO0wg3CHknSIA30PixPrDKV2HGqsXIbZFeUl+keLGTrwn/pFnM1yExwxbxEFKlvnX+12gJ1yUIm6ic14tKVQLGE2VjHoyPZJikl/eXiu+hDz6lfOQSAR5X8YbfWXnvEvAyF5GwKCk5IhuQe6fFAaRtWGEQZHMnQ0VDEvelPwhI5cfY9t89PIevgDar2hoRy1TXBGPdbFecxDYrDBnVHUJpIIlL6cTIuI0rUA8/YD9wuiAL8GVx36LbLk4APQho9wB4Mh4UWajstuG3tXqWAkjX2VuoKjH3slTqbdCFci3o2WF6ioy7QTtxzEftZxxmnu43uLisLH1DGra5Wuw5e0Gwct9mIXDk1cvXnUSPO9s/zAuowlLEDH/jJGjy3xb55QbNmdegShbl9ys7Opevd4n1JEYUsJ0aPegjmCW/x4KBqN1pJzmHMAAx7OwQZ8RTrvxc8yB6qXY5k8m+/ObYZZgS2Dbx5d0p7imtHy5b8lQiDsruBFwwP/K0tUUiql4j9uwNgCIKo0yFO8zyfiP9/Za6+gWOJqp X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: e0ce8efa-7fcd-421b-bad0-08dbd0255ef1 X-MS-Exchange-CrossTenant-AuthSource: AM6PR04MB4838.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 18 Oct 2023 21:58:33.1419 (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: sDnKadh8Dkm2zB3G29ciVkvcbYXveOX+49JQUvqWhV0C3Y3J0QPADVXiNur4ghbkwqR593SXcJtuXKZ1Fb8O+Q== X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB9PR04MB9750 Introduce a new slave core layer in order to support slave functions in linux kernel. This comprises the controller library and function library. Controller library implements functions specific to an slave controller and function library implements functions specific to an slave function. Introduce a new configfs entry to configure the slave function configuring and bind the slave function with slave controller. Signed-off-by: Frank Li --- drivers/i3c/Kconfig | 26 ++ drivers/i3c/Makefile | 2 + drivers/i3c/i3c-cfs.c | 389 +++++++++++++++++++++++++++++ drivers/i3c/slave.c | 453 ++++++++++++++++++++++++++++++++++ include/linux/i3c/slave.h | 503 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 1373 insertions(+) create mode 100644 drivers/i3c/i3c-cfs.c create mode 100644 drivers/i3c/slave.c create mode 100644 include/linux/i3c/slave.h diff --git a/drivers/i3c/Kconfig b/drivers/i3c/Kconfig index 30a441506f61c..d5f5ca7cd6a56 100644 --- a/drivers/i3c/Kconfig +++ b/drivers/i3c/Kconfig @@ -22,3 +22,29 @@ menuconfig I3C if I3C source "drivers/i3c/master/Kconfig" endif # I3C + +config I3C_SLAVE + bool "I3C Slave Support" + help + Support I3C Slave Mode. + + Enable this configuration option to support configurable I3C slave. + This should be enabled if the platform has a I3C controller that can + operate in slave mode. + + Enabling this option will build the I3C slave library, which includes + slave controller library and slave function library. + + If in doubt, say "N" to disable slave support. + +config I3C_SLAVE_CONFIGFS + bool "I3C Slave Configfs Support" + depends on I3C_SLAVE + select CONFIGFS_FS + help + Configfs entry for slave function and controller. + + This will enable the configfs entry that can be used to configure + the slave function and used to bind the function with a slave + controller. + diff --git a/drivers/i3c/Makefile b/drivers/i3c/Makefile index 11982efbc6d91..6407ddec3a4a9 100644 --- a/drivers/i3c/Makefile +++ b/drivers/i3c/Makefile @@ -2,3 +2,5 @@ i3c-y := device.o master.o obj-$(CONFIG_I3C) += i3c.o obj-$(CONFIG_I3C) += master/ +obj-$(CONFIG_I3C_SLAVE) += slave.o +obj-$(CONFIG_I3C_SLAVE_CONFIGFS) += i3c-cfs.o diff --git a/drivers/i3c/i3c-cfs.c b/drivers/i3c/i3c-cfs.c new file mode 100644 index 0000000000000..35e07da50ebb0 --- /dev/null +++ b/drivers/i3c/i3c-cfs.c @@ -0,0 +1,389 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Configfs to configure the I3C Slave + * + * Copyright (C) 2023 NXP + * Author: Frank Li + */ + +#include +#include +#include +#include +#include + +static DEFINE_MUTEX(functions_mutex); +static struct config_group *functions_group; +static struct config_group *controllers_group; + +struct i3c_slave_func_group { + struct config_group group; + struct i3c_slave_func *func; +}; + +struct i3c_slave_ctrl_group { + struct config_group group; + struct i3c_slave_ctrl *ctrl; +}; + +static inline struct i3c_slave_func_group *to_i3c_slave_func_group(struct config_item *item) +{ + return container_of(to_config_group(item), struct i3c_slave_func_group, group); +} + +static inline struct i3c_slave_ctrl_group *to_i3c_slave_ctrl_group(struct config_item *item) +{ + return container_of(to_config_group(item), struct i3c_slave_ctrl_group, group); +} + +static int i3c_slave_ctrl_func_link(struct config_item *ctrl_cfg, struct config_item *func_cfg) +{ + struct i3c_slave_func_group *func_group = to_i3c_slave_func_group(func_cfg); + struct i3c_slave_ctrl_group *ctrl_group = to_i3c_slave_ctrl_group(ctrl_cfg); + struct i3c_slave_ctrl *ctrl = ctrl_group->ctrl; + struct i3c_slave_func *func = func_group->func; + int ret; + + ret = i3c_slave_ctrl_add_func(ctrl, func); + if (ret) + return ret; + + ret = i3c_slave_func_bind(func); + if (ret) { + i3c_slave_ctrl_remove_func(ctrl, func); + return ret; + } + + return 0; +} + +static void i3c_slave_ctrl_func_unlink(struct config_item *ctrl_cfg, struct config_item *func_cfg) +{ + struct i3c_slave_func_group *func_group = to_i3c_slave_func_group(func_cfg->ci_parent); + struct i3c_slave_ctrl_group *ctrl_group = to_i3c_slave_ctrl_group(ctrl_cfg); + struct i3c_slave_ctrl *ctrl = ctrl_group->ctrl; + struct i3c_slave_func *func = func_group->func; + + i3c_slave_func_unbind(func); + i3c_slave_ctrl_remove_func(ctrl, func); +} + +static ssize_t i3c_slave_ctrl_hotjoin_store(struct config_item *item, const char *page, size_t len) +{ + struct i3c_slave_ctrl_group *ctrl_group = to_i3c_slave_ctrl_group(item); + struct i3c_slave_ctrl *ctrl; + int ret; + + ctrl = ctrl_group->ctrl; + + ret = i3c_slave_ctrl_hotjoin(ctrl); + if (ret) { + dev_err(&ctrl->dev, "failed to hotjoin i3c slave controller\n"); + return -EINVAL; + } + + return len; +} + +static ssize_t i3c_slave_ctrl_hotjoin_show(struct config_item *item, char *page) +{ + return sysfs_emit(page, "%d\n", 0); +} + +CONFIGFS_ATTR(i3c_slave_ctrl_, hotjoin); + +static struct configfs_item_operations i3c_slave_ctrl_item_ops = { + .allow_link = i3c_slave_ctrl_func_link, + .drop_link = i3c_slave_ctrl_func_unlink, +}; + +static struct configfs_attribute *i3c_slave_ctrl_attrs[] = { + &i3c_slave_ctrl_attr_hotjoin, + NULL, +}; + +static const struct config_item_type i3c_slave_ctrl_type = { + .ct_item_ops = &i3c_slave_ctrl_item_ops, + .ct_attrs = i3c_slave_ctrl_attrs, + .ct_owner = THIS_MODULE, +}; + +/** + * i3c_slave_cfs_add_ctrl_group() - add I3C slave controller group + * @ctrl: I3C slave controller device + * + * Return: Pointer to struct config_group + */ +struct config_group *i3c_slave_cfs_add_ctrl_group(struct i3c_slave_ctrl *ctrl) +{ + struct i3c_slave_ctrl_group *ctrl_group; + struct config_group *group; + int ret; + + ctrl_group = kzalloc(sizeof(*ctrl_group), GFP_KERNEL); + if (!ctrl_group) { + ret = -ENOMEM; + goto err; + } + + group = &ctrl_group->group; + + config_group_init_type_name(group, dev_name(&ctrl->dev), &i3c_slave_ctrl_type); + ret = configfs_register_group(controllers_group, group); + if (ret) { + pr_err("failed to register configfs group for %s\n", dev_name(&ctrl->dev)); + goto err_register_group; + } + + ctrl_group->ctrl = ctrl; + + return group; + +err_register_group: + kfree(ctrl_group); + +err: + return ERR_PTR(ret); +} +EXPORT_SYMBOL(i3c_slave_cfs_add_ctrl_group); + +/** + * i3c_slave_cfs_remove_ctrl_group() - remove I3C slave controller group + * @group: the group to be removed + */ +void i3c_slave_cfs_remove_ctrl_group(struct config_group *group) +{ + struct i3c_slave_ctrl_group *ctrl_group; + + if (!group) + return; + + ctrl_group = container_of(group, struct i3c_slave_ctrl_group, group); + i3c_slave_ctrl_put(ctrl_group->ctrl); + configfs_unregister_group(&ctrl_group->group); + kfree(ctrl_group); +} +EXPORT_SYMBOL(i3c_slave_cfs_remove_ctrl_group); + +#define I3C_SLAVE_ATTR_R(_name) \ +static ssize_t i3c_slave_func_##_name##_show(struct config_item *item, char *page) \ +{ \ + struct i3c_slave_func *func = to_i3c_slave_func_group(item)->func; \ + return sysfs_emit(page, "0x%04x\n", func->_name); \ +} + +#define I3C_SLAVE_ATTR_W(_name, _u) \ +static ssize_t i3c_slave_func_##_name##_store(struct config_item *item, \ + const char *page, size_t len) \ +{ \ + _u val; \ + struct i3c_slave_func *func = to_i3c_slave_func_group(item)->func; \ + if (kstrto##_u(page, 0, &val) < 0) \ + return -EINVAL; \ + func->_name = val; \ + return len; \ +} + +I3C_SLAVE_ATTR_R(vendor_id); +I3C_SLAVE_ATTR_W(vendor_id, u16); +CONFIGFS_ATTR(i3c_slave_func_, vendor_id); + +I3C_SLAVE_ATTR_R(vendor_info); +I3C_SLAVE_ATTR_W(vendor_info, u16); +CONFIGFS_ATTR(i3c_slave_func_, vendor_info); + +I3C_SLAVE_ATTR_R(part_id); +I3C_SLAVE_ATTR_W(part_id, u16); +CONFIGFS_ATTR(i3c_slave_func_, part_id); + +I3C_SLAVE_ATTR_R(instance_id); +I3C_SLAVE_ATTR_W(instance_id, u8); +CONFIGFS_ATTR(i3c_slave_func_, instance_id); + +I3C_SLAVE_ATTR_R(ext_id); +I3C_SLAVE_ATTR_W(ext_id, u16); +CONFIGFS_ATTR(i3c_slave_func_, ext_id); + +I3C_SLAVE_ATTR_R(max_write_len); +I3C_SLAVE_ATTR_W(max_write_len, u16); +CONFIGFS_ATTR(i3c_slave_func_, max_write_len); + +I3C_SLAVE_ATTR_R(max_read_len); +I3C_SLAVE_ATTR_W(max_read_len, u16); +CONFIGFS_ATTR(i3c_slave_func_, max_read_len); + +I3C_SLAVE_ATTR_R(bcr); +I3C_SLAVE_ATTR_W(bcr, u8); +CONFIGFS_ATTR(i3c_slave_func_, bcr); + +I3C_SLAVE_ATTR_R(dcr); +I3C_SLAVE_ATTR_W(dcr, u8); +CONFIGFS_ATTR(i3c_slave_func_, dcr); + +static struct configfs_attribute *i3c_slave_func_attrs[] = { + &i3c_slave_func_attr_vendor_id, + &i3c_slave_func_attr_vendor_info, + &i3c_slave_func_attr_part_id, + &i3c_slave_func_attr_instance_id, + &i3c_slave_func_attr_ext_id, + &i3c_slave_func_attr_max_write_len, + &i3c_slave_func_attr_max_read_len, + &i3c_slave_func_attr_bcr, + &i3c_slave_func_attr_dcr, + NULL, +}; + +static const struct config_item_type i3c_slave_func_type = { + .ct_attrs = i3c_slave_func_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_group *i3c_slave_func_make(struct config_group *group, const char *name) +{ + struct i3c_slave_func_group *func_group; + struct i3c_slave_func *func; + int err; + + func_group = kzalloc(sizeof(*func_group), GFP_KERNEL); + if (!func_group) + return ERR_PTR(-ENOMEM); + + config_group_init_type_name(&func_group->group, name, &i3c_slave_func_type); + + func = i3c_slave_func_create(group->cg_item.ci_name, name); + if (IS_ERR(func)) { + pr_err("failed to create i3c slave function device\n"); + err = -EINVAL; + goto free_group; + } + + func->group = &func_group->group; + + func_group->func = func; + + return &func_group->group; + +free_group: + kfree(func_group); + + return ERR_PTR(err); +} + +static void i3c_slave_func_drop(struct config_group *group, struct config_item *item) +{ + config_item_put(item); +} + +static struct configfs_group_operations i3c_slave_func_group_ops = { + .make_group = &i3c_slave_func_make, + .drop_item = &i3c_slave_func_drop, +}; + +static const struct config_item_type i3c_slave_func_group_type = { + .ct_group_ops = &i3c_slave_func_group_ops, + .ct_owner = THIS_MODULE, +}; + +/** + * i3c_slave_cfs_add_func_group() - add I3C slave function group + * @name: group name + * + * Return: Pointer to struct config_group + */ +struct config_group *i3c_slave_cfs_add_func_group(const char *name) +{ + struct config_group *group; + + group = configfs_register_default_group(functions_group, name, + &i3c_slave_func_group_type); + if (IS_ERR(group)) + pr_err("failed to register configfs group for %s function\n", + name); + + return group; +} +EXPORT_SYMBOL(i3c_slave_cfs_add_func_group); + +/** + * i3c_slave_cfs_remove_func_group() - add I3C slave function group + * @group: group to be removed + */ +void i3c_slave_cfs_remove_func_group(struct config_group *group) +{ + if (IS_ERR_OR_NULL(group)) + return; + + configfs_unregister_default_group(group); +} +EXPORT_SYMBOL(i3c_slave_cfs_remove_func_group); + +static const struct config_item_type i3c_slave_controllers_type = { + .ct_owner = THIS_MODULE, +}; + +static const struct config_item_type i3c_slave_functions_type = { + .ct_owner = THIS_MODULE, +}; + +static const struct config_item_type i3c_slave_type = { + .ct_owner = THIS_MODULE, +}; + +static struct configfs_subsystem i3c_slave_cfs_subsys = { + .su_group = { + .cg_item = { + .ci_namebuf = "i3c_slave", + .ci_type = &i3c_slave_type, + }, + }, + .su_mutex = __MUTEX_INITIALIZER(i3c_slave_cfs_subsys.su_mutex), +}; + +static int __init i3c_slave_cfs_init(void) +{ + int ret; + struct config_group *root = &i3c_slave_cfs_subsys.su_group; + + config_group_init(root); + + ret = configfs_register_subsystem(&i3c_slave_cfs_subsys); + if (ret) { + pr_err("Error %d while registering subsystem %s\n", + ret, root->cg_item.ci_namebuf); + goto err; + } + + functions_group = configfs_register_default_group(root, "functions", + &i3c_slave_functions_type); + if (IS_ERR(functions_group)) { + ret = PTR_ERR(functions_group); + pr_err("Error %d while registering functions group\n", + ret); + goto err_functions_group; + } + + controllers_group = + configfs_register_default_group(root, "controllers", + &i3c_slave_controllers_type); + if (IS_ERR(controllers_group)) { + ret = PTR_ERR(controllers_group); + pr_err("Error %d while registering controllers group\n", + ret); + goto err_controllers_group; + } + + return 0; + +err_controllers_group: + configfs_unregister_default_group(functions_group); + +err_functions_group: + configfs_unregister_subsystem(&i3c_slave_cfs_subsys); + +err: + return ret; +} +module_init(i3c_slave_cfs_init); + +MODULE_DESCRIPTION("I3C FUNC CONFIGFS"); +MODULE_AUTHOR("Frank Li "); diff --git a/drivers/i3c/slave.c b/drivers/i3c/slave.c new file mode 100644 index 0000000000000..4d2cea1d09299 --- /dev/null +++ b/drivers/i3c/slave.c @@ -0,0 +1,453 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * configfs to configure the I3C Slave + * + * Copyright (C) 2023 NXP + * Author: Frank Li + */ + +#include +#include +#include +#include +#include + +static DEFINE_MUTEX(func_lock); +static struct class *i3c_slave_ctrl_class; + +static void i3c_slave_func_dev_release(struct device *dev) +{ + struct i3c_slave_func *func = to_i3c_slave_func(dev); + + kfree(func->name); + kfree(func); +} + +static const struct device_type i3c_slave_func_type = { + .release = i3c_slave_func_dev_release, +}; + +static int i3c_slave_func_match_driver(struct device *dev, struct device_driver *drv) +{ + return !strncmp(dev_name(dev), drv->name, strlen(drv->name)); +} + +static int i3c_slave_func_device_probe(struct device *dev) +{ + struct i3c_slave_func *func = to_i3c_slave_func(dev); + struct i3c_slave_func_driver *driver = to_i3c_slave_func_driver(dev->driver); + + if (!driver->probe) + return -ENODEV; + + func->driver = driver; + + return driver->probe(func); +} + +static void i3c_slave_func_device_remove(struct device *dev) +{ + struct i3c_slave_func *func = to_i3c_slave_func(dev); + struct i3c_slave_func_driver *driver = to_i3c_slave_func_driver(dev->driver); + + if (driver->remove) + driver->remove(func); + func->driver = NULL; +} + +static const struct bus_type i3c_slave_func_bus_type = { + .name = "i3c_slave_func", + .probe = i3c_slave_func_device_probe, + .remove = i3c_slave_func_device_remove, + .match = i3c_slave_func_match_driver, +}; + +static void i3c_slave_ctrl_release(struct device *dev) +{ + kfree(to_i3c_slave_ctrl(dev)); +} + +static void devm_i3c_slave_ctrl_release(struct device *dev, void *res) +{ + struct i3c_slave_ctrl *ctrl = *(struct i3c_slave_ctrl **)res; + + i3c_slave_ctrl_destroy(ctrl); +} + +struct i3c_slave_ctrl * +__devm_i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops, + struct module *owner) +{ + struct i3c_slave_ctrl **ptr, *ctrl; + + ptr = devres_alloc(devm_i3c_slave_ctrl_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + ctrl = __i3c_slave_ctrl_create(dev, ops, owner); + if (!IS_ERR(ctrl)) { + *ptr = ctrl; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return ctrl; +} + +static int devm_i3c_slave_ctrl_match(struct device *dev, void *res, void *match_data) +{ + struct i3c_slave_ctrl **ptr = res; + + return *ptr == match_data; +} + +/** + * __i3c_slave_ctrl_create() - create a new slave controller device + * @dev: device that is creating the new slave controller + * @ops: function pointers for performing slave controller operations + * @owner: the owner of the module that creates the slave controller device + * + * Return: Pointer to struct i3c_slave_ctrl + */ +struct i3c_slave_ctrl * +__i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops, + struct module *owner) +{ + struct i3c_slave_ctrl *ctrl; + int ret; + + if (WARN_ON(!dev)) + return ERR_PTR(-EINVAL); + + ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return ERR_PTR(-ENOMEM); + + device_initialize(&ctrl->dev); + ctrl->dev.class = i3c_slave_ctrl_class; + ctrl->dev.parent = dev; + ctrl->dev.release = i3c_slave_ctrl_release; + ctrl->ops = ops; + + ret = dev_set_name(&ctrl->dev, "%s", dev_name(dev)); + if (ret) + goto put_dev; + + ret = device_add(&ctrl->dev); + if (ret) + goto put_dev; + + ctrl->group = i3c_slave_cfs_add_ctrl_group(ctrl); + if (!ctrl->group) + goto put_dev; + + return ctrl; + +put_dev: + put_device(&ctrl->dev); + kfree(ctrl); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(__i3c_slave_ctrl_create); + +/** + * devm_i3c_slave_ctrl_destroy() - destroy the slave controller device + * @dev: device that is creating the new slave controller device + * @ops: function pointers for performing slave controller operations + * @owner: the owner of the module that creates the slave controller device + * + * Invoke to create a new slave controller device and add it to i3c_slave class. While at that, it + * also associates the device with the i3c_slave using devres. On driver detach, release function is + * invoked on the devres data, then devres data is freed. + */ +void devm_i3c_slave_ctrl_destroy(struct device *dev, struct i3c_slave_ctrl *ctrl) +{ + int r; + + r = devres_destroy(dev, devm_i3c_slave_ctrl_release, devm_i3c_slave_ctrl_match, + ctrl); + dev_WARN_ONCE(dev, r, "couldn't find I3C controller resource\n"); +} +EXPORT_SYMBOL_GPL(devm_i3c_slave_ctrl_destroy); + +/** + * i3c_slave_ctrl_destroy() - destroy the slave controller device + * @ctrl: the slave controller device that has to be destroyed + * + * Invoke to destroy the I3C slave device + */ +void i3c_slave_ctrl_destroy(struct i3c_slave_ctrl *ctrl) +{ + i3c_slave_cfs_remove_ctrl_group(ctrl->group); + device_unregister(&ctrl->dev); +} +EXPORT_SYMBOL_GPL(i3c_slave_ctrl_destroy); + +/** + * i3c_slave_ctrl_add_func() - bind I3C slave function to an slave controller + * @ctrl: the controller device to which the slave function should be added + * @func: the slave function to be added + * + * An I3C slave device can have only one functions. + */ +int i3c_slave_ctrl_add_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func) +{ + if (ctrl->func) + return -EBUSY; + + ctrl->func = func; + func->ctrl = ctrl; + + return 0; +} +EXPORT_SYMBOL_GPL(i3c_slave_ctrl_add_func); + +/** + * i3c_slave_ctrl_remove_func() - unbind I3C slave function to an slave controller + * @ctrl: the controller device to which the slave function should be removed + * @func: the slave function to be removed + * + * An I3C slave device can have only one functions. + */ +void i3c_slave_ctrl_remove_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func) +{ + ctrl->func = NULL; +} +EXPORT_SYMBOL_GPL(i3c_slave_ctrl_remove_func); + +/** + * i3c_slave_ctrl() - get the I3C slave controller + * @name: device name of the slave controller + * + * Invoke to get struct i3c_slave_ctrl * corresponding to the device name of the + * slave controller + */ +struct i3c_slave_ctrl *i3c_slave_ctrl_get(const char *name) +{ + int ret = -EINVAL; + struct i3c_slave_ctrl *ctrl; + struct device *dev; + struct class_dev_iter iter; + + class_dev_iter_init(&iter, i3c_slave_ctrl_class, NULL, NULL); + while ((dev = class_dev_iter_next(&iter))) { + if (strcmp(name, dev_name(dev))) + continue; + + ctrl = to_i3c_slave_ctrl(dev); + if (!try_module_get(ctrl->ops->owner)) { + ret = -EINVAL; + goto err; + } + + class_dev_iter_exit(&iter); + get_device(&ctrl->dev); + return ctrl; + } + +err: + class_dev_iter_exit(&iter); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(i3c_slave_ctrl_get); + +/** + * i3c_slave_ctrl_put() - release the I3C endpoint controller + * @ctrl: slave controller returned by pci_slave_get() + * + * release the refcount the caller obtained by invoking i3c_slave_ctrl_get() + */ +void i3c_slave_ctrl_put(struct i3c_slave_ctrl *ctrl) +{ + if (!ctrl || IS_ERR(ctrl)) + return; + + module_put(ctrl->ops->owner); + put_device(&ctrl->dev); +} +EXPORT_SYMBOL_GPL(i3c_slave_ctrl_put); + +/** + * i3c_slave_ctrl_hotjoin() - trigger device hotjoin + * @ctrl: slave controller + * + * return: 0: success, others failure + */ +int i3c_slave_ctrl_hotjoin(struct i3c_slave_ctrl *ctrl) +{ + if (!ctrl || IS_ERR(ctrl)) + return -EINVAL; + + if (!ctrl->ops->hotjoin) + return -EINVAL; + + return ctrl->ops->hotjoin(ctrl); +} +EXPORT_SYMBOL_GPL(i3c_slave_ctrl_hotjoin); + +/** + * i3c_slave_func_bind() - Notify the function driver that the function device has been bound to a + * controller device + * @func: the function device which has been bound to the controller device + * + * Invoke to notify the function driver that it has been bound to a controller device + */ +int i3c_slave_func_bind(struct i3c_slave_func *func) +{ + struct device *dev = &func->dev; + int ret; + + if (!func->driver) { + dev_WARN(dev, "func device not bound to driver\n"); + return -EINVAL; + } + + if (!try_module_get(func->driver->owner)) + return -EAGAIN; + + mutex_lock(&func->lock); + ret = func->driver->ops->bind(func); + if (!ret) + func->is_bound = true; + mutex_unlock(&func->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(i3c_slave_func_bind); + +/** + * i3c_slave_func_unbind() - Notify the function driver that the binding between the function device + * and controller device has been lost + * @func: the function device which has lost the binding with the controller device + * + * Invoke to notify the function driver that the binding between the function device and controller + * device has been lost. + */ +void i3c_slave_func_unbind(struct i3c_slave_func *func) +{ + if (!func->driver) { + dev_WARN(&func->dev, "func device not bound to driver\n"); + return; + } + + mutex_lock(&func->lock); + if (func->is_bound) + func->driver->ops->unbind(func); + mutex_unlock(&func->lock); + + module_put(func->driver->owner); +} +EXPORT_SYMBOL_GPL(i3c_slave_func_unbind); + +/** + * i3c_slave_func_create() - create a new I3C function device + * @drv_name: the driver name of the I3C function device. + * @name: the name of the function device. + * + * Invoke to create a new I3C function device by providing the name of the function device. + */ +struct i3c_slave_func *i3c_slave_func_create(const char *drv_name, const char *name) +{ + struct i3c_slave_func *func; + struct device *dev; + int ret; + + func = kzalloc(sizeof(*func), GFP_KERNEL); + if (!func) + return ERR_PTR(-ENOMEM); + + dev = &func->dev; + device_initialize(dev); + dev->bus = &i3c_slave_func_bus_type; + dev->type = &i3c_slave_func_type; + mutex_init(&func->lock); + + ret = dev_set_name(dev, "%s.%s", drv_name, name); + if (ret) { + put_device(dev); + return ERR_PTR(ret); + } + + ret = device_add(dev); + if (ret) { + put_device(dev); + return ERR_PTR(ret); + } + + return func; +} +EXPORT_SYMBOL_GPL(i3c_slave_func_create); + +/** + * __i3c_slave_func_register_driver() - register a new I3C function driver + * @driver: structure representing I3C function driver + * @owner: the owner of the module that registers the I3C function driver + * + * Invoke to register a new I3C function driver. + */ +int __i3c_slave_func_register_driver(struct i3c_slave_func_driver *driver, struct module *owner) +{ + int ret = -EEXIST; + + if (!driver->ops) + return -EINVAL; + + if (!driver->ops->bind || !driver->ops->unbind) + return -EINVAL; + + driver->driver.bus = &i3c_slave_func_bus_type; + driver->driver.owner = owner; + + ret = driver_register(&driver->driver); + if (ret) + return ret; + + i3c_slave_cfs_add_func_group(driver->driver.name); + + return 0; +} +EXPORT_SYMBOL_GPL(__i3c_slave_func_register_driver); + +/** + * i3c_slave_func_unregister_driver() - unregister the I3C function driver + * @driver: the I3C function driver that has to be unregistered + * + * Invoke to unregister the I3C function driver. + */ +void i3c_slave_func_unregister_driver(struct i3c_slave_func_driver *fd) +{ + mutex_lock(&func_lock); + mutex_unlock(&func_lock); +} +EXPORT_SYMBOL_GPL(i3c_slave_func_unregister_driver); + +static int __init i3c_slave_init(void) +{ + int ret; + + i3c_slave_ctrl_class = class_create("i3c_slave"); + if (IS_ERR(i3c_slave_ctrl_class)) { + pr_err("failed to create i3c slave class --> %ld\n", PTR_ERR(i3c_slave_ctrl_class)); + return PTR_ERR(i3c_slave_ctrl_class); + } + + ret = bus_register(&i3c_slave_func_bus_type); + if (ret) { + class_destroy(i3c_slave_ctrl_class); + pr_err("failed to register i3c slave func bus --> %d\n", ret); + return ret; + } + + return 0; +} +module_init(i3c_slave_init); + +static void __exit i3c_slave_exit(void) +{ + class_destroy(i3c_slave_ctrl_class); + bus_unregister(&i3c_slave_func_bus_type); +} +module_exit(i3c_slave_exit); + diff --git a/include/linux/i3c/slave.h b/include/linux/i3c/slave.h new file mode 100644 index 0000000000000..19eb71cfa81f2 --- /dev/null +++ b/include/linux/i3c/slave.h @@ -0,0 +1,503 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2023 NXP. + * + * Author: Frank Li + */ + +#ifndef I3C_SLAVE_H +#define I3C_SLAVE_H + +#include + +struct i3c_slave_func; +struct i3c_slave_ctrl; + +/** + * struct i3c_slave_func_ops - set of function pointers for performing i3c slave function operations + * @bind: ops to perform when a controller device has been bound to function device + * @unbind: ops to perform when a binding has been lost between a controller device and function + * device + */ +struct i3c_slave_func_ops { + int (*bind)(struct i3c_slave_func *func); + void (*unbind)(struct i3c_slave_func *func); +}; + +/** + * struct i3c_slave_func_driver - represents the I3C function driver + * @probe: ops to perform when a new function device has been bound to the function driver + * @remove: ops to perform when the binding between the function device and function driver is + * broken + * @driver: I3C Function driver + * @ops: set of function pointers for performing function operations + * @owner: the owner of the module that registers the I3C function driver + * @epf_group: list of configfs group corresponding to the I3C function driver + */ +struct i3c_slave_func_driver { + int (*probe)(struct i3c_slave_func *func); + void (*remove)(struct i3c_slave_func *func); + + char *name; + struct device_driver driver; + struct i3c_slave_func_ops *ops; + struct module *owner; +}; + +/** + * struct i3c_slave_func - represents the I3C function device + * @dev: the I3C function device + * @name: the name of the I3C function device + * @driver: the function driver to which this function device is bound + * @group: configfs group associated with the EPF device + * @lock: mutex to protect i3c_slave_func_ops + * @ctrl: binded I3C controller device + * @is_bound: indicates if bind notification to function driver has been invoked + * @vednor_id: vendor id + * @part_id: part id + * @instance_id: instance id + * @ext_id: ext id + * @vendor_info: vendor info + * @static_addr: static address for I2C. It is 0 for I3C. + * @max_write_len: maximum write length + * @max_read_len: maximum read length + * @bcr: bus characteristics register (BCR) + * @dcr: device characteristics register (DCR) + */ +struct i3c_slave_func { + struct device dev; + char *name; + struct i3c_slave_func_driver *driver; + struct config_group *group; + /* mutex to protect against concurrent access of i3c_slave_func_ops */ + struct mutex lock; + struct i3c_slave_ctrl *ctrl; + bool is_bound; + + u16 vendor_id; + u16 part_id; + u8 instance_id; + u16 ext_id; + u8 vendor_info; + u16 static_addr; + u16 max_write_len; //0 is hardware default max value + u16 max_read_len; //0 is hardware default max value + u8 bcr; + u8 dcr; +}; + +enum i3c_request_stat { + I3C_REQUEST_OKAY, + I3C_REQUEST_PARTIAL, + I3C_REQUEST_ERR, + I3C_REQUEST_CANCEL, +}; + +/** + * struct i3c_request - represents the an I3C transfer request + * @buf: data buffer + * @length: data length + * @complete: call back function when request finished or cancelled + * @context: general data for complete callback function + * @status: transfer status + * @actual: how much actually transferred + * @ctrl: I3C slave controller associate with this request + * @tx: transfer direction, 1: slave to master, 0: master to slave + */ +struct i3c_request { + void *buf; + unsigned int length; + + void (*complete)(struct i3c_request *req); + void *context; + struct list_head list; + + enum i3c_request_stat status; + unsigned int actual; + struct i3c_slave_ctrl *ctrl; + bool tx; +}; + +/** + * struct i3c_slave_ctrl_features - represents I3C slave controller features. + * @tx_fifo_sz: tx hardware fifo size + * @rx_fifo_sz: rx hardware fifo size + */ +struct i3c_slave_ctrl_features { + u32 tx_fifo_sz; + u32 rx_fifo_sz; +}; + +/** + * struct i3c_slave_ctrl_ops - set of function pointers for performing controller operations + * @set_config: set I3C controller configuration + * @enable: enable I3C controller + * @disable: disable I3C controller + * @raise_ibi: rasie IBI interrupt to master + * @queue: queue an I3C transfer + * @dequeue: dequeue an I3C transfer + * @cancel_all_reqs: call all pending requests + * @fifo_status: current FIFO status + * @fifo_flush: flush hardware FIFO + * @get_features: ops to get the features supported by the I3C slave controller + * @owner: the module owner containing the ops + */ +struct i3c_slave_ctrl_ops { + int (*set_config)(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func); + int (*enable)(struct i3c_slave_ctrl *ctrl); + int (*disable)(struct i3c_slave_ctrl *ctrl); + int (*raise_ibi)(struct i3c_slave_ctrl *ctrl, void *p, u8 size); + + struct i3c_request *(*alloc_request)(struct i3c_slave_ctrl *ctrl, gfp_t gfp_flags); + void (*free_request)(struct i3c_request *req); + + int (*queue)(struct i3c_request *req, gfp_t gfp_flags); + int (*dequeue)(struct i3c_request *req); + + void (*cancel_all_reqs)(struct i3c_slave_ctrl *ctrl, bool tx); + + int (*fifo_status)(struct i3c_slave_ctrl *ctrl, bool tx); + void (*fifo_flush)(struct i3c_slave_ctrl *ctrl, bool tx); + int (*hotjoin)(struct i3c_slave_ctrl *ctrl); + int (*set_status_format1)(struct i3c_slave_ctrl *ctrl, u16 status); + u16 (*get_status_format1)(struct i3c_slave_ctrl *ctrl); + u8 (*get_addr)(struct i3c_slave_ctrl *ctrl); + const struct i3c_slave_ctrl_features *(*get_features)(struct i3c_slave_ctrl *ctrl); + struct module *owner; +}; + +/** + * struct i3c_slave_ctrl - represents the I3C slave device + * @dev: I3C slave device + * @ops: function pointers for performing endpoint operations + * @func: slave functions present in this controller device + * @group: configfs group representing the I3C controller device + */ +struct i3c_slave_ctrl { + struct device dev; + const struct i3c_slave_ctrl_ops *ops; + struct i3c_slave_func *func; + struct config_group *group; +}; + +/** + * i3c_slave_ctrl_raise_ibi() - Raise IBI to master + * @ctrl: I3C slave controller + * @p: optional data for IBI + * @size: size of optional data + * + * Returns: Zero for success, or an error code in case of failure + */ +static inline int i3c_slave_ctrl_raise_ibi(struct i3c_slave_ctrl *ctrl, void *p, u8 size) +{ + if (ctrl && ctrl->ops && ctrl->ops->raise_ibi) + return ctrl->ops->raise_ibi(ctrl, p, size); + + return -EINVAL; +} + +/** + * i3c_slave_ctrl_cancel_all_reqs() - Cancel all pending request + * @ctrl: I3C slave controller + * @tx: Transfer diretion queue + * @size: size of optional data + */ +static inline void i3c_slave_ctrl_cancel_all_reqs(struct i3c_slave_ctrl *ctrl, bool tx) +{ + if (ctrl && ctrl->ops && ctrl->ops->cancel_all_reqs) + ctrl->ops->cancel_all_reqs(ctrl, tx); +} + +/** + * i3c_slave_ctrl_set_config() - Set controller configuration + * @ctrl: I3C slave controller device + * @func: Function device + * + * Returns: Zero for success, or an error code in case of failure + */ +static inline int +i3c_slave_ctrl_set_config(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func) +{ + if (ctrl && ctrl->ops && ctrl->ops->set_config) + return ctrl->ops->set_config(ctrl, func); + + return -EINVAL; +} + +/** + * i3c_slave_ctrl_enable() - Enable I3C controller + * @ctrl: I3C slave controller device + * + * Returns: Zero for success, or an error code in case of failure + */ +static inline int +i3c_slave_ctrl_enable(struct i3c_slave_ctrl *ctrl) +{ + if (ctrl && ctrl->ops && ctrl->ops->enable) + return ctrl->ops->enable(ctrl); + + return -EINVAL; +} + +/** + * i3c_slave_ctrl_disable() - Disable I3C controller + * @ctrl: I3C slave controller device + * + * Returns: Zero for success, or an error code in case of failure + */ +static inline int +i3c_slave_ctrl_disable(struct i3c_slave_ctrl *ctrl) +{ + if (ctrl && ctrl->ops && ctrl->ops->disable) + return ctrl->ops->disable(ctrl); + + return -EINVAL; +} + +/** + * i3c_slave_ctrl_alloc_request() - Alloc an I3C transfer + * @ctrl: I3C slave controller device + * gfp_flags: additional gfp flags used when allocating the buffers + * + * Returns: Zero for success, or an error code in case of failure + */ +static inline struct i3c_request * +i3c_slave_ctrl_alloc_request(struct i3c_slave_ctrl *ctrl, gfp_t gfp_flags) +{ + struct i3c_request *req = NULL; + + if (ctrl && ctrl->ops && ctrl->ops->alloc_request) + req = ctrl->ops->alloc_request(ctrl, gfp_flags); + else + req = kzalloc(sizeof(*req), gfp_flags); + + if (req) + req->ctrl = ctrl; + + return req; +} + +/** + * i3c_slave_ctrl_free_request() - Free an I3C transfer + * @ctrl: I3C slave controller device + * + * Returns: Zero for success, or an error code in case of failure + */ +static inline void +i3c_slave_ctrl_free_request(struct i3c_request *req) +{ + struct i3c_slave_ctrl *ctrl; + + if (!req) + return; + + ctrl = req->ctrl; + if (ctrl && ctrl->ops && ctrl->ops->free_request) + ctrl->ops->free_request(req); + else + kfree(req); +} + +/** + * i3c_slave_ctrl_queue() - Queue an I3C transfer + * @ctrl: I3C slave controller device + * gfp_flags: additional gfp flags used when allocating the buffers + * + * Returns: Zero for success, or an error code in case of failure + */ +static inline int +i3c_slave_ctrl_queue(struct i3c_request *req, gfp_t gfp_flags) +{ + struct i3c_slave_ctrl *ctrl; + int ret = -EINVAL; + + if (!req) + return -EINVAL; + + ctrl = req->ctrl; + + req->actual = 0; + req->status = 0; + if (ctrl && ctrl->ops && ctrl->ops->queue) + ret = ctrl->ops->queue(req, gfp_flags); + + return ret; +} + +/** + * i3c_slave_ctrl_dequeue() - Dequeue an I3C transfer + * @ctrl: I3C slave controller device + * + * Returns: Zero for success, or an error code in case of failure + */ +static inline int +i3c_slave_ctrl_dequeue(struct i3c_request *req) +{ + struct i3c_slave_ctrl *ctrl; + int ret = -EINVAL; + + if (!req) + return -EINVAL; + + ctrl = req->ctrl; + if (ctrl && ctrl->ops && ctrl->ops->dequeue) + ret = ctrl->ops->dequeue(req); + + return ret; +} + +/** + * i3c_slave_ctrl_fifo_status() - Get controller FIFO status + * @ctrl: I3C slave controller device + * @tx: 1: Slave to master, 0: master to slave + * + * Returns: How much data in FIFO + */ +static inline int +i3c_slave_ctrl_fifo_status(struct i3c_slave_ctrl *ctrl, bool tx) +{ + if (ctrl && ctrl->ops && ctrl->ops->fifo_status) + return ctrl->ops->fifo_status(ctrl, tx); + + return 0; +} + +/** + * i3c_slave_ctrl_fifo_flush() - Flush controller FIFO + * @ctrl: I3C slave controller device + * @tx: 1: Slave to master, 0: master to slave + * + */ +static inline void +i3c_slave_ctrl_fifo_flush(struct i3c_slave_ctrl *ctrl, bool tx) +{ + if (ctrl && ctrl->ops && ctrl->ops->fifo_flush) + return ctrl->ops->fifo_flush(ctrl, tx); +} + +/** + * i3c_slave_ctrl_get_features() - Get controller supported features + * @ctrl: I3C slave controller device + * + * Returns: The pointer to struct i3c_slave_ctrl_features + */ +static inline const struct i3c_slave_ctrl_features* +i3c_slave_ctrl_get_features(struct i3c_slave_ctrl *ctrl) +{ + if (ctrl && ctrl->ops && ctrl->ops->get_features) + return ctrl->ops->get_features(ctrl); + + return NULL; +} + +/** + * i3c_slave_set_status_format1() - Set controller supported features + * &ctrl: I3C slave controller device + * &status: I3C GETSTATUS format1 + * + * Returns: Zero for success, or an error code in case of failure + */ +static inline int +i3c_slave_ctrl_set_status_format1(struct i3c_slave_ctrl *ctrl, u16 status) +{ + if (ctrl && ctrl->ops && ctrl->ops->set_status_format1) + return ctrl->ops->set_status_format1(ctrl, status); + + return -EINVAL; +} + +/** + * i3c_slave_get_status_format1() - Get controller supported features + * &ctrl: I3C slave controller device + * + * Return: I3C GETSTATUS format1 + */ +static inline u16 +i3c_slave_ctrl_get_status_format1(struct i3c_slave_ctrl *ctrl) +{ + if (ctrl && ctrl->ops && ctrl->ops->get_status_format1) + return ctrl->ops->get_status_format1(ctrl); + + return 0; +} + +/** + * i3c_slave_ctrl_get_addr() - Get controller address + * &ctrl: I3C slave controller device + * + * Return: address + */ +static inline u8 i3c_slave_ctrl_get_addr(struct i3c_slave_ctrl *ctrl) +{ + if (ctrl && ctrl->ops && ctrl->ops->get_addr) + return ctrl->ops->get_addr(ctrl); + + return 0; +} + +#define to_i3c_slave_ctrl(device) container_of((device), struct i3c_slave_ctrl, dev) + +#define to_i3c_slave_func(func_dev) container_of((func_dev), struct i3c_slave_func, dev) +#define to_i3c_slave_func_driver(drv) (container_of((drv), struct i3c_slave_func_driver, driver)) + +#define i3c_slave_ctrl_create(dev, ops) \ + __i3c_slave_ctrl_create((dev), (ops), THIS_MODULE) +#define devm_i3c_slave_ctrl_create(dev, ops) \ + __devm_i3c_slave_ctrl_create((dev), (ops), THIS_MODULE) + +struct i3c_slave_ctrl * +__devm_i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops, + struct module *owner); +struct i3c_slave_ctrl * +__i3c_slave_ctrl_create(struct device *dev, const struct i3c_slave_ctrl_ops *ops, + struct module *owner); + +void devm_i3c_slave_ctrl_destroy(struct device *dev, struct i3c_slave_ctrl *epc); +void i3c_slave_ctrl_destroy(struct i3c_slave_ctrl *epc); + +int i3c_slave_ctrl_add_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func); +void i3c_slave_ctrl_remove_func(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func); +int i3c_slave_ctrl_hotjoin(struct i3c_slave_ctrl *ctrl); + +struct config_group *i3c_slave_cfs_add_ctrl_group(struct i3c_slave_ctrl *ctrl); + +void i3c_slave_cfs_remove_ctrl_group(struct config_group *group); +struct config_group *i3c_slave_cfs_add_func_group(const char *name); +void i3c_slave_cfs_remove_func_group(struct config_group *group); +struct i3c_slave_ctrl *i3c_slave_ctrl_get(const char *name); +void i3c_slave_ctrl_put(struct i3c_slave_ctrl *ctrl); + +int i3c_slave_func_bind(struct i3c_slave_func *func); +void i3c_slave_func_unbind(struct i3c_slave_func *func); +struct i3c_slave_func *i3c_slave_func_create(const char *drv_name, const char *name); + +#define i3c_slave_func_register_driver(drv) \ + __i3c_slave_func_register_driver(drv, THIS_MODULE) + +int __i3c_slave_func_register_driver(struct i3c_slave_func_driver *drv, struct module *owner); +void i3c_slave_func_unregister_driver(struct i3c_slave_func_driver *drv); + +#define DECLARE_I3C_SLAVE_FUNC(_name, _probe, _remove, _ops) \ + static struct i3c_slave_func_driver _name ## i3c_func = { \ + .driver.name = __stringify(_name), \ + .owner = THIS_MODULE, \ + .probe = _probe, \ + .remove = _remove, \ + .ops = _ops \ + }; \ + MODULE_ALIAS("i3cfunc:" __stringify(_name)) + +#define DECLARE_I3C_SLAVE_INIT(_name, _probe, _remove, _ops) \ + DECLARE_I3C_SLAVE_FUNC(_name, _probe, _remove, _ops); \ + static int __init _name ## mod_init(void) \ + { \ + return i3c_slave_func_register_driver(&_name ## i3c_func); \ + } \ + static void __exit _name ## mod_exit(void) \ + { \ + i3c_slave_func_unregister_driver(&_name ## i3c_func); \ + } \ + module_init(_name ## mod_init); \ + module_exit(_name ## mod_exit) + +#endif From patchwork Wed Oct 18 21:58:06 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Frank Li X-Patchwork-Id: 735274 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (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 DC60D3FE42; Wed, 18 Oct 2023 21:58:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=nxp.com header.i=@nxp.com header.b="d/fnirVB" Received: from EUR02-VI1-obe.outbound.protection.outlook.com (mail-vi1eur02on2072.outbound.protection.outlook.com [40.107.241.72]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id EEC5912D; Wed, 18 Oct 2023 14:58:42 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=iDBcCKwZuWm0X4vzdPqKjhubmPP503LwcK71KyfIatAjtH1GYnMmCLexBygIbt87msxd//Zi9+hq3GlxOBigmez8R1VLZUgOm7GgqF9yZU2ZJexR8aabyR9XkJiwEpqMSKV2Jga6Epg1jFtNg06Y3j0bbH9K5cvEBr3ce0pb81hixCWkpOuCgIin3QLRz17tdRAava9EhrK3FdSma6Y0Eu3P90tdr4eeRrkQHWSiqHDVZdH/lpKkqdv9nb9kPONCPo8Q+Gq/41kK4MbeHnQoNGtplIlgqxhYOXHG/2DFLYi9t8GRsyxJMsF1RpZFlA0r/oFlLpMits7fn52F/QgE/w== 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=wdZfnlvizsuA9+zWpwMq8T6LU1CeI/2pm0F3LHVvLm4=; b=aW258WYc2PYAHuK+6aAjKJRysA++t9mzY/mAIGOEcPcxPo3Ccz9fhcqJZzMKAi2CFrx6EQbhvi7W40cYMQdWf4CpbfTKCOwaTV9f2A9IV1jkMVe8R7+gmGqkRHh8A+kVuK9GVL9bl8SN0jllQLrDQq1z06WNPeUM9C+ctbZPTCxXGjDDyfP2nHanpgnSo3J/nRhDRCsznL8JwSgHRFclIaDF8FnCiilJgRNyr8WBGeI/ZO4sMtNYphSqDi6g3CSA5VQCgE/xTbJ0OBnNwYPm5ZgYxGGmnJkQx7sG+wspJSCTg6SAEoOqDz9+nEkzTl9Of9sK2Mx2g8jdIJKh0mTC/A== 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=wdZfnlvizsuA9+zWpwMq8T6LU1CeI/2pm0F3LHVvLm4=; b=d/fnirVBn02TZU9T+MIOaLPP8qOSGRQMV+tyGd9fxgCSP4ox70D5qISRaKSD9L4rEltTa+F2fXgvgQM2gWp3B2jTLzT8dhAUXwwn4/Zh59nVgfUqm232/uxK6nd0KtldLgZ8yh96AMpmEtD9aCaNa6PaIqmfpXMDm5Q+NHX9NXQ= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from AM6PR04MB4838.eurprd04.prod.outlook.com (2603:10a6:20b:4::16) by DB9PR04MB9750.eurprd04.prod.outlook.com (2603:10a6:10:4c5::8) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6907.25; Wed, 18 Oct 2023 21:58:37 +0000 Received: from AM6PR04MB4838.eurprd04.prod.outlook.com ([fe80::1774:e25f:f99:aca2]) by AM6PR04MB4838.eurprd04.prod.outlook.com ([fe80::1774:e25f:f99:aca2%4]) with mapi id 15.20.6907.022; Wed, 18 Oct 2023 21:58:37 +0000 From: Frank Li To: miquel.raynal@bootlin.com, conor.culhane@silvaco.com, alexandre.belloni@bootlin.com, robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, conor+dt@kernel.org, corbet@lwn.net, joe@perches.com, Frank.Li@nxp.com, linux-i3c@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org Cc: gregkh@linuxfoundation.org, imx@lists.linux.dev, jirislaby@kernel.org, linux-serial@vger.kernel.org Subject: [PATCH 2/5] dt-bindings: i3c: svc: add compatible string i3c: silvaco,i3c-slave Date: Wed, 18 Oct 2023 17:58:06 -0400 Message-Id: <20231018215809.3477437-3-Frank.Li@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231018215809.3477437-1-Frank.Li@nxp.com> References: <20231018215809.3477437-1-Frank.Li@nxp.com> X-ClientProxiedBy: SJ0PR03CA0225.namprd03.prod.outlook.com (2603:10b6:a03:39f::20) To AM6PR04MB4838.eurprd04.prod.outlook.com (2603:10a6:20b:4::16) Precedence: bulk X-Mailing-List: devicetree@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: AM6PR04MB4838:EE_|DB9PR04MB9750:EE_ X-MS-Office365-Filtering-Correlation-Id: 04be4e98-8c9d-476e-752e-08dbd0256175 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: V0A0F124HK4nJNU6Sy48glFsXSX5+CTlLrhdzSMd9acdevtNdIuDl59UPeYYLfSzzQkBk0/yCR+ZoD7NP1KbNDk2acbARizZstoZIKEFp8C7OcdPebrAyZ2jqF7xPQ21oqFSGI07k0dTYkHeublIdwaoR4ExREmZyK/4bI7t1PlEHK4F+nwF+L+zs8OJR3SymxN4/OQ8ZgCzGU4II16ut9QyVwMO6DQcojusMijgco04yCc4q6WiB8J5fBrULQ8zR2SvSM++4/nEaW/R9zC6wUttizjiiKtY/fG7PoguUk1igTQntiE7Zo+6TIMTAIxtbvOitAZUE5y4N8pbLPhOglx7DMDcdwJYBcmKTEBgV+5w5U+HO52A+cYkhl3+0dC2fgZgadaCYcjU0iDBoJqbL3ywLih2t7J0UBLSev63RD2GqxEK7qs6vsdmJtSEJEpITneka3DZ7PSaDlnzH0AniIhpKwxpLyruQuVjWPpt7ZA2qiQTlV2N6ZCMlV3xhW//MacFqvTDTPT0iGcxxQoOemOZeTRFpJ6LjC547XPIYFgGZaFtE2WI06mEKTtyYP9SWKxKR9l8tIr1izBl+TNNuShEgwbQ+EuLfMSqwgNgqiuPlqZXDK2YbFTXMzUKfd/t X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:AM6PR04MB4838.eurprd04.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230031)(346002)(376002)(396003)(39860400002)(136003)(366004)(230922051799003)(1800799009)(451199024)(186009)(64100799003)(66946007)(66556008)(66476007)(316002)(6666004)(478600001)(6486002)(966005)(8936002)(8676002)(36756003)(5660300002)(41300700001)(4326008)(4744005)(2906002)(86362001)(7416002)(38100700002)(2616005)(52116002)(6512007)(1076003)(83380400001)(6506007)(26005)(921005)(38350700005); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: Fk94E/FCKhHguchspHvP/s5w6Cjp4HblN6gErTQ0rYpuaPpfmplZAyXWdPOQUItEQVVcLMBLYGFKSBSrNGmFE+NzzR2ixHIml0xEgenugpfRAOfaKhD2HqUdDkafdzHywTMm0IJbWPi5PAejcSLO+3z5wmNgAc/LHGquFcEEYNuwVyBcdKIg5xT3JaTHAgtLkzSl/ghoZfXoJP353ixvVH/f2CX2bDF5Z4F91EzbMAEE1g5xDDX63JD84JysaAt69wc0iMDD4pMxutdYfdsMXIdZJ0Q8RRIK5STSgSL4g/nkYUJ75vhqh0ew8WygTCAdlcescts/N5p24a+m0IeQyk/YZXG78IdSqWs+ZbHJoTIucwXOR5VMrWnOx1X6KkJgegna4V5HmRUu/CKDIaGxGkLP3YFE52WT27pgptVtd2blW+vbTsvGEPvd1VQZK9ebLjLruua9uo/TUpgEEZ4E7anpDbCeWI/qwuH69ldOGF3K30/7G21XLC8WEF0RLUEtr2LScAW4tGROU6IWOOnkPdnrvgZfJK6GHhpxG5XK31UBsTlagg9mz4O39/egKKD594HiIeOeRdjlXOZRPzU8mJJPLh+MVsMwgrEK9mWYGcLuwsGzWy1H03TXSlgB7YcBtyi0WwpYLIYL5xADDvQJ/LhFcQ1thl0dMTrOgawUsMToM0gxlLxY34TkFtpIw8GTz4xuVJKANReelkNzKvyu3+5YSEXPZhNbkwCOPg5gP3ISv+94FpvON4ZAFcnp5UHfvKPve7X727RpAZCib29NuMIzfKsl/cE9XlCRqxt2OiUp4gHyfbzdd0zmpqUiVs/7cws48bm4w++jgr84O6G6S0A3pajE1d9CQy87tlaXKe+4RY2ppgz/AYWsS2m4VrzpwRj5mVtAATy1soBhgcRYzJDu0ySORKFH+oRWS967w1G5qK5iaYoDQo/g+SG4rtVjGanF1hQIWnbbj5AAkpsnc2Y5VxIdrAI5b7zxPiplLK7DPuSUwpdjF1oasq1FaWghuZPQXxRbFo8kQ6MO9LBY0qoxhEsZVDDnMPFAPTmCmAHAJDwQRiG9K4mgj6KkfXRfjRyDBh+Dy8myPvCYe4ROqVGYIi85dLHFH80M9vrdwRjNMatRmbP4iY7Tk4TEqr2EwbrgW3egxnSy1AFG53z5m4RCX0okVeFpsawcZqATlG2eKZMTdHW/P5vJw8F2vO08+rgi19XkrajopkBVEvZh2BKtEW3s06k+ioKmb/UJFC5JcLNUAMEmkaSFZMZO8VJ7QToQHHbfpc+TU8RzzGAh4nFDWW9V7g9EYFJyEiBMk56qm5/sEw3qSBvCR06bXAmWl0hlNrGnMkjap+441ww912a7biLW6i9KUyy6zaTjsSLlbZFA9eG5eO+NRECykukZKoAgODBvCVq5eCHT8GgpSkwCDmUPq3ZtN9sXHVjgvqhtvXrenLLTuh9ukuipIgzE91kz8EyFscpkXNexStDJlcLPO7ZyioydICSPBsLCsYtYFXkkR5F/jkOZNgxm/LVhMrfvRSx+lVRLljgvLnJ07s6WhuUCDV7a21Kf8wH3Fq9NzqbjhI378XnFZTRH71zF X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 04be4e98-8c9d-476e-752e-08dbd0256175 X-MS-Exchange-CrossTenant-AuthSource: AM6PR04MB4838.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 18 Oct 2023 21:58:37.3284 (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: KnRLbmLXmcnMAGAEIKWburZUmL9Kux3bol+fU7mGOEzGW99PySvOLalaDhccLklgLwDakMcUQSpL/p8awXMnGQ== X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB9PR04MB9750 Add compatible string 'silvaco,i3c-slave' for slave mode. Signed-off-by: Frank Li --- .../devicetree/bindings/i3c/silvaco,i3c-master.yaml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml b/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml index 133855f11b4f5..63db63f00a509 100644 --- a/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml +++ b/Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml @@ -4,7 +4,7 @@ $id: http://devicetree.org/schemas/i3c/silvaco,i3c-master.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Silvaco I3C master +title: Silvaco I3C master/slave maintainers: - Conor Culhane @@ -14,8 +14,10 @@ allOf: properties: compatible: - const: silvaco,i3c-master-v1 - + const: + enum: + - silvaco,i3c-master-v1 + - silvaco,i3c-slave-v1 reg: maxItems: 1 From patchwork Wed Oct 18 21:58:07 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Frank Li X-Patchwork-Id: 735911 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (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 3D0BF44465; Wed, 18 Oct 2023 21:58:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=nxp.com header.i=@nxp.com header.b="D2kNKaIc" Received: from EUR02-VI1-obe.outbound.protection.outlook.com (mail-vi1eur02on2072.outbound.protection.outlook.com [40.107.241.72]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9487E139; Wed, 18 Oct 2023 14:58:44 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=BMLEcgFaFEDw3oqcl52vtgkf5rW4mPyCM5o6WabN+z/32uwT2ZlkzFh1oxvRTuYUmR3MFUR+BXWA/anuDv5RMXuj/IGgLZlbEfmJBX5DSl0elnqWlmDvgieW2eaavXLQlGADWSBSAV+M6PSC0KzEGk8oYI6MPzw5IR5FVE1pvM35e13qxRNFVXvUXd91w77dBq9bWVTaA2qQNA1JPd4Dmpt6tILj1tXU3P7qQI1Eu2yfBNz2O31bnLhJ28E39qQA9orPtFuMGRqfM+5WxtxXgZTK7qjPFyQ3C2uflArXhMJCdtOjzux+O78YvebZKMHwAWQrnLGN4k/MUqTk0H+bwA== 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=4twnLJur9ouNfayEnV0lYu44tuhyKc5XuQj935Zr33A=; b=U9R/Ye561jw7NfjYWlfHire0geMYN8Yt4+c0UKUiw1pcfoqICLM5/w1sXwEnTfTx5UzaapiKucOf1Lei2WVMcxs3vwUmpmkYcbQaXsIDbSP9UMq4QCTH/PvPscFVLv1hLnqWpzYoc/2spIPDysPrnMEw7l3scsgT1CcmCUCzhjqNsNKa7w7eBXf1TmnmYZfpGo9bYBTceaEcCXBPIQVH0jHMGDb5HnU8nOVyShU2D0EWomncqEPONMNEEyZRJJsHdJpwcIdaZYhf4XVtt/0CeJGtUIoAKR+SrnKP9OC0a+CMOMTO/l6/6hSY3NRDXUWMWnRBvTrJ6ktJeVgSZGCFGA== 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=4twnLJur9ouNfayEnV0lYu44tuhyKc5XuQj935Zr33A=; b=D2kNKaIc0PK5srgCARDuo+byR6O18iApTQaeeJGRUrmcdnq2rEhIA1DlsB2Ih4FNs6FoXMFNzXyBQ+/Uu8yFnh1XxUg4kAEu8pRyv//axYHVj/VECSUaGCnCkLeTvj1gj6dsOwkxvDywAayvpQK5XELrPh73nGW7zAw6rm+jxXQ= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from AM6PR04MB4838.eurprd04.prod.outlook.com (2603:10a6:20b:4::16) by DB9PR04MB9750.eurprd04.prod.outlook.com (2603:10a6:10:4c5::8) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6907.25; Wed, 18 Oct 2023 21:58:41 +0000 Received: from AM6PR04MB4838.eurprd04.prod.outlook.com ([fe80::1774:e25f:f99:aca2]) by AM6PR04MB4838.eurprd04.prod.outlook.com ([fe80::1774:e25f:f99:aca2%4]) with mapi id 15.20.6907.022; Wed, 18 Oct 2023 21:58:41 +0000 From: Frank Li To: miquel.raynal@bootlin.com, conor.culhane@silvaco.com, alexandre.belloni@bootlin.com, robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, conor+dt@kernel.org, corbet@lwn.net, joe@perches.com, Frank.Li@nxp.com, linux-i3c@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org Cc: gregkh@linuxfoundation.org, imx@lists.linux.dev, jirislaby@kernel.org, linux-serial@vger.kernel.org Subject: [PATCH 3/5] i3c: slave: add svc slave controller support Date: Wed, 18 Oct 2023 17:58:07 -0400 Message-Id: <20231018215809.3477437-4-Frank.Li@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231018215809.3477437-1-Frank.Li@nxp.com> References: <20231018215809.3477437-1-Frank.Li@nxp.com> X-ClientProxiedBy: SJ0PR03CA0225.namprd03.prod.outlook.com (2603:10b6:a03:39f::20) To AM6PR04MB4838.eurprd04.prod.outlook.com (2603:10a6:20b:4::16) Precedence: bulk X-Mailing-List: devicetree@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: AM6PR04MB4838:EE_|DB9PR04MB9750:EE_ X-MS-Office365-Filtering-Correlation-Id: db58b7a8-2016-4321-086a-08dbd02563f2 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: YnT4Pb3HvgvMYj/+Tcua5fDYCnDCbKkuGyGOcqIZ4tSqObzDPMvEXN4x8LsNaYcZ8CaL9fn8JzXH00FTt+pjCMAUmXaiCVwQPwNSZYvT4MJjOoxWHoiLmCnRxMCSpRZ3WW8SKF5T69Y9KWwxE/zMlO0r98G7Z4yKU62PdjtnN70z1bdypye2SqSv8iVEEK1i1HfW+bRgSS4WOaA6MzFc9dy4FgwFnH5bT43DLBSfENVRL5jfS/WZnLU/phQh9ts39WS92kuqwoEOqNFYBgT6yls0CDbMYKpqYJHkWv3ue2Bd8TaNVphx+AsreupzJDZJSZr9hwZXnlq2bUsXqaIFZlgxWDCYXIJ9hfZVVUkV687od1YTMkbPczWGzalN/uG/Ddeez2Wifq8KBA4oE9RZ9mBx2eq1eSYLoFu0lKSePeykyqxPX+WJvjwGXcJoVQn/UeZTiCgoH8vDGJW7x855+B+Q3mCJnfO16H60rauMUylyxbmwY+RsnTtDtgsIr6ktLV9oGDMvpFKzTGbIWPkia/ha4pPdRToPbwpo0Ai34bSA6qCl6UNwCtBSyYUBtDLuQYfPLpEUwzbPACHD1bjJ1Nz8eMYr93Aeo61dDA0AcNjczThm+G22LclrfVEhwluzKnUJTaQYqJkFX2P1Yy3Hvw== X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:AM6PR04MB4838.eurprd04.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230031)(346002)(376002)(396003)(39860400002)(136003)(366004)(230922051799003)(1800799009)(451199024)(186009)(64100799003)(66946007)(66556008)(66476007)(316002)(6666004)(478600001)(6486002)(8936002)(8676002)(36756003)(5660300002)(41300700001)(4326008)(30864003)(2906002)(86362001)(7416002)(38100700002)(2616005)(52116002)(6512007)(1076003)(83380400001)(6506007)(26005)(921005)(38350700005); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: qlqDa1LNT3XhV3PIiP6YG3Gb7xnFYrsD/MkyfIJo9ec2eDS051fAOox/j30Wz9W8QliboIjiFFU7Ac5Fj7IyYPprVvHG3s5E4GR+MnA1NMG0GnZa/itjPGvscIm+w0Cuq8mkUKqpN6lu1VTjs9uwkaZ0kjmZTnZipzqnGrygHiTupXfnoE8sARMJBSwNUFLKsd3QtiaRS2FRo0mokrGahAo5y73Vr0AlVFbLSOBDr/1gDbG/PQJTgdDmVG8+8hlVy5v4ru2Lcz0qn3QEU/6KFOBEQsAXB8S0RJUCo/C+fQxPEMVCHYLsfp6iL0FUYlKtKAkysmkUoIbTwcPd2t3CfpQKdyU3ArVZqXKDTKDZXJuypFqVoBEmj85Ec43SjUFYp4Itd4ZX1X/nvhOtFGs7EcZjGXgAUYXticvKVF0IrHL4Xg6+YsNUig291nbzyfaOQRDdyvnXg1w9nU6o7fu/gAev1Sh02BRAAKoAqgx7tkyJRm2BO+V0KeuEhrTBzPDGm9qc7JdoHG96TlqyFmnf64ZME5Lnaex/M2Ge3FN25DOeMDIpJldQRMcXw0xTB5TWbMLQ01vqtIOLz6/xr489Sm1F5AiLs3peCLxT/h5RootUTbfIqr8A/sc1yiLZZXE4gIXgM3n5lAQTxJ0mBhQyoa1i7Er8yQbd9Pukh3NVl5dxgir1ZNjDjv+8ZhATf3dEpQ/hfV4FH+I7shbKyyonXaCvTAjaKxV3bbo0Lxg5z6Y4lLkaS63VYzGbw1vGdrzFc+RZ2KJ+l+vNdJOhoKSHrRHzF5MUZXyS9d/fnGq0WYvSnfy1r234UZuJp0Mliy7cMTnUBGk2+7hP3tVjCgNIZu0su68nOQiVGygNs20WBUeHc/VFzxDlkhdViyvNNEHyopTLNDm+NDhtHOmJ6xt+LkeFgB2bGoRtc0DLTT2zVuSfaCPz9fwclBQe0pESyqlUHkPadsnZGSPqmSrMY/3AZuhUayuxnzFsOLRjIELMJ4+N98sOAwuGhuC8DH10jwIWBDaYJ2q1AQsHhzRPBvHK5QKykhY35iXR2kQyAw3wC656YBHIXsxvaFQEd23c5lnltuDUSlP84dMDF94XjUAnP56jLXtRFB3jPMnyfvpRyXvE1UAzdXIphufnnspbjqlMoSA+nfVfi6gYjzhWmHNZJJ64EgBc4w1+MunP6ju5TFCESKRQrk3UZLH6FS1kie78ALLdJhgSCbQAyaT4B2xy6JTTWRc7og/9SxajMEegor/DHw/z4TwDZgfdyADzGoIU9mw6TN5xaQDGIK4U+wcpeIf6ffBzOGyE0M0tX8hYhLD5hB1mM7HNBxZbnZd91FHXcFOkLI7iQVN8pqIL7GDmVwIXD4wklnj/mRuEs6tsMECwl08tfRHH9mdEBdYaWVt5bThGOdePCzSweJjdID4ra5D0w9S1jt75PPBVE422QPLY+Xk4OtA+aA1T67DpBQnOykXCvP6F/yip55miCRtRczjeBGoPyh485FzzLt4y/kitOPrJsXUlAZaB1KOtaWIGKu6p6A45/o/Rd28ggxyxDE8Hcvk3vj0oX4UQsyTcAf1RzKPBsHv14SptZwPjMamP X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: db58b7a8-2016-4321-086a-08dbd02563f2 X-MS-Exchange-CrossTenant-AuthSource: AM6PR04MB4838.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 18 Oct 2023 21:58:41.5149 (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: LLL0puCqltKx97ol0+rnLejanqLxCPJQfr3kExV5VxAPJ3bA6HvHUyDKLfgYfzMNG4nOxxW8jEOdBKoJReVhLA== X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB9PR04MB9750 Add Silvaco I3C slave controller support Signed-off-by: Frank Li --- drivers/i3c/Kconfig | 3 + drivers/i3c/Makefile | 1 + drivers/i3c/slave/Kconfig | 9 + drivers/i3c/slave/Makefile | 4 + drivers/i3c/slave/svc-i3c-slave.c | 795 ++++++++++++++++++++++++++++++ 5 files changed, 812 insertions(+) create mode 100644 drivers/i3c/slave/Kconfig create mode 100644 drivers/i3c/slave/Makefile create mode 100644 drivers/i3c/slave/svc-i3c-slave.c diff --git a/drivers/i3c/Kconfig b/drivers/i3c/Kconfig index d5f5ca7cd6a56..802a9b3576f13 100644 --- a/drivers/i3c/Kconfig +++ b/drivers/i3c/Kconfig @@ -48,3 +48,6 @@ config I3C_SLAVE_CONFIGFS the slave function and used to bind the function with a slave controller. +if I3C_SLAVE +source "drivers/i3c/slave/Kconfig" +endif # I3C_SLAVE diff --git a/drivers/i3c/Makefile b/drivers/i3c/Makefile index 6407ddec3a4a9..ef1acbe13fe60 100644 --- a/drivers/i3c/Makefile +++ b/drivers/i3c/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_I3C) += i3c.o obj-$(CONFIG_I3C) += master/ obj-$(CONFIG_I3C_SLAVE) += slave.o obj-$(CONFIG_I3C_SLAVE_CONFIGFS) += i3c-cfs.o +obj-$(CONFIG_I3C_SLAVE) += slave/ diff --git a/drivers/i3c/slave/Kconfig b/drivers/i3c/slave/Kconfig new file mode 100644 index 0000000000000..e385dbdea193b --- /dev/null +++ b/drivers/i3c/slave/Kconfig @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 + +config I3C_SLAVE_CTRL_SVC + tristate "Silvaco I3C Dual-Role Slave driver" + depends on I3C + depends on HAS_IOMEM + depends on !(ALPHA || PARISC) + help + Support for Silvaco I3C Dual-Role Slave Controller. diff --git a/drivers/i3c/slave/Makefile b/drivers/i3c/slave/Makefile new file mode 100644 index 0000000000000..612be24536311 --- /dev/null +++ b/drivers/i3c/slave/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-${CONFIG_I3C_SLAVE_CTRL_SVC} += svc-i3c-slave.o + diff --git a/drivers/i3c/slave/svc-i3c-slave.c b/drivers/i3c/slave/svc-i3c-slave.c new file mode 100644 index 0000000000000..2de741a9e0ff8 --- /dev/null +++ b/drivers/i3c/slave/svc-i3c-slave.c @@ -0,0 +1,795 @@ +// 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 + +enum i3c_clks { + PCLK, + FCLK, + SCLK, + MAXCLK, +}; + +struct svc_i3c_slave { + 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_slave_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_slave_enable(struct i3c_slave_ctrl *ctrl) +{ + struct svc_i3c_slave *svc; + u32 val; + + svc = dev_get_drvdata(&ctrl->dev); + + val = readl_relaxed(svc->regs + I3C_SCONFIG); + val |= I3C_SCONFIG_SLVENA_MASK; + writel_relaxed(val, svc->regs + I3C_SCONFIG); + + return 0; +} + +static int svc_i3c_slave_disable(struct i3c_slave_ctrl *ctrl) +{ + struct svc_i3c_slave *svc; + u32 val; + + svc = dev_get_drvdata(&ctrl->dev); + + val = readl_relaxed(svc->regs + I3C_SCONFIG); + val &= ~I3C_SCONFIG_SLVENA_MASK; + writel_relaxed(val, svc->regs + I3C_SCONFIG); + + return 0; +} + +static int svc_i3c_slave_set_config(struct i3c_slave_ctrl *ctrl, struct i3c_slave_func *func) +{ + struct svc_i3c_slave *svc; + u32 val; + u32 wm, rm; + + svc = dev_get_drvdata(&ctrl->dev); + + if (func->static_addr > 0x7F) + return -EINVAL; + + val = readl_relaxed(svc->regs + I3C_SCONFIG); + val &= ~I3C_SCONFIG_SLVENA_MASK; + val |= 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 = func->max_write_len == 0 ? + FIELD_GET(I3C_SMAXLIMITS_MAXWR_MASK, I3C_SMAXLIMITS_MAXWR_MASK) : func->max_write_len; + + wm = max_t(u32, val, 8); + + rm = func->max_read_len == 0 ? + FIELD_GET(I3C_SMAXLIMITS_MAXRD_MASK, I3C_SMAXLIMITS_MAXRD_MASK) : func->max_read_len; + rm = max_t(u32, val, 16); + + val = FIELD_PREP(I3C_SMAXLIMITS_MAXRD_MASK, rm) | FIELD_PREP(I3C_SMAXLIMITS_MAXWR_MASK, wm); + writel_relaxed(val, svc->regs + I3C_SMAXLIMITS); + + writel_relaxed(func->vendor_id, svc->regs + I3C_SVENDORID); + return 0; +} + +const struct i3c_slave_ctrl_features *svc_i3c_get_features(struct i3c_slave_ctrl *ctrl) +{ + struct svc_i3c_slave *svc; + + svc = dev_get_drvdata(&ctrl->dev); + + if (!svc) + return NULL; + + return &svc->features; +} + +static void svc_i3c_queue_complete(struct svc_i3c_slave *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_slave *svc) +{ + struct i3c_request *req, *complete = NULL; + unsigned long flags; + int val; + + spin_lock_irqsave(&svc->txq_lock, flags); + while ((!!(req = 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 = *(u8 *)(req->buf + req->actual); + + if (req->actual + 1 == req->length) + writel_relaxed(val, svc->regs + I3C_SWDATAE); + else + writel_relaxed(val, svc->regs + I3C_SWDATAB); + + req->actual++; + + if (req->actual == req->length) { + list_del(&req->list); + complete = req; + spin_unlock_irqrestore(&svc->txq_lock, flags); + + svc_i3c_queue_complete(svc, complete); + + spin_lock_irqsave(&svc->txq_lock, flags); + break; + } + } + } + spin_unlock_irqrestore(&svc->txq_lock, flags); +} + +static int svc_i3c_slave_queue(struct i3c_request *req, gfp_t) +{ + struct svc_i3c_slave *svc; + struct list_head *q; + unsigned long flags; + spinlock_t *lk; + + svc = dev_get_drvdata(&req->ctrl->dev); + if (!svc) + return -EINVAL; + + if (req->tx) { + q = &svc->txq; + lk = &svc->txq_lock; + } else { + q = &svc->rxq; + lk = &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_slave *svc; + unsigned long flags; + spinlock_t *lk; + + svc = dev_get_drvdata(&req->ctrl->dev); + if (!svc) + return -EINVAL; + + if (req->tx) + lk = &svc->txq_lock; + else + lk = &svc->rxq_lock; + + spin_lock_irqsave(lk, flags); + list_del(&req->list); + spin_unlock_irqrestore(lk, flags); + + return 0; +} + +static void svc_i3c_slave_fifo_flush(struct i3c_slave_ctrl *ctrl, bool tx) +{ + struct svc_i3c_slave *svc; + u32 val; + + svc = dev_get_drvdata(&ctrl->dev); + + val = readl_relaxed(svc->regs + I3C_SDATACTRL); + + val |= tx ? I3C_SDATACTRL_FLUSHTB_MASK : I3C_SDATACTRL_FLUSHFB_MASK; + + writel_relaxed(val, svc->regs + I3C_SDATACTRL); +} + +static int +svc_i3c_slave_raise_ibi(struct i3c_slave_ctrl *ctrl, void *p, u8 size) +{ + struct svc_i3c_slave *svc; + unsigned long flags; + u8 *ibidata = p; + u32 ext1 = 0, ext2 = 0; + u32 val; + int ret; + + svc = dev_get_drvdata(&ctrl->dev); + + if (size && !p) + return -EINVAL; + + if (size > 8) + return -EINVAL; + + val = readl_relaxed(svc->regs + I3C_SSTATUS); + if (val & I3C_SSTATUS_IBIDIS_MASK) + return -EINVAL; + + ret = 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 &= ~I3C_SCTRL_EVENT_MASK; + writel_relaxed(val, svc->regs + I3C_SCTRL); + return -ENAVAIL; + } + + spin_lock_irqsave(&svc->ctrl_lock, flags); + + val = readl_relaxed(svc->regs + I3C_SCTRL); + + val &= ~I3C_SCTRL_EVENT_MASK | I3C_SCTRL_IBIDATA_MASK; + val |= FIELD_PREP(I3C_SCTRL_EVENT_MASK, I3C_SCTRL_EVENT_IBI); + + if (size) { + val |= FIELD_PREP(I3C_SCTRL_IBIDATA_MASK, *ibidata); + ibidata++; + + if (size > 1) + val |= I3C_SCTRL_EXTDATA_MASK; + + size--; + if (size > 0) { + ext1 |= (size + 2); + ext1 |= (*ibidata++) << I3C_IBIEXT1_EXT1_SHIFT; + size--; + } + + if (size > 0) { + ext1 |= (*ibidata++) << I3C_IBIEXT1_EXT2_SHIFT; + size--; + } + + if (size > 0) { + ext1 |= (*ibidata++) << I3C_IBIEXT1_EXT3_SHIFT; + size--; + } + + writel_relaxed(ext1, svc->regs + I3C_IBIEXT1); + + if (size > 0) { + ext2 |= (*ibidata++) << I3C_IBIEXT2_EXT4_SHIFT; + size--; + } + + if (size > 0) { + ext2 |= (*ibidata++) << I3C_IBIEXT2_EXT5_SHIFT; + size--; + } + + if (size > 0) { + ext2 |= (*ibidata++) << I3C_IBIEXT2_EXT6_SHIFT; + size--; + } + + if (size > 0) { + ext2 |= (*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 = 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 = readl_relaxed(svc->regs + I3C_SCTRL); + val &= ~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_slave_complete(struct work_struct *work) +{ + struct svc_i3c_slave *svc = container_of(work, struct svc_i3c_slave, work); + struct i3c_request *req; + unsigned long flags; + + spin_lock_irqsave(&svc->cq_lock, flags); + while (!list_empty(&svc->cq)) { + req = 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_slave_irq_handler(int irq, void *dev_id) +{ + struct i3c_request *req, *complete = NULL; + struct svc_i3c_slave *svc = dev_id; + unsigned long flags; + u32 statusFlags; + + statusFlags = 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 = 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) = + readl_relaxed(svc->regs + I3C_SRDATAB); + req->actual++; + + if (req->actual == req->length) { + complete = req; + list_del(&req->list); + break; + } + } + + if (req->actual != req->length && (statusFlags & I3C_SSTATUS_STOP_MASK)) { + complete = 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 = 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_slave_ctrl *ctrl, bool tx) +{ + struct svc_i3c_slave *svc; + struct i3c_request *req; + struct list_head *q; + unsigned long flags; + spinlock_t *lk; + + svc = dev_get_drvdata(&ctrl->dev); + if (!svc) + return; + + if (tx) { + q = &svc->txq; + lk = &svc->txq_lock; + } else { + q = &svc->rxq; + lk = &svc->rxq_lock; + } + + spin_lock_irqsave(lk, flags); + while (!list_empty(q)) { + req = list_first_entry(q, struct i3c_request, list); + list_del(&req->list); + spin_unlock_irqrestore(lk, flags); + + req->status = I3C_REQUEST_CANCEL; + req->complete(req); + spin_lock_irqsave(lk, flags); + } + spin_unlock_irqrestore(lk, flags); +} + +static int svc_i3c_hotjoin(struct i3c_slave_ctrl *ctrl) +{ + struct svc_i3c_slave *svc; + int ret; + u32 val; + u32 cfg; + + svc = dev_get_drvdata(&ctrl->dev); + if (!svc) + return -EINVAL; + + reinit_completion(&svc->dacomplete); + + val = 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 = 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 = readl_relaxed(svc->regs + I3C_SCONFIG); + cfg |= I3C_SCONFIG_OFFLINE_MASK; + writel_relaxed(cfg, svc->regs + I3C_SCONFIG); + + val &= ~(I3C_SCTRL_EVENT_MASK | I3C_SCTRL_IBIDATA_MASK); + val |= FIELD_PREP(I3C_SCTRL_EVENT_MASK, I3C_SCTRL_EVENT_HOTJOIN); + /* Issue hotjoin*/ + writel_relaxed(val, svc->regs + I3C_SCTRL); + + ret = readl_relaxed_poll_timeout(svc->regs + I3C_SCTRL, val, + !(val & I3C_SCTRL_EVENT_MASK), 0, 100000); + if (ret) { + val &= ~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 = readl_relaxed(svc->regs + I3C_SSTATUS); + val = FIELD_GET(I3C_SSTATUS_EVDET_MASK, val); + if (val != 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 = wait_for_completion_timeout(&svc->dacomplete, msecs_to_jiffies(100)); + writel_relaxed(I3C_SINT_DACHG, svc->regs + I3C_SINTCLR); + if (!ret) { + dev_err(&ctrl->dev, "wait for da assignment timeout\n"); + return -EIO; + } + + val = readl_relaxed(svc->regs + I3C_SMAPCTRL0); + val = 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_slave_ctrl *ctrl, u16 status) +{ + struct svc_i3c_slave *svc; + unsigned long flags; + u32 val; + + svc = dev_get_drvdata(&ctrl->dev); + + spin_lock_irqsave(&svc->ctrl_lock, flags); + val = readl_relaxed(svc->regs + I3C_SCTRL); + val &= 0xFFFF; + val |= 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_slave_ctrl *ctrl) +{ + struct svc_i3c_slave *svc; + + svc = dev_get_drvdata(&ctrl->dev); + + return readl_relaxed(svc->regs + I3C_SCTRL) >> 16; +} + +static u8 svc_i3c_get_addr(struct i3c_slave_ctrl *ctrl) +{ + struct svc_i3c_slave *svc; + int val; + + svc = dev_get_drvdata(&ctrl->dev); + + val = readl_relaxed(svc->regs + I3C_SMAPCTRL0); + + if (val & I3C_SMAPCTRL0_ENA_MASK) + return FIELD_GET(I3C_SMAPCTRL0_DA_MASK, val); + + return 0; +} + +int svc_i3c_fifo_status(struct i3c_slave_ctrl *ctrl, bool tx) +{ + struct svc_i3c_slave *svc; + int val; + + svc = dev_get_drvdata(&ctrl->dev); + + val = 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_slave_ctrl_ops svc_i3c_slave_ops = { + .set_config = svc_i3c_slave_set_config, + .enable = svc_i3c_slave_enable, + .disable = svc_i3c_slave_disable, + .queue = svc_i3c_slave_queue, + .dequeue = svc_i3c_dequeue, + .raise_ibi = svc_i3c_slave_raise_ibi, + .fifo_flush = svc_i3c_slave_fifo_flush, + .cancel_all_reqs = svc_i3c_cancel_all_reqs, + .get_features = svc_i3c_get_features, + .hotjoin = svc_i3c_hotjoin, + .fifo_status = svc_i3c_fifo_status, + .set_status_format1 = svc_i3c_set_status_format1, + .get_status_format1 = svc_i3c_get_status_format1, + .get_addr = svc_i3c_get_addr, +}; + +static int svc_i3c_slave_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct i3c_slave_ctrl *slave; + struct svc_i3c_slave *svc; + int ret; + u32 val; + + svc = devm_kzalloc(dev, sizeof(*svc), GFP_KERNEL); + if (!svc) + return -ENOMEM; + + svc->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(svc->regs)) + return PTR_ERR(svc->regs); + + svc->clks[PCLK].id = "pclk"; + svc->clks[FCLK].id = "fast_clk"; + svc->clks[SCLK].id = "slow_clk"; + + ret = devm_clk_bulk_get(dev, MAXCLK, svc->clks); + if (ret < 0) { + dev_err(dev, "fail get clks: %d\n", ret); + return ret; + } + + ret = clk_bulk_prepare_enable(MAXCLK, svc->clks); + if (ret < 0) { + dev_err(dev, "fail enable clks: %d\n", ret); + return ret; + } + + svc->irq = 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_slave_complete); + svc->workqueue = 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 = readl_relaxed(svc->regs + I3C_SCAPABILITIES); + svc->features.tx_fifo_sz = FIELD_GET(I3C_SCAPABILITIES_FIFOTX_MASK, val); + svc->features.tx_fifo_sz = 2 << svc->features.tx_fifo_sz; + + svc->features.rx_fifo_sz = FIELD_GET(I3C_SCAPABILITIES_FIFORX_MASK, val); + svc->features.rx_fifo_sz = 2 << svc->features.rx_fifo_sz; + + ret = devm_request_irq(dev, svc->irq, svc_i3c_slave_irq_handler, 0, "svc-i3c-irq", svc); + if (ret) + return -ENOENT; + + slave = devm_i3c_slave_ctrl_create(dev, &svc_i3c_slave_ops); + if (!slave) + return -ENOMEM; + + dev_set_drvdata(&slave->dev, svc); + + return 0; +} + +static int svc_i3c_slave_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id svc_i3c_slave_of_match_tbl[] = { + { .compatible = "silvaco,i3c-slave-v1" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, svc_i3c_slave_of_match_tbl); + +static struct platform_driver svc_i3c_slave = { + .probe = svc_i3c_slave_probe, + .remove = svc_i3c_slave_remove, + .driver = { + .name = "silvaco-i3c-slave", + .of_match_table = svc_i3c_slave_of_match_tbl, + //.pm = &svc_i3c_pm_ops, + }, +}; +module_platform_driver(svc_i3c_slave); + +MODULE_DESCRIPTION("Silvaco dual-role I3C slave driver"); +MODULE_LICENSE("GPL"); From patchwork Wed Oct 18 21:58:08 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Frank Li X-Patchwork-Id: 735273 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (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 491C542C12; Wed, 18 Oct 2023 21:58:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=nxp.com header.i=@nxp.com header.b="JIvTBSSU" Received: from EUR02-VI1-obe.outbound.protection.outlook.com (mail-vi1eur02on2072.outbound.protection.outlook.com [40.107.241.72]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DE0D611C; Wed, 18 Oct 2023 14:58:48 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=nDCCyFtK++lBPRYsBmfmy+Z7YktHKr3obxCXkjVbSa6AX2fPkn4evHSdsH0oKbK2HXImFbTH9BNjmZspLxdMtDrGdp7dcsQoPLeZv35eCd5s1Rsfe0+4mBDptstMx+Y02kfFXY44KLvj0njJalC3hVnuExCS251Ps1GBjyKR0zXQ/Bufd1QGx9BCDbavIGEXuRJPCF8ARyr6z7fwG5+ZgceVJyNNb7mAU2N0U6kYU1Zi1dCo6IquuFUGzjs4YRO6AIIBP21N5PR6XrO3ssalipVAhysK96NmIaVmx6TneG0b0NSQMEIqZLz0HEt2FxCn7PAqASUli9dfBQhIQtMEYA== 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=uRPg3ZAAzqjPrdoK4+1RF+xDwAfq/WQ+nzB00HHSd3w=; b=Dp4AdxvzvitHn6fNdM2Yxhj5PO01bv+RfBvZphJ32JQ3CANcXdIWSnmJfncCKFsWBE6Ou/hEh5T1jJ3GPhK++zAIB0kERP+qVr1aT/YKlRTAx8Vzj3h/QV5IA8YCfqxJM16AVN/y6PxjS4FDcXu9erE7UgEMC7Cr6EjZ6reOxQcykzlONrfINnyqDgT3V8dfm2uIrp5u3W0reaGy2g+5KNuBtuHY5MU7olo4B5B5hZJopKVwE+jeA/wtQhV623D/G30t+p55XIt3DtY4vK9LA3omNkc+1m2vgZgc6kDHgxsrUuD34wRs7LWcNNCBc4PxrBZe7KOCEypyQVNVRyNHng== 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=uRPg3ZAAzqjPrdoK4+1RF+xDwAfq/WQ+nzB00HHSd3w=; b=JIvTBSSUfZ/6N2UVd8zBTk92KZFaGByDLS7ycDze+fnY+IDexy8pt6wrg+omA4I4rUrm8wK6kzMzQi2ZK2Gtn+5Sk+hG5ZHtwgC3b5GOGpM89hYes4nnrxCAQcaiBiRvDypfJQE7tdTYckoBl6RpkbHpuy9CHbvUjVP8CP/MRtM= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from AM6PR04MB4838.eurprd04.prod.outlook.com (2603:10a6:20b:4::16) by DB9PR04MB9750.eurprd04.prod.outlook.com (2603:10a6:10:4c5::8) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6907.25; Wed, 18 Oct 2023 21:58:45 +0000 Received: from AM6PR04MB4838.eurprd04.prod.outlook.com ([fe80::1774:e25f:f99:aca2]) by AM6PR04MB4838.eurprd04.prod.outlook.com ([fe80::1774:e25f:f99:aca2%4]) with mapi id 15.20.6907.022; Wed, 18 Oct 2023 21:58:45 +0000 From: Frank Li To: miquel.raynal@bootlin.com, conor.culhane@silvaco.com, alexandre.belloni@bootlin.com, robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, conor+dt@kernel.org, corbet@lwn.net, joe@perches.com, Frank.Li@nxp.com, linux-i3c@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org Cc: gregkh@linuxfoundation.org, imx@lists.linux.dev, jirislaby@kernel.org, linux-serial@vger.kernel.org Subject: [PATCH 4/5] i3c: slave: func: add tty driver Date: Wed, 18 Oct 2023 17:58:08 -0400 Message-Id: <20231018215809.3477437-5-Frank.Li@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231018215809.3477437-1-Frank.Li@nxp.com> References: <20231018215809.3477437-1-Frank.Li@nxp.com> X-ClientProxiedBy: SJ0PR03CA0225.namprd03.prod.outlook.com (2603:10b6:a03:39f::20) To AM6PR04MB4838.eurprd04.prod.outlook.com (2603:10a6:20b:4::16) Precedence: bulk X-Mailing-List: devicetree@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: AM6PR04MB4838:EE_|DB9PR04MB9750:EE_ X-MS-Office365-Filtering-Correlation-Id: da267885-dddc-4eb2-fdcb-08dbd0256670 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: BjjFDqtAnuOUw8qpuLzWGCidVTRxmuhS9xiI7sagQs0WewmVuOBq+SuR1xQH3JrldUgjbYPACyCO/nGUip28VZ6JcdengYxOL71fYJKjvzrbYlC0n1vOl2Bw/VSpnNE0J5qRcgALNyELzcE/SGuEcZgg1+igwqR5YA1TiA8LKKSYVf6OwgsLp2B42YPcNiEvrlubMcziVrSR7W83eBsEXtitkGwOQdQahK65f2dqtPzxzOWBtoJXq7MNKeHtM5G8xJ8wCnL9g0YFquMw+i/mKBkvig8cjd37QOHF7m7G/ryyDyzbFqKZ45psORrsGUnhwjot/VAxFVHeIwM/02lh+VPt1q7jW5imry91lRD0YcB2RqY6Yfoi4FBlWxh7+4aAm5zoMnmUixnFUBZyeKNI4uUuMlCZB6UrRvbICgrN6al5crZzEsgJJWwNy0XtD6M0wOsSRXL0g9e6c98hdQuFAppB7hwod2yNbTZ6GqlIaCDgt2NyIuHXIAUGVo0M6EkHd9SHHf/lJzCJGTiOWuLmSTJ4QxG6M8NsyAbDMH1xvtxyAJ/91HXmtxDZF+PQziEr1tGRlNfXfSeUvSRBLGeWr9PGupl2I6D/iVjZ0v5+NLgqniXh7HMQ760yU1dy2HINa5r4EZc6Z4DOYSUEHwUG6g== X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:AM6PR04MB4838.eurprd04.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230031)(346002)(376002)(396003)(39860400002)(136003)(366004)(230922051799003)(1800799009)(451199024)(186009)(64100799003)(66946007)(66556008)(66476007)(316002)(6666004)(478600001)(6486002)(8936002)(8676002)(36756003)(5660300002)(41300700001)(4326008)(30864003)(2906002)(86362001)(7416002)(38100700002)(2616005)(52116002)(6512007)(1076003)(83380400001)(6506007)(26005)(921005)(38350700005); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: VmIQHfkrDEXQwzUBidAsLqgYl65CUUY9OeK1/it4EMp0qElxZRtnkdmcYCs23ZT1Q4gGUX5F6jKV3Ii/ennRvPhVjz7wOMybR+XVle+DNoQQ8w4AxgggtpRCBKumV2nCs4oXLPQmqma6xDPeNyH0Mgy1Oa5vskNCnSvVDIhHutIM5rJvWUbB5/7LGf3iiUvBaljfYP3TpCp3IJRTk2spJRhZKgSj47ifOsBS1TpmNaqtToxUiaxwd/cktlSXrixgMmJMEKXurPhYBfLRqv+UE15i4YebyBQvcvhNBud7YohMKWOsBPmeqbt4eBO7CXMYEiKCTs0wqVbhBMvqH0O8YpCybq7Zil8wem0aGvChjh6mQEtAEPiShqdjx/2cqBFVq6xuFoxb4DSdgYENHR0ycVoAf7zgvZrPkFbtqqIxFbV11LJ4rREnP0HQsWytqtwWnuXPXmIh6kfqnRu1E9jVnI8E9iXyNOPA7k7JupRpsdKEtvYfbHYciNRIJrcnCcL0txycLY6en2hhyLFdn+l8BN/OZVpVGKj1hjoZlZU1MqNuoUBkvy61650cLrjd9Yp6rcWvJcW4uGkBU22lQYgK6W6WHwW0CmQ3YKS9aLs8A/blLYJRRFM84LaizCSVuah8xThxEz+UwZC4RXFVaE4K2sfgHza06iEM/b3I3cFO8F7y5VBOVOyfGVTFus7ARJQBZutnORHJ1c7mpFQHyf+4Iva6oi0+zqssbI3rjXMZyM7PUbIb7oj4n3zTCKSKqwMx2FXzlgxN6cMk6wR93QP2eFcnulDQCpR/ryKsgXeKZcrM1rSZWN2XZypTMBRa2TtZNL8y5dGYlt61X2pmc7K4IcItKOiF16z7/vUeQByQ0JZeBTeVHnt+dtpGu2KeDOVzEX+ZwSTEqIhIY/50743R8cFdTNzl1T4IRpLbO04o/D12EuZtb06XGQ+LzptIZc88wPgD4mWYPVj8IChNjZswq+hV2OI3NnJBjO24NsDQGDEutSw13tSb6u5xJJjsxMXVjSkm6+n5Eyd1F0QfEmNUH9lvsrraGDdKpZ3Wahub1fmRMQ7FFqUQZh+VKd8TRJ3d/ClRZwdWXca7WEHG+foieegjId0kOgObwWBdrxg4SciVpmASTaNGRj93HBf0MEiW8DxNWAU7/nSp5Yo6Ds7GL8EO+1a01DR+l56jyLGlzK8wUqKz1TQclwxLLfbM3SyaveLnqkcZ40GnSJK3kvm5Dgv3uItoVLl9ULCjT4kZXObvJb+JDATyFzSvQEubPF+6zHL3p7XV2mDZPmNQgPs80IiVD5GzfccqNkEEuEDxvKTmssmkXpyNj/OnrsYmc2qiYHK/FtshlcgAmecSmnKl5USNkaejz/nv2Sh+0C+06ZGyxKVw4iVMSZZcMH+3vHDTJzLL1vaX+u2GPBqejJrn0F3ESwqwRmIYktnOlAujZHA2IcZvkPd8NXfjbQPY8sUJn28njVNqAwJIWZQ0YMZQ6Xia3bdMNBQCgvSSNaoi2V0wNTPAcNCaHfWuxHhu/8RUWJrYk3v5uc5IArdXFdt1OA+LsKUyb631jcLoBM13nzskHKSqkP6ny6sxKmZ/T8D3 X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: da267885-dddc-4eb2-fdcb-08dbd0256670 X-MS-Exchange-CrossTenant-AuthSource: AM6PR04MB4838.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 18 Oct 2023 21:58:45.5640 (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: 0JrJ9EdRnx0wSKF7IL4PCKP4wvJk65/Q50FLAGIQkHs7I6B7nTNYvIhTsJnM/zJrgCA0kXD2r5F9XGhmN6P91Q== X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB9PR04MB9750 Add tty over I3C slave function driver. Signed-off-by: Frank Li --- drivers/i3c/Kconfig | 1 + drivers/i3c/Makefile | 1 + drivers/i3c/func/Kconfig | 9 + drivers/i3c/func/Makefile | 3 + drivers/i3c/func/tty.c | 548 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 562 insertions(+) create mode 100644 drivers/i3c/func/Kconfig create mode 100644 drivers/i3c/func/Makefile create mode 100644 drivers/i3c/func/tty.c diff --git a/drivers/i3c/Kconfig b/drivers/i3c/Kconfig index 802a9b3576f13..9b87cb4a84d85 100644 --- a/drivers/i3c/Kconfig +++ b/drivers/i3c/Kconfig @@ -50,4 +50,5 @@ config I3C_SLAVE_CONFIGFS if I3C_SLAVE source "drivers/i3c/slave/Kconfig" +source "drivers/i3c/func/Kconfig" endif # I3C_SLAVE diff --git a/drivers/i3c/Makefile b/drivers/i3c/Makefile index ef1acbe13fe60..7814bf2dd9b40 100644 --- a/drivers/i3c/Makefile +++ b/drivers/i3c/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_I3C) += master/ obj-$(CONFIG_I3C_SLAVE) += slave.o obj-$(CONFIG_I3C_SLAVE_CONFIGFS) += i3c-cfs.o obj-$(CONFIG_I3C_SLAVE) += slave/ +obj-$(CONFIG_I3C_SLAVE) += func/ diff --git a/drivers/i3c/func/Kconfig b/drivers/i3c/func/Kconfig new file mode 100644 index 0000000000000..3ebf5bd2592a2 --- /dev/null +++ b/drivers/i3c/func/Kconfig @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 + +config I3C_SLAVE_FUNC_TTY + tristate "PCI Endpoint Test driver" + depends on I3C_SLAVE + help + I3C Slave TTY Function Driver. + + General TTY over I3C slave controller function drivers. diff --git a/drivers/i3c/func/Makefile b/drivers/i3c/func/Makefile new file mode 100644 index 0000000000000..db3262e402edd --- /dev/null +++ b/drivers/i3c/func/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_I3C_SLAVE_FUNC_TTY) += tty.o diff --git a/drivers/i3c/func/tty.c b/drivers/i3c/func/tty.c new file mode 100644 index 0000000000000..ea48db49b2764 --- /dev/null +++ b/drivers/i3c/func/tty.c @@ -0,0 +1,548 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 NXP + * Author: Frank Li + */ + +#include +#include +#include +#include +#include + +static DEFINE_IDR(i3c_tty_minors); +static DEFINE_MUTEX(i3c_tty_minors_lock); + +static struct tty_driver *i3c_tty_driver; + +#define I3C_TTY_MINORS 256 + +#define I3C_TX_NOEMPTY BIT(0) +#define I3C_TTY_TRANS_SIZE 16 +#define I3C_TTY_IBI_TX BIT(0) + +struct ttyi3c_port { + struct tty_port port; + int minor; + struct i3c_slave_func *i3cdev; + struct circ_buf xmit; + int tail_in_queue; + struct completion txcomplete; + spinlock_t xlock; + void *buffer; + struct work_struct work; + struct workqueue_struct *workqueue; + u16 status; +}; + +static void i3c_slave_tty_rx_complete(struct i3c_request *req) +{ + struct ttyi3c_port *port = req->context; + + if (req->status == I3C_REQUEST_CANCEL) { + i3c_slave_ctrl_free_request(req); + return; + } + + for (int i = 0; i < req->actual; i++) + tty_insert_flip_char(&port->port, *(u8 *)(req->buf + i), 0); + + tty_flip_buffer_push(&port->port); + req->actual = 0; + req->status = 0; + i3c_slave_ctrl_queue(req, GFP_KERNEL); +} + +static void i3c_slave_tty_tx_complete(struct i3c_request *req) +{ + struct ttyi3c_port *sport = req->context; + struct circ_buf *xmit = &sport->xmit; + unsigned long flags; + int cnt; + + if (req->status == I3C_REQUEST_CANCEL) { + i3c_slave_ctrl_free_request(req); + return; + } + + spin_lock_irqsave(&sport->xlock, flags); + xmit->tail = (xmit->tail + req->actual) & (UART_XMIT_SIZE - 1); + cnt = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE); + if (cnt == 0) + complete(&sport->txcomplete); + else + queue_work(sport->workqueue, &sport->work); + + spin_unlock_irqrestore(&sport->xlock, flags); + + if (cnt < WAKEUP_CHARS) + tty_port_tty_wakeup(&sport->port); + + i3c_slave_ctrl_free_request(req); +} + +static void i3c_slave_tty_i3c_work(struct work_struct *work) +{ + struct ttyi3c_port *sport = container_of(work, struct ttyi3c_port, work); + struct circ_buf *xmit = &sport->xmit; + int cnt = CIRC_CNT(xmit->head, sport->tail_in_queue, UART_XMIT_SIZE); + u8 ibi; + + if (cnt == 0) + return; + + while (cnt > 0) { + struct i3c_request *req = i3c_slave_ctrl_alloc_request(sport->i3cdev->ctrl, + GFP_KERNEL); + if (!req) + return; + + req->length = CIRC_CNT_TO_END(xmit->head, sport->tail_in_queue, UART_XMIT_SIZE); + + req->buf = xmit->buf + sport->tail_in_queue; + req->complete = i3c_slave_tty_tx_complete; + req->context = sport; + req->tx = true; + + if (i3c_slave_ctrl_queue(req, GFP_KERNEL)) + return; + + sport->tail_in_queue += req->length; + sport->tail_in_queue &= UART_XMIT_SIZE - 1; + + cnt = CIRC_CNT(xmit->head, sport->tail_in_queue, UART_XMIT_SIZE); + } + + ibi = I3C_TTY_IBI_TX; + i3c_slave_ctrl_raise_ibi(sport->i3cdev->ctrl, &ibi, 1); +} + +static int i3c_port_activate(struct tty_port *port, struct tty_struct *tty) +{ + struct ttyi3c_port *sport = container_of(port, struct ttyi3c_port, port); + const struct i3c_slave_ctrl_features *feature; + struct i3c_slave_func *func = sport->i3cdev; + struct i3c_request *req; + int rxfifo_size; + int offset = 0; + + feature = i3c_slave_ctrl_get_features(func->ctrl); + if (!feature) + return -EINVAL; + + rxfifo_size = feature->rx_fifo_sz; + + if (!rxfifo_size) + rxfifo_size = I3C_TTY_TRANS_SIZE; + + do { + req = i3c_slave_ctrl_alloc_request(func->ctrl, GFP_KERNEL); + if (!req) + goto err; + + req->buf = (void *) (sport->buffer + offset); + req->length = rxfifo_size; + req->context = sport; + req->complete = i3c_slave_tty_rx_complete; + offset += rxfifo_size; + + if (i3c_slave_ctrl_queue(req, GFP_KERNEL)) + goto err; + } while (req && (offset + rxfifo_size) < UART_XMIT_SIZE); + + reinit_completion(&sport->txcomplete); + + return 0; +err: + i3c_slave_ctrl_cancel_all_reqs(func->ctrl, false); + return -ENOMEM; +} + +static void i3c_port_shutdown(struct tty_port *port) +{ + struct ttyi3c_port *sport = + container_of(port, struct ttyi3c_port, port); + + cancel_work_sync(&sport->work); + + i3c_slave_ctrl_cancel_all_reqs(sport->i3cdev->ctrl, true); + i3c_slave_ctrl_cancel_all_reqs(sport->i3cdev->ctrl, false); + + sport->xmit.tail = sport->tail_in_queue = sport->xmit.head; + + i3c_slave_ctrl_fifo_flush(sport->i3cdev->ctrl, true); + i3c_slave_ctrl_fifo_flush(sport->i3cdev->ctrl, false); +} + +static void i3c_port_destruct(struct tty_port *port) +{ + struct ttyi3c_port *sport = + container_of(port, struct ttyi3c_port, port); + + mutex_lock(&i3c_tty_minors_lock); + idr_remove(&i3c_tty_minors, sport->minor); + mutex_unlock(&i3c_tty_minors_lock); +} + +static const struct tty_port_operations i3c_port_ops = { + .shutdown = i3c_port_shutdown, + .activate = i3c_port_activate, + .destruct = i3c_port_destruct, +}; + +static int i3c_slave_tty_bind(struct i3c_slave_func *func) +{ + struct ttyi3c_port *sport; + struct device *tty_dev; + int minor; + int ret; + + sport = dev_get_drvdata(&func->dev); + + if (i3c_slave_ctrl_set_config(func->ctrl, func)) { + dev_err(&func->dev, "failure set i3c config\n"); + return -EINVAL; + } + + sport->buffer = (void *)get_zeroed_page(GFP_KERNEL); + if (!sport->buffer) + return -ENOMEM; + + sport->xmit.buf = (void *)get_zeroed_page(GFP_KERNEL); + if (!sport->xmit.buf) + goto err_alloc_xmit; + + + spin_lock_init(&sport->xlock); + init_completion(&sport->txcomplete); + + mutex_lock(&i3c_tty_minors_lock); + ret = minor = idr_alloc(&i3c_tty_minors, sport, 0, I3C_TTY_MINORS, GFP_KERNEL); + mutex_unlock(&i3c_tty_minors_lock); + + if (minor < 0) + goto err_idr_alloc; + + tty_port_init(&sport->port); + sport->port.ops = &i3c_port_ops; + + tty_dev = tty_port_register_device(&sport->port, i3c_tty_driver, minor, + &func->dev); + if (IS_ERR(tty_dev)) { + ret = PTR_ERR(tty_dev); + goto err_register_port; + } + + sport->minor = minor; + ret = i3c_slave_ctrl_enable(func->ctrl); + if (ret) + goto err_ctrl_enable; + + return 0; + +err_ctrl_enable: + tty_port_unregister_device(&sport->port, i3c_tty_driver, sport->minor); +err_register_port: + mutex_lock(&i3c_tty_minors_lock); + idr_remove(&i3c_tty_minors, sport->minor); + mutex_unlock(&i3c_tty_minors_lock); +err_idr_alloc: + i3c_slave_ctrl_cancel_all_reqs(func->ctrl, false); + free_page((unsigned long)sport->xmit.buf); +err_alloc_xmit: + free_page((unsigned long)sport->buffer); + + dev_err(&func->dev, "bind failure\n"); + + return ret; +} + +static void i3c_slave_tty_unbind(struct i3c_slave_func *func) +{ + struct ttyi3c_port *sport; + + sport = dev_get_drvdata(&func->dev); + + cancel_work_sync(&sport->work); + + i3c_slave_ctrl_disable(func->ctrl); + i3c_slave_ctrl_cancel_all_reqs(func->ctrl, 0); + i3c_slave_ctrl_cancel_all_reqs(func->ctrl, 1); + + tty_port_unregister_device(&sport->port, i3c_tty_driver, sport->minor); + + free_page((unsigned long)sport->buffer); +} + +static struct i3c_slave_func_ops i3c_func_ops = { + .bind = i3c_slave_tty_bind, + .unbind = i3c_slave_tty_unbind, +}; + +static int i3c_tty_probe(struct i3c_slave_func *func) +{ + struct device *dev = &func->dev; + struct ttyi3c_port *port; + + port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + port->i3cdev = func; + dev_set_drvdata(&func->dev, port); + + port->workqueue = alloc_workqueue("%s", 0, 0, dev_name(&func->dev)); + if (!port->workqueue) + return -ENOMEM; + + INIT_WORK(&port->work, i3c_slave_tty_i3c_work); + + return 0; +} + +static void i3c_tty_remove(struct i3c_slave_func *func) +{ + struct ttyi3c_port *port; + + port = dev_get_drvdata(&func->dev); + + destroy_workqueue(port->workqueue); +} + +static struct ttyi3c_port *i3c_get_by_minor(unsigned int minor) +{ + struct ttyi3c_port *sport; + + mutex_lock(&i3c_tty_minors_lock); + sport = idr_find(&i3c_tty_minors, minor); + mutex_unlock(&i3c_tty_minors_lock); + + return sport; +} + +static int i3c_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct ttyi3c_port *sport; + int ret; + + sport = i3c_get_by_minor(tty->index); + if (!sport) + return -ENODEV; + + ret = tty_standard_install(driver, tty); + if (ret) + return ret; + + tty->driver_data = sport; + + return 0; +} + +static ssize_t i3c_write(struct tty_struct *tty, const unsigned char *buf, size_t count) +{ + struct ttyi3c_port *sport = tty->driver_data; + struct circ_buf *circ = &sport->xmit; + unsigned long flags; + int c, ret = 0; + + spin_lock_irqsave(&sport->xlock, flags); + while (1) { + c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) + break; + + memcpy(circ->buf + circ->head, buf, c); + circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1); + buf += c; + count -= c; + ret += c; + } + i3c_slave_ctrl_set_status_format1(sport->i3cdev->ctrl, sport->status | I3C_TX_NOEMPTY); + spin_unlock_irqrestore(&sport->xlock, flags); + + if (circ->head != circ->tail) + queue_work(sport->workqueue, &sport->work); + + return ret; +} + +static int i3c_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct ttyi3c_port *sport = tty->driver_data; + struct circ_buf *circ = &sport->xmit; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&sport->xlock, flags); + + if (sport && CIRC_SPACE(circ->head, circ->tail, UART_XMIT_SIZE) != 0) { + circ->buf[circ->head] = ch; + circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1); + ret = 1; + } + + spin_unlock_irqrestore(&sport->xlock, flags); + + return ret; +} + +static void i3c_flush_chars(struct tty_struct *tty) +{ + struct ttyi3c_port *sport = tty->driver_data; + struct circ_buf *circ = &sport->xmit; + unsigned long flags; + + spin_lock_irqsave(&sport->xlock, flags); + if (CIRC_SPACE(circ->head, circ->tail, UART_XMIT_SIZE)) + queue_work(sport->workqueue, &sport->work); + spin_unlock_irqrestore(&sport->xlock, flags); +} + +static unsigned int i3c_write_room(struct tty_struct *tty) +{ + struct ttyi3c_port *sport = tty->driver_data; + struct circ_buf *circ = &sport->xmit; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&sport->xlock, flags); + ret = CIRC_SPACE(circ->head, circ->tail, UART_XMIT_SIZE); + spin_unlock_irqrestore(&sport->xlock, flags); + + return ret; +} + +static void i3c_throttle(struct tty_struct *tty) +{ + struct ttyi3c_port *sport = tty->driver_data; + + i3c_slave_ctrl_cancel_all_reqs(sport->i3cdev->ctrl, false); +} + +static void i3c_unthrottle(struct tty_struct *tty) +{ + struct ttyi3c_port *sport = tty->driver_data; + + i3c_port_activate(&sport->port, tty); +} + +static int i3c_open(struct tty_struct *tty, struct file *filp) +{ + struct ttyi3c_port *sport = tty->driver_data; + int ret; + + if (!i3c_slave_ctrl_get_addr(sport->i3cdev->ctrl)) { + dev_info(&sport->i3cdev->dev, "No slave addr assigned, try hotjoin"); + ret = i3c_slave_ctrl_hotjoin(sport->i3cdev->ctrl); + if (ret) { + dev_err(&sport->i3cdev->dev, "Hotjoin failure, check connection"); + return ret; + } + } + + return tty_port_open(&sport->port, tty, filp); +} + +static void i3c_close(struct tty_struct *tty, struct file *filp) +{ + struct ttyi3c_port *sport = tty->driver_data; + + if (!sport) + return; + + tty_port_close(tty->port, tty, filp); +} + +static void i3c_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct ttyi3c_port *sport = tty->driver_data; + struct circ_buf *circ = &sport->xmit; + int val; + int ret; + u8 ibi = I3C_TTY_IBI_TX; + int retry = 100; + + if (circ->head != circ->tail) { + + do { + ret = wait_for_completion_timeout(&sport->txcomplete, timeout / 100); + if (ret) + break; + i3c_slave_ctrl_raise_ibi(sport->i3cdev->ctrl, &ibi, 1); + } while (retry--); + + reinit_completion(&sport->txcomplete); + } + + read_poll_timeout(i3c_slave_ctrl_fifo_status, val, !val, 100, timeout, false, + sport->i3cdev->ctrl, true); + + i3c_slave_ctrl_set_status_format1(sport->i3cdev->ctrl, sport->status & (~I3C_TX_NOEMPTY)); +} + +static const struct tty_operations i3c_tty_ops = { + .install = i3c_install, + .open = i3c_open, + .close = i3c_close, + .write = i3c_write, + .put_char = i3c_put_char, + .flush_chars = i3c_flush_chars, + .write_room = i3c_write_room, + .throttle = i3c_throttle, + .unthrottle = i3c_unthrottle, + .wait_until_sent = i3c_wait_until_sent, +}; + +DECLARE_I3C_SLAVE_FUNC(tty, i3c_tty_probe, i3c_tty_remove, &i3c_func_ops); + +static int __init i3c_tty_init(void) +{ + int ret; + + i3c_tty_driver = tty_alloc_driver( + I3C_TTY_MINORS, TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV); + + if (IS_ERR(i3c_tty_driver)) + return PTR_ERR(i3c_tty_driver); + + i3c_tty_driver->driver_name = "ttySI3C", i3c_tty_driver->name = "ttySI3C", + i3c_tty_driver->minor_start = 0, + i3c_tty_driver->type = TTY_DRIVER_TYPE_SERIAL, + i3c_tty_driver->subtype = SERIAL_TYPE_NORMAL, + i3c_tty_driver->init_termios = tty_std_termios; + i3c_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | + CLOCAL; + i3c_tty_driver->init_termios.c_lflag = 0; + + tty_set_operations(i3c_tty_driver, &i3c_tty_ops); + + ret = tty_register_driver(i3c_tty_driver); + if (ret) { + tty_driver_kref_put(i3c_tty_driver); + return ret; + } + + ret = i3c_slave_func_register_driver(&ttyi3c_func); + if (ret) { + tty_unregister_driver(i3c_tty_driver); + tty_driver_kref_put(i3c_tty_driver); + } + + return ret; +} + +static void __exit i3c_tty_exit(void) +{ + i3c_slave_func_unregister_driver(&ttyi3c_func); + tty_unregister_driver(i3c_tty_driver); + tty_driver_kref_put(i3c_tty_driver); + idr_destroy(&i3c_tty_minors); +} + +module_init(i3c_tty_init); +module_exit(i3c_tty_exit); + +MODULE_LICENSE("GPL"); + From patchwork Wed Oct 18 21:58:09 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Frank Li X-Patchwork-Id: 735910 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (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 91E3F42BFE; Wed, 18 Oct 2023 21:58:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=nxp.com header.i=@nxp.com header.b="YXgfzjLm" Received: from EUR02-VI1-obe.outbound.protection.outlook.com (mail-vi1eur02on2072.outbound.protection.outlook.com [40.107.241.72]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 21236113; Wed, 18 Oct 2023 14:58:52 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=JcAYYjMFBA/kC1S/nelka+oBcs44YXRdb11oBBEqd1KW2s3wP59ZA9PjsA1Qy630ptFsBV3sgJwXc6lAViQM63qzt8KLeZNGoG7KR33gybT4s9egKwBcFGgwvbUNwY6QdgfvVhIVJGdx5oglfIxvoTL+YpUCZnDuCJCEgb87xxOoifRoU0jMcyEMdvt9VUXhMYGbummpZILuiXOShvwjKU5LC8Zk4V+nWoXMvHvmaz5DXe8R5Q2xXo9XzPy+kC7k64Zg3H/iAuKQ9R+ZM/w4DFSfaeBulpd/+IIP0Hy+/1aTdBs2Mrdu/Aa0dFWDCvL2He9uOOIpBrgRBv359Mn80A== 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=vDAV/oMkZ1fz60cAdAxt4H3rVGFC+p9cVJ/KX9+e6iQ=; b=JOC0aLyiysVKEgqK6LdPOMxoRABbICFta9h89aL2vIuD3rXvOr6ql2Iq/rtQj5jVFa30OO4esKWrpJa94EuPbfPCkSFiYBOr1ceC+fA+/NvfqEO/nE4V5m885k2rp8dCIWaW6py/MZbBCVDJmeUAMc4q0d/P3Kts9KSUzYITwMSpy6NIITd1mBhHXJvt4U7UbfTeA1H9VaH7M46SRY/itI46urjjlfupZobf2NDOl6d2wtn220pvjrSgRa6J1I70lM8DQKfOXqPDqmf3IwA/WNZoKF3o46EAhzf+H4gQC+C3UJJiA/p6Cqx13n+wofdKHuK7DBBpWhf2UAxFFN2elg== 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=vDAV/oMkZ1fz60cAdAxt4H3rVGFC+p9cVJ/KX9+e6iQ=; b=YXgfzjLmSCE1YZegABImpKL7kujGZ8IJVp6bUQyqAu1xaTgYn6JGvJlyIwbwgkuS1HJZATv9rOshcIbs1/iY213C1XxvbOl/cUvnLWG+GYMciVlQ/w2g1D0P+Ogv84+opAORfV9XssevadUw15s+YeN1IFqeGhiJr4B62SJWmUs= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nxp.com; Received: from AM6PR04MB4838.eurprd04.prod.outlook.com (2603:10a6:20b:4::16) by DB9PR04MB9750.eurprd04.prod.outlook.com (2603:10a6:10:4c5::8) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6907.25; Wed, 18 Oct 2023 21:58:49 +0000 Received: from AM6PR04MB4838.eurprd04.prod.outlook.com ([fe80::1774:e25f:f99:aca2]) by AM6PR04MB4838.eurprd04.prod.outlook.com ([fe80::1774:e25f:f99:aca2%4]) with mapi id 15.20.6907.022; Wed, 18 Oct 2023 21:58:49 +0000 From: Frank Li To: miquel.raynal@bootlin.com, conor.culhane@silvaco.com, alexandre.belloni@bootlin.com, robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, conor+dt@kernel.org, corbet@lwn.net, joe@perches.com, Frank.Li@nxp.com, linux-i3c@lists.infradead.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org Cc: gregkh@linuxfoundation.org, imx@lists.linux.dev, jirislaby@kernel.org, linux-serial@vger.kernel.org Subject: [PATCH 5/5] Documentation: i3c: Add I3C slave mode controller and function Date: Wed, 18 Oct 2023 17:58:09 -0400 Message-Id: <20231018215809.3477437-6-Frank.Li@nxp.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231018215809.3477437-1-Frank.Li@nxp.com> References: <20231018215809.3477437-1-Frank.Li@nxp.com> X-ClientProxiedBy: SJ0PR03CA0225.namprd03.prod.outlook.com (2603:10b6:a03:39f::20) To AM6PR04MB4838.eurprd04.prod.outlook.com (2603:10a6:20b:4::16) Precedence: bulk X-Mailing-List: devicetree@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: AM6PR04MB4838:EE_|DB9PR04MB9750:EE_ X-MS-Office365-Filtering-Correlation-Id: b8258b18-9b98-4c11-7e40-08dbd02568da X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: /zfxVNneIsSkHo1ac3YGnHYi6VbzfF9cX6UB+LTnjjTN9lSDnnpjo6G6AfcX1FzsAdCtcnvAyD+bZqpjlQrl472xDzk+2YEUE6cvd9sceE8Tbia9rvAqYNtQlj+2CPyTxIPVOp5nGSi60kN70By/kjmI02aNd7PgaAKqzejKeHMNMG/njKQAW4rIEEZpelw9Ae2qJWIKgqLwvKsjJE+/942U1I9USx+k2JFtria8Gi+a8mHvJQHT7LkG9c+lh9qWF1essp92Cbe7z9FKMMie8wzVztKcIoYe1ZVkKuvYAkXCRIxISIkSnIl4G1WhHXHXl8Xf0Wm+6G59BfGni2uEzV93QNfJtSeFYwnUbgWvUyZQCH1ZchrdDk+CDxhRTcSW+u4iZX2VCiEcUhxxBswvK+sI90wXdVDVlv2y8FiPpkqATiJ8tRTFryfSm4DYRTtNsL0dV2lcoryIG1xmAM/KjOitxToodrpbNOXyNfmit39z5otj5e5AyquSwmEOLh41w2SaUris4u3gFX/hHkQQp4/6MRl7q6rlB6eOHxawVq6tb9+CRdXS+/EzJ82LU2qwk5UCrCMt7bq1N3d/f2AYS15ps/JSrl0w5Gw2BpUPxKpK6Yi4Hi/vnZIg1pIHbqL5l0bOpHFX/WvrzL67YGoVfA== X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:AM6PR04MB4838.eurprd04.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230031)(346002)(376002)(396003)(39860400002)(136003)(366004)(230922051799003)(1800799009)(451199024)(186009)(64100799003)(66946007)(66556008)(66476007)(316002)(6666004)(478600001)(6486002)(8936002)(8676002)(36756003)(5660300002)(41300700001)(4326008)(30864003)(2906002)(86362001)(7416002)(38100700002)(2616005)(52116002)(6512007)(1076003)(83380400001)(6506007)(26005)(921005)(38350700005); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: YO+Y/UIhyMSHBBdMEOSasKaFJgyOTbwMcfrFuVLjzAFfPbLsIUX3RDnQwZWEWR7PUOUg0+qHeA43X8NMgNMsy2uVOU1DELR1PD17e4pnR3qB+sWRJtsxztWK9CTIUu7SoLK43bqm0Nf+mircMuHHvdDYo2ysV8hP+Fytwc8pJmVjAwePrOapSyWi2rDO+l32VimeX5AF9CnwEIvCS/IG0nESMPXGSXTSd5NUejXjjRKMwK3AuMXllB+XohAUxIFdIsnZO2JGIIH42jABv/dm8e6B3TiGMJ9wtCPY7TkvR8XyHaKNK55LB9FRPphdXozC8cgtfyzZdrJKT2ovPhFWmfYa+KiNv2xivaSz7ajGlgiUYeJpcEUS2zNa4AvCic0VMseqODxP+oKxSZgwUPCrr9HYq6kQTAGxVs1P5VMprcMwfPFL6wagr1BIbTaCQp8MXoAFcX2PP1otmETPVEKSGHTe8znKZqg2WoKlFjhahVYToRpevMDcXcvgixZgU8hzfl3ImKN/rCH2sowJZlHanMB2tVvZrPVlMXMvXeeDhheFlnAAxXbohpA8obDXu+2vXxJ9pb9Wp0aHGJFytje40L+9WKQeT8AW4yEKwZxwM2du0OChg8cFa+epuDy9WVkYliTOWH8EJbC8bVJloM6bX/IQjGA04gJeC7nC0Cq3H0K+3qbUAD0lzmTTFmsHj1UnipV/N8yJ0ULkQ4LpYcaOMijtMdjyIb7X4NKjgLfgeuQgV0qKD2ukD1Ctc+VjMZ7khkqX7M7sG5EDSkQ5s60h2F3rPzWvFHBJElE8Hrx771ulFAPa0dbJRs2PJ6FZtco05CyluoOmqWsVUuq8JDXXP0eQOzZU1gLxaOvjJQ1pQxcPEOvnEYjKon7NDnglmmozKXCNd5pyRgBq0uxabXQ1zjJg1AVAN0q+O1PLGsZPiTTvForwpmC0x5zXjH6XHVVS5k7qsEwEsWpKyCo+kYUbCd91DJFezESCn2JDGBGaB+e5VvGHmVcqiORCpCd42j2YAwDIKahkpnxFXO84H4LBsicfFW6AAwJNtgToSk+LdRnIv8oCch+KaapCctmtJ60H+FFRi3zHpIlzzluxMYP8GzZGjDuLLkhyWJE+HtzPwEbKGB4Kxf9EioNWkeZlHhJ53jXtGnDBHW25aDgc63FVpexCmvAAU8WJwtEHNVv8FfVV7qedTk5irgsksZlOExQlUamjfD9h/8RUELyQu6U+BFBaU4/mQ5LVwbZUX7Yl0OjSf6jTwbuFViFF7GEXrcuPUqjxXgVRhcBC4XvaDaDKXooVeBXEmNmoir+iw1Sridy3k2MAJX3lzVI/ly0xtuTqqkPRMypsi16BIOlaFrfuJq4sod55NbH5/HGOl0aaUVav9USZlaHeqKPn3bmYAdGLKJVXk8IhQPu5lkPpaJMieIv8oBVZgGxhpmdZrBOZG8bOQx1cUWbE/ZPUQhaNaIdb25wA0R5M7R7dRj7pGRPk0cDovn1ddfdPFrNFkprscbDMPqUkZsyr0rxQscggSll8V9nk+eIneeZZSU79tMxTH6luaVGTTLjXuasPzDDrDit2fBW1y9cIFaGPMyt49pb/ X-OriginatorOrg: nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: b8258b18-9b98-4c11-7e40-08dbd02568da X-MS-Exchange-CrossTenant-AuthSource: AM6PR04MB4838.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 18 Oct 2023 21:58:49.7192 (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: yq/G6RZ0MEtihLZuQ6fXi5e1RvFAzjrY6i6n20NA4S1Erta6ame/ru9nb42AzvPtTY9ePq1pOoXxjg20JdGmTQ== X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB9PR04MB9750 Add I3C slave mode and tty over i3c func driver document. Signed-off-by: Frank Li --- Documentation/driver-api/i3c/index.rst | 1 + .../driver-api/i3c/slave/i3c-slave-cfs.rst | 109 ++++++++++ .../driver-api/i3c/slave/i3c-slave.rst | 189 ++++++++++++++++++ .../driver-api/i3c/slave/i3c-tty-function.rst | 103 ++++++++++ .../driver-api/i3c/slave/i3c-tty-howto.rst | 109 ++++++++++ Documentation/driver-api/i3c/slave/index.rst | 13 ++ 6 files changed, 524 insertions(+) create mode 100644 Documentation/driver-api/i3c/slave/i3c-slave-cfs.rst create mode 100644 Documentation/driver-api/i3c/slave/i3c-slave.rst create mode 100644 Documentation/driver-api/i3c/slave/i3c-tty-function.rst create mode 100644 Documentation/driver-api/i3c/slave/i3c-tty-howto.rst create mode 100644 Documentation/driver-api/i3c/slave/index.rst diff --git a/Documentation/driver-api/i3c/index.rst b/Documentation/driver-api/i3c/index.rst index 783d6dad054b6..63fc51fc8bd58 100644 --- a/Documentation/driver-api/i3c/index.rst +++ b/Documentation/driver-api/i3c/index.rst @@ -9,3 +9,4 @@ I3C subsystem protocol device-driver-api master-driver-api + slave/index diff --git a/Documentation/driver-api/i3c/slave/i3c-slave-cfs.rst b/Documentation/driver-api/i3c/slave/i3c-slave-cfs.rst new file mode 100644 index 0000000000000..d78fcbc4e5587 --- /dev/null +++ b/Documentation/driver-api/i3c/slave/i3c-slave-cfs.rst @@ -0,0 +1,109 @@ +.. SPDX-License-Identifier: GPL-2.0 + +======================================= +Configuring I3C Slave Using CONFIGFS +======================================= + +:Author: Frank Li + +The I3C Slave Core exposes configfs entry (i3c_slave) to configure the I3C +slave function and to bind the slave function +with the slave controller. (For introducing other mechanisms to +configure the I3C Slave Function refer to [1]). + +Mounting configfs +================= + +The I3C Slave Core layer creates i3c_slave directory in the mounted configfs +directory. configfs can be mounted using the following command:: + + mount -t configfs none /sys/kernel/config + +Directory Structure +=================== + +The i3c_slave configfs has two directories at its root: controllers and +functions. Every Controller device present in the system will have an entry in +the *controllers* directory and every Function driver present in the system +will have an entry in the *functions* directory. +:: + + /sys/kernel/config/i3c_slave/ + .. controllers/ + .. functions/ + +Creating Function Device +=================== + +Every registered Function driver will be listed in controllers directory. The +entries corresponding to Function driver will be created by the Function core. +:: + + /sys/kernel/config/i3c_slave/functions/ + .. / + ... / + ... / + ... / + .. / + ... / + ... / + +In order to create a of the type probed by , +the user has to create a directory inside . + +Every directory consists of the following entries that can be +used to configure the standard configuration header of the slave function. +(These entries are created by the framework when any new is +created) +:: + + .. / + ... / + ... vendor_id + ... part_id + ... bcr + ... dcr + ... ext_id + ... instance_id + ... max_read_len + ... max_write_len + ... vendor_info + +Controller Device +========== + +Every registered Controller device will be listed in controllers directory. The +entries corresponding to Controller device will be created by the Controller +core. +:: + + /sys/kernel/config/i3c_slave/controllers/ + .. / + ... / + .. / + ... / + +The directory will have a list of symbolic links to +. These symbolic links should be created by the user to +represent the functions present in the slave device. Only +that represents a physical function can be linked to a Controller device. + +:: + + | controllers/ + | / + | + | functions/ + | / + | / + | vendor_id + | part_id + | bcr + | dcr + | ext_id + | instance_id + | max_read_len + | max_write_len + | vendor_info + +[1] Documentation/I3C/slave/pci-slave.rst diff --git a/Documentation/driver-api/i3c/slave/i3c-slave.rst b/Documentation/driver-api/i3c/slave/i3c-slave.rst new file mode 100644 index 0000000000000..363421241b594 --- /dev/null +++ b/Documentation/driver-api/i3c/slave/i3c-slave.rst @@ -0,0 +1,189 @@ +.. SPDX-License-Identifier: GPL-2.0 + +:Author: Frank Li + +This document is a guide to use the I3C Slave Framework in order to create +slave controller driver, slave function driver, and using configfs +interface to bind the function driver to the controller driver. + +Introduction +============ + +Linux has a comprehensive I3C subsystem to support I3C controllers that +operates in master mode. The subsystem has capability to scan I3C bus,assign +i3c device address, load I3C driver (based on Manufacturer ID, part ID), +support other services like hot-join, In-Band Interrupt(IBI). + +However the I3C controller IP integrated in some SoCs is capable of operating +either in Master mode or Slave mode. I3C Slave Framework will add slave mode +support in Linux. This will help to run Linux in an slave system which can +have a wide variety of use cases from testing or validation, co-processor +accelerator, etc. + +I3C Slave Core +================= + +The I3C Slave Core layer comprises 3 components: the Slave Controller +library, the Slave Function library, and the configfs layer to bind the +slave function with the slave controller. + +I3C Slave Controller Library +------------------------------------ + +The Controller library provides APIs to be used by the controller that can +operate in slave mode. It also provides APIs to be used by function +driver/library in order to implement a particular slave function. + +APIs for the I3C Slave controller Driver +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This section lists the APIs that the I3C Slave core provides to be used +by the I3C controller driver. + +* devm_i3c_slave_ctrl_create()/i3c_slave_ctrl_create() + + The I3C controller driver should implement the following ops: + + * set_config: ops to set i3c configuration + * enable: ops to enable controller + * disable: ops to disable controller + * raise_ibi: ops to raise IBI to master controller + * alloc_request: ops to alloc a transfer request + * free_request: ops to free a transfer request + * queue: ops to queue a request to transfer queue + * dequeue: ops to dequeue a request from transfer queue + * cancel_all_reqs: ops to cancel all request from transfer queue + * fifo_status: ops to get fifo status + * fifo_flush: ops to flush hardware fifo + * get_features: ops to get controller supported features + + The I3C controller driver can then create a new Controller device by + invoking devm_i3c_slave_ctrl_create()/i3c_slave_ctrl_create(). + +* devm_i3c_slave_ctrl_destroy()/i3c_slave_ctrl_destroy() + + The I3C controller driver can destroy the Controller device created by + either devm_i3c_slave_ctrl_create() or i3c_slave_ctrl_create() using + devm_i3c_slave_ctrl_destroy() or i3c_slave_ctrl_destroy(). + +I3C Slave Controller APIs for the I3C Slave Function Driver +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This section lists the APIs that the I3C Slave core provides to be used +by the I3C slave function driver. + +* i3c_slave_ctrl_set_config() + + The I3C slave function driver should use i3c_slave_ctrl_set_config() to + write i3c configuration to the slave controller. + +* i3c_slave_ctrl_enable()/i3c_slave_ctrl_disable() + + The I3C slave function driver should use i3c_slave_ctrl_enable()/ + i3c_slave_ctrl_disable() to enable/disable i3c slave controller. + +* i3c_slave_ctrl_alloc_request()/i3c_slave_ctrl_free_request() + + The I3C slave function driver should usei3c_slave_ctrl_alloc_request() / + i3c_slave_ctrl_free_request() to alloc/free a i3c request. + +* i3c_slave_ctrl_raise_ibi() + + The I3C slave function driver should use i3c_slave_ctrl_raise_ibi() to + raise IBI. + +* i3c_slave_ctrl_queue()/i3c_slave_ctrl_dequeue() + + The I3C slave function driver should use i3c_slave_ctrl_queue()/ + i3c_slave_ctrl_dequeue(), to queue/dequeue I3C transfer to/from transfer + queue. + +* i3c_slave_ctrl_get_features() + + The I3C slave function driver should use i3c_slave_ctrl_get_features() + to get I3C slave controller supported features. + +Other I3C Slave Controller APIs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are other APIs provided by the Controller library. These are used for +binding the I3C Slave Function device with Controlller device. i3c-cfs.c can +be used as reference for using these APIs. + +* i3c_slave_ctrl_get() + + Get a reference to the I3C slave controller based on the device name of + the controller. + +* i3c_slave_ctrl_put() + + Release the reference to the I3C slave controller obtained using + i3c_slave_ctrl_get() + +* i3c_slave_ctrl_add_func() + + Add a I3C slave function to a I3C slave controller. + +* i3c_slave_ctrl_remove_func() + + Remove the I3C slave function from I3C slave controller. + +I3C Slave Function Library +---------------------------------- + +The I3C Slave Function library provides APIs to be used by the function driver +and the Controller library to provide slave mode functionality. + +I3C Slave Function APIs for the I3C Slave Function Driver +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This section lists the APIs that the I3C Slave core provides to be used +by the I3C slave function driver. + +* i3c_slave_func_register_driver() + + The I3C Slave Function driver should implement the following ops: + * bind: ops to perform when a Controller device has been bound to + Function device + * unbind: ops to perform when a binding has been lost between a + Controller device and Function device + + The I3C Function driver can then register the I3C Function driver by using + i3c_slave_func_register_driver(). + +* i3c_slave_func_unregister_driver() + + The I3C Function driver can unregister the I3C Function driver by using + i3c_epf_unregister_driver(). + +APIs for the I3C Slave Controller Library +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This section lists the APIs that the I3C Slave core provides to be used +by the I3C slave controller library. + +Other I3C Slave APIs +~~~~~~~~~~~~~~~~~~~~ + +There are other APIs provided by the Function library. These are used to +notify the function driver when the Function device is bound to the EPC device. +i3c-cfs.c can be used as reference for using these APIs. + +* i3c_slave_func_create() + + Create a new I3C Function device by passing the name of the I3C EPF device. + This name will be used to bind the Function device to a Function driver. + +* i3c_slave_func_destroy() + + Destroy the created I3C Function device. + +* i3c_slave_func_bind() + + i3c_slave_func_bind() should be invoked when the EPF device has been bound + to a Controller device. + +* i3c_slave_func_unbind() + + i3c_slave_func_unbind() should be invoked when the binding between EPC + device and function device is lost. diff --git a/Documentation/driver-api/i3c/slave/i3c-tty-function.rst b/Documentation/driver-api/i3c/slave/i3c-tty-function.rst new file mode 100644 index 0000000000000..3c8521d7aa31a --- /dev/null +++ b/Documentation/driver-api/i3c/slave/i3c-tty-function.rst @@ -0,0 +1,103 @@ +.. SPDX-License-Identifier: GPL-2.0 + +================= +PCI Test Function +================= + +:Author: Kishon Vijay Abraham I + +Traditionally PCI RC has always been validated by using standard +PCI cards like ethernet PCI cards or USB PCI cards or SATA PCI cards. +However with the addition of EP-core in linux kernel, it is possible +to configure a PCI controller that can operate in EP mode to work as +a test device. + +The PCI endpoint test device is a virtual device (defined in software) +used to test the endpoint functionality and serve as a sample driver +for other PCI endpoint devices (to use the EP framework). + +The PCI endpoint test device has the following registers: + + 1) PCI_ENDPOINT_TEST_MAGIC + 2) PCI_ENDPOINT_TEST_COMMAND + 3) PCI_ENDPOINT_TEST_STATUS + 4) PCI_ENDPOINT_TEST_SRC_ADDR + 5) PCI_ENDPOINT_TEST_DST_ADDR + 6) PCI_ENDPOINT_TEST_SIZE + 7) PCI_ENDPOINT_TEST_CHECKSUM + 8) PCI_ENDPOINT_TEST_IRQ_TYPE + 9) PCI_ENDPOINT_TEST_IRQ_NUMBER + +* PCI_ENDPOINT_TEST_MAGIC + +This register will be used to test BAR0. A known pattern will be written +and read back from MAGIC register to verify BAR0. + +* PCI_ENDPOINT_TEST_COMMAND + +This register will be used by the host driver to indicate the function +that the endpoint device must perform. + +======== ================================================================ +Bitfield Description +======== ================================================================ +Bit 0 raise legacy IRQ +Bit 1 raise MSI IRQ +Bit 2 raise MSI-X IRQ +Bit 3 read command (read data from RC buffer) +Bit 4 write command (write data to RC buffer) +Bit 5 copy command (copy data from one RC buffer to another RC buffer) +======== ================================================================ + +* PCI_ENDPOINT_TEST_STATUS + +This register reflects the status of the PCI endpoint device. + +======== ============================== +Bitfield Description +======== ============================== +Bit 0 read success +Bit 1 read fail +Bit 2 write success +Bit 3 write fail +Bit 4 copy success +Bit 5 copy fail +Bit 6 IRQ raised +Bit 7 source address is invalid +Bit 8 destination address is invalid +======== ============================== + +* PCI_ENDPOINT_TEST_SRC_ADDR + +This register contains the source address (RC buffer address) for the +COPY/READ command. + +* PCI_ENDPOINT_TEST_DST_ADDR + +This register contains the destination address (RC buffer address) for +the COPY/WRITE command. + +* PCI_ENDPOINT_TEST_IRQ_TYPE + +This register contains the interrupt type (Legacy/MSI) triggered +for the READ/WRITE/COPY and raise IRQ (Legacy/MSI) commands. + +Possible types: + +====== == +Legacy 0 +MSI 1 +MSI-X 2 +====== == + +* PCI_ENDPOINT_TEST_IRQ_NUMBER + +This register contains the triggered ID interrupt. + +Admissible values: + +====== =========== +Legacy 0 +MSI [1 .. 32] +MSI-X [1 .. 2048] +====== =========== diff --git a/Documentation/driver-api/i3c/slave/i3c-tty-howto.rst b/Documentation/driver-api/i3c/slave/i3c-tty-howto.rst new file mode 100644 index 0000000000000..11c8900fd16f3 --- /dev/null +++ b/Documentation/driver-api/i3c/slave/i3c-tty-howto.rst @@ -0,0 +1,109 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=================== +I3C TTY User Guide +=================== + +:Author: Frank Li + +This document is a guide to help users use i3c-slave-tty function driver +and i3ctty master driver for testing I3C. The list of steps to be followed in the +master side and slave side is given below. + +Endpoint Device +=============== + +Endpoint Controller Devices +--------------------------- + +To find the list of slave controller devices in the system:: + + # ls /sys/class/i3c_slave/ + 44330000.i3c-slave + +If CONFIG_I3C_SLAVE_CONFIGFS is enabled:: + + # ls /sys/kernel/config/i3c_slave/controllers/ + 44330000.i3c-slave + + +Endpoint Function Drivers +------------------------- + +To find the list of slave function drivers in the system:: + + # ls /sys/bus/i3c_slave_func/drivers + tty + +If CONFIG_I3C_SLAVE_CONFIGFS is enabled:: + + # ls /sys/kernel/config/i3c_slave/functions + tty + + +Creating i3c-slave-tty Device +---------------------------- + +I3C slave function device can be created using the configfs. To create +i3c-slave-tty device, the following commands can be used:: + + # mount -t configfs none /sys/kernel/config + # cd /sys/kernel/config/i3c_slave/ + # mkdir functions/tty/func1 + +The "mkdir func1" above creates the i3c-slave-tty function device that will +be probed by i3c tty driver. + +The I3C slave framework populates the directory with the following +configurable fields:: + + # ls functions/tty/func1 + bcr dcr ext_id instance_id max_read_len max_write_len + part_id vendor_id vendor_info + +The I3C slave function driver populates these entries with default values +when the device is bound to the driver. The i3c-slave-tty driver populates +vendorid with 0xffff and interrupt_pin with 0x0001:: + + # cat functions/tty/func1/vendor_id + 0x0 + +Configuring i3c-slave-tty Device +------------------------------- + +The user can configure the i3c-slave-tty device using configfs entry. In order +to change the vendorid, the following commands can be used:: + + # echo 0x011b > functions/tty/func1/vendor_id + # echo 0x1000 > functions/tty/func1/part_id + # echo 0x6 > functions/tty/t/bcr + +Binding i3c-slave-tty Device to slave Controller +------------------------------------------------ + +In order for the slave function device to be useful, it has to be bound to +a I3C slave controller driver. Use the configfs to bind the function +device to one of the controller driver present in the system:: + + # ln -s functions/pci_epf_test/func1 controllers/44330000.i3c-slave/ + +I3C Master Device +================ + +Check I3C tty device is probed + + # ls /sys/bus/i3c/devices/0-23610000000 + 0-23610000000:0 bcr dcr driver dynamic_address hdrcap + modalias pid power subsystem tty uevent + +Using Slave TTY function Device +----------------------------------- + +Host side: + cat /dev/ttyI3C0 +Slave side + echo abc >/dev/ttyI3C0 + +You will see "abc" show at console. + +You can use other tty tool to test I3C slave tty device. diff --git a/Documentation/driver-api/i3c/slave/index.rst b/Documentation/driver-api/i3c/slave/index.rst new file mode 100644 index 0000000000000..69727ccf985db --- /dev/null +++ b/Documentation/driver-api/i3c/slave/index.rst @@ -0,0 +1,13 @@ +.. SPDX-License-Identifier: GPL-2.0 + +====================== +I3C Slave Framework +====================== + +.. toctree:: + :maxdepth: 2 + + i3c-slave + i3c-slave-cfs + i3c-tty-howto +