From patchwork Wed Nov 13 10:10:58 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Andrei Stefanescu X-Patchwork-Id: 13873410 Received: from AS8PR04CU009.outbound.protection.outlook.com (mail-westeuropeazon11011013.outbound.protection.outlook.com [52.101.70.13]) (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 79B451FC7E7 for ; Wed, 13 Nov 2024 10:12:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.70.13 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731492771; cv=fail; b=PGfe/4a1Lc2eMdjIVbGjWlzFSSnYrCcCpCU192PmSVIYYmPGczZV0XOCNnSji+oZ2THfk/QOR3U8bLVE1hots57jxRSa9a+TkfaP8qOWNQK28hzADntPGIUKxTGuj5z+v6MfSqJHrNmKIhtMRG46p3j7qbFnUMMUeV9b7tM3Mao= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731492771; c=relaxed/simple; bh=kF8Nr8hIBNh1rtPdDeE/GfQ/g573NEXdWCPfpGxzTL0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: Content-Type:MIME-Version; b=TAagBfdxgkTIueU8ASWj8W6ICmyRMk6ueoK5hFteyrhtFiHOo687DW7ot3c2yKI8X1t7ZyrOjo3nD3oxd9jyCw89A/QEhO6+Zy4YYyH/YCcXiD7jY7qK2Tj9Hp422yWzHvoauNmcCnYs2oM7tZ1NXt8+5XD1PTib0UxPuTrtNBc= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=oss.nxp.com; spf=pass smtp.mailfrom=oss.nxp.com; dkim=pass (2048-bit key) header.d=NXP1.onmicrosoft.com header.i=@NXP1.onmicrosoft.com header.b=KYtIXwD9; arc=fail smtp.client-ip=52.101.70.13 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=oss.nxp.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.nxp.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=NXP1.onmicrosoft.com header.i=@NXP1.onmicrosoft.com header.b="KYtIXwD9" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=uP4zjWBVZwDojD1Yvhe6PJ9CECthvggq3Yy7PtM1ZN97tIAzbM9WR8qd+0JW+GOZjAmVi1cbsf9axG0+gkblY8ROgctMUO5NEDBkYOsexPWMTyMHH5qz+xShWtz7kGodDNh1cNnQZ1lq6ibagqOqGlNZ5CfagIjRTNM4ZxMVTb+gGLBTIKKmYU//E0ThCEgtKoeSmOgE5CVJrlSFeNUmPIX6bRvDfu7uQvtxum267QWou2zXcG+BWzx+Avk3ZI1ri+7uk+/2GUZmabN2ZXbJt3MkDr9m4upwjv8iJ1r6mUPJWMrIK3aA9pBjoxmrjlZ0orLjw6AH/vmyVbrEzBTIlQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; 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=+54voebqoeKg4PDvduKb7kbojQ1Q0j2NGqVvHwKT85I=; b=NG/zewrueEkYdVdkKEKAyZCFfnkrstQSfgEj4wTgztzk0hkgSVgSAQPXYZkXm8wNDZtORw73vBhySWOaiG3dq7+Dx3uj2M6qnOCsaNaQBiGGftpar0EmGfXT8rNXlLA6tP5tozXO87NOWnAxzvp9JTxWs/LPhGeDBV0qgv+0YcfZfQfqa9B1rT7jrYz7zIuR1h7VitsW+H4kEgsqDgACSfUpKZR2cIGeo7Rl2s3gS9Pzp8egp5qiL3YvcJKMUy8voebV/g2zAvEvr44laDh2IGDptx1wJQTDLlyh9eZmYSpZbQMlhkpF8mz06AKsQUOR5WZF61oZjF8SUVkqMzwO4g== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=oss.nxp.com; dmarc=pass action=none header.from=oss.nxp.com; dkim=pass header.d=oss.nxp.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=NXP1.onmicrosoft.com; s=selector1-NXP1-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=+54voebqoeKg4PDvduKb7kbojQ1Q0j2NGqVvHwKT85I=; b=KYtIXwD9DJydHpGvp5awOFWvKECoIhi4FReSg7P+uzGV5Hg5byarPiiO64B8KuqyDuYBmBI6O8zDRUpSk2MpHFkkcW2YXUVlKcrYpr/uEp7PrJ25XlLOCX1MJV0euDF8Cx71le8wu6xr9xmfVRlBkZZBdmGZGQDYWmlm2p7Qs1hJtchfacLDhjz/vVONPAqDfoRBM78bKtvManiyxCZXXet3rKXGP40wXFUpt2fybWozfttCBmylPDGbePj8xBX9uOlc8DEpnQwEOd5cMryp2NqHfMYhS1RpVCGkMeUGDZIQ+BWDtfzfH4UnwsDvcY8Nrp3zoOmCT+5eUwoGKlYR1A== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=oss.nxp.com; Received: from AM9PR04MB8487.eurprd04.prod.outlook.com (2603:10a6:20b:41a::6) by DBBPR04MB7739.eurprd04.prod.outlook.com (2603:10a6:10:1eb::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8137.29; Wed, 13 Nov 2024 10:12:43 +0000 Received: from AM9PR04MB8487.eurprd04.prod.outlook.com ([fe80::6d7a:8d2:f020:455]) by AM9PR04MB8487.eurprd04.prod.outlook.com ([fe80::6d7a:8d2:f020:455%4]) with mapi id 15.20.8158.013; Wed, 13 Nov 2024 10:12:43 +0000 From: Andrei Stefanescu To: Linus Walleij , Bartosz Golaszewski , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Chester Lin , Matthias Brugger , Ghennadi Procopciuc , Larisa Grigore , Greg Kroah-Hartman , "Rafael J. Wysocki" , Lee Jones , Shawn Guo , Sascha Hauer , Fabio Estevam , Dong Aisheng , Jacky Bai Cc: linux-gpio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, NXP S32 Linux Team , Christophe Lizzi , Alberto Ruiz , Enric Balletbo , Pengutronix Kernel Team , imx@lists.linux.dev, Andrei Stefanescu Subject: [PATCH v6 6/7] pinctrl: s32cc: implement GPIO functionality Date: Wed, 13 Nov 2024 12:10:58 +0200 Message-ID: <20241113101124.1279648-7-andrei.stefanescu@oss.nxp.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20241113101124.1279648-1-andrei.stefanescu@oss.nxp.com> References: <20241113101124.1279648-1-andrei.stefanescu@oss.nxp.com> X-ClientProxiedBy: AM8P251CA0005.EURP251.PROD.OUTLOOK.COM (2603:10a6:20b:21b::10) To AM9PR04MB8487.eurprd04.prod.outlook.com (2603:10a6:20b:41a::6) Precedence: bulk X-Mailing-List: imx@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: AM9PR04MB8487:EE_|DBBPR04MB7739:EE_ X-MS-Office365-Filtering-Correlation-Id: 2cbab589-f131-46f2-47f9-08dd03cbb66d X-MS-Exchange-SharedMailbox-RoutingAgent-Processed: True X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|52116014|7416014|376014|1800799024|366016|921020|38350700014; X-Microsoft-Antispam-Message-Info: =?utf-8?q?BXpwXrFmxvtDHQocJrLLfVhdNchokcn?= =?utf-8?q?+p4FxjM6EHOxR6SrHOnEJS5oLsL4LR93Zpv8hjf/O1AEJuuJqAjRUDJHrVaEpdvgi?= =?utf-8?q?LnA9VNpRd+FMPWhva9LRLrpEaVToSh88YoYnTC3zKs92LEw6MP1fo78YHIjxAdnD1?= =?utf-8?q?qMiZd3WFANueyymkWKoPUdkCDTP+nC2r3e+NdvAKWf8pN9hUbVslToOzItfu48Tdc?= =?utf-8?q?M/xsAj6RCFmVlh8JIUOIVBLD3kbgJ4jQkkiFhYhg5pqDf/7g0OFdW91BVpO5upcIX?= =?utf-8?q?evmPFA2wnAStCZ49j6US0XPpI690Vz+EVDCTddMtEcOw/RN8SDaREuA4e7rRBrLEE?= =?utf-8?q?oz+/qh8fb9BR1g1gZHKotlORLGfzwRvaXZ/h38/ilZoWgYF4irqRjbTTKyCinM8Hn?= =?utf-8?q?kta7zm3WafefPXW4a7IW02vei7Dq58AFnTn6YNmw17nsa/rqlX1/9uQRgRCBFEGQe?= =?utf-8?q?wVgEdS/chTD1kRJyqI7249W8tdTG8HeGnKht+MGcZ79b1+aCO+KJJrqSEaT2HVujZ?= =?utf-8?q?9iZUoUMwxvS3kg2Ir6snXBBO/eE6GwXnzBA7Xk7IwQ6WU1/i2Jaf1d4YI9NE4yKL9?= =?utf-8?q?IsPEzYyNgU9aItSKRwRhC2Y549PMnq5LMZsgLNsYPY57EHpTDcibLyhHWsJWNKak2?= =?utf-8?q?iPEAGbBIpf3M6NmaO0aYjrovr0StThTxK1roPLyfG0mjYp4yg/BTN+3DmIH0ebxOR?= =?utf-8?q?BevWAno2uaoa30XFGz+/EIrcF19pddOvBGWN/lN2Vl6/dHz5yFnSyueknPn9IcPQM?= =?utf-8?q?7JvDvDtl/z/2A8tHT55QoRYA8FNgG1CFJhVa3IHvUlnTzDehTwwyE72f3Z4uq+RmI?= =?utf-8?q?qoG5paNkgilLjC6XDxAcepf169MkvbjeCTuXzOysmLP7GzBQOr42ldgi0HgZ92F7H?= =?utf-8?q?bg8wk59WRpdnePI4Ij8TV8Ry7VbkaOdqXBBbEtI54MqUPZHzgojjcgZa5qQ3YwA6m?= =?utf-8?q?m/t2y4PXtwS6UKvy86IGjf2fPezVf1r2Bw6l+/HgqGjSnxzs/zVBJMteiGNyrBDWw?= =?utf-8?q?/C5Ggft7/dqNoi5sazNS5h/ayN9VRdxkRC3+rFFfBAR4yHs52ML/2dlUMosLEY2mN?= =?utf-8?q?9cppwAkUEdAdDE8ZpqdS0l/Vf0zYEb0Hx5xEJ+6aZ4enpI8yBeLqnyoYT5gMbjyHW?= =?utf-8?q?uWnAtwNdBgrCOvwQyu0sbc+gCkqNZxmPPR+OYgW9bQA5QN35Ikw6WIWA1q736qhM/?= =?utf-8?q?f7IO+kMBJu2nOCTly1hCx49C0bjjFh8uVaqOml0D/diBF3P1R2W7rukrhT7tybTSj?= =?utf-8?q?u7WufoLZL6afCoAh/jCTP6aP6ScF4G56iJsuLMWHU2cdmTrcNXr42f13PyLqPKyJO?= =?utf-8?q?1MxXAMobhlx5?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:AM9PR04MB8487.eurprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(52116014)(7416014)(376014)(1800799024)(366016)(921020)(38350700014);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?q?WB7RSkjM5KpJbaSo09JbPclg9cbp?= =?utf-8?q?NXUm+thhLc2rAghw4AKCVPb2PiJJpAM0HPt7La/+klzUB5USviaW0Szi2pkx/C1Iz?= =?utf-8?q?Q4OE+yBYrsoQkcXMVBDF+qCLuvdPhKUkZJHedP+ueaWkHyTbdEX3+XxEx4/jSrN7J?= =?utf-8?q?ovNFHbnPSODhZ/XUlyXFqChlpo/qVd+EdQk59CcTZUP/LRO+vukNrviW/J9VnO39w?= =?utf-8?q?4uAPL9kYaV//Od0qnwVy+DC32y4vDSmH/ruWPg/8Afaiac7Xm/6txm7DQ2MLJd1v4?= =?utf-8?q?SVBxuCLSvzVIpOR54Dw2VCZ90n3WW96Q2qn/XtPxirxOOsEmBXV0elxdrAqcXoVsW?= =?utf-8?q?G2OOG4sKabjkL73kIJde2al9AvJgE5G5/+jD5s13jfc5rZjK7mvdV6TjjYmahPmg8?= =?utf-8?q?O+az3WGXFu2PLFqG9fuLpHQ73c/vNbtAxkeEP+Rlov7+gC5gG5jLziFq8o5yNwVL4?= =?utf-8?q?4Y5CCLJ97FiUYvnyNKS5NSIHtPwvfpLNF02ClmzljZhFetbXHwGN6dWQj5StvlKes?= =?utf-8?q?415Q1SUCisrlPEzoqV41g6d5cqoF10TnJNBly10P5U+VLssvl6GXapOqeZl/u076X?= =?utf-8?q?l46Aztu4jk1C5CnEJTwKHbIdfmSmU9X+AdeuxfNMbZ3UqtL1W6qOiWDbZE897XpY4?= =?utf-8?q?z2M7+UWbT+cK99ra8V7m/TjfZNl4FFaN9fyPCqJUH58VbffhkRmONsmj4zgb/BhFb?= =?utf-8?q?cScHXbN2j/1Kb71g+8DVWvCuk4XHJc+w6ZAFDWUcJ1YdMTE/aJYiTRCWWxVS5eOsI?= =?utf-8?q?mEkePxwQ77BTOeGiTZCYPK3EiBmBK/fbzP52Y6Q+jxFPeIeZ76O+DQnz4JlN0pHc2?= =?utf-8?q?/jm106PbQMQqzQmetQeZObKO8C9PIrILeevHnltevw+eE8qpK/CjFn5OBP7fWsqIw?= =?utf-8?q?F3ldCcWoSVd3tq1qpmVTguWEqL2zfel9ERTL52Q5poZpVgUvD69Ypd8p1ji7xdoM8?= =?utf-8?q?49ehwtMA6yZpemYxUFSiOK63pFiibK95kCS/LYXgE4FK7NTGUC91qjrjY9rnXEeWl?= =?utf-8?q?BPTfT+7eKvLgmP2/0ZX/kFyD4nzQLo1v0sCjcNIJBVnaw+hwH6uL1AOQLRXkDH2Mm?= =?utf-8?q?QVNBdp+QljI6YLyZKOTF7gFOommBC4uJj4ZyuSYCTVJ2SRxAW0rHxByqHfWBp0k/t?= =?utf-8?q?3xZjhp1q5f4b2Vxd5licK5QBRcJ/C316FO8T2iaBGxMP8b8wl1sRRNJs6xtiFlGm2?= =?utf-8?q?hjnC6m90uc1IdMGQN/Ebj+9j8pQdFEbBt+ohZbNsztjOrmpkQAl3kb1HI9Cm0mRev?= =?utf-8?q?B3CCWZ07s5AWjZjhYIxLKuvHGe+Zw3TYscek7BiWt9A+e7lhsYIxX9w5BUcfaw2F+?= =?utf-8?q?Tlk8a23XZPJhrR2Vt7QK0cXyNhU9g8O+2ZHUoZJkrKYK7l2Q3Xu9Oufey+H9u6ujA?= =?utf-8?q?yLyFMWAz97wHabKhXJeICWvv6JJEwBIhVVcs/tGChaGon5y6BOr3BxCkX7uRqQKg/?= =?utf-8?q?aUu5wCnKSmew9wHMC1k0zhrAe34giIyKRLYI1r72vpWYd25Wl2rglYOx2+LbIDzIR?= =?utf-8?q?oV8Vj28aScFucdwInjujg20yQWCZkvjlRQ=3D=3D?= X-OriginatorOrg: oss.nxp.com X-MS-Exchange-CrossTenant-Network-Message-Id: 2cbab589-f131-46f2-47f9-08dd03cbb66d X-MS-Exchange-CrossTenant-AuthSource: AM9PR04MB8487.eurprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 13 Nov 2024 10:12:43.3145 (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: 7B1WWiva/XvoHxAKtmUy/Pd6LiKQABV9Ni8MJVtJkttSO6klW4OyApsJTnS8BrE4Eg8hnqjy/rDs9RTZVJWVFc61PndrzfbUJh99wKVe1fU= X-MS-Exchange-Transport-CrossTenantHeadersStamped: DBBPR04MB7739 Add basic GPIO functionality (request, free, get, set) for the existing pinctrl SIUL2 driver since the hardware for pinctrl&GPIO is tightly coupled. Also, remove pinmux_ops which are no longer needed. Signed-off-by: Andrei Stefanescu --- drivers/pinctrl/nxp/pinctrl-s32cc.c | 406 +++++++++++++++++++++++----- 1 file changed, 344 insertions(+), 62 deletions(-) diff --git a/drivers/pinctrl/nxp/pinctrl-s32cc.c b/drivers/pinctrl/nxp/pinctrl-s32cc.c index 0b79c7445929..e213a910d958 100644 --- a/drivers/pinctrl/nxp/pinctrl-s32cc.c +++ b/drivers/pinctrl/nxp/pinctrl-s32cc.c @@ -40,6 +40,14 @@ #define S32_MSCR_ODE BIT(20) #define S32_MSCR_OBE BIT(21) +/* PGPDOs are 16bit registers that come in big endian + * order if they are grouped in pairs of two. + * + * For example, the order is PGPDO1, PGPDO0, PGPDO3, PGPDO2... + */ +#define S32_PGPD(N) (((N) ^ 1) * 2) +#define S32_PGPD_SIZE 16 + enum s32_write_type { S32_PINCONF_UPDATE_ONLY, S32_PINCONF_OVERWRITE, @@ -84,6 +92,7 @@ struct s32_pinctrl_context { * struct s32_pinctrl - private driver data * @dev: a pointer back to containing device * @pctl: a pointer to the pinctrl device structure + * @gc: a pointer to the gpio_chip * @regions: reserved memory regions with start/end pin * @info: structure containing information about the pin * @gpio_configs: Saved configurations for GPIO pins @@ -93,6 +102,7 @@ struct s32_pinctrl_context { struct s32_pinctrl { struct device *dev; struct pinctrl_dev *pctl; + struct gpio_chip gc; struct s32_pinctrl_mem_region *regions; struct s32_pinctrl_soc_info *info; struct list_head gpio_configs; @@ -366,66 +376,6 @@ static int s32_pmx_get_groups(struct pinctrl_dev *pctldev, return 0; } -static int s32_pmx_gpio_request_enable(struct pinctrl_dev *pctldev, - struct pinctrl_gpio_range *range, - unsigned int offset) -{ - struct s32_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); - struct gpio_pin_config *gpio_pin; - unsigned int config; - unsigned long flags; - int ret; - - ret = s32_regmap_read(pctldev, offset, &config); - if (ret) - return ret; - - /* Save current configuration */ - gpio_pin = kmalloc(sizeof(*gpio_pin), GFP_KERNEL); - if (!gpio_pin) - return -ENOMEM; - - gpio_pin->pin_id = offset; - gpio_pin->config = config; - - spin_lock_irqsave(&ipctl->gpio_configs_lock, flags); - list_add(&gpio_pin->list, &ipctl->gpio_configs); - spin_unlock_irqrestore(&ipctl->gpio_configs_lock, flags); - - /* GPIO pin means SSS = 0 */ - config &= ~S32_MSCR_SSS_MASK; - - return s32_regmap_write(pctldev, offset, config); -} - -static void s32_pmx_gpio_disable_free(struct pinctrl_dev *pctldev, - struct pinctrl_gpio_range *range, - unsigned int offset) -{ - struct s32_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); - struct gpio_pin_config *gpio_pin, *tmp; - unsigned long flags; - int ret; - - spin_lock_irqsave(&ipctl->gpio_configs_lock, flags); - - list_for_each_entry_safe(gpio_pin, tmp, &ipctl->gpio_configs, list) { - if (gpio_pin->pin_id == offset) { - ret = s32_regmap_write(pctldev, gpio_pin->pin_id, - gpio_pin->config); - if (ret != 0) - goto unlock; - - list_del(&gpio_pin->list); - kfree(gpio_pin); - break; - } - } - -unlock: - spin_unlock_irqrestore(&ipctl->gpio_configs_lock, flags); -} - static int s32_pmx_gpio_set_direction(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned int offset, @@ -449,8 +399,6 @@ static const struct pinmux_ops s32_pmx_ops = { .get_function_name = s32_pmx_get_func_name, .get_function_groups = s32_pmx_get_groups, .set_mux = s32_pmx_set, - .gpio_request_enable = s32_pmx_gpio_request_enable, - .gpio_disable_free = s32_pmx_gpio_disable_free, .gpio_set_direction = s32_pmx_gpio_set_direction, }; @@ -669,6 +617,311 @@ static const struct pinconf_ops s32_pinconf_ops = { .pin_config_group_dbg_show = s32_pinconf_group_dbg_show, }; +static struct s32_pinctrl *to_s32_pinctrl(struct gpio_chip *chip) +{ + return container_of(chip, struct s32_pinctrl, gc); +} + +static struct regmap *s32_gpio_get_pgpd_regmap(struct gpio_chip *chip, + unsigned int pin, + bool output) +{ + struct s32_pinctrl *ipctl = to_s32_pinctrl(chip); + struct nxp_siul2_mfd *mfd; + u32 base, num; + int i; + + mfd = dev_get_drvdata(ipctl->dev->parent); + + for (i = 0; i < mfd->num_siul2; i++) { + base = mfd->siul2[i].gpio_base; + num = mfd->siul2[i].gpio_num; + + if (pin >= base && pin < base + num) + return output ? mfd->siul2[i].regmaps[SIUL2_PGPDO] : + mfd->siul2[i].regmaps[SIUL2_PGPDI]; + } + + return NULL; +} + +static int s32_gpio_request(struct gpio_chip *gc, unsigned int gpio) +{ + struct s32_pinctrl *ipctl = to_s32_pinctrl(gc); + struct pinctrl_dev *pctldev = ipctl->pctl; + struct gpio_pin_config *gpio_pin; + unsigned int config; + int ret; + + ret = s32_regmap_read(pctldev, gpio, &config); + if (ret) + return ret; + + /* Save current configuration */ + gpio_pin = kmalloc(sizeof(*gpio_pin), GFP_KERNEL); + if (!gpio_pin) + return -ENOMEM; + + gpio_pin->pin_id = gpio; + gpio_pin->config = config; + + scoped_guard(spinlock_irqsave, &ipctl->gpio_configs_lock) + list_add(&gpio_pin->list, &ipctl->gpio_configs); + + /* GPIO pin means SSS = 0 */ + config &= ~S32_MSCR_SSS_MASK; + + return s32_regmap_write(pctldev, gpio, config); +} + +static void s32_gpio_free(struct gpio_chip *gc, unsigned int gpio) +{ + struct s32_pinctrl *ipctl = to_s32_pinctrl(gc); + struct pinctrl_dev *pctldev = ipctl->pctl; + struct gpio_pin_config *gpio_pin, *tmp; + unsigned long flags; + int ret; + + spin_lock_irqsave(&ipctl->gpio_configs_lock, flags); + + list_for_each_entry_safe(gpio_pin, tmp, &ipctl->gpio_configs, list) { + if (gpio_pin->pin_id == gpio) { + ret = s32_regmap_write(pctldev, gpio_pin->pin_id, + gpio_pin->config); + if (ret != 0) + goto unlock; + + list_del(&gpio_pin->list); + kfree(gpio_pin); + break; + } + } + +unlock: + spin_unlock_irqrestore(&ipctl->gpio_configs_lock, flags); +} + +static int s32_gpio_get_dir(struct gpio_chip *chip, unsigned int gpio) +{ + struct s32_pinctrl *ipctl = to_s32_pinctrl(chip); + unsigned int reg_value; + int ret; + + ret = s32_regmap_read(ipctl->pctl, gpio, ®_value); + if (ret) + return ret; + + if (!(reg_value & S32_MSCR_IBE)) + return -EINVAL; + + return reg_value & S32_MSCR_OBE ? GPIO_LINE_DIRECTION_OUT : + GPIO_LINE_DIRECTION_IN; +} + +static unsigned int s32_pin2pad(unsigned int pin) +{ + return pin / S32_PGPD_SIZE; +} + +static u16 s32_pin2mask(unsigned int pin) +{ + /** + * From Reference manual : + * PGPDOx[PPDOy] = GPDO(x × 16) + (15 - y)[PDO_(x × 16) + (15 - y)] + */ + return BIT(S32_PGPD_SIZE - 1 - pin % S32_PGPD_SIZE); +} + +static struct regmap *s32_gpio_get_regmap_offset_mask(struct gpio_chip *chip, + unsigned int gpio, + unsigned int *reg_offset, + u16 *mask, + bool output) +{ + struct regmap *regmap; + unsigned int pad; + + regmap = s32_gpio_get_pgpd_regmap(chip, gpio, output); + if (!regmap) + return NULL; + + *mask = s32_pin2mask(gpio); + pad = s32_pin2pad(gpio); + + *reg_offset = S32_PGPD(pad); + + return regmap; +} + +static void s32_gpio_set_val(struct gpio_chip *chip, unsigned int gpio, + int value) +{ + unsigned int reg_offset; + struct regmap *regmap; + u16 mask; + + regmap = s32_gpio_get_regmap_offset_mask(chip, gpio, ®_offset, + &mask, true); + if (!regmap) + return; + + value = value ? mask : 0; + + regmap_update_bits(regmap, reg_offset, mask, value); +} + +static void s32_gpio_set(struct gpio_chip *chip, unsigned int gpio, + int value) +{ + if (s32_gpio_get_dir(chip, gpio) != GPIO_LINE_DIRECTION_OUT) + return; + + s32_gpio_set_val(chip, gpio, value); +} + +static int s32_gpio_get(struct gpio_chip *chip, unsigned int gpio) +{ + unsigned int reg_offset, value; + struct regmap *regmap; + u16 mask; + int ret; + + if (s32_gpio_get_dir(chip, gpio) != GPIO_LINE_DIRECTION_IN) + return -EINVAL; + + regmap = s32_gpio_get_regmap_offset_mask(chip, gpio, ®_offset, + &mask, false); + if (!regmap) + return -EINVAL; + + ret = regmap_read(regmap, reg_offset, &value); + if (ret) + return ret; + + return !!(value & mask); +} + +static int s32_gpio_dir_out(struct gpio_chip *chip, unsigned int gpio, + int val) +{ + struct s32_pinctrl *ipctl = to_s32_pinctrl(chip); + + s32_gpio_set_val(chip, gpio, val); + + return s32_pmx_gpio_set_direction(ipctl->pctl, NULL, gpio, false); +} + +static int s32_gpio_dir_in(struct gpio_chip *chip, unsigned int gpio) +{ + struct s32_pinctrl *ipctl = to_s32_pinctrl(chip); + + return s32_pmx_gpio_set_direction(ipctl->pctl, NULL, gpio, true); +} + +static int s32_gpio_gen_names(struct device *dev, unsigned int cnt, char **names, + char *ch_index, unsigned int *num_index) +{ + unsigned int i; + + for (i = 0; i < cnt; i++) { + if (i != 0 && !(*num_index % 16)) + (*ch_index)++; + + names[i] = devm_kasprintf(dev, GFP_KERNEL, "P%c_%02d", + *ch_index, 0xFU & (*num_index)++); + if (!names[i]) + return -ENOMEM; + } + + return 0; +} + +static int s32_gpio_remove_reserved_names(struct device *dev, + struct s32_pinctrl *ipctl, + char **names) +{ + struct device_node *np = dev->of_node; + u32 base_gpio, num_gpio, tmp; + int num_ranges, i, j, ret; + + /* Parse the gpio-reserved-ranges to know which GPIOs to exclude. */ + + num_ranges = of_property_count_u32_elems(dev->of_node, + "gpio-reserved-ranges"); + + /* The "gpio-reserved-ranges" is optional. */ + if (num_ranges < 0) + return 0; + num_ranges /= 2; + + for (i = 0; i < num_ranges; i++) { + ret = of_property_read_u32_index(np, "gpio-reserved-ranges", + i * 2, &base_gpio); + if (ret) + return dev_err_probe(dev, ret, + "Cannot parse start GPIO: %d\n", + ret); + + ret = of_property_read_u32_index(np, "gpio-reserved-ranges", + i * 2 + 1, &num_gpio); + if (ret) + return dev_err_probe(dev, ret, + "Cannot parse num. GPIOs: %d\n", + ret); + + if (check_add_overflow(base_gpio, num_gpio, &tmp) || + base_gpio + num_gpio > ipctl->gc.ngpio) + return dev_err_probe(dev, -EINVAL, + "Invalid reserved GPIOs\n"); + + /* Remove names set for reserved GPIOs. */ + for (j = base_gpio; j < base_gpio + num_gpio; j++) { + devm_kfree(dev, names[j]); + names[j] = NULL; + } + } + + return 0; +} + +static int s32_gpio_populate_names(struct device *dev, + struct s32_pinctrl *ipctl) +{ + struct nxp_siul2_mfd *mfd = dev_get_drvdata(ipctl->dev->parent); + unsigned int num_index = 0; + char ch_index = 'A'; + char **names; + int i, ret; + + names = devm_kcalloc(dev, ipctl->gc.ngpio, sizeof(*names), + GFP_KERNEL); + if (!names) + return -ENOMEM; + + for (i = 0; i < mfd->num_siul2; i++) { + if (mfd->siul2[i].gpio_base % 16 == 0) + num_index = 0; + + ret = s32_gpio_gen_names(dev, mfd->siul2[i].gpio_num, + names + mfd->siul2[i].gpio_base, + &ch_index, &num_index); + if (ret) + return dev_err_probe(dev, ret, + "Error setting SIUL2_%d names\n", + i); + + ch_index++; + } + + ret = s32_gpio_remove_reserved_names(dev, ipctl, names); + if (ret) + return ret; + + ipctl->gc.names = (const char *const *)names; + + return 0; +} + #ifdef CONFIG_PM_SLEEP static bool s32_pinctrl_should_save(struct s32_pinctrl *ipctl, unsigned int pin) @@ -896,12 +1149,14 @@ static int s32_pinctrl_probe_dt(struct platform_device *pdev, int s32_pinctrl_probe(struct platform_device *pdev, const struct s32_pinctrl_soc_data *soc_data) { + struct nxp_siul2_mfd *mfd = dev_get_drvdata(pdev->dev.parent); #ifdef CONFIG_PM_SLEEP struct s32_pinctrl_context *saved_context; #endif struct pinctrl_desc *s32_pinctrl_desc; struct s32_pinctrl_soc_info *info; struct s32_pinctrl *ipctl; + struct gpio_chip *gc; int ret; if (!soc_data || !soc_data->pins || !soc_data->npins) @@ -967,5 +1222,32 @@ int s32_pinctrl_probe(struct platform_device *pdev, dev_info(&pdev->dev, "Initialized s32 pinctrl driver\n"); + gc = &ipctl->gc; + gc->parent = &pdev->dev; + gc->label = dev_name(&pdev->dev); + gc->base = -1; + /* In some cases, there is a gap between the SIUL GPIOs. */ + gc->ngpio = mfd->siul2[mfd->num_siul2 - 1].gpio_base + + mfd->siul2[mfd->num_siul2 - 1].gpio_num; + ret = s32_gpio_populate_names(&pdev->dev, ipctl); + if (ret) + return ret; + + gc->set = s32_gpio_set; + gc->get = s32_gpio_get; + gc->set_config = gpiochip_generic_config; + gc->request = s32_gpio_request; + gc->free = s32_gpio_free; + gc->direction_output = s32_gpio_dir_out; + gc->direction_input = s32_gpio_dir_in; + gc->get_direction = s32_gpio_get_dir; + + ret = devm_gpiochip_add_data(&pdev->dev, gc, ipctl); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Unable to add gpiochip\n"); + + dev_info(&pdev->dev, "Initialized s32 GPIO functionality\n"); + return 0; }