From nobody Sun Dec 14 06:34:44 2025 Received: from OS8PR02CU002.outbound.protection.outlook.com (mail-japanwestazon11022119.outbound.protection.outlook.com [40.107.75.119]) (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 B150B2D0C85; Fri, 12 Dec 2025 16:41:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.75.119 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765557685; cv=fail; b=RlcCOxhvpscqmHwVf6FX7SBqRDhll1A5PXeOIcYjS1J9A3g6svBmiwBnbdyjuLDatHJwpvGNGMutjkUMejKyh6m0RMOaFZ0mjPTCf4sxZhG7cD6rjB9TNQC42h9bobg/vrSV8m0jZyJg2hLw0QN8DTE7t5cQsHK9BCltdxsceMI= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765557685; c=relaxed/simple; bh=lXC7et4a4FSUnt9QFccIWKfFZtAdsq+cQdB1VHPdxP8=; h=From:Date:Subject:Content-Type:Message-Id:References:In-Reply-To: To:Cc:MIME-Version; b=rVQWjKqs8XoY1FqC29ePjPFTzCGC2fqA4KZwc2gym55UjPMmidR2ppKZkQbn52JN+ehz2ZwC0StGdSHP+aKwjsbABqFW/+E9JAippp/GpIISQyUjmt+zNIEioigtkOBumliMAgByVSQHVMiMJKcRsGEJAKMQBqdFcoTQmgltRrw= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=advantech.com; spf=pass smtp.mailfrom=advantech.de; dkim=pass (2048-bit key) header.d=advantech.com header.i=@advantech.com header.b=NWHaunG8; arc=fail smtp.client-ip=40.107.75.119 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=advantech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=advantech.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=advantech.com header.i=@advantech.com header.b="NWHaunG8" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=XLwRNMEZ0jQmM0YX+H/MB21bEemyJQOE8P0sauJzrCJOlAUdJqK3v9eYn7iPD4k2nbndOmQLIpu1BCOMeyjEbZogBsxDHZErjZYZ1K79fq0/AZIXaMdJMC1f/UAsS8KYGTaONCTSDubXDydLe7TEGax0oAvGme4GX5WZVQOttbyrDG4olNWwv0thvOxqt8GFaI5LE3ADz5tKg5i/kgqxJqcCNySAuOepcR+OOM8SqE0hxmG0gr2RWsz2XQquwK7K5tDPr1NG/744bN5Og3JmytMTyBsQzZHFTbVD0GMoGwW5OsHb9UTeEEul5LMlM8WkM1kZ8gUz9KpvuQ5GUcrimw== 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=XKKBFqjzpIUBryC6LbQr8FENDTfj1AFX515qLe9FEpQ=; b=GxIEuZTbicvJNIRVhAvtp33jQ9knpittzj/0JHn1h7scN6Xc5ejLXwzhVMMzGar0xvSrluLy/b0xUZehSmPXQRW5lRj1RWomRn9GiSw7IHmjvTaivAWpYCwTwqjqDSwSf/31nJGy1rkJlRiC00PRqPj53X9Kqz30i0G6byCO0SvpM0m0FHZ/UsUl0ExlsYM4YiecNzb/toR1jHPzMIFpojGP3Truw44D2r6+1fuRITzjsxF49kNNObidEOUvwC30mSjDHe2n0hNaEQJ6k7UbiDOIv9WvX101qDNI7FEsvfzT2fd/tcUDa+6UhgMboazapO6/3AUZUNoWr4VZqV/Acw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=advantech.de; dmarc=pass action=none header.from=advantech.com; dkim=pass header.d=advantech.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=advantech.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=XKKBFqjzpIUBryC6LbQr8FENDTfj1AFX515qLe9FEpQ=; b=NWHaunG8xTesBj08XDDkstAPRUMKjinIIc0xXzS+pf7Mz+3BKLfZB6aMbXuDe2kOOY0GFoIGPeqDQgQCvNmno4QZspZOJIwYYB5rUq3afUpywe6HCbLtCLyXM4B8WxwpP5LTJwoojeORT2IAjFETRftdxeNt7UuMh1XKXVf8pYRH5KMg5IleOhkeRhhNCrHY8NG4l/luUJ4wT4hqQcmnO91BvnG3iLRtWz6/vbxxBC3Rscl6v0d9a7OmEt2cigFA2Q+3WAIjisCMos6UZwxbZtrv+CUMegfHvtA8CXLmMcvWvhCS/g1jWJD6pb4it4nbnRZcp4K0y7u4JkcdkUNkGw== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=advantech.com; Received: from PSAPR02MB4502.apcprd02.prod.outlook.com (2603:1096:301:21::6) by JH0PR02MB6564.apcprd02.prod.outlook.com (2603:1096:990:3f::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9412.11; Fri, 12 Dec 2025 16:41:19 +0000 Received: from PSAPR02MB4502.apcprd02.prod.outlook.com ([fe80::59c9:fe0:25f6:702b]) by PSAPR02MB4502.apcprd02.prod.outlook.com ([fe80::59c9:fe0:25f6:702b%6]) with mapi id 15.20.9412.005; Fri, 12 Dec 2025 16:41:19 +0000 From: Ramiro Oliveira Date: Fri, 12 Dec 2025 17:40:52 +0100 Subject: [PATCH 1/8] Add Advantech EIO MFD driver Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251212-upstream-v1-v1-1-d50d40ec8d8a@advantech.com> References: <20251212-upstream-v1-v1-0-d50d40ec8d8a@advantech.com> In-Reply-To: <20251212-upstream-v1-v1-0-d50d40ec8d8a@advantech.com> To: Lee Jones , Linus Walleij , Bartosz Golaszewski , Guenter Roeck , Andi Shyti , Daniel Thompson , Jingoo Han , Helge Deller , Wim Van Sebroeck , "Rafael J. Wysocki" , Daniel Lezcano , Zhang Rui , Lukasz Luba Cc: linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, linux-hwmon@vger.kernel.org, linux-i2c@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-fbdev@vger.kernel.org, linux-watchdog@vger.kernel.org, linux-pm@vger.kernel.org, Wenkai Chung , Francisco Aragon-Trivino , Hongzhi Wang , Mikhail Tsukerman , Thomas Kastner , Ramiro Oliveira X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=22601; i=ramiro.oliveira@advantech.com; h=from:subject:message-id; bh=lXC7et4a4FSUnt9QFccIWKfFZtAdsq+cQdB1VHPdxP8=; b=owEB7QES/pANAwAKAc7t0Ke8vbAJAcsmYgBpPEWg3Hk0ZNzIS5owkz+uvJLjFRrvxJ7TFBwfI lSXWzt7NASJAbMEAAEKAB0WIQS1Nkng0ZvJmBKh6GLO7dCnvL2wCQUCaTxFoAAKCRDO7dCnvL2w CR3KDACs4trscsGC/mbHigiBiVIGa5GkpBZTuLKLqEAgnPSW7ZoLDm4esTDiFE/wrkicS0CgiSk owKLvUGzqKj+mnn//KdGaGsm5TFRUZlvWe6FnRQON88kE/vJ3GKPrYVckAF236fvwu3bu94Frov EOmH0blaNOQqU0Lb4Iuv3IBFV5/holZAHXXOJGPfQkf5K8PDxx/7p9DZfGTAs3Y1qz8u7r2GvHe YGdmLwHDECq7JLifUpRqMvUIHrE+rvVpQ39LAsyuvW7RfDhG5JUCe/MobuVWFm9DveHLjKEmhjj 36PKdVmQ+pdR4hdzpTQrLTZOiLDmXw06O+dAh7mIRcC3hNiTlIm2Wscfeyw2EF1iLaASix+2172 UqeBH79axcgqh0PEHeuMYKwNL7MIB/nuge9p/PYdMws0WZeZk84qtfStbPeTjicPg7EbZ7a+GU+ jRGNx/pjoSTa14uVUwaTEig9/Jy5viN8kyGs4aaQYT8xnNj2IN8xX3mDqW8Eunm5Y6sUQ= X-Developer-Key: i=ramiro.oliveira@advantech.com; a=openpgp; fpr=B53649E0D19BC99812A1E862CEEDD0A7BCBDB009 X-ClientProxiedBy: FR4P281CA0175.DEUP281.PROD.OUTLOOK.COM (2603:10a6:d10:b7::12) To PSAPR02MB4502.apcprd02.prod.outlook.com (2603:1096:301:21::6) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PSAPR02MB4502:EE_|JH0PR02MB6564:EE_ X-MS-Office365-Filtering-Correlation-Id: cde7381d-816c-497c-7af3-08de399d46b6 X-MS-Exchange-AtpMessageProperties: SA X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|7416014|366016|52116014|376014|38350700014|921020; X-Microsoft-Antispam-Message-Info: =?utf-8?B?UXRpa1lFa1ZSNkNXaHlYRVlQdDRSMHZBZE5nQXdhNU1ZcEJpeE5HazdzQm16?= =?utf-8?B?NVhjTnJDek93K2NkQ1dzRnRTLzlLcEZTVVRodEduMGN4Y0FMeGgxUUt5R1E2?= =?utf-8?B?MkVHWHlnaFIzYzg0UTkwd2pGUnNYNmYzSmhxR0VheE9zczdyeHpDUVdsa2xk?= =?utf-8?B?ZUpMQldQTnFsNmhBcE9vdE9MWjMxaWhBaE1jeU1ZRnI0YlZhcERiVmQ5SnM2?= =?utf-8?B?TlVHbFBHQno5Z3FGbzhhTEdDNjQ2Z3lkZEhNUlVmREtyTDBWR04zTXN5TXIz?= =?utf-8?B?R0V3MnVyRGVxd04vUXdTWHR1MWNEOUhkSDViaGloV2RmN012NkI2SlVGQk5Y?= =?utf-8?B?YTFpNmlOSXFIUVQ5Wno3Tll1dVhDT0FTcEZ6MHgzdk9ra2JOOFEzWUYyZllZ?= =?utf-8?B?akphMzlPdWpodEc1VVhsdTJMRFZQUkI1MStsVFhNKzQvOUtzdkJLQzdVeUhv?= =?utf-8?B?SnczT0oyUHI5bG83eFp5WGJ3ZXFCeE01dXpwam1nVmlVQ2JESkV0R1NXOVBi?= =?utf-8?B?d1FDZm5BL0VwbnNKdlBldFc1V3kvcTVEdjRSRHJ4M1NoUW9PbHY4cEIxUkkz?= =?utf-8?B?N2dNMzAzekw5bmhWYlVCMkkzN3dFTUN3RzM5RTFjSlV6QkVSRmJoKyt1UFBR?= =?utf-8?B?M1Vidm8vMXdxaE05d3lJdHRWN016TlBzT1IvN3Y4WE04UzlWeHg0dnIybnFk?= =?utf-8?B?VVQxL0M4NEwrOUdDVFFRSE5jTTVXbUc1WXJoMXdRMVpRVjgwUDRac28vRy81?= =?utf-8?B?S0xZd0pvUUxFbWU4U1U3bGMzU3kzd0V2RWZKczNOdWdzb056REx1MUtOeUZP?= =?utf-8?B?Ti9sZDYxNC9HS1NSdlJ3YVNPZVRxaUt4c01QVXJ0cG1CY0s2RGZNcTNpMkJv?= =?utf-8?B?SWl3K3ovTkplODkrc290em5wQzNMbWF4clI5cEpTbHAyL0xKUDVDV2s0bVdX?= =?utf-8?B?Ry91UjNrYUNyd2tNQTBjb0hmdGRoV3AzcFgvbWh3VHFMNEU5Z1A3clVFUkhZ?= =?utf-8?B?a1ZsSWRwSEc5bE5WQVlvWGFWRUNYTDJSdGsvbnFYUnBHc296VEtQSm1zYmxj?= =?utf-8?B?QkpQNlozeEtWRk1aMjBFNUpKVlR0UFRZVkFLV2pCdzdPRjZJNEM5WTVtQUhC?= =?utf-8?B?ZHVGKzBXY3h2ZEIzbDg3U0dJNkJYMDkvRUU2eVFhSXBRWGMxSi9WZ24wYnhK?= =?utf-8?B?Mzl2QmFLNXVoRkUvRGlrdFdHWnJwZFIyUTNFb0krRHpEZFhyWGtWb3RqOTRx?= =?utf-8?B?aTZEYVFicHpZUnA2OWNVVUxKQjNNTWpzTTNjQzRlTk8vNmxNL1NwZEo3WDRC?= =?utf-8?B?T1BjNEp0NmkrbTlrb0EySEJhYlNzK1hUN3plMmhwa0F1UEVuSjNwVlFhN2F0?= =?utf-8?B?RlhCeW9ubmU1SHVPeWlvcDBiMHd1dWRRbGdNWUFmVVVkSjg2b3pRcjFyUUxr?= =?utf-8?B?eXpmYWg1aEx1SHc0Zyt2enF6Zmo2SmNncVhhTjBPMWxuQWg0MHNKS0RJaER1?= =?utf-8?B?ekZMUitWZW5RUkVSQ3I5U1FYV1FjYkE0aDlma1ppTFNEZmRVOEhnekkwZWRF?= =?utf-8?B?WVdka0RyQzNOcUpvTmVTQXNPUDFqVVBEU1oyYi9udEFwUm02SndMdWUyRWxO?= =?utf-8?B?M1V4NVFtL21yendPNkJaTU0yOFo3cS9Vd09sTUxTbVJ3UmVJY3BiYkhnSmNt?= =?utf-8?B?dVlRTGVzdWl3ditOMXB1NlR5cWJ5TXBLN3dYeW1SK1VFTm8xUUlnR3c0QXQx?= =?utf-8?B?MFZ4azVhRDVRZ1MzQzlKTGZxaE8rbFhvdGpxU3l1ZXc0MkNYRzc0UkxZVkVF?= =?utf-8?B?VStzV1lkMGZyYVdQcjFxaDJHeXg3Ulg2cEh2Mm83U1I3TmlOWStwREFkWnBP?= =?utf-8?B?V3ByNVhpYk1sTFV5cmJqOG82NUdpUllKUDM5VUNvaTByY1h1VCtLSEVGby9o?= =?utf-8?B?U001WGRocVlDZy96dExJYW5PdnpRRVN6ZmY3RE90NHVFWTdkR284NENBcXFm?= =?utf-8?B?cDFONUJNanE4Qm1XNWlwM2xnRkpDOFA2cFFrTVBDUnhxL25iUWxuaUV2MllF?= =?utf-8?B?RUtoWm9jMk4zUzljRW1QNW9MTWpzRHBWOWxGZz09?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PSAPR02MB4502.apcprd02.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(7416014)(366016)(52116014)(376014)(38350700014)(921020);DIR:OUT;SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?L1JNR0ZZNGc0bHZadWY4bWRoQVpvVitBRmVyQjJjZ05KNzZRZmN1bkpwL3Vr?= =?utf-8?B?YzdvWGdSdG9nUWlaMFdTKzA0TllGOVJIOUN5L000VkRjeXo5dk5zOWhNeUNF?= =?utf-8?B?NTVWQ2pNRTJKR25nbE1NUVQrL05vQ2MreXE3V2p6ZG03eklHWWtrcXVUbVE3?= =?utf-8?B?VCt4TmVHVVRsaEREQ2dnTXVudWQ1cTRsOTNabzNXanJSdCtXcVhRRDlXNGc4?= =?utf-8?B?V2c0dTFKODhGRmdtMUxjbWN6enpXdHlSUXVycHIvaHFEVDh4Um9RVVVnVEpm?= =?utf-8?B?OERBbUxWWXhYWkxRcnBGMTA1Q3ArWVZocUJXNHhuY2ErWktQWld4M1Ftd0dC?= =?utf-8?B?YnZTYWJTbXJmd0RzdjIvY3dsbWY1RkRSNVVQOE95RXFqZ0p5bUNGQjRBMHYv?= =?utf-8?B?eGpVb3R1ajFCTnhxMGlLeC9QL0xlaFA3aTd2VlJHT1d4aC9JcWtTSGlETDl1?= =?utf-8?B?TzZkL29CbEtrZFh3TzBxbEpVVUJ4ZlhpdDk0YTY5V1ducFZscS92SGV6Z09j?= =?utf-8?B?cG5Ca2h5blVSYTZodGplTnNLSitoS3JVb2NQYXhBT20xL0ptVURDVlJhejY4?= =?utf-8?B?WXhPUmczUStxNEl0dTlVL0JFd2krOWlWTUtnRnRVWE5NQ0hXQVpDL0o0Y09T?= =?utf-8?B?K29ENDBvZkc4QjBURGdlY2NGMkV0V0lXbStKTEpxdlFoVnROQ0JGNmR6aDUy?= =?utf-8?B?MDhlc1NXYUkrY0RWOUVxM3JFVnpxVUlXWnRMbThvZ0IyZytTYnB5ekoraG81?= =?utf-8?B?MmJPVVhScmlnRVpXcEl1d1pvbG1YNFZZeEFaV0VMY1dITnNlZlVVU3RXZmFC?= =?utf-8?B?Tk5NMDFoU2ZQOUVLNnZmR2lhcGZabmNoYUVaOE5KZHRWVDBLa0FxSlA0c3Iw?= =?utf-8?B?SDI5S3ZqRkcyZDh6WCtNemFBVGhoUHYvQjhNVG1XU1B6Rms1M2p6ZDBSWlBG?= =?utf-8?B?SGxKb3ByNTlSN0NuTlE5Q0gzQ05ReHJKS014SUw3eG04MGRYTnBJQ2Z2WVhL?= =?utf-8?B?d2dzVmZWd0tTVW4rZldMQUp6UlZYbWs4Q0xGSjZicllTYzlhb0JlQjFlS3Fj?= =?utf-8?B?djBIRzBjaExJZkpjM3J6UW9CZVI3Y3g1Unk1VU9YU0lzVlFoVlVweHV3bmJN?= =?utf-8?B?ekZ4b1Ivb1Zpd3U4b1Q1aVNyMzJvek0yM3llZnpTeG9udVdkcEU3MWhJMk44?= =?utf-8?B?ZWtUS1N5MVhyeVJJTGNsMEdvTE5pVFJKSVZ4bXNZWHowMFJkbkJaWkMvYjRE?= =?utf-8?B?aFQyc0k1ZkwrZ3RydlVNUWt6YXJWL3RsWmVHV25sc2Z3eldrNVpQeWd1Y1lo?= =?utf-8?B?VTBHQ3ZHT2VDNlh4RUE3OVNISGJnYitvMy96cndTbjR4a2tMNmlsRldvUC8r?= =?utf-8?B?dkNSNnNLMkEvWHMxWEZ5OVkvbkNzalFoaHZKUFlhdnh6TnV6MmpJQmJibWUr?= =?utf-8?B?RkVTWHhFZUx5WFdKeWE2VnpVODhKQXJKMDQzb3dEbWpMS1hJbzdNdW1TM0tp?= =?utf-8?B?MnV1akd6SjZSeDkxaWc1L3EyUWlzM1kxY0pJbzNiekEvTlE2RldxNXYyRU91?= =?utf-8?B?STZRNForQTUrTFBOWWU2VTZZK2VFWVlaRjB1SzM5VzEyKzEvdndYa1M4NTFY?= =?utf-8?B?NWwvblU1S3hLdmUrQWdENDlrb1hrNVFLT3o2c0dTdWpQZDdNSGhBNU4zZllQ?= =?utf-8?B?bXZaVFIyYVliOXlTaDV6Zm5JTGdnNFlkRmVxczNLNWRYOUxYZUVTN3dvaWpZ?= =?utf-8?B?dnJJRTZvZXVONytseUFnRm90L3FZa3JCb3owNXlENHYwUmZxd0gvNXU4YlR0?= =?utf-8?B?U1NHdFdQcktmL3Yvb0NpZ2ZzUzVudjNlTjhSb2Y2TDI4a0tVbnFsendQUHla?= =?utf-8?B?OS9UbXpSS0tTbFhIcFlBZ2R2ZURvSlNra0RZYk9rSVVWNzRmVHYxS0RtWEk2?= =?utf-8?B?dHdkVk0veE1tRTJGNENtK3hnK0Y4WVVMSVo2UnBjN2FBMVVOUjhzTlVvdkFG?= =?utf-8?B?OWJMZHNpWFROdVI1ZGVocCtWY0k1U0hBSUcraXU1TlRjUlFsREdjeHhWdjll?= =?utf-8?B?R1paM25GOHNFNmhqbFFwaXRLTTczMzVRbU0zMngzcFR5c2t0L0xzZzhFUnVl?= =?utf-8?B?Q080eUh2ZEZWakRNejZlZmdpeW5XQ1d3UlBMdTBQZjBaVUdIVE9Ha2tGR0RL?= =?utf-8?Q?Ng0EuEUcbwkC3l5y3bW4B/o=3D?= X-OriginatorOrg: advantech.com X-MS-Exchange-CrossTenant-Network-Message-Id: cde7381d-816c-497c-7af3-08de399d46b6 X-MS-Exchange-CrossTenant-AuthSource: PSAPR02MB4502.apcprd02.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 Dec 2025 16:41:19.4357 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: a77d40d9-dcba-4dda-b571-5f18e6da853f X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: b9egq59I8bQ/5dv22zjSa7Sd7cZVrDV/2Cek1SIMuhsnDBpJ5n6niQHjbnFfBZA2iDT0UZihcoafH6aPOO+cFqwm496i2w7tIOpSPIwl0P4= X-MS-Exchange-Transport-CrossTenantHeadersStamped: JH0PR02MB6564 Creating the MFD core driver for Advantech EIO, all other drivers (GPIO, I2C, etc) depend on this core driver. Signed-off-by: Ramiro Oliveira --- MAINTAINERS | 6 + drivers/mfd/Kconfig | 10 + drivers/mfd/Makefile | 1 + drivers/mfd/eio_core.c | 621 ++++++++++++++++++++++++++++++++++++++++++++= ++++ include/linux/mfd/eio.h | 127 ++++++++++ 5 files changed, 765 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 663e86eb9ff1..bd9279796c2f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -616,6 +616,12 @@ L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/adv_swbutton.c =20 +ADVANTECH EIO DRIVER +M: Ramiro Oliveira +S: Maintained +F: drivers/mfd/eio_core.c +F: include/linux/mfd/eio.h + ADXL313 THREE-AXIS DIGITAL ACCELEROMETER DRIVER M: Lucas Stankus S: Supported diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index aace5766b38a..02a0b324eb6a 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -506,6 +506,16 @@ config MFD_DLN2 etc. must be enabled in order to use the functionality of the device. =20 +config MFD_EIO + tristate "Advantech EIO MFD core" + select MFD_CORE + help + This enables support for the Advantech EIO multi-function device. + This core driver provides register access and coordination for the + EIO's subdevices (GPIO, watchdog, hwmon, thermal, backlight, I2C). + This driver supports EIO-IS200, EIO-201, EIO-210 and EIO-211. + + config MFD_ENE_KB3930 tristate "ENE KB3930 Embedded Controller support" depends on I2C diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index e75e8045c28a..f8c53b55b679 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_MFD_CROS_EC_DEV) +=3D cros_ec_dev.o obj-$(CONFIG_MFD_CS42L43) +=3D cs42l43.o obj-$(CONFIG_MFD_CS42L43_I2C) +=3D cs42l43-i2c.o obj-$(CONFIG_MFD_CS42L43_SDW) +=3D cs42l43-sdw.o +obj-$(CONFIG_MFD_EIO) +=3D eio_core.o obj-$(CONFIG_MFD_ENE_KB3930) +=3D ene-kb3930.o obj-$(CONFIG_MFD_EXYNOS_LPASS) +=3D exynos-lpass.o obj-$(CONFIG_MFD_GATEWORKS_GSC) +=3D gateworks-gsc.o diff --git a/drivers/mfd/eio_core.c b/drivers/mfd/eio_core.c new file mode 100644 index 000000000000..7a58c62595a5 --- /dev/null +++ b/drivers/mfd/eio_core.c @@ -0,0 +1,621 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Advantech Embedded Controller base Driver + * + * This driver provides an interface to access the EIO Series EC + * firmware via its own Power Management Channel (PMC) for subdrivers: + * + * A system may have one or two independent EIO devices. + * + * Copyright (C) 2025 Advantech Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TIMEOUT_MAX (10 * USEC_PER_SEC) +#define TIMEOUT_MIN 200 +#define SLEEP_MAX 200 +#define DEFAULT_TIMEOUT 5000 + +/** + * Timeout: Default timeout in microseconds when a PMC command's + * timeout is unspecified. PMC command responses typically range + * from 200us to 2ms. 5ms is quite a safe value for timeout. In + * In some cases, responses are longer. In such situations, please + * adding the timeout parameter loading related sub-drivers or + * this core driver (not recommended). + */ +static uint timeout =3D DEFAULT_TIMEOUT; +module_param(timeout, uint, 0444); +MODULE_PARM_DESC(timeout, "Default PMC command timeout in usec.\n"); + +struct eio_dev_port { + u16 idx_port; + u16 data_port; +}; + +static struct eio_dev_port pnp_port[] =3D { + { .idx_port =3D EIO_PNP_INDEX, .data_port =3D EIO_PNP_DATA }, + { .idx_port =3D EIO_SUB_PNP_INDEX, + .data_port =3D EIO_SUB_PNP_DATA }, +}; + +static struct mfd_cell mfd_devs[] =3D { + { .name =3D "eio_wdt" }, + { .name =3D "gpio_eio" }, + { .name =3D "eio_hwmon" }, + { .name =3D "i2c_eio" }, + { .name =3D "eio_thermal" }, + { .name =3D "eio_fan" }, + { .name =3D "eio_bl" }, +}; + +static const struct regmap_range eio_range[] =3D { + regmap_reg_range(EIO_PNP_INDEX, EIO_PNP_DATA), + regmap_reg_range(EIO_SUB_PNP_INDEX, EIO_SUB_PNP_DATA), + regmap_reg_range(0x200, 0x3FF), +}; + +static const struct regmap_access_table volatile_regs =3D { + .yes_ranges =3D eio_range, + .n_yes_ranges =3D ARRAY_SIZE(eio_range), +}; + +static const struct regmap_config pnp_regmap_config =3D { + .name =3D "eio_core", + .reg_bits =3D 16, + .val_bits =3D 8, + .volatile_table =3D &volatile_regs, + .io_port =3D true, + .cache_type =3D REGCACHE_NONE, +}; + +static struct { + char name[32]; + int cmd; + int ctrl; + int dev; + int size; + enum { + HEX, + NUMBER, + PNP_ID, + } type; + +} attrs[] =3D { + { "board_name", 0x53, 0x10, 0, 16 }, + { "board_serial", 0x53, 0x1F, 0, 16 }, + { "board_manufacturer", 0x53, 0x11, 0, 16 }, + { "board_id", 0x53, 0x1E, 0, 4 }, + { "firmware_version", 0x53, 0x21, 0, 4 }, + { "firmware_name", 0x53, 0x22, 0, 16 }, + { "firmware_build", 0x53, 0x23, 0, 26 }, + { "firmware_date", 0x53, 0x24, 0, 16 }, + { "chip_id", 0x53, 0x12, 0, 12 }, + { "chip_detect", 0x53, 0x15, 0, 12 }, + { "platform_type", 0x53, 0x13, 0, 16 }, + { "platform_revision", 0x53, 0x04, 0x44, 4 }, + { "eapi_version", 0x53, 0x04, 0x64, 4 }, + { "eapi_id", 0x53, 0x31, 0, 4 }, + { "boot_count", 0x55, 0x10, 0, 4, NUMBER }, + { "powerup_hour", 0x55, 0x11, 0, 4, NUMBER }, + { "pnp_id", 0x53, 0x04, 0x68, 4, PNP_ID }, +}; + +static ssize_t info_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + uint i; + + for (i =3D 0; i < ARRAY_SIZE(attrs); i++) { + int ret; + char str[32] =3D ""; + int val; + + struct pmc_op op =3D { + .cmd =3D attrs[i].cmd, + .control =3D attrs[i].ctrl, + .device_id =3D attrs[i].dev, + .payload =3D (u8 *)str, + .size =3D attrs[i].size, + }; + + if (strcmp(attr->attr.name, attrs[i].name)) + continue; + + ret =3D eio_core_pmc_operation(dev, &op); + if (ret) + return ret; + + if (attrs[i].size !=3D 4) + return sprintf(buf, "%s\n", str); + + val =3D *(u32 *)str; + + if (attrs[i].type =3D=3D HEX) + return sprintf(buf, "0x%08X\n", val); + + if (attrs[i].type =3D=3D NUMBER) + return sprintf(buf, "%d\n", val); + + /* Should be pnp_id */ + return sprintf(buf, "%c%c%c, %X\n", (val >> 14 & 0x3F) + 0x40, + ((val >> 9 & 0x18) | (val >> 25 & 0x07)) + 0x40, + (val >> 20 & 0x1F) + 0x40, val & 0xFFF); + } + + return -EINVAL; +} + +#define PMC_DEVICE_ATTR_RO(_name) = \ + static ssize_t _name##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ + { \ + return info_show(dev, attr, buf); \ + } \ + static DEVICE_ATTR_RO(_name) + +PMC_DEVICE_ATTR_RO(board_name); +PMC_DEVICE_ATTR_RO(board_serial); +PMC_DEVICE_ATTR_RO(board_manufacturer); +PMC_DEVICE_ATTR_RO(firmware_name); +PMC_DEVICE_ATTR_RO(firmware_version); +PMC_DEVICE_ATTR_RO(firmware_build); +PMC_DEVICE_ATTR_RO(firmware_date); +PMC_DEVICE_ATTR_RO(chip_id); +PMC_DEVICE_ATTR_RO(chip_detect); +PMC_DEVICE_ATTR_RO(platform_type); +PMC_DEVICE_ATTR_RO(platform_revision); +PMC_DEVICE_ATTR_RO(board_id); +PMC_DEVICE_ATTR_RO(eapi_version); +PMC_DEVICE_ATTR_RO(eapi_id); +PMC_DEVICE_ATTR_RO(boot_count); +PMC_DEVICE_ATTR_RO(powerup_hour); +PMC_DEVICE_ATTR_RO(pnp_id); + +static struct attribute *pmc_attrs[] =3D { &dev_attr_board_name.attr, + &dev_attr_board_serial.attr, + &dev_attr_board_manufacturer.attr, + &dev_attr_firmware_name.attr, + &dev_attr_firmware_version.attr, + &dev_attr_firmware_build.attr, + &dev_attr_firmware_date.attr, + &dev_attr_chip_id.attr, + &dev_attr_chip_detect.attr, + &dev_attr_platform_type.attr, + &dev_attr_platform_revision.attr, + &dev_attr_board_id.attr, + &dev_attr_eapi_version.attr, + &dev_attr_eapi_id.attr, + &dev_attr_boot_count.attr, + &dev_attr_powerup_hour.attr, + &dev_attr_pnp_id.attr, + NULL }; + +ATTRIBUTE_GROUPS(pmc); + +static unsigned int eio_pnp_read(struct device *dev, + struct eio_dev_port *port, u8 idx) +{ + struct eio_dev *eio =3D dev_get_drvdata(dev); + unsigned int val; + + if (regmap_write(eio->map, port->idx_port, idx)) + dev_err(dev, "Error port write 0x%X\n", port->idx_port); + + if (regmap_read(eio->map, port->data_port, &val)) + dev_err(dev, "Error port read 0x%X\n", port->data_port); + + return val; +} + +static void eio_pnp_write(struct device *dev, struct eio_dev_port *port, + u8 idx, u8 data) +{ + struct eio_dev *eio =3D dev_get_drvdata(dev); + + if (regmap_write(eio->map, port->idx_port, idx) || + regmap_write(eio->map, port->data_port, data)) + dev_err(dev, "Error port write 0x%X %X\n", port->idx_port, + port->data_port); +} + +static void eio_pnp_enter(struct device *dev, struct eio_dev_port *port) +{ + struct eio_dev *eio =3D dev_get_drvdata(dev); + /* Write 0x87 to index port twice to unlock IO port */ + if (regmap_write(eio->map, port->idx_port, + EIO_EXT_MODE_ENTER) || + regmap_write(eio->map, port->idx_port, EIO_EXT_MODE_ENTER)) + dev_err(dev, "Error port write 0x%X\n", port->idx_port); +} + +static void eio_pnp_leave(struct device *dev, struct eio_dev_port *port) +{ + struct eio_dev *eio =3D dev_get_drvdata(dev); + /* Write 0xAA to index port once to lock IO port */ + if (regmap_write(eio->map, port->idx_port, EIO_EXT_MODE_EXIT)) + dev_err(dev, "Error port write 0x%X\n", port->idx_port); +} + +static int pmc_write_data(struct device *dev, int id, u8 value, u16 timeou= t) +{ + struct eio_dev *eio =3D dev_get_drvdata(dev); + int ret; + + if (WAIT_IBF(dev, id, timeout)) + return -ETIME; + + ret =3D regmap_write(eio->map, eio->pmc[id].data, value); + if (ret) + dev_err(dev, "Error PMC write %X:%X\n", + eio->pmc[id].data, value); + + return ret; +} + +static int pmc_write_cmd(struct device *dev, int id, u8 value, u16 timeout) +{ + struct eio_dev *eio =3D dev_get_drvdata(dev); + int ret; + + if (WAIT_IBF(dev, id, timeout)) + return -ETIME; + + ret =3D regmap_write(eio->map, eio->pmc[id].cmd, value); + if (ret) + dev_err(dev, "Error PMC write %X:%X\n", + eio->pmc[id].cmd, value); + + return ret; +} + +static int pmc_read_data(struct device *dev, int id, u8 *value, u16 timeou= t) +{ + struct eio_dev *eio =3D dev_get_drvdata(dev); + unsigned int val; + int ret; + + if (WAIT_OBF(dev, id, timeout)) + return -ETIME; + + ret =3D regmap_read(eio->map, eio->pmc[id].data, &val); + if (ret) + dev_err(dev, "Error PMC read %X\n", eio->pmc[id].data); + else + *value =3D (u8)(val & 0xFF); + + return ret; +} + +static int pmc_read_status(struct device *dev, int id) +{ + struct eio_dev *eio =3D dev_get_drvdata(dev); + unsigned int val; + + if (regmap_read(eio->map, eio->pmc[id].status, &val)) { + dev_err(dev, "Error PMC read %X\n", + eio->pmc[id].status); + return 0; + } + + return val; +} + +static void pmc_clear(struct device *dev, int id) +{ + struct eio_dev *eio =3D dev_get_drvdata(dev); + unsigned int val; + + /* Check if input buffer blocked */ + if ((pmc_read_status(dev, id) & EIO_PMC_STATUS_IBF) =3D=3D 0) + return; + + /* Read out previous garbage */ + if (regmap_read(eio->map, eio->pmc[id].data, &val)) + dev_err(dev, "Error pmc clear\n"); + + usleep_range(10, 100); +} + +int eio_core_pmc_wait(struct device *dev, int id, + enum eio_pmc_wait wait, uint max_duration) +{ + struct eio_dev *eio =3D dev_get_drvdata(dev); + uint val; + int new_timeout =3D max_duration ? max_duration : timeout; + + if (new_timeout < TIMEOUT_MIN || new_timeout > TIMEOUT_MAX) { + dev_err(dev, + "Error timeout value: %dus. Timeout value should between %d and %ld\n", + new_timeout, TIMEOUT_MIN, TIMEOUT_MAX); + return -ETIME; + } + + if (wait =3D=3D PMC_WAIT_INPUT) + return regmap_read_poll_timeout(eio->map, eio->pmc[id].status, + val, (val & EIO_PMC_STATUS_IBF) =3D=3D 0, + SLEEP_MAX, new_timeout); + return regmap_read_poll_timeout(eio->map, + eio->pmc[id].status, val, + (val & EIO_PMC_STATUS_OBF) !=3D 0, + SLEEP_MAX, new_timeout); +} +EXPORT_SYMBOL_GPL(eio_core_pmc_wait); + +int eio_core_pmc_operation(struct device *dev, struct pmc_op *op) +{ + struct eio_dev *eio =3D dev_get_drvdata(dev); + u8 i; + int ret; + bool read_cmd =3D op->cmd & EIO_FLAG_PMC_READ; + ktime_t t =3D ktime_get(); + + mutex_lock(&eio->mutex); + + pmc_clear(dev, op->chip); + + ret =3D pmc_write_cmd(dev, op->chip, op->cmd, op->timeout); + if (ret) + goto err; + + ret =3D pmc_write_data(dev, op->chip, op->control, op->timeout); + if (ret) + goto err; + + ret =3D pmc_write_data(dev, op->chip, op->device_id, op->timeout); + if (ret) + goto err; + + ret =3D pmc_write_data(dev, op->chip, op->size, op->timeout); + if (ret) + goto err; + + for (i =3D 0; i < op->size; i++) { + if (read_cmd) + ret =3D pmc_read_data(dev, op->chip, &op->payload[i], + op->timeout); + else + ret =3D pmc_write_data(dev, op->chip, op->payload[i], + op->timeout); + + if (ret) + goto err; + } + + mutex_unlock(&eio->mutex); + + return 0; + +err: + mutex_unlock(&eio->mutex); + + dev_err(dev, "PMC error duration:%lldus", + ktime_to_us(ktime_sub(ktime_get(), t))); + dev_err(dev, + ".cmd=3D0x%02X, .ctrl=3D0x%02X .id=3D0x%02X, .size=3D0x%02X .data=3D0x%0= 2X%02X", + op->cmd, op->control, op->device_id, op->size, op->payload[0], + op->payload[1]); + + return ret; +} +EXPORT_SYMBOL_GPL(eio_core_pmc_operation); + +static int get_pmc_port(struct device *dev, int id, + struct eio_dev_port *port) +{ + struct eio_dev *eio =3D dev_get_drvdata(dev); + struct _pmc_port *pmc =3D &eio->pmc[id]; + + eio_pnp_enter(dev, port); + + /* Switch to PMC device page */ + eio_pnp_write(dev, port, EIO_LDN, EIO_LDN_PMC1); + + /* Active this device */ + eio_pnp_write(dev, port, EIO_LDAR, EIO_LDAR_LDACT); + + /* Get PMC cmd and data port */ + pmc->data =3D eio_pnp_read(dev, port, EIO_IOBA0H) << 8; + pmc->data |=3D eio_pnp_read(dev, port, EIO_IOBA0L); + pmc->cmd =3D eio_pnp_read(dev, port, EIO_IOBA1H) << 8; + pmc->cmd |=3D eio_pnp_read(dev, port, EIO_IOBA1L); + + /* Disable IRQ */ + eio_pnp_write(dev, port, EIO_IRQCTRL, 0); + + eio_pnp_leave(dev, port); + + /* Make sure IO ports are not occupied */ + if (!devm_request_region(dev, pmc->data, 2, KBUILD_MODNAME)) { + dev_err(dev, "Request region %X error\n", pmc->data); + return -EBUSY; + } + + return 0; +} + +static int eio_init(struct device *dev) +{ + struct eio_dev *eio =3D dev_get_drvdata(dev); + u16 chip_id =3D 0; + u8 tmp =3D 0; + int chip =3D 0; + int ret =3D -ENOMEM; + + for (chip =3D 0; chip < ARRAY_SIZE(pnp_port); chip++) { + struct eio_dev_port *port =3D pnp_port + chip; + + if (!devm_request_region(dev, pnp_port[chip].idx_port, + pnp_port[chip].data_port - + pnp_port[chip].idx_port, + KBUILD_MODNAME)) + continue; + + eio_pnp_enter(dev, port); + + chip_id =3D eio_pnp_read(dev, port, EIO_CHIPID1) << 8; + chip_id |=3D eio_pnp_read(dev, port, EIO_CHIPID2); + + if (chip_id !=3D EIO200_CHIPID && chip_id !=3D EIO201_211_CHIPID) + continue; + + /* Turn on the enable flag */ + tmp =3D eio_pnp_read(dev, port, EIO_SIOCTRL); + tmp |=3D EIO_SIOCTRL_SIOEN; + + eio_pnp_write(dev, port, EIO_SIOCTRL, tmp); + + eio_pnp_leave(dev, port); + + ret =3D get_pmc_port(dev, chip, port); + if (ret) + return ret; + + if (chip =3D=3D 0) + eio->flag |=3D EIO_F_CHIP_EXIST; + else + eio->flag |=3D EIO_F_SUB_CHIP_EXIST; + } + + return ret; +} + +static uint8_t acpiram_access(struct device *dev, uint8_t offset) +{ + u8 val; + int ret; + int timeout =3D 0; + struct eio_dev *eio =3D dev_get_drvdata(dev); + + /* We only store information on primary EC */ + int chip =3D 0; + + mutex_lock(&eio->mutex); + + pmc_clear(dev, chip); + + ret =3D pmc_write_cmd(dev, chip, EIO_PMC_CMD_ACPIRAM_READ, timeout); + if (ret) + goto err; + + ret =3D pmc_write_data(dev, chip, offset, timeout); + if (ret) + goto err; + + ret =3D pmc_write_data(dev, chip, sizeof(val), timeout); + if (ret) + goto err; + + ret =3D pmc_read_data(dev, chip, &val, timeout); + if (ret) + goto err; + +err: + mutex_unlock(&eio->mutex); + return ret ? 0 : val; +} + +static int firmware_code_base(struct device *dev) +{ + struct eio_dev *eio =3D dev_get_drvdata(dev); + u8 ic_vendor, ic_code, code_base; + + ic_vendor =3D acpiram_access(dev, EIO_ACPIRAM_ICVENDOR); + ic_code =3D acpiram_access(dev, EIO_ACPIRAM_ICCODE); + code_base =3D acpiram_access(dev, EIO_ACPIRAM_CODEBASE); + + if (ic_vendor !=3D 'R') + return -ENODEV; + + if (ic_code !=3D EIO200_ICCODE && ic_code !=3D EIO201_ICCODE && + ic_code !=3D EIO211_ICCODE) + goto err; + + if (code_base =3D=3D EIO_ACPIRAM_CODEBASE_NEW) { + eio->flag |=3D EIO_F_NEW_CODE_BASE; + return 0; + } + + if (code_base =3D=3D 0 && + (ic_code !=3D EIO201_ICCODE && ic_code !=3D EIO211_ICCODE)) { + dev_info(dev, "Old code base not supported, yet."); + return -ENODEV; + } + +err: + /* Codebase error. This should only happen on firmware error. */ + dev_err(dev, + "Codebase check fail: vendor: 0x%X, code: 0x%X, base: 0x%X\n", + ic_vendor, ic_code, code_base); + return -ENODEV; +} + +static int eio_probe(struct device *dev, unsigned int id) +{ + int ret =3D 0; + struct eio_dev *eio; + + eio =3D devm_kzalloc(dev, sizeof(*eio), GFP_KERNEL); + if (!eio) + return -ENOMEM; + + eio->dev =3D dev; + mutex_init(&eio->mutex); + + eio->iomem =3D devm_ioport_map(dev, 0, EIO_SUB_PNP_DATA + 1); + if (IS_ERR(eio->iomem)) + return PTR_ERR(eio->iomem); + + eio->map =3D devm_regmap_init_mmio(dev, eio->iomem, &pnp_regmap_config); + if (IS_ERR(eio->map)) + return PTR_ERR(eio->map); + + /* publish instance for subdrivers (dev_get_drvdata(dev->parent)) */ + dev_set_drvdata(dev, eio); + + if (eio_init(dev)) { + dev_dbg(dev, "No device found\n"); + return -ENODEV; + } + + ret =3D firmware_code_base(dev); + if (ret) { + dev_err(dev, "Chip code base check fail\n"); + return ret; /* keep helper's return (e.g., -EIO) */ + } + + ret =3D devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, + mfd_devs, ARRAY_SIZE(mfd_devs), + NULL, 0, NULL); + if (ret) + dev_err(dev, "Cannot register child devices (error =3D %d)\n", ret); + + dev_dbg(dev, "Module insert completed\n"); + return 0; +} + +static struct isa_driver eio_driver =3D { + .probe =3D eio_probe, + + .driver =3D { + .name =3D "eio_core", + .dev_groups =3D pmc_groups, + }, +}; +module_isa_driver(eio_driver, 1); + +MODULE_AUTHOR("Wenkai Chung "); +MODULE_AUTHOR("Ramiro Oliveira "); +MODULE_DESCRIPTION("Advantech EIO series EC core driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/eio.h b/include/linux/mfd/eio.h new file mode 100644 index 000000000000..b87614274201 --- /dev/null +++ b/include/linux/mfd/eio.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Header for the Advantech EIO core driver and its sub-drivers + * + * Copyright (C) 2025 Advantech Co., Ltd. + */ + +#ifndef _MFD_EIO_H_ +#define _MFD_EIO_H_ +#include +#include + +/* Definition */ +#define EIO_CHIPID1 0x20 +#define EIO_CHIPID2 0x21 +#define EIO_CHIPVER 0x22 +#define EIO_SIOCTRL 0x23 +#define EIO_SIOCTRL_SIOEN BIT(0) +#define EIO_SIOCTRL_SWRST BIT(1) +#define EIO_IRQCTRL 0x70 +#define EIO200_CHIPID 0x9610 +#define EIO201_211_CHIPID 0x9620 +#define EIO200_ICCODE 0x10 +#define EIO201_ICCODE 0x20 +#define EIO211_ICCODE 0x21 + +/* LPC PNP */ +#define EIO_PNP_INDEX 0x299 +#define EIO_PNP_DATA 0x29A +#define EIO_SUB_PNP_INDEX 0x499 +#define EIO_SUB_PNP_DATA 0x49A +#define EIO_EXT_MODE_ENTER 0x87 +#define EIO_EXT_MODE_EXIT 0xAA + +/* LPC LDN */ +#define EIO_LDN 0x07 +#define EIO_LDN_PMC0 0x0C +#define EIO_LDN_PMC1 0x0D + +/* PMC registers */ +#define EIO_PMC_STATUS_IBF BIT(1) +#define EIO_PMC_STATUS_OBF BIT(0) +#define EIO_LDAR 0x30 +#define EIO_LDAR_LDACT BIT(0) +#define EIO_IOBA0H 0x60 +#define EIO_IOBA0L 0x61 +#define EIO_IOBA1H 0x62 +#define EIO_IOBA1L 0x63 +#define EIO_FLAG_PMC_READ BIT(0) + +/* PMC command list */ +#define EIO_PMC_CMD_ACPIRAM_READ 0x31 +#define EIO_PMC_CMD_CFG_SAVE 0x56 + +/* OLD PMC */ +#define EIO_PMC_NO_INDEX 0xFF + +/* ACPI RAM Address Table */ +#define EIO_ACPIRAM_VERSIONSECTION (0xFA) +#define EIO_ACPIRAM_ICVENDOR (EIO_ACPIRAM_VERSIONSECTION + 0x00) +#define EIO_ACPIRAM_ICCODE (EIO_ACPIRAM_VERSIONSECTION + 0x01) +#define EIO_ACPIRAM_CODEBASE (EIO_ACPIRAM_VERSIONSECTION + 0x02) + +#define EIO_ACPIRAM_CODEBASE_NEW BIT(7) + +/* Firmware */ +#define EIO_F_SUB_NEW_CODE_BASE BIT(6) +#define EIO_F_SUB_CHANGED BIT(7) +#define EIO_F_NEW_CODE_BASE BIT(8) +#define EIO_F_CHANGED BIT(9) +#define EIO_F_SUB_CHIP_EXIST BIT(30) +#define EIO_F_CHIP_EXIST BIT(31) + +/* Others */ +#define EIO_EC_NUM 2 + +struct _pmc_port { + union { + u16 cmd; + u16 status; + }; + u16 data; +}; + +struct pmc_op { + u8 cmd; + u8 control; + u8 device_id; + u8 size; + u8 *payload; + u8 chip; + u16 timeout; +}; + +enum eio_rw_operation { + OPERATION_READ, + OPERATION_WRITE, +}; + +struct eio_dev { + struct device *dev; + struct regmap *map; + void __iomem *iomem; + struct mutex mutex; /* Protects PMC command access */ + struct _pmc_port pmc[EIO_EC_NUM]; + u32 flag; +}; + +int eio_core_pmc_operation(struct device *dev, struct pmc_op *operation); + +enum eio_pmc_wait { + PMC_WAIT_INPUT, + PMC_WAIT_OUTPUT, +}; + +int eio_core_pmc_wait(struct device *dev, int id, enum eio_pmc_wait wait, + uint timeout); + +#define WAIT_IBF(dev, id, timeout) eio_core_pmc_wait(dev, id, PMC_WAIT_INP= UT, timeout) +#define WAIT_OBF(dev, id, timeout) eio_core_pmc_wait(dev, id, PMC_WAIT_OUT= PUT, timeout) + +#ifdef pr_fmt +#undef pr_fmt +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#endif + +#endif --=20 2.43.0 From nobody Sun Dec 14 06:34:44 2025 Received: from TYDPR03CU002.outbound.protection.outlook.com (mail-japaneastazon11023122.outbound.protection.outlook.com [52.101.127.122]) (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 984FF25C821; Fri, 12 Dec 2025 16:41:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.127.122 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765557692; cv=fail; b=gMgrAC7+ng7eU16dt59BBVUyaAxCO69kjhezQBLSXwmAEGeQuGsAAdDJ8d5zAlWKuu3Ul0508N1fzweBbCQhAwUa5F9baO2tHhSbMz/0I9sAQOYXjQnevVZyS01lI0e3LF5TExm4MZgp2nSkBNMyR/SKFDMs6HCiODWHfcnVdHA= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765557692; c=relaxed/simple; bh=q6wa7ZjhA7DO7xNYwNG50IKMS8562P6Qm3UwUjgwYrQ=; h=From:Date:Subject:Content-Type:Message-Id:References:In-Reply-To: To:Cc:MIME-Version; b=dsGkgTwApsZqImxQpKxAXNxAkkwlFuB6CpQFQuTE873971Z/P0lyDv3l0uYv8e45K5iIhw6m3kgKtJDlajX6Fj5zfXzYi/exNExNxG2m16wVjNwaww7jKA+VNZ6H5JpDeyYIePMVgGjxyF/PNcgoSbh20aZfC0OfYFtOfagl9ts= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=advantech.com; spf=pass smtp.mailfrom=advantech.de; dkim=pass (2048-bit key) header.d=advantech.com header.i=@advantech.com header.b=Q2cZcvgZ; arc=fail smtp.client-ip=52.101.127.122 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=advantech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=advantech.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=advantech.com header.i=@advantech.com header.b="Q2cZcvgZ" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=S7jtCS19bMFDVvJ3VpggTMCNtycWveE6aCz2oqEvaCZvjy+5ok19v9de145Ea6m/RlnY4TN9lVsWRwpBm+YiJMb1cVGz8CY2iR11oNPhvOoqOATWsmacWzFZlzq5OMW0ZaWf2DazY6RcaZ2VGelkBKYS3Bhzotu52/7rDBzwngG4w7y3MVDRPRdeHIOIoMOC3vqne2h1W9HbXdEqmBTuUGQC2qJ1pa8a/ZrzbFfmy9D4a4EaAox7ZJ8EOLMXXBgppPPedOc4ZopyKOnlXeSdW7haOGU/aRkV5LDl+zEuiSE03kZBwrFdnm6w5ygRrhM91zg5syeuyVYIrPqAtFr6SA== 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=slJgHHGMHOS4Qi9U7h5cbySlj/f2icD044PV+7i7KvA=; b=megg39Fc9dFY4+nKjHmd+BhpGB3DDu7gR0aKgq/KndxheBsp87/3wk4FZAVcEJLgm35Bnk+pm+4P48jRJKZ8YypMZVDOmHOGo9JVUy0ifq3Gt+uezXjjLzmHCYKwFYprIF97a3+rG11f5g5JRBdTdH9ynoa74ZfRtT8LVAaWyvq1i7Q73q5F8flilsyCMVJx6wBf+ooCktsrbkNmgHInKMvTdsf+iyCE1t35B8UOMXbZdH29PgfB1lsbIn6TcbEzMihFoytERPf6OvVWJSqpIrtkhuBbRV/+WYl5X1IwNl1wEwgyFJkrg1c+u9mPdY54pGbOlaQMBizrDluAiHffPA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=advantech.de; dmarc=pass action=none header.from=advantech.com; dkim=pass header.d=advantech.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=advantech.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=slJgHHGMHOS4Qi9U7h5cbySlj/f2icD044PV+7i7KvA=; b=Q2cZcvgZ1jqci+9oseadv2IRg3IGKdY9QdqpaU8QRmvuTZFEsFkuYOhKeqJFdH008pd7zmGBql4OHMbl26hNXOpxINm4jO//jaW3LiF27ey3zPaE4kGtaS4+/es8DsGNkFAD0IB1dhLdV7pxjc53yC0RBakomz1spq6Ktnk7S+v73LpSM8K3ji4yU1+YfACu163l+hb+e4KtN8bhTjJw5MBDlQMPJ9psJ/5NS2tET/WMlNJgw3gCWRIOF0+fcyPNinwLo4EWJb/nHD0+ry61k636G/7TMX/j/EKgEEz6Yc0dHr5K1POSEQvoT8ROOf2bra+Fd6jOQKqxD26f8HHpXQ== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=advantech.com; Received: from PSAPR02MB4502.apcprd02.prod.outlook.com (2603:1096:301:21::6) by JH0PR02MB6564.apcprd02.prod.outlook.com (2603:1096:990:3f::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9412.11; Fri, 12 Dec 2025 16:41:26 +0000 Received: from PSAPR02MB4502.apcprd02.prod.outlook.com ([fe80::59c9:fe0:25f6:702b]) by PSAPR02MB4502.apcprd02.prod.outlook.com ([fe80::59c9:fe0:25f6:702b%6]) with mapi id 15.20.9412.005; Fri, 12 Dec 2025 16:41:26 +0000 From: Ramiro Oliveira Date: Fri, 12 Dec 2025 17:40:53 +0100 Subject: [PATCH 2/8] Add Advantech EIO GPIO driver Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251212-upstream-v1-v1-2-d50d40ec8d8a@advantech.com> References: <20251212-upstream-v1-v1-0-d50d40ec8d8a@advantech.com> In-Reply-To: <20251212-upstream-v1-v1-0-d50d40ec8d8a@advantech.com> To: Lee Jones , Linus Walleij , Bartosz Golaszewski , Guenter Roeck , Andi Shyti , Daniel Thompson , Jingoo Han , Helge Deller , Wim Van Sebroeck , "Rafael J. Wysocki" , Daniel Lezcano , Zhang Rui , Lukasz Luba Cc: linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, linux-hwmon@vger.kernel.org, linux-i2c@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-fbdev@vger.kernel.org, linux-watchdog@vger.kernel.org, linux-pm@vger.kernel.org, Wenkai Chung , Francisco Aragon-Trivino , Hongzhi Wang , Mikhail Tsukerman , Thomas Kastner , Ramiro Oliveira X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=8215; i=ramiro.oliveira@advantech.com; h=from:subject:message-id; bh=q6wa7ZjhA7DO7xNYwNG50IKMS8562P6Qm3UwUjgwYrQ=; b=owEB7QES/pANAwAKAc7t0Ke8vbAJAcsmYgBpPEWggb/tK8mBEyTV/JmgJmq6JhdHCUTe/LhdQ QRRaQ2DbI+JAbMEAAEKAB0WIQS1Nkng0ZvJmBKh6GLO7dCnvL2wCQUCaTxFoAAKCRDO7dCnvL2w CZReC/4y6qkdnWeQ5ze9BOXE6+E8/fWQ21Mk5Emm55k29muLzc+cVEMuhA4jtmBmh8XVEaK6yMT 8Fa4WiwYBJhR04803SAWAcqtL0BQpIEyXSs0E3om3KEhHDP5reCIsePUKkpRojC+VjKHzPIj2yS CbhHNaGJhGlHmphouO/mVjAZW6oe69pzc67rt1mT/FjaNd1XocjBbplOIo0IECIhYDt1/v+1l7S gCnuzJ0awsq5dvSECX9o9+Vqm3MmvQ4NxIbGxTbSqX4N5juLU2omA29IQwEOeUE4XVLiNALITaq 9XWx57q4txiA88uJ+TNwamLF/lJvCC56mCb+6li05yzeIJAY4TDRQ7ng8hk6+zhQLDLZNlzucrt m302GfTUUCcqczpgKL5xFfIPbDqcagEcvaleUD7VAfplJ6J13U95RM+jODJ7i0CiKTcrl47lfh0 RVYwvIqPkwJtOMQM7m8Nx26qG7M4jX3fWwkgsVHgPD6uF2EaKAHSni2Za2RhNoCqDeN8k= X-Developer-Key: i=ramiro.oliveira@advantech.com; a=openpgp; fpr=B53649E0D19BC99812A1E862CEEDD0A7BCBDB009 X-ClientProxiedBy: FR4P281CA0175.DEUP281.PROD.OUTLOOK.COM (2603:10a6:d10:b7::12) To PSAPR02MB4502.apcprd02.prod.outlook.com (2603:1096:301:21::6) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PSAPR02MB4502:EE_|JH0PR02MB6564:EE_ X-MS-Office365-Filtering-Correlation-Id: 15b65323-0eff-4b48-38bb-08de399d4b1d X-MS-Exchange-AtpMessageProperties: SA X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|7416014|366016|52116014|376014|38350700014|921020; X-Microsoft-Antispam-Message-Info: =?utf-8?B?TVRGcjN2VUVsOFhIamhxajhDOWl0dXU2NU1yQlFXa0UvVGE2RUNUMFgxVUl4?= =?utf-8?B?Q25xVUFrMW9CZGc2MGVjc3BSQm9YWEZ1THRZYWRkVkU3TkRxSy8rWWpyY0lI?= =?utf-8?B?Qmx4dFJUNVpWUkNyN2pldUVMaWhFN3o2eHFKcmhpZ3pNeWRsN0FwaWhYd0NW?= =?utf-8?B?TU8yNy80aTFSYThQKzlEZnFWTkxua0lJKy9mcTVJQVN5TkpWdmY3dGs3YjFP?= =?utf-8?B?a3JJZzBXSS9jMlpLVGNOUUZSdVEyVlY0ZFF4RG0ybXlXeWszaExiaUZqdFNo?= =?utf-8?B?blV0eWxrREUwT3ltTXRHcGY1NEEycFdiUDNOVnVoSEpyTmxtNzRhWjEzYWQv?= =?utf-8?B?eEVncDhORng2WDkzeUoxL3Fzc0JsQnEvbGNlR2didzhBZXJtMGd2cU5JNUh5?= =?utf-8?B?SnJTTGNJTlpzMVgyTXZJWDFNdGYrZnpnbHpGeDVKcFdxVjV6RnVpd2hRbUtr?= =?utf-8?B?dGduRGRuQnFSTVVuMlRnZmVZcE9DUVM0bG42ZWYrZXE1cmRtVjY2NVFpcGxE?= =?utf-8?B?cVhXZDBKMnRyNjROaDZUbXZCeUdkRDZIWHQ2cU1JYys2TDhaNGl4eXhBd2lW?= =?utf-8?B?KzdYcDNlVFBLTjBwcHJ3bWF3akdxWXNLM2hnSzJ3TS9jS1QxQktlS1FuRmJo?= =?utf-8?B?SnhJeFRIWXVlS1dGUDhWaS9KdFZJcjI2R2hKT1JwTHE2S2paV3B3NUlOWEp1?= =?utf-8?B?azF1dDRNWlpWcFk5SElzVGNuc0l4NWJDOTNDV3hNbEpLT1ZXSEZkbjFxN1d1?= =?utf-8?B?aDRWVnJzVkxESElKbXRrSEVtYUhydDBIaW82bkdlREVWRjI2ZDgzdGx6bUg2?= =?utf-8?B?OHJDSmhKNHRSUHVVK0xwUktmVzRSTytJOHRBYmFHTFRLeDRvVU15bjR3aWl3?= =?utf-8?B?Y0xEbmhRNi9temRHc3A5dFRoTzZocW9GYmpRa0w3S214bVlzS1lvWXlIbE5r?= =?utf-8?B?SjdRVytGTy8yKzVkUGtlRUhneDZkRzhCRHhTWE9QaFNOOEVZZDFZSjNsK3ZG?= =?utf-8?B?Z2hDWXdLdTdLTSthalZBSnR5SDYvdy9QSUlMdEFXVGxXSlY4aTVqQVI3ZEp5?= =?utf-8?B?bTNwakVlL25oZHB4MW9rZS8walp4VmpKblYwbnN0K3FmWjg5N05xU0FKb3Rj?= =?utf-8?B?WjltanY0RE5WMmplRmp5a3Q1WjNNcUlveHozYlJpc0VPNzJXYzh6SUFTajdw?= =?utf-8?B?bXpnaytrS1FjTWRZNXFoYllLRGZFQzFGVXRQcWxoRTBDNzZZc01VNTFyZWhG?= =?utf-8?B?d08rSThEbm5Yb0ZhUW1yT0c4cFQwS3NYR09XRzFtZkc2azhtajNwOVFKckJ3?= =?utf-8?B?b1BZRkxHMlcvMVRXQ0RrNXo1cjE4djVNYXJzK2JtbjRkeGVram1DU0xzdnpF?= =?utf-8?B?cE1KRHEwNHpzNFdpUFNTUVZTV1QweStVYlRGV1Q5dWZ2L3g5UW9URkxuaUk5?= =?utf-8?B?OFlidU5kTGlrTG5iUlNCV0Y1NVpPMGV5MXNKS2g3MC9NMGEwUFN6K3RXQ0RM?= =?utf-8?B?enN0RHBPcEZXWlppR1pLSC9jajA1NDdkQndQUWlsWklBT0luSXd5Z0pnQWxx?= =?utf-8?B?bFZLOC84RmhwY1VlYWpBcXJLV0dSQnBoNkpMd25xRlpObWNBUXBFcFkwc0hY?= =?utf-8?B?THZMR3VuclVGRk5jSlRGeEs5ZXZta2dtbXhTaHdPUTc2YWtWRitUZ3grcTdE?= =?utf-8?B?V1pJYStUYlpQL0ZpUHFWdmozZTlNblpUUU51SXd3MzFQaCtRUGQyemhneThH?= =?utf-8?B?WTcvVDhPYXJxU2R4ZkpvTmV6eGZJQllpVHpBa3dIeERId2ppYWg2L0Z6akd4?= =?utf-8?B?bFVjZm5UcUpYcTlUZHRkL2J4OE1hYytoQkQzWTJFOG55alE2QkpWQUNMWi9Y?= =?utf-8?B?WEZLMWIvbkI0b1ZkVmhnSFBkUU5idHNFR1gwbHcySm1FRnM1NFdSeSsreEJv?= =?utf-8?B?Q1FhRkQ3NnJGYWJybEZHcVdQUC9YYzBoY3dHVjEvcG9jWEgvN1dTZ2cxWE1D?= =?utf-8?B?MUNlY0pWK2laQzFIUEFMc2xRdjNldk10TGx5cS9MRVJJUWlxSTk2SFgvRSts?= =?utf-8?B?UzRhOUVYaUpqZWFpbXFwRkFGakh1S1JySkJMUT09?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PSAPR02MB4502.apcprd02.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(7416014)(366016)(52116014)(376014)(38350700014)(921020);DIR:OUT;SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?S0hUNTBaczF6VTVnUThUQ2x5eXM2OGRqem5IQ3B6RFVpVEMrcVFBNGpSUE1I?= =?utf-8?B?YzNWcTFuV25qUk5FaTZwTTg0TWduZXViUDlGNTE4NUxEZFNqaHBaaDc5ZVI5?= =?utf-8?B?VTR1ZzhSTW9EZ24vaU9VZWc1NFAyc25NNE9wb09tZ2N6amtyUFh1eEJkdUNY?= =?utf-8?B?dVBTUDJsRUNqSHpEWHoweW9BODBRSW5OYXU0RlJYdldsUllXVUVCRE0vTWFT?= =?utf-8?B?OXlDWFNxb3BnYzhTVS8ydWNSbDhxNDVFVlBmTnRIR3gxWU1mcHAxV2E0aEVN?= =?utf-8?B?RUZzd3pMemlIOFRCaFkrZVI4Y05RQkJDU3hEUDBDbWVhdG51Lzh4VFAwU2Zq?= =?utf-8?B?SU9IMEhCTk10eEpyQ3FlQkxIK0d5dHJEMGNoRnBrNDdWZmU2T0tCTmNWNmJ6?= =?utf-8?B?L0x1RlFUZW9Vc3NDcnR1NWJieVZ0eVcwZ1NnTFhIa25jckIzVEJZWWJ6Q0xv?= =?utf-8?B?RENTNXNQOGNIeTZYN2xlRFhKZFhZS01zcDhVUXBBdUxOZG1uc0FUak5yakpF?= =?utf-8?B?RmlvRDNyRllKWm9POGUrYVRqb1dIZWUzWVN4d3krcFE3cUNtOGFUZUJZc2dn?= =?utf-8?B?NGNrVGxNSWJFZGFFN3ZTdHFYellBRFNPRlhYcUZTWEJtLzZRaHNKWEZldTNN?= =?utf-8?B?aFV2VmxSMXRoWFZxWnpHRmUvSnUzUHc0Wk5VL3g0S1NJV0ZIMFRGVTNUb2NL?= =?utf-8?B?OEs1VnUvNGEybGxkZFRPYit0TWxZbnNha0NsRVVuRjFQR21NeXFPOHp3ZTdD?= =?utf-8?B?b0paRkZRVXJZbmJKTGZvemYvb3hBcG1tTFRXUGlYZ1cwQTNzbnpIcUhGUFRB?= =?utf-8?B?T1ZlQUx3TWFaSXRIUCtLdzVMSWVGNFFmeGV1UEpSZkM2ZUZITnEvQXNzdlFt?= =?utf-8?B?WWtUdnhVbFZmeDhVWHVhK0xZQ05ZU1k5ZW1BeWRrbDByY1ZncUhYaFBHYi8v?= =?utf-8?B?N3A5SFhvUk4wUyt0U2dNUVlYbStQVjdOYVlMdDJCcDJ2SjJsMnNZTUVLbERk?= =?utf-8?B?anRaaGFpNWJJZnI3dEVBa3BhRHJYS08xeHY3K25iK0ZjdFRYNmh6ZHVqNmN3?= =?utf-8?B?eDFlL1AzYjRIL2VIRnRRMlhFRkd5RlVFNTc3ZVFHR3JBbDNZZ3VhTURWQkFF?= =?utf-8?B?Vm5ZcmpCRFY1Q3B4SnZpSWFmdzNLMndCMmpYbnRvMjJwRFF1YkpyczQxSG92?= =?utf-8?B?OW45bUYxSk1SaVl4N0w2UGo2S21rbHRaTlZvNVdPZk1qRmQrV1Vtdkd0YWk1?= =?utf-8?B?cFQ3RHI1ZEtOckVDYitnVlVsRTd4NzlKbGJERkFBcllydEVnK1ZpQVl6UEJM?= =?utf-8?B?VEtmeUttLzVQNDIzQjFrdzdVaEZtbDRuTi9WdUhlTVYvZWJNcHpLL0t6TUxF?= =?utf-8?B?R2pkS1BHQ3RYaDlFTFB3VGkxOUFPeG1kd3VBTVU3VHNyUHAyd3JtMnZwZmRV?= =?utf-8?B?cUJqQlA2Q1hMdHRVeHErUUhSdXk3YjVkelNuM05FRHFndElWK1VLSmFhdzk5?= =?utf-8?B?b3BiazZWWVNsWmNmUnlFSEZmQTJFU1hiZXhKd01oSVNQS3dicldXdy9JM3lU?= =?utf-8?B?YUxhUkVjM0YzeEZUOWxtdmZtZk0yTkZrbTlwbVdxUjBlUkI5dHlUUzBDUEhU?= =?utf-8?B?elBJOUhydjBqV3llMFJQUGNKQ3dPblEwSTNGL1BBS21Ec1lrbVdwRE0ycWxq?= =?utf-8?B?TXBqRE8zRVVHYkJLQ3NEc2pKMnNjSFBJMVV3OTgvOHRiQlJLdjhkaU9NRHBB?= =?utf-8?B?NTQ4ZkNUNFdwQzlKVGpkdjc4R2ptbWdqMHlvcldQNnYyNGJSZGdCY0VyVmVz?= =?utf-8?B?TFhZTTN4UjN6N0x5V01xU2VNZHNWeWhUVUZ1dFcxbmxhVFZ6TnBlWUFGSkpk?= =?utf-8?B?cnNvelhqSEJRZFdsTTA0Vkl5eHdFMGFML280dTVNZkorYTJnNmJlVnBJNE1V?= =?utf-8?B?anc5eVVlMVFXS2hEbWs4K0dMaUs2SWZUcEJubTc2UzVZOThPcXdyTXBsN01a?= =?utf-8?B?ajBIU3NXd296dDlrQnFxOTgxU3VOT1dwT0tCZlpuTWNaZzFuazRCb1ZUTEJR?= =?utf-8?B?MisxTm1kcUJZTFQ4V0xGQStEcWQvYW5rTTRMSnEwMGg0N3NUQ3Y0VjVuWFJx?= =?utf-8?B?eGM5UEZnQzJ5MjlxcGwxeVlzZTBpbUFsREhqb1dqb2syaDNaanBNMU9mZytm?= =?utf-8?Q?zm3Dhn40b8lvD+HdK18dpaU=3D?= X-OriginatorOrg: advantech.com X-MS-Exchange-CrossTenant-Network-Message-Id: 15b65323-0eff-4b48-38bb-08de399d4b1d X-MS-Exchange-CrossTenant-AuthSource: PSAPR02MB4502.apcprd02.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 Dec 2025 16:41:26.7458 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: a77d40d9-dcba-4dda-b571-5f18e6da853f X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: sGL25qnqpfYID+hMYoDrPNqNAXWem03s6nTlKfws+dyIXjNbLXQpiiWTwmnBp5QqUIGIjmfFirlz6UDFI1SrX8xT4bgYQZehfHSTalP0YD0= X-MS-Exchange-Transport-CrossTenantHeadersStamped: JH0PR02MB6564 This driver controls the GPIO component of the Advantech EIO chip. Signed-off-by: Ramiro Oliveira --- MAINTAINERS | 1 + drivers/gpio/Kconfig | 6 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-eio.c | 273 ++++++++++++++++++++++++++++++++++++++++++++= ++++ 4 files changed, 281 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index bd9279796c2f..359d4a13f212 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -619,6 +619,7 @@ F: drivers/platform/x86/adv_swbutton.c ADVANTECH EIO DRIVER M: Ramiro Oliveira S: Maintained +F: drivers/gpio/gpio-eio.c F: drivers/mfd/eio_core.c F: include/linux/mfd/eio.h =20 diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index bd185482a7fd..628a914842bd 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -277,6 +277,12 @@ config GPIO_DWAPB Say Y or M here to build support for the Synopsys DesignWare APB GPIO block. =20 +config GPIO_EIO + tristate "Advantech EIO GPIO" + depends on MFD_EIO + help + Say Y or M to build support for Advantech EIO GPIO block. + config GPIO_EIC_SPRD tristate "Spreadtrum EIC support" depends on ARCH_SPRD || COMPILE_TEST diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 2421a8fd3733..ba3883d5e4a0 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -64,6 +64,7 @@ obj-$(CONFIG_GPIO_DLN2) +=3D gpio-dln2.o obj-$(CONFIG_GPIO_DS4520) +=3D gpio-ds4520.o obj-$(CONFIG_GPIO_DWAPB) +=3D gpio-dwapb.o obj-$(CONFIG_GPIO_EIC_SPRD) +=3D gpio-eic-sprd.o +obj-$(CONFIG_GPIO_EIO) +=3D gpio-eio.o obj-$(CONFIG_GPIO_ELKHARTLAKE) +=3D gpio-elkhartlake.o obj-$(CONFIG_GPIO_EM) +=3D gpio-em.o obj-$(CONFIG_GPIO_EN7523) +=3D gpio-en7523.o diff --git a/drivers/gpio/gpio-eio.c b/drivers/gpio/gpio-eio.c new file mode 100644 index 000000000000..50f66a325e8f --- /dev/null +++ b/drivers/gpio/gpio-eio.c @@ -0,0 +1,273 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * GPIO driver for Advantech EIO Embedded controller. + * + * Copyright (C) 2025 Advantech Corporation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include + +#define GPIO_MAX_PINS 48 +#define GPIO_WRITE 0x18 +#define GPIO_READ 0x19 + +struct eio_gpio_dev { + u64 avail; + int max; + struct gpio_chip chip; + struct device *dev; +}; + +struct { + int size; + bool write; +} ctrl_para[] =3D { + { 0x01, false }, { 0x00, false }, { 0x00, false }, { 0x02, false }, + { 0x01, false }, { 0x00, false }, { 0x00, false }, { 0x00, false }, + { 0x00, false }, { 0x00, false }, { 0x00, false }, { 0x00, false }, + { 0x00, false }, { 0x00, false }, { 0x00, false }, { 0x00, false }, + { 0x01, true }, { 0x01, true }, { 0x02, true }, { 0x02, true }, + { 0x02, false }, { 0x10, false } +}; + +enum { + GPIO_STATUS =3D 0, + GPIO_GROUP_AVAIL =3D 3, + GPIO_ERROR =3D 0x04, + GPIO_PIN_DIR =3D 0x10, + GPIO_PIN_LEVEL =3D 0x11, + GPIO_GROUP_DIR =3D 0x12, + GPIO_GROUP_LEVEL =3D 0x13, + GPIO_MAPPING =3D 0x14, + GPIO_NAME =3D 0x15 +} gpio_ctrl; + +struct { + int group; + int port; +} group_map[] =3D { + { 0, 0 }, { 0, 1 }, + { 1, 0 }, { 1, 1 }, + { 2, 0 }, { 2, 1 }, + { 3, 0 }, { 3, 1 }, + { 3, 2 }, { 3, 3 }, + { 3, 4 }, { 3, 5 }, + { 3, 6 }, { 3, 7 } +}; + +static int timeout; +module_param(timeout, int, 0444); +MODULE_PARM_DESC(timeout, "Set PMC command timeout value.\n"); + +static int pmc_write(struct device *mfd_dev, u8 ctrl, u8 dev_id, void *dat= a) +{ + struct pmc_op op =3D { + .cmd =3D GPIO_WRITE, + .control =3D ctrl, + .device_id =3D dev_id, + .payload =3D (u8 *)data, + .timeout =3D timeout, + }; + + if (ctrl > ARRAY_SIZE(ctrl_para)) + return -ENOMEM; + + if (!ctrl_para[ctrl].write) + return -EINVAL; + + op.size =3D ctrl_para[ctrl].size; + + return eio_core_pmc_operation(mfd_dev, &op); +} + +static int pmc_read(struct device *mfd_dev, u8 ctrl, u8 dev_id, void *data) +{ + struct pmc_op op =3D { + .cmd =3D GPIO_READ, + .control =3D ctrl, + .device_id =3D dev_id, + .payload =3D (u8 *)data, + .timeout =3D timeout, + }; + + if (ctrl > ARRAY_SIZE(ctrl_para)) + return -ENOMEM; + + op.size =3D ctrl_para[ctrl].size; + + return eio_core_pmc_operation(mfd_dev, &op); +} + +static int get_dir(struct gpio_chip *chip, unsigned int offset) +{ + u8 dir; + int ret; + + ret =3D pmc_read(chip->parent, GPIO_PIN_DIR, offset, &dir); + if (ret) + return ret; + + return dir ? 0 : 1; +} + +static int dir_input(struct gpio_chip *chip, unsigned int offset) +{ + u8 dir =3D 0; + + return pmc_write(chip->parent, GPIO_PIN_DIR, offset, &dir); +} + +static int dir_output(struct gpio_chip *chip, unsigned int offset, int val= ue) +{ + u8 dir =3D 1; + u8 val =3D value; + + pmc_write(chip->parent, GPIO_PIN_DIR, offset, &dir); + + return pmc_write(chip->parent, GPIO_PIN_LEVEL, offset, &val); +} + +static int gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + u8 level; + int ret; + + ret =3D pmc_read(chip->parent, GPIO_PIN_LEVEL, offset, &level); + if (ret) + return ret; + + return level; +} + +static int gpio_set(struct gpio_chip *chip, unsigned int offset, int value) +{ + u8 val =3D value; + + pmc_write(chip->parent, GPIO_PIN_LEVEL, offset, &val); + + return 0; +} + +static int check_support(struct device *dev) +{ + u8 data; + int ret; + + ret =3D pmc_read(dev, GPIO_STATUS, 0, &data); + if (!ret) + return ret; + + if ((data & 0x01) =3D=3D 0) + return -EOPNOTSUPP; + + return 0; +} + +static int check_pin(struct device *dev, int pin) +{ + int ret; + int group, bit; + u16 data; + + /* Get pin mapping */ + ret =3D pmc_read(dev, GPIO_MAPPING, pin, &data); + if (ret) + return ret; + + if ((data & 0xFF) > ARRAY_SIZE(group_map)) + return -EINVAL; + + group =3D group_map[data & 0xFF].group; + bit =3D data >> 8; + + /* Check mapped pin */ + ret =3D pmc_read(dev, GPIO_GROUP_AVAIL, group, &data); + if (ret) + return ret; + + return data & BIT(bit) ? 0 : -EOPNOTSUPP; +} + +static int gpio_init(struct device *mfd, struct eio_gpio_dev *eio_gpio) +{ + int ret; + int i; + char str[GPIO_MAX_PINS + 1]; + + memset(str, 0x30, sizeof(str)); + + ret =3D check_support(mfd); + if (ret) { + dev_err(eio_gpio->dev, "GPIO not supported (%d)\n", ret); + return ret; + } + + eio_gpio->avail =3D 0; + + for (i =3D 0 ; i < GPIO_MAX_PINS ; i++) { + ret =3D check_pin(mfd, i); + if (ret) + continue; + + eio_gpio->avail |=3D BIT(i); + eio_gpio->max =3D i + 1; + str[GPIO_MAX_PINS - i] =3D '1'; + } + + dev_info(eio_gpio->dev, "GPIO pins=3D%s\n", str); + + return eio_gpio->max ? 0 : -EOPNOTSUPP; +} + +static const struct gpio_chip eio_gpio_chip =3D { + .label =3D KBUILD_MODNAME, + .owner =3D THIS_MODULE, + .direction_input =3D dir_input, + .get =3D gpio_get, + .direction_output =3D dir_output, + .set =3D gpio_set, + .get_direction =3D get_dir, + .base =3D -1, + .can_sleep =3D true, +}; + +static int gpio_probe(struct platform_device *pdev) +{ + struct device *dev =3D &pdev->dev; + struct eio_gpio_dev *eio_gpio; + struct eio_dev *eio_dev =3D dev_get_drvdata(dev->parent); + + if (!eio_dev) { + dev_err(dev, "Error contact eio_core\n"); + return -ENODEV; + } + + eio_gpio =3D devm_kzalloc(dev, sizeof(*eio_gpio), GFP_KERNEL); + eio_gpio->dev =3D dev; + + if (gpio_init(dev->parent, eio_gpio)) + return -EIO; + + eio_gpio->chip =3D eio_gpio_chip; + eio_gpio->chip.parent =3D dev->parent; + eio_gpio->chip.ngpio =3D eio_gpio->max; + + return devm_gpiochip_add_data(dev, &eio_gpio->chip, eio_gpio); +} + +static struct platform_driver gpio_driver =3D { + .probe =3D gpio_probe, + .driver =3D { .name =3D KBUILD_MODNAME, }, +}; + +module_platform_driver(gpio_driver); + +MODULE_AUTHOR("Wenkai Chung "); +MODULE_AUTHOR("Ramiro Oliveira "); +MODULE_DESCRIPTION("GPIO driver for Advantech EIO embedded controller"); +MODULE_LICENSE("GPL"); --=20 2.43.0 From nobody Sun Dec 14 06:34:44 2025 Received: from TYDPR03CU002.outbound.protection.outlook.com (mail-japaneastazon11023092.outbound.protection.outlook.com [52.101.127.92]) (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 C6C392D0C85; Fri, 12 Dec 2025 16:41:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.127.92 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765557699; cv=fail; b=foaIn6WfchqiHw/fwRfSUMBX6sEACiGjCQ6KNu28T6TctzeLXWoWXRnh7mgP1GXjLjWqNX96Jw/qogrcr+zaxLNjLufo4f9BMBSK7et69wH98SeM1gujaKArgP4O2xeZCLceX1fkJlxpfULSw/aBPqIt2fGWiI1+yQ6KgU66iG8= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765557699; c=relaxed/simple; bh=lQM7Zvt0uk1UgbA1S5B02YrrGq66MHgNIx8FkVfsMMw=; h=From:Date:Subject:Content-Type:Message-Id:References:In-Reply-To: To:Cc:MIME-Version; b=m2tQXSZaTlbGEj9/xOw7RgsqHUh7rqaloVrYkpkcEIHv2jnHts1VPeujLC5NB703oIW5PDwvIfgiQtWimWvSmZDjRx1OppbPCVvxWmnJhAgQpGQ+UKWnUA2nRr7YPBcvI49+cJe26XmiwE1yW+zo1Fh4ou0ojLwfb0zptE5Sgno= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=advantech.com; spf=pass smtp.mailfrom=advantech.de; dkim=pass (2048-bit key) header.d=advantech.com header.i=@advantech.com header.b=eBLPd70g; arc=fail smtp.client-ip=52.101.127.92 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=advantech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=advantech.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=advantech.com header.i=@advantech.com header.b="eBLPd70g" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=PyFchG4+4Bt9VioVtL3/0EpMgTLVK3uJODPdIocAKI0mcymgB3jSgAQmva35cQUTRnJRy9SWDnhwp3XVY32xoNimY+y1WOYkYBKGkUdNU+zwvNyTz1pw/qjTiD/GhJpu/N3OlDwKUy6vpjaIL3B9pMFN6eG+H4OR+wtVxkPaIQmDy+AW/pZFfwN/Bg+9UwXpASNB1/Z9rtjOVjMr/b/dGluMG1N+y7lx5j2Rb7LaiGorK2w3SWoa5pPGZ4UphkiC9M8n6xqbtB2NjniuYZsQ8RLV0u7dWUpI0KP8agGmhZTAJmk32rvnLl4CYi5GU+FJXh88lM4t3sG0PdgxVbwy4g== 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=UiEEJ5gy5Muv/Yg+R46RHmTq/PhJ7kPOP5XmcVpUvrE=; b=Hxh0PvY+NNA/ic+7CYbjX2SVma5w6kX/pGtyGUU2A4B8nRXYC5yE8JWyU0atvQ/f6211+NxZqJqBjK2B5Dfwx1qn/mnKXOIpui6P4owHq8OaxZ+x/zx9Rs+dSAlBuOZ1CIQftf4hrF9Gf03RFfdSuxkiftksjw4jAy+b9shJlDN4zu+UUQ5OS8JyFAh49eNgAnOlOdJ3f1LysvQ3zey60BLeWhaem9RiyV5+JFWBnjf8P/ND/DeTXt1WYH2vVcN035QTrYaO4WfLAVXkzfF0Yb+Kkhz2aB5GjnxlnpLo7kOOzeMVxp0nFp45nX/PC7oQ7qAxcd/k5wppgkzNE97GNA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=advantech.de; dmarc=pass action=none header.from=advantech.com; dkim=pass header.d=advantech.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=advantech.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=UiEEJ5gy5Muv/Yg+R46RHmTq/PhJ7kPOP5XmcVpUvrE=; b=eBLPd70gAxTC1X6IBuYk9nvwDpke9eaDRKZMXCCBw4bobmi+QdEDareM2KxCkeP45agMiTalYUiTRc5AULiYawI7+n1NPWDvMFkyU1d7m0p7QzsLwEIlHFKlrLOZxACFSgAEqBbp+G9tMxsZNethhitqNnLoJpmuxqTypt7qdOTVdHW+pUdkOyZIw38BAhfgbNoADlJCjYOLe7mJWZb0K29CMT4W2Zu2IV8CBU457EJlNKFicGsEriDjeeh/QQDvpS/UPXB7W5UIvcyD5ublucf7ywROVm2WbJ69MU+T4ImXp5luNB5a/1/dKWLazAJFJy0V0tPuN+9oG8ES0O7c9A== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=advantech.com; Received: from PSAPR02MB4502.apcprd02.prod.outlook.com (2603:1096:301:21::6) by JH0PR02MB6564.apcprd02.prod.outlook.com (2603:1096:990:3f::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9412.11; Fri, 12 Dec 2025 16:41:33 +0000 Received: from PSAPR02MB4502.apcprd02.prod.outlook.com ([fe80::59c9:fe0:25f6:702b]) by PSAPR02MB4502.apcprd02.prod.outlook.com ([fe80::59c9:fe0:25f6:702b%6]) with mapi id 15.20.9412.005; Fri, 12 Dec 2025 16:41:33 +0000 From: Ramiro Oliveira Date: Fri, 12 Dec 2025 17:40:54 +0100 Subject: [PATCH 3/8] Add Advantech EIO Hardware Monitor driver Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251212-upstream-v1-v1-3-d50d40ec8d8a@advantech.com> References: <20251212-upstream-v1-v1-0-d50d40ec8d8a@advantech.com> In-Reply-To: <20251212-upstream-v1-v1-0-d50d40ec8d8a@advantech.com> To: Lee Jones , Linus Walleij , Bartosz Golaszewski , Guenter Roeck , Andi Shyti , Daniel Thompson , Jingoo Han , Helge Deller , Wim Van Sebroeck , "Rafael J. Wysocki" , Daniel Lezcano , Zhang Rui , Lukasz Luba Cc: linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, linux-hwmon@vger.kernel.org, linux-i2c@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-fbdev@vger.kernel.org, linux-watchdog@vger.kernel.org, linux-pm@vger.kernel.org, Wenkai Chung , Francisco Aragon-Trivino , Hongzhi Wang , Mikhail Tsukerman , Thomas Kastner , Ramiro Oliveira X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=10783; i=ramiro.oliveira@advantech.com; h=from:subject:message-id; bh=lQM7Zvt0uk1UgbA1S5B02YrrGq66MHgNIx8FkVfsMMw=; b=owEB7QES/pANAwAKAc7t0Ke8vbAJAcsmYgBpPEWgkZy4sHStD9pAY+x/DYrU4+ajgM6pPyF5V UDiE/kjd0SJAbMEAAEKAB0WIQS1Nkng0ZvJmBKh6GLO7dCnvL2wCQUCaTxFoAAKCRDO7dCnvL2w CS88DACL7fuBfWPP2G3hS5zDcGDcEBDQJ6dv5Goi9d4EB1eXar8B6AEN8rmH3rDY5Jb8FvvUJNO 7JqsTF+P1HCH22Feb96N/ry26pdM9d26GkJhdCjkz3XT0QpWbRMMI3IYCGqRzdZg716t/BwMomQ iqPlqf0HjG2PfqvpCcRr7TNLnr36rJXHpjQ6ZoP8583uN0BBCkBJvEWZQJAv4EtKvi9g5+Ql9xe qHQqWDlKPBq7R5vXX+BFMCeKgr1qYMvQbKuQLrci8p0vX/iCJXZJqnLa9HXgrv+sWQZc2ummLOo 4BvmLJXphZZSiC2xat9p7RcTGT4CebpBC6FClhKCTsZ8yD0//6lLlnvkaTbXZMXOOIcKtcILiMB DnvjuRLQ2t+ZClXjaaDJFYECDQ6KL6gtDKZWs1awx76A17NQRGMr7cS8kLsMWyxB1KVmu5eJhCg IZiOcHnGu19NPmHHWfBFSUwu6OBj+8oMEwS0rcycqJts0sJm5Sx8ZT7h2pOB/mjVBOWkc= X-Developer-Key: i=ramiro.oliveira@advantech.com; a=openpgp; fpr=B53649E0D19BC99812A1E862CEEDD0A7BCBDB009 X-ClientProxiedBy: FR4P281CA0175.DEUP281.PROD.OUTLOOK.COM (2603:10a6:d10:b7::12) To PSAPR02MB4502.apcprd02.prod.outlook.com (2603:1096:301:21::6) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PSAPR02MB4502:EE_|JH0PR02MB6564:EE_ X-MS-Office365-Filtering-Correlation-Id: d6dcd453-bfbe-4b52-a37b-08de399d4f5a X-MS-Exchange-AtpMessageProperties: SA X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|7416014|366016|52116014|376014|38350700014|921020; X-Microsoft-Antispam-Message-Info: =?utf-8?B?SDQycWJiMEpsb1F2OW9BckdEVEpUdzlCa09RWmtaY3N2QXpoRWdmNy9SNURz?= =?utf-8?B?U3hlUXR3WG96MXlLQ1Nwc2tpNGg5OHloK1dkNFlONzhCZGdHalNYd1FRTURI?= =?utf-8?B?SFFOTlJ2NzlZRGFjQmRiS051TlRoVkxGWFQ1MmhnbVgvSWkyb3BaeEViRHA0?= =?utf-8?B?TXptNWhaUmZsbUJhMXUvb3NHcU1MNGNReUZVMkZYM1p1RC9NWmlxdFY1Y3Uz?= =?utf-8?B?MCs2aFJqbmJIWHBhRnFIUlNGc0UwbUJCdjNJczBmM2NYUlZvS3llWlVnNmZs?= =?utf-8?B?bVBYSlV3d0tHc0JFWSszV2pmZnoyOUc0YWZVV216M295RUFmL0VSWjNsanZs?= =?utf-8?B?ampXaGdOUENBRE1OeVB2KzhRdTBOaUdlbWdUT0ZmZDQxZVNmc2tYMmhCbkM5?= =?utf-8?B?ekc1cGJpWGNRWk9UTEV1am92a0xCc2IyM0QxKzI4WWpCckNpbHV2L3ZYZncx?= =?utf-8?B?YzhDOWlLa2NuWmpkOGRSbXdyYU41UUE0SjkyUHdNeDR3TlFOUFlsTGlEbmtl?= =?utf-8?B?MDFvK0dmZEtnN1NYZFRtVWhLdW9aOG9STTNKUmNTeW96Y0Q1UVYvOW95cDRj?= =?utf-8?B?eERad29ETTVWWWY1WWROQ29PcVBiY0pPUWZueWIvVmlnMVJNbjBKVjZ2S3BZ?= =?utf-8?B?LzEzeHRTWk5MZkhub3JiZk9lajQ0VzBONzhXTnRlQWlxRDdKMnpTeGxtYlVh?= =?utf-8?B?Wm9zSmlSbURNeVk4ME9aenR6c0NoRjVsbHgreFhtaW9RL0dnaWNUa3ovQlNi?= =?utf-8?B?dU4wMVpHODhOSzdDQklxVDNDUXEvYktMazYyOEVIS0hxK3UrNXoyVzFFNUZI?= =?utf-8?B?RTRUR2xFZVdjdEMyY0c4NHMwWWxsSUxJMHFKQ0lQT3hYekZMWjdrQWJBWExZ?= =?utf-8?B?VjJ4UGJVV1hNTTlOaTJ6b2RoZXpvMk5mbXVxSEhDTFNsUlZNYW96UXdVemdD?= =?utf-8?B?anp0OUNrTUMwSUFBWmZFZVlJU0hXaisyZ1NyQ0QyYWZ5OE9QcGNBZVhhWDlG?= =?utf-8?B?QVdiNWpLQ2d3Nkprb2xkSjZST0FKbU1pOTVZdUhsNlRHZ1ZvdTMwdG9aWlVI?= =?utf-8?B?bVFya0hjeEQ0YU9ZblhSS1hhR3hRY2ZGZ0loRTVmenhZVmlubW1yUkdOeUJH?= =?utf-8?B?c1JJQzRQc0lLdElvLzNlTEhTa3VzNVNqbWUxS1BseXRDRzFONld1ME4vNzlQ?= =?utf-8?B?MDBBQ3R4V0lIUE82bVdVTVdxYW1ZZWJPTHBRYWxaaW5OUkxDdWY5clJiWEpT?= =?utf-8?B?M0luTkpKQ0o2cGJldk1wMXRUVURvQkFVazJsZVJVMUxwOVZFcnBBRTF6L2Jn?= =?utf-8?B?bzlCODNVMHZDZUNuYXEvaXp5WHZJRDlpMTJ2eVRYNEpoaWNkOG0zeFptMzEr?= =?utf-8?B?MUJMMHhoY3cvWkxtQzE4QVE5cFg3OVRUeitJQW5KYjF0aTBYNno4K28xdFpY?= =?utf-8?B?NFFKUERMTlAvbENXa0ZCZ1hyZnJhSFpBNDNXczBZaHZ4cjZGbTVRaVVCVUJz?= =?utf-8?B?MURJQzUySDduRGIzYXpvaG5xeTV2ck9KVkRNZkFnZ3RsWXQ1M296M1ZodTZw?= =?utf-8?B?SXRtUVhMZlVlM3dXN1JZRVJJand5TTl4bElDSy9wbStlUGhqWC9mdm5FZ3o4?= =?utf-8?B?NVRpb3MyeU1tUSszWEEzNHRFbzhhU2VRSk82T1RlcDV3Y0d0RVNnM2NRTFM1?= =?utf-8?B?RGVHMmdyZHRsby9FaStFYU9vTGMrNk1uTHVkTWx5MkoySHU4dEN5cW91bjJW?= =?utf-8?B?bHlXMElWQm9oSEhhSEtSYk44ektOZ0hxdjZyRGZCN3FIWDBPK0pLbXlQdndR?= =?utf-8?B?T2VVSERzYkV4WUE1WnZGZEQ0MTY3R01zQXgxN2VVbGpMOXp6aVdUTTVORUlB?= =?utf-8?B?b0NVdm1HZ3BoZ2Rud2k0WHM3eExSeFZGdmJvdkpjMExrRjd1Qm5iWkF5VFFF?= =?utf-8?B?WUYxcFJ1M1BlVWtEV0w0dnBPQjBuclFGd0VyYndpSVd3S0x5K2pIM0tMMmNO?= =?utf-8?B?OFkvVVlOa0VVUGkvUEhPdHZTYjEzbkJVVjdWS2c2dWtJVmlTS0hBWXVZblpm?= =?utf-8?B?SlZhYVpXc2tjd0lneEdoenJKRUloY3kzSVRxUT09?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PSAPR02MB4502.apcprd02.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(7416014)(366016)(52116014)(376014)(38350700014)(921020);DIR:OUT;SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?cmlVc3ZSME95akNMbXpGNjZ3bXg5TForN1FLbng1SGlRd3FyUVNSeUZXRXZW?= =?utf-8?B?WnVUcW9JWnZzMGFuczI2UjI5cXcvb1pyby9icGZIZ0FjdHpNZW1RY3czUTIw?= =?utf-8?B?K3I4UUNwUjJTL1RXMDZpWGRxeWF2N1JxMy93bndkVFUwZ1BKbDNEelpvWHJS?= =?utf-8?B?WFJjN2ZQanNRQUpaRnlvWFFYTmQ5dEJoYkVsNWs2RDJYbkF3MmtSN0JDTkV5?= =?utf-8?B?ek5QZFBKYlFpZVJHc1RPTTFNaDRUVnVOdnVtS3U0ZkNTYWxnYW1HZjFkVnlv?= =?utf-8?B?Z2dDdm9udGl3M3V3M1FqUnNhS0czTWtJZXE4Y3Bqa2ZPd04zR3VFQ0tkQTl2?= =?utf-8?B?M0s3Qkl2UmxIWTFWTFQ0Zlkwb0dJbjNyYlBoTnFPN0w0cm1sd2ozd3dvWi8y?= =?utf-8?B?ODZvaHZHamlpYnhJWkJBTFFnMStVTkVsdk5nV3dDaFJjSEhXdmVYRU5nYkdl?= =?utf-8?B?UnNDTFFzUW1pa2VaRWhMc04xMVJJVUNlN084VWl6YWI4aElBeTdIZVZtQUdB?= =?utf-8?B?elpIaHR5UlcwZFg1RXJMeW13MXJWeVpNK3c5UmZ3QlE0WnZwOTBscHYxck9M?= =?utf-8?B?M2ZsRkVKalhJUFA1dzhqM3B4Y3I4NEprRDdpV1liQm1RblB6Sm00Mi8ydWZ5?= =?utf-8?B?NnViZXdoYmNrclc1RXlvVGZQZiszY3RYZzM3TWVKRU5FMkR3OFh3YmhBc0I3?= =?utf-8?B?dEN1NWgyVDJ5VitSekwwR3F1NEdzZUVsVWdXL0VtczAyNFdzZnU2MmpIZU1u?= =?utf-8?B?dFQxMEptS3g5SU5lSEEzQWdOMVBzNzdycUhSZmh5YlNwQW00aDVvWjRaZ1l1?= =?utf-8?B?enMzUjBpckFiK0ZZQk9hNFlXQTVtZWlMYk8vU05icmsrS2xtYjZKVFlzREY2?= =?utf-8?B?UmNqUGVsSDh1dTY0SUpsUWdoVVBUZ3UyaXlSOEZCbTlJOStuZDF3Z0JFa2pE?= =?utf-8?B?Y0FPa3VweFozak5tQWk0Q01oczljSGFEQTAzZ1NnUVM1RVMzWGhDNGQrQ1h5?= =?utf-8?B?YkpwY2t1VFoyZHNjVWszcXM5YUFUdEErTTJLN0pSSkgzcUJ5MXg3TkY0OXh2?= =?utf-8?B?eFJ3ZVRyZ0lXWmxZOVlPb2NuNElxL2MyNDhyeHdPeE9YYVJOTEFLbCtvcUs1?= =?utf-8?B?UUVvdWZvSzFHb2x5VTFLaEd6bEgyUFRDaTM2c24xUS9hdnRCcWFHRGxpYk5I?= =?utf-8?B?N1JxakNsYVpCQnRiK1dzSmFBV2NXdjFKOUNzck8wanVnZGVtaVU0MFdtZUR5?= =?utf-8?B?UkJ2Mnc4WU9rY2JSSldMRHM3RXZDUmxyTmtleW02SC9vV1NwK205LytWOExR?= =?utf-8?B?c2YrU3lLU0l4azE1RTlTQzdKNFJZbTVYeVl3ZC9nSzYwUWhlV0Y0QjdWRXlm?= =?utf-8?B?OXRZNzk1SW8zWGtSYmRhQzhteFJQdk1vR3h4eEs4TThIaGF6UXFuR3pFSm15?= =?utf-8?B?Q3NxYk96d3NJeWxPOWRrMGRiSzg1elY5S2VKb0ZQQXJHN2xHSXFpUTcyMVRO?= =?utf-8?B?eE1INDZweGluOXRMemZNY05jcVkyTmZadDV0dU1xNElVUDZHVkN3dEMvODdr?= =?utf-8?B?R3ozUXNITjM5c1VOL0NUVURXWCtoOWxsNnFiOXVLUk5oRlFjWVdydjMrYVNC?= =?utf-8?B?WHlwZTVydkJrM29kQVhBT1ZlQkpMSnpuaHBwWDRTSGxoY1VTaTRlWjNwcWhr?= =?utf-8?B?d0l3MzZxVGlXUzIvazZpajdaTm1DRFROKytyVUo1RG9wdlZVci9tdlhpYm1t?= =?utf-8?B?cWZ1YUhoMmpCVklsd0ZiZUt3VTBzcGlHMERDYVdnYk4vSGt1VElCWENENFpT?= =?utf-8?B?TzBSdnJuY09GeDA0Rk9Mb1loUzNDUEJqcFB5aWN0OTZzTWNJc00rcHZVb1lO?= =?utf-8?B?TlNnTEFhTEtuclNhV1lLeGZ5Z01hQ1NJY1JST3VuOWY2MWpQam5hTkt6ajdr?= =?utf-8?B?KzJGbjM5Z2IxLzY4WDV0eWRqZ0V1eTExTCs5Y3A2Nk0rNVVNM0RnZzM4dDNy?= =?utf-8?B?dmREVlhRWXh3ZldScHhvazNzditVMXQ4MDNGZDFueTBOTTVvSnNuMXl1bG42?= =?utf-8?B?ek1xN24vbWdpa2tOaGhEUkRBVXE5ZVBjbEpUWFY5M3hpM1Z2ZE1ldnFNVmpQ?= =?utf-8?B?WkJSblhRcnBQaUJpTEZzb1E5SStMNXB5TTQ5NGUwelRROWh6dW5tKytzNFJJ?= =?utf-8?Q?6qVN5dDiMhmzEc/77ckpKHI=3D?= X-OriginatorOrg: advantech.com X-MS-Exchange-CrossTenant-Network-Message-Id: d6dcd453-bfbe-4b52-a37b-08de399d4f5a X-MS-Exchange-CrossTenant-AuthSource: PSAPR02MB4502.apcprd02.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 Dec 2025 16:41:33.8286 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: a77d40d9-dcba-4dda-b571-5f18e6da853f X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: J4O/586Qpr6tGjJMPxtOsfGyVRTufL5Xdz4x/aaZMOGlJhuNUdnEb+s+ryZFp6SeZvQfVfafUEcP8n0RRDBK3IwLcPs9VzpYAEV9Ytp2wfA= X-MS-Exchange-Transport-CrossTenantHeadersStamped: JH0PR02MB6564 This driver controls the Hardware Monitor block of the Advantech EIO chip. Signed-off-by: Ramiro Oliveira --- MAINTAINERS | 1 + drivers/hwmon/Kconfig | 10 ++ drivers/hwmon/Makefile | 1 + drivers/hwmon/eio-hwmon.c | 344 ++++++++++++++++++++++++++++++++++++++++++= ++++ 4 files changed, 356 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 359d4a13f212..fdd39b152f41 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -620,6 +620,7 @@ ADVANTECH EIO DRIVER M: Ramiro Oliveira S: Maintained F: drivers/gpio/gpio-eio.c +F: drivers/hwmon/eio-hwmon.c F: drivers/mfd/eio_core.c F: include/linux/mfd/eio.h =20 diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 157678b821fc..08993b993596 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -2043,6 +2043,16 @@ config SENSORS_DME1737 This driver can also be built as a module. If so, the module will be called dme1737. =20 +config SENSORS_EIO + tristate "Advantech EIO HWMON" + depends on MFD_EIO + help + If you say yes here you get support for the Advantech EIO + temperature, voltage and fan speed monitoring block. + + This driver can also be built as a module. If so, the module + will be called eio-hwmon + config SENSORS_EMC1403 tristate "SMSC EMC1403/23 thermal sensor" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index eade8e3b1bde..e69f03b41fae 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -72,6 +72,7 @@ obj-$(CONFIG_SENSORS_DME1737) +=3D dme1737.o obj-$(CONFIG_SENSORS_DRIVETEMP) +=3D drivetemp.o obj-$(CONFIG_SENSORS_DS620) +=3D ds620.o obj-$(CONFIG_SENSORS_DS1621) +=3D ds1621.o +obj-$(CONFIG_SENSORS_EIO) +=3D eio-hwmon.o obj-$(CONFIG_SENSORS_EMC1403) +=3D emc1403.o obj-$(CONFIG_SENSORS_EMC2103) +=3D emc2103.o obj-$(CONFIG_SENSORS_EMC2305) +=3D emc2305.o diff --git a/drivers/hwmon/eio-hwmon.c b/drivers/hwmon/eio-hwmon.c new file mode 100644 index 000000000000..164591aa31a7 --- /dev/null +++ b/drivers/hwmon/eio-hwmon.c @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * GPIO driver for Advantech EIO embedded controller. + * + * Copyright (C) 2025 Advantech Corporation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include + +#define MAX_DEV 128 +#define MAX_NAME 32 + +static uint timeout; +module_param(timeout, uint, 0444); +MODULE_PARM_DESC(timeout, + "Default pmc command timeout in micro-seconds.\n"); + +struct eio_hwmon_dev { + struct device *mfd; +}; + +enum _sen_type { + NONE, + VOLTAGE, + CURRENT, + TEMP, + PWM, + TACHO, + FAN, + CASEOPEN, +}; + +struct eio_key { + enum _sen_type type; + u8 chan; + u8 item; + u8 label_idx; +}; + +struct eio_attr { + struct sensor_device_attribute sda; + struct eio_key key; +}; + +static struct { + u8 cmd; + u8 max; + signed int shift; + char name[32]; + u8 ctrl[16]; + u16 multi[16]; + char item[16][32]; + char labels[32][32]; + +} sen_info[] =3D { + { 0x00, 0, 0, "none" }, + { 0x12, 8, 0, "in", + { 0xFF, 0x10, 0x11, 0x12 }, + { 1, 10, 10, 10 }, + { "label", "input", "max", "min" }, + { "5V", "5Vs5", "12V", "12Vs5", + "3V3", "3V3", "5Vsb", "3Vsb", + "Vcmos", "Vbat", "Vdc", "Vstb", + "Vcore_a", "Vcore_b", "", "", + "Voem0", "Voem1", "Voem2", "Voem3" + }, + }, + { 0x1a, 2, 0, "curr", + { 0xFF, 0x10, 0x11, 0x12 }, + { 1, 10, 10, 10 }, + { "label", "input", "max", "min" }, + { "dc", "oem0" }, + }, + { 0x10, 4, -2731, "temp", + { 0xFF, 0x10, 0x11, 0x12, 0x21, 0x41 }, + { 1, 100, 100, 100, 100, 100 }, + { "label", "input", "max", "min", "crit", "emergency" }, + { "cpu0", "cpu1", "cpu2", "cpu3", + "sys0", "sys1", "sys2", "sys3", + "aux0", "aux1", "aux2", "aux3", + "dimm0", "dimm1", "dimm2", "dimm3", + "pch", "gpu", "", "", + "", "", "", "", + "", "", "", "", + "oem0", "oem1", "oem", "oem3" }, + }, + { 0x14, 0, 0, "pwm", + { 0xFF, 0x11, 0x12 }, + { 1, 1, 1 }, + { "label", "polarity", "freq" }, + { "pwm0", "pwm0", "pwm0", "pwm0" }, + }, + { 0x16, 2, 0, "tacho", + { 0xFF, 0x10 }, + { 1, 1 }, + { "label", "input"}, + { "cpu0", "cpu1", "cpu2", "cpu3", + "sys0", "sys1", "sys2", "sys3", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", + "oem0", "oem1", "oem2", "oem3" + }, + }, + { 0x24, 4, 0, "fan", + { 0xFF, 0x1A }, + { 1, 1 }, + { "label", "input"}, + { "cpu0", "cpu1", "cpu2", "cpu3", + "sys0", "sys1", "sys2", "sys3", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", + "oem0", "oem1", "oem2", "oem3", + }, + }, + { 0x28, 1, 0, "intrusion", + { 0xFF, 0x02 }, + { 1, 1 }, + { "label", "input" }, + { "case_open" } + } +}; + +static struct { + enum _sen_type type; + u8 ctrl; + int size; + bool write; + +} ctrl_para[] =3D { + { NONE, 0x00, 0, false }, + + { VOLTAGE, 0x00, 1, false }, { VOLTAGE, 0x01, 1, false }, + { VOLTAGE, 0x10, 2, false }, { VOLTAGE, 0x11, 2, false }, + { VOLTAGE, 0x12, 2, false }, + + { CURRENT, 0x00, 1, false }, { CURRENT, 0x01, 1, false }, + { CURRENT, 0x10, 2, false }, { CURRENT, 0x11, 2, false }, + { CURRENT, 0x12, 2, false }, + + { TEMP, 0x00, 2, false }, { TEMP, 0x01, 1, false }, + { TEMP, 0x04, 1, false }, { TEMP, 0x10, 2, false }, + { TEMP, 0x11, 2, false }, { TEMP, 0x12, 2, false }, + { TEMP, 0x21, 2, false }, { TEMP, 0x41, 2, false }, + + { PWM, 0x00, 1, false }, { PWM, 0x10, 1, true }, + { PWM, 0x11, 1, true }, { PWM, 0x12, 4, true }, + + { TACHO, 0x00, 1, false }, { TACHO, 0x01, 1, false }, + { TACHO, 0x10, 4, true }, + + { FAN, 0x00, 1, false }, { FAN, 0x01, 1, false }, + { FAN, 0x03, 1, true }, { FAN, 0x1A, 2, false }, + + { CASEOPEN, 0x00, 1, false }, { CASEOPEN, 0x02, 1, true }, +}; + +static int para_idx(enum _sen_type type, u8 ctrl) +{ + int i; + + for (i =3D 1 ; i < ARRAY_SIZE(ctrl_para) ; i++) + if (type =3D=3D ctrl_para[i].type && + ctrl =3D=3D ctrl_para[i].ctrl) + return i; + + return 0; +} + +static int pmc_read(struct device *mfd, enum _sen_type type, u8 dev_id, u8= ctrl, void *data) +{ + int idx =3D para_idx(type, ctrl); + int ret =3D 0; + + if (idx =3D=3D 0) + return -EINVAL; + + if (WARN_ON(!data)) + return -EINVAL; + + struct pmc_op op =3D { + .cmd =3D sen_info[type].cmd | EIO_FLAG_PMC_READ, + .control =3D ctrl, + .device_id =3D dev_id, + .size =3D ctrl_para[idx].size, + .payload =3D (u8 *)data, + .timeout =3D timeout, + }; + + ret =3D eio_core_pmc_operation(mfd, &op); + return ret; +} + +static ssize_t eio_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct eio_hwmon_dev *eio_hwmon =3D dev_get_drvdata(dev); + struct eio_attr *eio_attr =3D + container_of(attr, struct eio_attr, sda.dev_attr); + const struct eio_key *eio_key =3D &eio_attr->key; + int ret; + u8 data[2]; + u32 temp_val; + signed int final_val; + + switch (eio_key->item) { + case 0: + return sysfs_emit(buf, "%s\n", + sen_info[eio_key->type].labels[eio_key->label_idx]); + + default: + ret =3D pmc_read(eio_hwmon->mfd, eio_key->type, eio_key->chan, + sen_info[eio_key->type].ctrl[eio_key->item], + &data); + if (ret) + return ret; + + temp_val =3D data[0] | data[1] << 8; + + final_val =3D (signed int)temp_val + (signed int)(sen_info[eio_key->type= ].shift); + final_val =3D final_val * (signed int)sen_info[eio_key->type].multi[eio_= key->item]; + + return sysfs_emit(buf, "%d\n", final_val); + } + + return -EINVAL; +} + +static char devname[MAX_DEV][MAX_NAME]; +static struct eio_attr devattrs[MAX_DEV]; +static struct attribute *attrs[MAX_DEV]; + +static struct attribute_group group =3D { + .attrs =3D attrs, +}; + +static const struct attribute_group *groups[] =3D { + &group, + NULL +}; + +static int hwmon_init(struct device *mfd, struct eio_hwmon_dev *eio_hwmon) +{ + enum _sen_type type; + u8 i, j, data[16]; + int sum =3D 0; + int ret; + + for (type =3D VOLTAGE ; type <=3D CASEOPEN ; type++) { + int cnt =3D 1; + + for (i =3D 0 ; i < sen_info[type].max ; i++) { + if (pmc_read(mfd, type, i, 0x00, data) || + (data[0] & 0x01) =3D=3D 0) + continue; + + memset(data, 0, sizeof(data)); + ret =3D pmc_read(mfd, type, i, 0x01, data); + if (ret !=3D 0 && ret !=3D -EINVAL) { + dev_info(mfd, "read type id error\n"); + continue; + } + + for (j =3D 0 ; j < ARRAY_SIZE(sen_info->item) ; j++) { + struct eio_attr *eio_attr; + + if (sen_info[type].item[j][0] =3D=3D 0) + continue; + + eio_attr =3D &devattrs[sum]; + + eio_attr->key.type =3D type; + eio_attr->key.chan =3D i; + eio_attr->key.item =3D j; + eio_attr->key.label_idx =3D data[0]; + + snprintf(devname[sum], sizeof(devname[sum]), + "%s%d_%s", sen_info[type].name, cnt, + sen_info[type].item[j]); + + eio_attr->sda.dev_attr.attr.name =3D devname[sum]; + eio_attr->sda.dev_attr.attr.mode =3D 0444; + eio_attr->sda.dev_attr.show =3D eio_show; + + attrs[sum] =3D &eio_attr->sda.dev_attr.attr; + + if (++sum >=3D MAX_DEV) + break; + } + cnt++; + } + } + + return sum; +} + +static int hwmon_probe(struct platform_device *pdev) +{ + struct device *dev =3D &pdev->dev; + struct eio_hwmon_dev *eio_hwmon; + struct eio_dev *eio_dev =3D dev_get_drvdata(dev->parent); + struct device *hwmon; + + if (!eio_dev) { + dev_err(dev, "Error contact eio_core\n"); + return -ENODEV; + } + + eio_hwmon =3D devm_kzalloc(dev, sizeof(*eio_hwmon), GFP_KERNEL); + if (!eio_hwmon) + return -ENOMEM; + + eio_hwmon->mfd =3D dev->parent; + platform_set_drvdata(pdev, eio_hwmon); + + if (hwmon_init(dev->parent, eio_hwmon) <=3D 0) + return -ENODEV; + + hwmon =3D devm_hwmon_device_register_with_groups(dev, KBUILD_MODNAME, + eio_hwmon, + groups); + return PTR_ERR_OR_ZERO(hwmon); +} + +static struct platform_driver eio_hwmon_driver =3D { + .probe =3D hwmon_probe, + .driver =3D { + .name =3D "eio_hwmon", + }, +}; + +module_platform_driver(eio_hwmon_driver); + +MODULE_AUTHOR("Wenkai Chung "); +MODULE_AUTHOR("Ramiro Oliveira "); +MODULE_DESCRIPTION("Hardware monitor driver for Advantech EIO embedded con= troller"); +MODULE_LICENSE("GPL"); + --=20 2.43.0 From nobody Sun Dec 14 06:34:44 2025 Received: from OS8PR02CU002.outbound.protection.outlook.com (mail-japanwestazon11022133.outbound.protection.outlook.com [40.107.75.133]) (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 422152D238D; Fri, 12 Dec 2025 16:41:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.75.133 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765557707; cv=fail; b=TPY1XDxtMv6ljr0MeqaNO63r52Hf/er4c5ntzLSZjoWaMrtdOZeQIQvrYtt+c5TvMO7L9kNv61PU9ZlWNIszp3FWnHGUrsLhpzRE1LwgPeFJfWIBkD/Wr9GwNM13XriXrAyqdGruf11c5tMBhUFyNhH53kbXlDHPmfoWHLB1JJw= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765557707; c=relaxed/simple; bh=glViC9RP31OLgtx/NwTcSX4pjWfi958TcTl+PaOdfUw=; h=From:Date:Subject:Content-Type:Message-Id:References:In-Reply-To: To:Cc:MIME-Version; b=ogF4alryt1g95mNf5bUu+11WCNbiedw++Po2Z8cGu7QvRnZiLNSKXhGlW5pG85nim+C5UcEEMIkGS6BRKyVKzxUy9RBQIEcWXt3SW6QSOPaem793ipEomuJDSdC6QF0uWOTvft7OsXqLMCbOVLq8b2FiIk7QFRlftbuyvGBYhaY= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=advantech.com; spf=pass smtp.mailfrom=advantech.de; dkim=pass (2048-bit key) header.d=advantech.com header.i=@advantech.com header.b=HJ3BPKyW; arc=fail smtp.client-ip=40.107.75.133 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=advantech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=advantech.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=advantech.com header.i=@advantech.com header.b="HJ3BPKyW" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=RE2/zjSAbQPpCga8bq9CCueZaXx+oG1zainlfD10IZfde6F48Zr/dqT5QtA0nRw3SDm8rQrdy9kdJtHPEGU9b987zp8FE6x3sITK2rN/M0HZ2PG34fg6+XXx0vTpdTR0S8hqNLJ4ulaJwmZu6NYr/N/z7WkGtVKr8th9WYHq84yISeUR6MmwkqNeD6VjbsPRT5FcZE0Ihi7CsynPno/LuAlFWwgsaLRh9LuBqBPzGYi3Ir5dctziTCLCglQiuV7sjhotEGwzzgay4JPjwWjlMMeqWZxq9H75rgbgqLpr4WmOo0n4GZ1GyHpMWOER6PAPp6C0uIqAWy3oJMLydWwb3Q== 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=9dGBG0bHBvLRU866UNd/QFI3P/aIY1C4T8QLLlTXmOI=; b=P5fCIqmYWTW5j6bKFsVINUSHO62AFkYxFm/XVkBiseoE/21jAsQ1tbI7mWldewXDPNW1Jxlvetqgiv39b0KfhJKEGbycRDYQnX1HKm36LMLoA9g/5Ahd0tpzRagEMOZUTE8e4+vOjvDjh+NCdbxKvCF8GP8/iCbBZnVerirk7XpmaJZ/AlbcecD5dku4HAbAElfp6u8leg+/aMM8uH06HTCqb7ahUTyDGdQC8ppcuwOepZqUOWBOUQAeG85GMgz1yxLusja52EJc8AXwkRp46Yc/6RPFg7UM8+VgOHikdYfO0vllRu9QJLdexWjjwKp0vUuKBwBy+4KGJpxsdcGRbw== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=advantech.de; dmarc=pass action=none header.from=advantech.com; dkim=pass header.d=advantech.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=advantech.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=9dGBG0bHBvLRU866UNd/QFI3P/aIY1C4T8QLLlTXmOI=; b=HJ3BPKyWrrR5RdrKRlVT4+Px0smxUc7eB2AZcQUMt0GOXWvQKMCrXU7Jbv6sbXk/fq6nZo48GSKj8w4+GZ23Bz9CjJ2wGw4svVxfKdfhfG/5IZrAV7FooQnCU7Ffu69sjiyAFEzFfwGUAQWNJn7+t7IHwoWrFtnz9hXYOMUE40bx0xhJadTv+zHZbLc98s9ubIQT5uraWPG2n+A4s4bZKvEf9T983I5as+qZWTP3hatSiT2aH9GqilMmMLqALqdN6p0aN+ZwfRe0a75dq2xOIRthdsIrHKa5p0QVc1ZeXj6x5qPlRgcbQk6KUrkUFyFtyRTb29EjeyJCp5rDGBG+zg== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=advantech.com; Received: from PSAPR02MB4502.apcprd02.prod.outlook.com (2603:1096:301:21::6) by JH0PR02MB6564.apcprd02.prod.outlook.com (2603:1096:990:3f::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9412.11; Fri, 12 Dec 2025 16:41:41 +0000 Received: from PSAPR02MB4502.apcprd02.prod.outlook.com ([fe80::59c9:fe0:25f6:702b]) by PSAPR02MB4502.apcprd02.prod.outlook.com ([fe80::59c9:fe0:25f6:702b%6]) with mapi id 15.20.9412.005; Fri, 12 Dec 2025 16:41:40 +0000 From: Ramiro Oliveira Date: Fri, 12 Dec 2025 17:40:55 +0100 Subject: [PATCH 4/8] Add Advantech EIO I2C driver Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251212-upstream-v1-v1-4-d50d40ec8d8a@advantech.com> References: <20251212-upstream-v1-v1-0-d50d40ec8d8a@advantech.com> In-Reply-To: <20251212-upstream-v1-v1-0-d50d40ec8d8a@advantech.com> To: Lee Jones , Linus Walleij , Bartosz Golaszewski , Guenter Roeck , Andi Shyti , Daniel Thompson , Jingoo Han , Helge Deller , Wim Van Sebroeck , "Rafael J. Wysocki" , Daniel Lezcano , Zhang Rui , Lukasz Luba Cc: linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, linux-hwmon@vger.kernel.org, linux-i2c@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-fbdev@vger.kernel.org, linux-watchdog@vger.kernel.org, linux-pm@vger.kernel.org, Wenkai Chung , Francisco Aragon-Trivino , Hongzhi Wang , Mikhail Tsukerman , Thomas Kastner , Ramiro Oliveira X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=33269; i=ramiro.oliveira@advantech.com; h=from:subject:message-id; bh=glViC9RP31OLgtx/NwTcSX4pjWfi958TcTl+PaOdfUw=; b=owEB7QES/pANAwAKAc7t0Ke8vbAJAcsmYgBpPEWge/2aKaxGTRPfSpM4DwypZRCsAxKWPGxDw KTqiNLT8tOJAbMEAAEKAB0WIQS1Nkng0ZvJmBKh6GLO7dCnvL2wCQUCaTxFoAAKCRDO7dCnvL2w CaG1C/wIo0ng52kBd1RCW2272FrYaV7QJhVGbqC++NxQHH0yDOtxqV1V2WTw3y2CiahchqAOxbt 31tBIIZv3lm5W4oXQIU9xPHRK1w3UpMejtioqYlk4LNgrrXQ3szsoy0i+2RksDEnITVkM6EStBW 16vi12k4Jo7Lxgw/llaGopLYSytnXpbaGrSjx8NZOija2q4TXbyocUlAsi42UC+JWBXwqcr+Bs5 uYNNvPlnEWhOx2Mg9o1Z07bF3b8pSxibOgdOGZp/l1h5rx88Ner/YzaowlNbRI4+YkpFPckEhX2 8Kwau0cBjny6XxOcMVpng+XFP70w40rf/Tg+zQps2YflkGvxvDtTR11uG3kPaSrc22i6TiDwy7q C5/X8mn6PCbhOihHsEpUYjeScP6HRe3GL4jyGhwFl3HNJJ+Q1DE6/T2TTMaZQWWzLyWswHPAu0F yNyi3826FW2mZb9f+DU3Ii/6AJw924d66qJszZ3bx/szMQBPslhBFhkuSDDVvNf7oV6P0= X-Developer-Key: i=ramiro.oliveira@advantech.com; a=openpgp; fpr=B53649E0D19BC99812A1E862CEEDD0A7BCBDB009 X-ClientProxiedBy: FR4P281CA0175.DEUP281.PROD.OUTLOOK.COM (2603:10a6:d10:b7::12) To PSAPR02MB4502.apcprd02.prod.outlook.com (2603:1096:301:21::6) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PSAPR02MB4502:EE_|JH0PR02MB6564:EE_ X-MS-Office365-Filtering-Correlation-Id: 8673f184-bea3-4c90-29e2-08de399d5386 X-MS-Exchange-AtpMessageProperties: SA X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|7416014|366016|52116014|376014|38350700014|921020; X-Microsoft-Antispam-Message-Info: =?utf-8?B?YzdYdFhVREhVUFpCMnZuRkxhRUF0b01CeE0vYWFneXRlVXpVeWViOFFQZllx?= =?utf-8?B?RXFnZVZKenFwaDVEdjdFbWFzeDR5YVo4amZ4N0dDWGNrYVRib2xWaTMzZXJV?= =?utf-8?B?UjdBNTBqN2RYWDdka1NHdW5YOXZlRzYzbStmWGdzMGRVU3ZRQllXM3pCOVBj?= =?utf-8?B?U2ZYaTlUOVhzTTRCRmhQQTRLWDE5dzYzeENoUjlrQzZrV01kSEd6NkJyNmZZ?= =?utf-8?B?ZUJzblRvc0c5V0JrNC9QTVR3cnMzZFhmdldnR1Y2OCtoa2Jic1VETFVnT0Jw?= =?utf-8?B?SjJYQ1lYMmNzZEpRbUtraTRWb25QbGw1TmMxcTdib3hXNnU0YmJOSFY0MVNW?= =?utf-8?B?Y1kwdytEWVBwRGdDdVV6Yk80YnY5bG8zZ1UvWUJxZkFzQU51QTVyeU1ObVIr?= =?utf-8?B?ZnVxcVF1c2F2b2J4bGtRVlptNG5uT3RjOElIL0U4KzJRTzN1a3hHdHU5RlA2?= =?utf-8?B?WDhiN0RyUmVhYlMyendlR1ZXWUZ4WmQwdnNTUy8wU0hWRnkxTmxJTVBNaFpn?= =?utf-8?B?MFNuV1ZJaGkxZmh2NEdsZHdPNEVEcmVSZ0J5VkdhVEFHbWJuYy9pTTZ2a1lC?= =?utf-8?B?UnljdWxZMGs1WnhTRU9uQUx1eHA1VHk1TFV5WTF5b052RDVLV1lrakJ4S2Vp?= =?utf-8?B?dVIrWFcwNmsxcjZpL0tGeXdUYXJJZjU4YXFBSDl1L0tIaEZZYTZDMFQxRWpP?= =?utf-8?B?T1pTdHYwcXNxUWZ6TUZ3aWJYTGdqMCt6dGVnczVqa0Rvc1AxeFA4c2JQbnpr?= =?utf-8?B?YWx3NE5aeHowa2dxNHhRNEJLQ0ptUndvMHZUdUY3UkFWSmJhQWpQRElwb1Zj?= =?utf-8?B?UTByNVYxRzdZL0FXaTY1T3kwVzgyT3JZWTlLdU1mSi9uMzJ6cDhOcndBZjM2?= =?utf-8?B?NktFeWZhQ3J0bWdQMlZTYm1kREwwN1FzcmQvOEhjNVVhck9od1RkUEQ4TThP?= =?utf-8?B?YTllbzE1eU1zanFxUEVsTVJrVTY2VHQwSVNRWmtCcHVWakNmT3NjSTZkWUlW?= =?utf-8?B?Rm1SYnY2d0ZNb1VGanNrNnA5Ti9mOTNKTmFDTFNzcFlUNTE0NzNqNXQ2czBw?= =?utf-8?B?OHJlWWNPdUhCMmYyQ1lkNGpjNmNJNkN1WWhuSllrVXE2ZlhWQjNtd2VWYmRN?= =?utf-8?B?TXFHUHIzejAwQzFMS3YrR2xOLzdEblU0ZzFjUFA0aWxKWUxRU2M1ZkpUbGRH?= =?utf-8?B?bk5kcWowYnBqQTRhaE52ZTMwZG16YWpuYnlnVEhMT0JybCtSOUxObmhtRkc5?= =?utf-8?B?aytMTWJhamJZMWJXdkZGQXhFMUV1TjVwY3dmNlY5Ky9Oc3Q4Ujl3ODIrVTFk?= =?utf-8?B?VXVKbCtyc3VQQU5KNTVHNGhCNXNWbjVYOTZZc0xVNjg2MEtSV3hwaEIzVXFl?= =?utf-8?B?WXRId3VpV3JmdG9mUm9Ld0Z0VitrUWpsZEpSZmNYdm5BVmM4OFZGRllaMFJX?= =?utf-8?B?OTJmenZGdVVyRmZTb2NGUzltRS81dkUzUnRIY1I0MTBaQ01BT0FTbjdrSTZV?= =?utf-8?B?Zm9CbmVpa2R0aUN1SENSTmxReUxBMjZkalZGTmV5NWtQRlQxZXVUK3p3VG1I?= =?utf-8?B?Q3NSRWRScXlOZ2xSSGc5ZUY3M2Yzb1Y2MHRDaEp2c3ExNHR4TjRVUlQvSDF1?= =?utf-8?B?RTc1anduVDZXclJvOVNhRXcwRFBUUXU5eGJ5Qk1jVldGOGs2YVIySTdrWWdG?= =?utf-8?B?S3VieXRFaXR5ZThWQlVkSVhKUktIWm80emFVaVhCSHVYZVd5bjlZQm9yYnRX?= =?utf-8?B?YTZSOGhIa1JpbmVzT1ovL1VNZklEZ081OXJvRkZoZWt1RlZRTG5hanlCMm94?= =?utf-8?B?bE1RZCtBQm43NTFUeEwyV09SWXA0ZEVucURHSEl1SmMvdnVWb0t0RTlYNzRZ?= =?utf-8?B?Qy80S25mNTc2ZUt5U0Rha1EwQWZiSU9pQ1NuZ0tibG5yMkFFOGRtWW9JRi9F?= =?utf-8?B?TFR6ek5Sb1hBZkFLMWpvYnlDZUZQV0wwOEpocllBaFFFQkJiYjFPNWh6MHhj?= =?utf-8?B?QkduQ0R1NXFQQ0hEdVhQMVRwbEVLRTZvK0hyNVBqRStpNTNTcW1CSnZ4NmZt?= =?utf-8?B?enJ3aXN4RlcvZk9NUGg0ZGNDb2ZjMkNkQXpjZz09?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PSAPR02MB4502.apcprd02.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(7416014)(366016)(52116014)(376014)(38350700014)(921020);DIR:OUT;SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?eUNTV1JVNzRQSXB2eDVtVTdxaXFUcWw1aGQvdjR4NktrMFUxVnA1dDFDcWhP?= =?utf-8?B?K0xxUHI0QWZuQXdjeUhXckt3c2NwWkg3eXpPK3oyekViSXRLdFhoK1o0M0Nn?= =?utf-8?B?eU03TkdUb2wxcUxaYWJpTU5tT0NQV0RTaTJ6M3BJcm1JTUE0b2FXYzlIOVBi?= =?utf-8?B?M3ZoWHhjT2RqVGdpTXpxaUFvZ0R5dXF4NWt5VVZ3bzJOaGxzUzlFRmNsZ3Nh?= =?utf-8?B?M01HWi84SWp0OUdETklsd3JOQkNuUlNkZDlzTk5CckpHSXdQK21jbGlsa0Vt?= =?utf-8?B?REw0Qjc4WXRSZ3BTN3dWT0cwMUUvVnprelIvZ2cvMmtPcld6V0ZIUUFyb0Ni?= =?utf-8?B?S0tuNStzSVM0aVFnTzYwaXFCcExOY29rQU9nT3l5L3BuK2doeWtEOUwzSHdE?= =?utf-8?B?S0gxNndxOVVSUGdIaG5Vam9rZ2QySlZlNG9mdndPMXY1QVlJV09LMXB0Y09L?= =?utf-8?B?aU9iMHArc2EwVG5QSGNCNEJKeVlxNEtNdWo5eE1BR0VkYmhJczVPV3V5Rkgr?= =?utf-8?B?cXNHZWw4Q0VDSnJwWkpJY1poVUNhVVI4SXJqNXdJc0xUcW1lQTByZDUxMG9Z?= =?utf-8?B?OEY4Y3V3UC9GZzRldmo2Zklqc044T3pFeFo0Z3NvQWlTcnE5NDllMGRieWRV?= =?utf-8?B?YTUvWGFlcXhLNVFuRkhubWxKc0tRb2ZPNDEyQ0tROTRzYTducWQxNFlDUDdo?= =?utf-8?B?eEpEQ280U1lEZzdvTmoySkRCZjRQOXlKeUNkbkVteHR0OUdvcFc4VE0ydkVv?= =?utf-8?B?b1VOeU5PeWZ2QUNWUUhrVVBZbytuekVoejAwZzdBcDBYMFFzUW0yRENoSDR2?= =?utf-8?B?MVkyMDVYL0Z3UkxwaVkvaWhicjlvN3Z0UXZPOVNwcVJzVkNCWnZKZkNHUjhp?= =?utf-8?B?a1JMSjdNQ0JBWnRoUFVRbTNrQWxNSGcxOGZTd1FUY05DTmJhVytmOXp2aTZz?= =?utf-8?B?OGI0bDNNVXJPKzh3bEVDWnpNNWN4R3k4MHFxMWNsUnM2S2FnaTNqQ2xZTUdy?= =?utf-8?B?S3ZLalMxcUdxdkVQY0kxV1AwZnhkbDVZUjJ4cUE3VXp0ZGtVeDRlUEZyaGR1?= =?utf-8?B?MlhXbFB2OXVnWjl6dXVmOTJMZ0g4ZUZjZWRwZnptSEFWSFQ4czFCTEgyZmVq?= =?utf-8?B?WEs0VzJOenpuMkFzam9WZlduKzRBYTZIT0FncjhvbkptaGM5S0VxaWN1OWlL?= =?utf-8?B?K01nenA1VS9ybERBMTdUNWxnSTlrWklNYXFmSndMd1lOaStZWklLVlo1cE8x?= =?utf-8?B?WDZpZno3V1BSd0RHWUxHS1podEtxV0xWUHpGUmM4ZUEvZUxTbWowa3ZEWDNG?= =?utf-8?B?RTZSQ292RlVrRldkK1NyOXNhRU1XM2NlNGdNQXhKdTJKR3ZCaTVSdXRHczJB?= =?utf-8?B?SStFbVU2ME1ZNUlMNjAvNEdtenRGc3ZtWmFna1c0cjJIdU85UUxUdjJFWXZi?= =?utf-8?B?YzQ4OW9TMWM0MDl4cTgyOThqMGdJekFhbXZ2MXBZaW13U1ptemhnY0p1eXFV?= =?utf-8?B?RW1OSW5SRzRSSUp5V1lBVGQzSkhNTGJXR0FCa0Yxc2p3REhKcDRKQzdSbCs1?= =?utf-8?B?VGgxK0tUYmZqbGdUd0pJR2ZILzI1WkhRNExLa0E0Z0FZRVFUYnVlSTdpaXFZ?= =?utf-8?B?OGFZZ2FkRjVMOGQ5RFlEOWxWQ1laQlRZYS8xRnhBRzVmM1hRcXo0Nkg5SkhV?= =?utf-8?B?bnVKQlQ2aVBnbUlwZnBRMUk3d29BekFIZ0x3bit4YzdGbEFzbnl3a3ErWVl6?= =?utf-8?B?N3V6c2RoY3B0MEdrdEZURWJBSW9LVHltNmRTQmo2YUlMajhNb0d1TkV3TlFz?= =?utf-8?B?K3FwZVo3NVIvalRsS3NUOXZka0VvYUxxUGVFeGpUMHMxNm5RUkRtUlpYMmZn?= =?utf-8?B?d3FqVmQ1ek5NYnNRTi9nb3N3SVFXMTdnRUpYSHIrVzRkeEMzdjY5S0QxazR0?= =?utf-8?B?elpqNWR2aEVWcHF0cVQzVlhpS3UxNWMyQ0tMNHpKY2NWYVoxUlVxUXBRYjVR?= =?utf-8?B?NTRjVFA0b1JCeUU0b1hEdzNVTXRJZnZwL1BVdGt3S0tBRVNaNURGNmdYS2ZK?= =?utf-8?B?MUswbXhmM1pROUcvb3dSR3l5cXQ5Rk9iWFBrVmtBN3F2VnpxaHZ3dkJ0aFBE?= =?utf-8?B?WTdCMWhwNUJSblhHaU1MY2hQRndtRk1iU2UvYzJDOFdWZXJjaG5NallhYThY?= =?utf-8?Q?8B0NQwkNFhTsALm8ZQuBNlM=3D?= X-OriginatorOrg: advantech.com X-MS-Exchange-CrossTenant-Network-Message-Id: 8673f184-bea3-4c90-29e2-08de399d5386 X-MS-Exchange-CrossTenant-AuthSource: PSAPR02MB4502.apcprd02.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 Dec 2025 16:41:40.8770 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: a77d40d9-dcba-4dda-b571-5f18e6da853f X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: ABvz0k1t+Reu6eD4Ksbvn0z1na/7CMZdS7okft7Pf9oYyuOlbYueW39lVFEwtMOAPwtOgVRb9pp99gUajkaq6ORWJhBaa4F+dPKeYGUxhgU= X-MS-Exchange-Transport-CrossTenantHeadersStamped: JH0PR02MB6564 This commit adds the driver to control the Advantech EIO I2C block, this block is included in the Advantech EIO MFD. Signed-off-by: Ramiro Oliveira --- MAINTAINERS | 1 + drivers/i2c/busses/Kconfig | 6 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-eio.c | 1142 ++++++++++++++++++++++++++++++++++++++= ++++ 4 files changed, 1150 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index fdd39b152f41..be9d3c4e1ce1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -621,6 +621,7 @@ M: Ramiro Oliveira S: Maintained F: drivers/gpio/gpio-eio.c F: drivers/hwmon/eio-hwmon.c +F: drivers/i2c/busses/i2c-eio.c F: drivers/mfd/eio_core.c F: include/linux/mfd/eio.h =20 diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 09ba55bae1fa..e597c08414e4 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -656,6 +656,12 @@ config I2C_DIGICOLOR This driver can also be built as a module. If so, the module will be called i2c-digicolor. =20 +config I2C_EIO + tristate "Advantech EIO I2C bus" + depends on MFD_EIO + help + Say Y or M to build support for Advantech EIO I2C block. + config I2C_EG20T tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) I2C" depends on PCI && (X86_32 || MIPS || COMPILE_TEST) diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index fb985769f5ff..b65bb06b14c6 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -62,6 +62,7 @@ obj-$(CONFIG_I2C_DESIGNWARE_AMDISP) +=3D i2c-designware-a= mdisp.o obj-$(CONFIG_I2C_DESIGNWARE_PCI) +=3D i2c-designware-pci.o i2c-designware-pci-y :=3D i2c-designware-pcidrv.o obj-$(CONFIG_I2C_DIGICOLOR) +=3D i2c-digicolor.o +obj-$(CONFIG_I2C_EIO) +=3D i2c-eio.o obj-$(CONFIG_I2C_EG20T) +=3D i2c-eg20t.o obj-$(CONFIG_I2C_EMEV2) +=3D i2c-emev2.o obj-$(CONFIG_I2C_EXYNOS5) +=3D i2c-exynos5.o diff --git a/drivers/i2c/busses/i2c-eio.c b/drivers/i2c/busses/i2c-eio.c new file mode 100644 index 000000000000..a867f24a4809 --- /dev/null +++ b/drivers/i2c/busses/i2c-eio.c @@ -0,0 +1,1142 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * I2C and SMBus driver of EIO embedded driver + * + * Copyright (C) 2025 Advantech Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SUPPORTED_COMMON (I2C_FUNC_I2C | \ + I2C_FUNC_SMBUS_QUICK | \ + I2C_FUNC_SMBUS_BYTE | \ + I2C_FUNC_SMBUS_BYTE_DATA | \ + I2C_FUNC_SMBUS_WORD_DATA | \ + I2C_FUNC_SMBUS_I2C_BLOCK) +#define SUPPORTED_SMB (SUPPORTED_COMMON | I2C_FUNC_SMBUS_BLOCK_DATA) +#define SUPPORTED_I2C (SUPPORTED_COMMON | I2C_FUNC_10BIT_ADDR) + +#define MAX_I2C_SMB 4 + +#define REG_PNP_INDEX 0x299 +#define REG_PNP_DATA 0x29A +#define REG_SUB_PNP_INDEX 0x499 +#define REG_SUB_PNP_DATA 0x49A +#define REG_EXT_MODE_ENTER 0x87 +#define REG_EXT_MODE_EXIT 0xAA +#define REG_LDN 0x07 + +#define LDN_I2C0 0x20 +#define LDN_I2C1 0x21 +#define LDN_SMBUS0 0x22 +#define LDN_SMBUS1 0x23 + +#define REG_BASE_HI 0x60 +#define REG_BASE_LO 0x61 + +#define I2C_REG_CTRL 0x00 +#define I2C_CTRL_STOP BIT(1) + +#define I2C_REG_STAT 0x01 +#define I2C_STAT_RXREADY BIT(6) +#define I2C_STAT_TXDONE BIT(5) +#define I2C_STAT_NAK_ERR BIT(4) +#define I2C_STAT_ARL_ERR BIT(3) +#define I2C_STAT_SLV_STP BIT(2) +#define I2C_STAT_BUSY BIT(1) +#define I2C_STAT_MST_SLV BIT(0) + +#define I2C_REG_MYADDR 0x02 +#define I2C_REG_ADDR 0x03 +#define I2C_REG_DATA 0x04 +#define I2C_REG_PRESCALE1 0x05 +#define I2C_REG_PRESCALE2 0x06 + +#define I2C_REG_ECTRL 0x07 +#define I2C_ECTRL_RST BIT(7) + +#define I2C_REG_SEM 0x08 +#define I2C_SEM_INUSE BIT(1) + +#define SMB_REG_HC2 0x0C + +#define SMB_REG_HS 0x00 +#define SMB_HS_BUSY BIT(0) +#define SMB_HS_FINISH BIT(1) +#define SMB_HS_ARL_ERR BIT(3) +#define SMB_HS_FAILED BIT(4) +#define SMB_HS_RX_READY BIT(5) +#define SMB_HS_INUSE BIT(6) +#define SMB_HS_TX_DONE BIT(7) + +#define SMB_REG_HS2 0x01 +#define SMB_HS2_HNOTIFY BIT(0) +#define SMB_HS2_PEC_ERR BIT(1) +#define SMB_HS2_NACK_ERR BIT(2) +#define SMB_HS2_ALERT_STS BIT(3) +#define SMB_HS2_TO_ERR BIT(4) +#define SMB_HS2_SSTOP_STS BIT(5) +#define SMB_HS2_STX_REQ BIT(6) +#define SMB_HS2_SMODE BIT(7) + +#define SMB_REG_HC 0x02 +#define SMB_HC_I2C_NACKEN BIT(0) +#define SMB_HC_KILL BIT(1) +#define SMB_HC_CMD_SHIFT 2 +#define SMB_HC_LAST_BYTE BIT(5) +#define SMB_HC_START BIT(6) +#define SMB_HC_PEC_EN BIT(7) + +#define SMB_REG_HCMD 0x03 +#define SMB_REG_HADDR 0x04 +#define SMB_REG_HD0 0x05 +#define SMB_REG_HD1 0x06 +#define SMB_REG_HBLOCK 0x07 +#define SMB_REG_HPEC 0x08 +#define SMB_REG_SADDR 0x09 +#define SMB_REG_SD0 0x0A +#define SMB_REG_SD1 0x0B + +#define SMB_REG_HC2 0x0C +#define SMB_HC2_HNOTIFY_DIS BIT(0) +#define SMB_HC2_I2C_EN BIT(1) +#define SMB_HC2_AAPEC BIT(2) +#define SMB_HC2_E32B BIT(3) +#define SMB_HC2_SRESET BIT(7) + +#define SMB_REG_HPIN 0x0D +#define SMB_REG_HC3 0x0E +#define SMB_REG_HC4 0x0F +#define SMB_REG_NOTIFY_D0 0x11 +#define SMB_REG_NOTIFY_D1 0x12 +#define SMB_REG_HPRESCALE1 0x13 +#define SMB_REG_HPRESCALE2 0x14 +#define SMB_REG_HEXTRA 0x15 + +#define I2C_TIMEOUT (10 * USEC_PER_MSEC) +#define USE_DEFAULT -1 + +#define CHIP_CLK 50000 +#define I2C_SCLH_HIGH 2500 +#define I2C_SCLH_LOW 1000 +#define I2C_SCL_FAST_MODE 0x80 +#define I2C_THRESHOLD_SPEED 100 +#define I2C_THRESHOLD_SCLH 30 +#define I2C_FREQ_MAX 400 +#define I2C_FREQ_MIN 8 + +enum eio_chan_id { + EIO_I2C0 =3D 0, + EIO_I2C1, + EIO_SMB0, + EIO_SMB1, +}; + +struct eio_i2c_dev { + struct device *dev; + struct device *mfd; + struct regmap *regmap; + struct mutex pnp_mutex; /* Mutex for PNP acces */ + struct eio_i2c_chan *chan[MAX_I2C_SMB]; +}; + +struct eio_i2c_chan { + u16 base; + enum eio_chan_id id; + struct eio_i2c_dev *parent; + struct i2c_adapter adap; + struct mutex lock; /* Mutex for regmap writes */ + int freq_override; /* kHz or USE_DEFAULT */ +}; + +static int timeout =3D I2C_TIMEOUT; +module_param(timeout, int, 0444); +MODULE_PARM_DESC(timeout, "Set IO timeout value.\n"); + +static int i2c0_freq =3D USE_DEFAULT; +module_param(i2c0_freq, int, 0444); +MODULE_PARM_DESC(i2c0_freq, "Set EIO's I2C0 freq.\n"); + +static int i2c1_freq =3D USE_DEFAULT; +module_param(i2c1_freq, int, 0444); +MODULE_PARM_DESC(i2c1_freq, "Set EIO's I2C1 freq.\n"); + +static int smb0_freq =3D USE_DEFAULT; +module_param(smb0_freq, int, 0444); +MODULE_PARM_DESC(smb0_freq, "Set EIO's SMB0 freq.\n"); + +static int smb1_freq =3D USE_DEFAULT; +module_param(smb1_freq, int, 0444); +MODULE_PARM_DESC(smb1_freq, "Set EIO's SMB1 freq.\n"); + +static inline u16 eio_enc_7bit_addr(u16 x) +{ + return ((x & 0x07F) << 1); +} + +static inline u16 eio_enc_10bit_addr(u16 x) +{ + return ((x & 0xFF) | ((x & 0x0300) << 1) | 0xF000); +} + +static inline bool is_i2c(const struct eio_i2c_chan *i2c_chan) +{ + return i2c_chan->id =3D=3D EIO_I2C0 || i2c_chan->id =3D=3D EIO_I2C1; +} + +static inline struct device *eio_dev(const struct eio_i2c_chan *i2c_chan) +{ + return i2c_chan->parent->dev; +} + +static inline struct regmap *eio_map(const struct eio_i2c_chan *i2c_chan) +{ + return i2c_chan->parent->regmap; +} + +static inline int eio_reg_write(struct eio_i2c_chan *i2c_chan, + unsigned int reg_off, unsigned int val) +{ + return regmap_write(eio_map(i2c_chan), i2c_chan->base + reg_off, val); +} + +static inline int eio_reg_read(const struct eio_i2c_chan *chan, + unsigned int reg, unsigned int *val) +{ + int ret; + + ret =3D regmap_read(chan->parent->regmap, chan->base + reg, val); + return ret; +} + +static inline int eio_reg_set_bits(const struct eio_i2c_chan *chan, + unsigned int reg, unsigned int mask) +{ + return regmap_update_bits(chan->parent->regmap, reg, mask, mask); +} + +static inline int eio_reg_clear_bits(const struct eio_i2c_chan *chan, + unsigned int reg, unsigned int mask) +{ + return regmap_update_bits(chan->parent->regmap, reg, mask, 0); +} + +static inline int eio_reg_or(struct eio_i2c_chan *chan, + unsigned int reg, unsigned int mask) +{ + return eio_reg_set_bits(chan, reg, mask); +} + +static inline int eio_reg_and(struct eio_i2c_chan *chan, + unsigned int reg, unsigned int mask) +{ + return eio_reg_clear_bits(chan, reg, ~mask); +} + +static inline unsigned int eio_chan_reg(const struct eio_i2c_chan *i2c_cha= n, + unsigned int i2c_reg, + unsigned int smb_reg) +{ + return is_i2c(i2c_chan) ? i2c_reg : smb_reg; +} + +static inline int eio_trigger_read(struct eio_i2c_chan *i2c_chan, u32 *dat= a) +{ + unsigned int reg =3D eio_chan_reg(i2c_chan, I2C_REG_DATA, SMB_REG_HD0); + + return eio_reg_read(i2c_chan, reg, data); +} + +static int wait_busy(struct eio_i2c_chan *i2c_chan) +{ + ktime_t time_end =3D ktime_add_us(ktime_get(), timeout); + unsigned int reg =3D eio_chan_reg(i2c_chan, I2C_REG_STAT, SMB_REG_HS); + unsigned int target =3D eio_chan_reg(i2c_chan, I2C_STAT_BUSY, SMB_HS_BUSY= ); + unsigned int val; + int cnt =3D 0; + + do { + fsleep(cnt++); + + if (ktime_after(ktime_get(), time_end)) { + dev_err(eio_dev(i2c_chan), "Wait I2C bus busy timeout\n"); + return -ETIME; + } + + if (eio_reg_read(i2c_chan, reg, &val)) + return -EIO; + + } while (val & target); + + return 0; +} + +static void reset_bus(struct eio_i2c_chan *i2c_chan) +{ + ktime_t time_end =3D ktime_add_us(ktime_get(), timeout); + unsigned int reg =3D eio_chan_reg(i2c_chan, I2C_REG_ECTRL, SMB_REG_HC2); + unsigned int target =3D eio_chan_reg(i2c_chan, I2C_ECTRL_RST, SMB_HC2_SRE= SET); + unsigned int val =3D 0; + unsigned int cnt =3D 0; + + dev_dbg(eio_dev(i2c_chan), "i2c[%d] bus reset\n", i2c_chan->id); + + if (is_i2c(i2c_chan)) + eio_reg_write(i2c_chan, I2C_REG_ECTRL, I2C_ECTRL_RST); + else + eio_reg_or(i2c_chan, SMB_REG_HC2, SMB_HC2_SRESET); + + do { + fsleep(cnt++); + + if (ktime_after(ktime_get(), time_end)) { + dev_err(eio_dev(i2c_chan), "bus reset timeout\n"); + return; + } + + if (eio_reg_read(i2c_chan, reg, &val)) + return; + + } while (val & target); + + wait_busy(i2c_chan); +} + +static int wait_bus_free(struct eio_i2c_chan *i2c_chan) +{ + ktime_t time_end =3D ktime_add_us(ktime_get(), timeout); + unsigned int val; + int cnt =3D 1; + + /* Wait if channel is resetting */ + do { + fsleep(cnt); + + if (ktime_after(ktime_get(), time_end)) { + dev_err(eio_dev(i2c_chan), "Wait bus reset timeout\n"); + return -ETIME; + } + + if (eio_reg_read(i2c_chan, + eio_chan_reg(i2c_chan, I2C_REG_ECTRL, SMB_REG_HC2), + &val)) + return -EIO; + + } while (val & eio_chan_reg(i2c_chan, I2C_ECTRL_RST, SMB_HC2_SRESET)); + + /* Wait INUSE */ + time_end =3D ktime_add_us(ktime_get(), timeout); + + do { + fsleep(cnt); + + if (ktime_after(ktime_get(), time_end)) { + dev_err(eio_dev(i2c_chan), "Timeout: I2C bus in use\n"); + return -ETIME; + } + + if (eio_reg_read(i2c_chan, + eio_chan_reg(i2c_chan, I2C_REG_SEM, SMB_REG_HS), + &val)) + return -EIO; + + } while (val & eio_chan_reg(i2c_chan, I2C_SEM_INUSE, SMB_HS_INUSE)); + + return 0; +} + +static int let_stop(struct eio_i2c_chan *i2c_chan) +{ + unsigned int reg =3D eio_chan_reg(i2c_chan, I2C_REG_CTRL, SMB_REG_HC); + unsigned int target =3D eio_chan_reg(i2c_chan, I2C_CTRL_STOP, SMB_HC_LAST= _BYTE); + + return eio_reg_or(i2c_chan, reg, target); +} + +static int clr_inuse(struct eio_i2c_chan *i2c_chan) +{ + if (is_i2c(i2c_chan)) + return eio_reg_write(i2c_chan, I2C_REG_SEM, I2C_SEM_INUSE); + + return eio_reg_or(i2c_chan, SMB_REG_HS, SMB_HS_INUSE); +} + +static int bus_stop(struct eio_i2c_chan *i2c_chan) +{ + ktime_t time_end =3D ktime_add_us(ktime_get(), timeout); + unsigned int reg =3D eio_chan_reg(i2c_chan, I2C_REG_CTRL, SMB_REG_HC); + unsigned int target =3D eio_chan_reg(i2c_chan, I2C_CTRL_STOP, SMB_HC_LAST= _BYTE); + unsigned int val =3D 0; + int cnt =3D 0; + + /* Set STOP bit */ + eio_reg_or(i2c_chan, reg, target); + + /* Wait until STOP bit clears */ + do { + fsleep(cnt++); + + if (ktime_after(ktime_get(), time_end)) + return -ETIME; + + if (eio_reg_read(i2c_chan, reg, &val)) + return -EIO; + + } while (val & target); + + return 0; +} + +static void switch_i2c_mode(struct eio_i2c_chan *i2c_chan, bool on) +{ + u32 tmp; + + if (is_i2c(i2c_chan)) + return; + + if (eio_reg_read(i2c_chan, SMB_REG_HC2, &tmp)) + return; + + eio_reg_write(i2c_chan, SMB_REG_HC2, + on ? (tmp | SMB_HC2_I2C_EN | SMB_HC2_SRESET) + : (tmp & ~SMB_HC2_I2C_EN)); +} + +static void i2c_clear(struct eio_i2c_chan *i2c_chan) +{ + if (is_i2c(i2c_chan)) { + eio_reg_write(i2c_chan, I2C_REG_STAT, 0xFF); + } else { + eio_reg_or(i2c_chan, SMB_REG_HS, 0xA9); + eio_reg_or(i2c_chan, SMB_REG_HS2, 0x4C); + } +} + +static int wait_write_done(struct eio_i2c_chan *i2c_chan, bool no_ack) +{ + ktime_t time_end =3D ktime_add_us(ktime_get(), timeout); + unsigned int val =3D 0; + int cnt =3D 0; + unsigned int reg =3D eio_chan_reg(i2c_chan, I2C_REG_STAT, SMB_REG_HS); + unsigned int target =3D eio_chan_reg(i2c_chan, I2C_STAT_TXDONE, SMB_HS_TX= _DONE); + + do { + fsleep(cnt++); + if (ktime_after(ktime_get(), time_end)) { + if (is_i2c(i2c_chan)) { + eio_reg_or(i2c_chan, I2C_REG_STAT, 0); + } else { + eio_reg_or(i2c_chan, SMB_REG_HS, 0); + eio_reg_or(i2c_chan, SMB_REG_HS2, 0); + } + dev_err(eio_dev(i2c_chan), "wait write complete timeout %X %X\n", + val, target); + return -ETIME; + } + if (eio_reg_read(i2c_chan, reg, &val)) + return -EIO; + + } while ((val & target) =3D=3D 0); + + if (no_ack) + return 0; + + if (is_i2c(i2c_chan)) { + eio_reg_or(i2c_chan, I2C_REG_STAT, 0); + return (val & I2C_STAT_NAK_ERR) ? -EIO : 0; + } + + eio_reg_or(i2c_chan, SMB_REG_HS, 0); + if (eio_reg_read(i2c_chan, SMB_REG_HS2, &val)) + return -EIO; + eio_reg_write(i2c_chan, SMB_REG_HS2, val); + + return (val & SMB_HS2_NACK_ERR) ? -EIO : 0; +} + +static int wait_ready(struct eio_i2c_chan *i2c_chan) +{ + int ret; + + ret =3D wait_bus_free(i2c_chan); + if (ret) + return ret; + + if (wait_busy(i2c_chan) =3D=3D 0) + return 0; + + reset_bus(i2c_chan); + + return wait_busy(i2c_chan); +} + +static int write_addr(struct eio_i2c_chan *i2c_chan, int addr, bool no_ack) +{ + eio_reg_write(i2c_chan, eio_chan_reg(i2c_chan, I2C_REG_ADDR, SMB_REG_HADD= R), + addr); + + return wait_write_done(i2c_chan, no_ack); +} + +static int write_data(struct eio_i2c_chan *i2c_chan, int data, bool no_ack) +{ + eio_reg_write(i2c_chan, eio_chan_reg(i2c_chan, I2C_REG_DATA, SMB_REG_HD0), + data); + + return wait_write_done(i2c_chan, no_ack); +} + +static int read_data(struct eio_i2c_chan *i2c_chan, u8 *data) +{ + unsigned int val =3D 0, tmp; + int cnt =3D 0; + ktime_t time_end =3D ktime_add_us(ktime_get(), timeout); + unsigned int stat =3D eio_chan_reg(i2c_chan, I2C_REG_STAT, SMB_REG_HS); + unsigned int target =3D eio_chan_reg(i2c_chan, I2C_STAT_RXREADY, SMB_HS_R= X_READY); + unsigned int reg =3D eio_chan_reg(i2c_chan, I2C_REG_DATA, SMB_REG_HD0); + + do { + fsleep(cnt++); + + if (ktime_after(ktime_get(), time_end)) { + eio_reg_or(i2c_chan, stat, 0); + dev_err(eio_dev(i2c_chan), "read data timeout\n"); + return -ETIME; + } + + if (eio_reg_read(i2c_chan, stat, &val)) + return -EIO; + + } while ((val & target) !=3D target); + + /* clear status */ + eio_reg_write(i2c_chan, stat, val); + + /* Must read data after clearing status */ + if (eio_reg_read(i2c_chan, reg, &tmp)) + return -EIO; + *data =3D (u8)tmp; + + return 0; +} + +static int set_freq(struct eio_i2c_chan *i2c_chan, int freq) +{ + u8 pre1, pre2; + u16 speed; + unsigned int reg1 =3D eio_chan_reg(i2c_chan, I2C_REG_PRESCALE1, SMB_REG_H= PRESCALE1); + unsigned int reg2 =3D eio_chan_reg(i2c_chan, I2C_REG_PRESCALE2, SMB_REG_H= PRESCALE2); + + dev_dbg(eio_dev(i2c_chan), "set freq: %dkHz\n", freq); + if (freq > I2C_FREQ_MAX || freq < I2C_FREQ_MIN) { + dev_err(eio_dev(i2c_chan), "Invalid i2c freq: %d\n", freq); + return -EINVAL; + } + + speed =3D (freq < I2C_THRESHOLD_SCLH) ? I2C_SCLH_LOW : I2C_SCLH_HIGH; + + pre1 =3D (u8)(CHIP_CLK / speed); + pre2 =3D (u8)((speed / freq) - 1); + + if (freq > I2C_THRESHOLD_SCLH) + pre2 |=3D I2C_SCL_FAST_MODE; + + eio_reg_write(i2c_chan, reg1, pre1); + eio_reg_write(i2c_chan, reg2, pre2); + + return 0; +} + +static int get_freq(struct eio_i2c_chan *i2c_chan, int *freq) +{ + int clk; + unsigned int pre1 =3D 0, pre2 =3D 0; + unsigned int reg1 =3D eio_chan_reg(i2c_chan, I2C_REG_PRESCALE1, SMB_REG_H= PRESCALE1); + unsigned int reg2 =3D eio_chan_reg(i2c_chan, I2C_REG_PRESCALE2, SMB_REG_H= PRESCALE2); + + if (eio_reg_read(i2c_chan, reg1, &pre1)) + return -EIO; + if (eio_reg_read(i2c_chan, reg2, &pre2)) + return -EIO; + + clk =3D (pre2 & I2C_SCL_FAST_MODE) ? I2C_SCLH_HIGH : I2C_SCLH_LOW; + pre2 &=3D ~I2C_SCL_FAST_MODE; + + *freq =3D clk / ((int)pre2 + 1); + + return 0; +} + +static int smb_access(struct eio_i2c_chan *i2c_chan, u8 addr, bool is_read= , u8 cmd, + int size, union i2c_smbus_data *data) +{ + int i, tmp, ret =3D 0; + unsigned int st1, st2; + int len =3D 0; + + mutex_lock(&i2c_chan->lock); + + ret =3D wait_ready(i2c_chan); + if (ret) + goto exit; + + /* Force SMBus mode */ + switch_i2c_mode(i2c_chan, false); + + addr =3D eio_enc_7bit_addr(addr) | (is_read ? 1 : 0); + eio_reg_write(i2c_chan, SMB_REG_HADDR, addr); + eio_reg_write(i2c_chan, SMB_REG_HCMD, cmd); + + dev_dbg(eio_dev(i2c_chan), "SMB[%d], addr:0x%02X, cmd:0x%02X size=3D%d\n", + i2c_chan->id, addr, cmd, size); + + switch (size) { + case I2C_SMBUS_QUICK: + dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_QUICK\n"); + break; + + case I2C_SMBUS_BYTE: + if (!is_read) { + dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_BYTE\n"); + eio_reg_write(i2c_chan, SMB_REG_HCMD, cmd); + } + break; + + case I2C_SMBUS_BYTE_DATA: + dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_BYTE_DATA\n"); + if (!is_read) { + eio_reg_write(i2c_chan, SMB_REG_HD0, data->byte); + dev_dbg(eio_dev(i2c_chan), "write %X\n", data->byte); + } + break; + + case I2C_SMBUS_WORD_DATA: + dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_WORD_DATA\n"); + if (!is_read) { + eio_reg_write(i2c_chan, SMB_REG_HD0, data->block[0]); + eio_reg_write(i2c_chan, SMB_REG_HD1, data->block[1]); + } + break; + + case I2C_SMBUS_PROC_CALL: + dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_PROC_CALL\n"); + eio_reg_write(i2c_chan, SMB_REG_HD0, data->block[0]); + eio_reg_write(i2c_chan, SMB_REG_HD1, data->block[1]); + break; + + case I2C_SMBUS_BLOCK_DATA: + dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_BLOCK_DATA\n"); + if (is_read) + break; + + /* Program command type */ + eio_reg_read(i2c_chan, SMB_REG_HC, (unsigned int *)&tmp); + tmp &=3D ~(0x07 << SMB_HC_CMD_SHIFT); + tmp |=3D (size << SMB_HC_CMD_SHIFT); + eio_reg_write(i2c_chan, SMB_REG_HC, tmp); + + /* Force write for payload stage */ + eio_reg_write(i2c_chan, SMB_REG_HADDR, addr & ~0x01); + + /* Reset internal buffer index pointer */ + eio_reg_and(i2c_chan, SMB_REG_HC2, (int)~SMB_HC2_E32B); + eio_reg_or(i2c_chan, SMB_REG_HC2, SMB_HC2_E32B); + + /* Write length + data */ + eio_reg_write(i2c_chan, SMB_REG_HD0, data->block[0]); + for (i =3D 1; i <=3D data->block[0]; i++) + eio_reg_write(i2c_chan, SMB_REG_HBLOCK, data->block[i]); + break; + + case I2C_SMBUS_BLOCK_PROC_CALL: + /* Set command type field */ + eio_reg_and(i2c_chan, SMB_REG_HC, (0x07 << SMB_HC_CMD_SHIFT)); + eio_reg_write(i2c_chan, SMB_REG_HD0, data->block[0]); + + /* Reset buffer index */ + eio_reg_and(i2c_chan, SMB_REG_HC2, (int)~SMB_HC2_E32B); + eio_reg_or(i2c_chan, SMB_REG_HC2, SMB_HC2_E32B); + + for (i =3D 1; i <=3D data->block[0]; i++) + eio_reg_write(i2c_chan, SMB_REG_HBLOCK, data->block[i]); + break; + + default: + ret =3D -EINVAL; + goto exit; + } + + /* Launch transaction */ + eio_reg_read(i2c_chan, SMB_REG_HC, (unsigned int *)&tmp); + tmp &=3D ~(0x07 << SMB_HC_CMD_SHIFT); + tmp |=3D (size << SMB_HC_CMD_SHIFT) | SMB_HC_START; + tmp &=3D ~(SMB_HC_I2C_NACKEN | SMB_HC_KILL | SMB_HC_PEC_EN); + eio_reg_write(i2c_chan, SMB_REG_HC, tmp); + + ret =3D wait_busy(i2c_chan); + if (ret) + goto exit; + + eio_reg_read(i2c_chan, SMB_REG_HS, &st1); + eio_reg_read(i2c_chan, SMB_REG_HS2, &st2); + + if (st1 & SMB_HS_FAILED) { + dev_err(eio_dev(i2c_chan), "HS FAILED\n"); + ret =3D -EIO; + } else if (st1 & SMB_HS_ARL_ERR) { + dev_err(eio_dev(i2c_chan), "ARL FAILED\n"); + ret =3D -EIO; + } else if (st2 & SMB_HS2_TO_ERR) { + dev_err(eio_dev(i2c_chan), "timeout\n"); + ret =3D -ETIME; + } else if (st2 & SMB_HS2_NACK_ERR) { + dev_err(eio_dev(i2c_chan), "NACK err\n"); + ret =3D -EIO; + } else if (st2 & SMB_HS2_PEC_ERR) { + dev_err(eio_dev(i2c_chan), "PEC err\n"); + ret =3D -EIO; + } + if (ret) + goto exit; + + switch (size) { + case I2C_SMBUS_QUICK: + dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_QUICK\n"); + break; + + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + if (is_read) { + unsigned int v; + + dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_BYTE/I2C_SMBUS_BYTE_DATA\n"); + eio_reg_read(i2c_chan, SMB_REG_HD0, &v); + data->block[0] =3D (u8)v; + dev_dbg(eio_dev(i2c_chan), "read %X\n", data->block[0]); + } + break; + + case I2C_SMBUS_WORD_DATA: { + unsigned int v0, v1; + + if (is_read) { + dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_WORD_DATA\n"); + eio_reg_read(i2c_chan, SMB_REG_HD0, &v0); + eio_reg_read(i2c_chan, SMB_REG_HD1, &v1); + data->block[0] =3D (u8)v0; + data->block[1] =3D (u8)v1; + } + break; + } + + case I2C_SMBUS_PROC_CALL: { + unsigned int v0, v1; + + dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_PROC_CALL\n"); + eio_reg_read(i2c_chan, SMB_REG_HD0, &v0); + eio_reg_read(i2c_chan, SMB_REG_HD1, &v1); + data->block[0] =3D (u8)v0; + data->block[1] =3D (u8)v1; + break; + } + + case I2C_SMBUS_BLOCK_DATA: + if (!is_read) + break; + + dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_BLOCK_DATA\n"); + eio_reg_read(i2c_chan, SMB_REG_HD0, (unsigned int *)&len); + len =3D min(len, I2C_SMBUS_BLOCK_MAX); + data->block[0] =3D len; + + for (i =3D 1; i <=3D len; i++) + eio_reg_read(i2c_chan, SMB_REG_HBLOCK, + (unsigned int *)&data->block[i]); + break; + + default: + ret =3D -EINVAL; + goto exit; + } + +exit: + /* Clear latched status */ + eio_reg_write(i2c_chan, SMB_REG_HS, 0xFF); + eio_reg_write(i2c_chan, SMB_REG_HS2, 0xFF); + + mutex_unlock(&i2c_chan->lock); + return ret; +} + +static int i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int nm= sgs) +{ + int msg, data; + int addr =3D 0; + int dummy; + int ret =3D 0; + struct eio_i2c_chan *i2c_chan =3D i2c_get_adapdata(adap); + + mutex_lock(&i2c_chan->lock); + + ret =3D wait_ready(i2c_chan); + if (ret) + goto exit; + + switch_i2c_mode(i2c_chan, true); + + dev_dbg(eio_dev(i2c_chan), "Transmit %d I2C messages\n", nmsgs); + for (msg =3D 0; msg < nmsgs; msg++) { + int is_read =3D msgs[msg].flags & I2C_M_RD; + bool no_ack =3D msgs[msg].flags & I2C_M_IGNORE_NAK; + + dev_dbg(eio_dev(i2c_chan), "message %d len=3D%d\n", msg, msgs[msg].len); + + if (!msgs[msg].len) + let_stop(i2c_chan); + + if (msgs[msg].flags & I2C_M_TEN) { + addr =3D eio_enc_10bit_addr(msgs[msg].addr); + addr |=3D is_read; + dev_dbg(eio_dev(i2c_chan), "10-bit addr: %X\n", addr); + + ret =3D write_addr(i2c_chan, addr >> 8, no_ack); + if (!ret) + ret =3D write_data(i2c_chan, addr & 0x7F, no_ack); + } else { + addr =3D eio_enc_7bit_addr(msgs[msg].addr); + addr |=3D is_read; + dev_dbg(eio_dev(i2c_chan), "7-bit addr: %X\n", addr); + + ret =3D write_addr(i2c_chan, addr, no_ack); + } + + if (ret) + goto exit; + + if (!msgs[msg].len) + goto exit; + + if (is_read) + ret =3D eio_trigger_read(i2c_chan, (u32 *)&dummy); + + /* Transmit all messages */ + for (data =3D 0; data < msgs[msg].len; data++) { + if (msgs[msg].flags & I2C_M_RD) { + bool last =3D (msgs[msg].len =3D=3D data + 1); + + if (last) + let_stop(i2c_chan); + + ret =3D read_data(i2c_chan, &msgs[msg].buf[data]); + dev_dbg(eio_dev(i2c_chan), "I2C read[%d] =3D %x\n", + data, msgs[msg].buf[data]); + + /* Don't stop twice */ + if (last && ret =3D=3D 0) + goto exit; + } else { + ret =3D write_data(i2c_chan, msgs[msg].buf[data], no_ack); + dev_dbg(eio_dev(i2c_chan), "I2C write[%d] =3D %x\n", + data, msgs[msg].buf[data]); + } + if (ret) + goto exit; + } + } + + if (!ret) + ret =3D bus_stop(i2c_chan); + + if (!ret) + goto exit; + +exit: + if (ret) + reset_bus(i2c_chan); + + i2c_clear(i2c_chan); + clr_inuse(i2c_chan); + + mutex_unlock(&i2c_chan->lock); + return ret ? ret : nmsgs; +} + +static int smbus_xfer(struct i2c_adapter *adap, u16 addr, + u16 flags, char is_read, u8 cmd, + int size, union i2c_smbus_data *data) +{ + int ret; + struct eio_i2c_chan *i2c_chan =3D i2c_get_adapdata(adap); + int nmsgs =3D is_read ? 2 : 1; + u8 buf[I2C_SMBUS_BLOCK_MAX + sizeof(u32)] =3D { cmd, }; + struct i2c_msg msgs[2] =3D { + { .addr =3D addr, .flags =3D flags & ~I2C_M_RD, .buf =3D buf + 0 }, + { .addr =3D addr, .flags =3D flags | I2C_M_RD, .buf =3D buf + 1 }, + }; + + /* Non-I2C channels use the SMB engine, except I2C block variants we emul= ate */ + if (!is_i2c(i2c_chan) && size !=3D I2C_SMBUS_I2C_BLOCK_DATA) + return smb_access(i2c_chan, addr, is_read, cmd, size, data); + + if (data) { + buf[0] =3D cmd; + /* FIX: preserve other flags; only toggle I2C_M_RD */ + msgs[0].flags =3D is_read ? (flags | I2C_M_RD) : (flags & ~I2C_M_RD); + msgs[1].buf =3D data->block; + } + + switch (size) { + case I2C_SMBUS_QUICK: + dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_QUICK on I2C\n"); + nmsgs =3D 1; + break; + + case I2C_SMBUS_BYTE: + dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_BYTE on I2C\n"); + nmsgs =3D 1; + msgs[0].len =3D 1; + msgs[0].buf =3D is_read ? data->block : buf; + msgs[0].flags =3D is_read ? (flags | I2C_M_RD) : (flags & ~I2C_M_RD); + break; + + case I2C_SMBUS_BYTE_DATA: + dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_BYTE_DATA on I2C\n"); + if (!data) + return -EINVAL; + msgs[0].len =3D is_read ? 1 : 2; + buf[1] =3D data->block[0]; + msgs[1].len =3D 1; + break; + + case I2C_SMBUS_WORD_DATA: + dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_WORD_DATA on I2C\n"); + if (!data) + return -EINVAL; + msgs[0].len =3D is_read ? 1 : 3; + msgs[1].len =3D 2; + buf[1] =3D data->block[0]; + buf[2] =3D data->block[1]; + msgs[1].buf =3D data->block; + break; + + case I2C_SMBUS_I2C_BLOCK_DATA: + case I2C_SMBUS_I2C_BLOCK_BROKEN: + dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_I2C_BLOCK_(DATA/BROKEN) on I2C len= =3D%d\n", + data->block[0]); + if (!data) + return -EINVAL; + msgs[0].len =3D is_read ? 1 : data->block[0] + 1; + msgs[1].len =3D data->block[0]; + msgs[1].buf =3D data->block + 1; + if (msgs[0].len >=3D I2C_SMBUS_BLOCK_MAX || + msgs[1].len >=3D I2C_SMBUS_BLOCK_MAX) + return -EINVAL; + if (!is_read) + memcpy(buf + 1, data->block + 1, msgs[0].len); + break; + + case I2C_SMBUS_PROC_CALL: + dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_PROC_CALL on I2C\n"); + if (!data) + return -EINVAL; + nmsgs =3D 2; + msgs[0].flags =3D flags & ~I2C_M_RD; + msgs[0].len =3D 3; + buf[1] =3D data->block[0]; + buf[2] =3D data->block[1]; + msgs[1].len =3D 2; + break; + + case I2C_SMBUS_BLOCK_DATA: + dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_BLOCK_DATA on I2C not supported\n"= ); + return -EINVAL; + + case I2C_SMBUS_BLOCK_PROC_CALL: + dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_BLOCK_PROC_CALL on I2C not support= ed\n"); + return -EINVAL; + + default: + return -EINVAL; + } + + ret =3D i2c_xfer(adap, msgs, nmsgs); + return ret < 0 ? ret : 0; +} + +static int load_i2c(struct device *dev, enum eio_chan_id id, + struct eio_i2c_chan *i2c_chan) +{ + u32 base_lo, base_hi, base; + int ldn =3D LDN_I2C0 + id; + struct eio_i2c_dev *eio_i2c =3D i2c_chan->parent; + struct regmap *map; + + if (!eio_i2c || !eio_i2c->regmap) + return dev_err_probe(dev, -ENODEV, "missing parent/regmap\n"); + + map =3D eio_i2c->regmap; + + /* Read channel I/O base via shared PNP window */ + mutex_lock(&eio_i2c->pnp_mutex); + if (regmap_write(map, REG_PNP_INDEX, REG_EXT_MODE_ENTER) || + regmap_write(map, REG_PNP_INDEX, REG_EXT_MODE_ENTER) || + regmap_write(map, REG_PNP_INDEX, REG_LDN) || + regmap_write(map, REG_PNP_DATA, ldn) || + regmap_write(map, REG_PNP_INDEX, REG_BASE_HI) || + regmap_read(map, REG_PNP_DATA, &base_hi) || + regmap_write(map, REG_PNP_INDEX, REG_BASE_LO) || + regmap_read(map, REG_PNP_DATA, &base_lo) || + regmap_write(map, REG_PNP_INDEX, REG_EXT_MODE_EXIT)) { + mutex_unlock(&eio_i2c->pnp_mutex); + dev_err(dev, "error read/write I2C[%d] IO port\n", id); + return -EIO; + } + mutex_unlock(&eio_i2c->pnp_mutex); + + base =3D (base_hi << 8) | base_lo; + if (base =3D=3D 0xFFFF || base =3D=3D 0) { + dev_dbg(dev, "i2c[%d] base addr=3D%#x (not in-use)\n", id, base); + return -ENODEV; + } + + dev_dbg(dev, "i2c[%d] base addr=3D%#x\n", id, base); + + /* Bind channel (no per-chan dev) */ + i2c_chan->base =3D (u16)base; + i2c_chan->id =3D id; + + /* Per-channel frequency policy */ + if (i2c_chan->freq_override !=3D USE_DEFAULT) + set_freq(i2c_chan, i2c_chan->freq_override); + + get_freq(i2c_chan, &i2c_chan->freq_override); + + return 0; +} + +static u32 functionality(struct i2c_adapter *adap) +{ + struct eio_i2c_chan *i2c_chan =3D i2c_get_adapdata(adap); + + return is_i2c(i2c_chan) ? SUPPORTED_I2C : SUPPORTED_SMB; +} + +static const struct i2c_algorithm algo =3D { + .smbus_xfer =3D smbus_xfer, + .master_xfer =3D i2c_xfer, + .functionality =3D functionality, +}; + +static int eio_i2c_probe(struct platform_device *pdev) +{ + static const char * const names[] =3D { "i2c0", "i2c1", "smb0", "smb1" }; + struct device *dev =3D &pdev->dev; + struct eio_i2c_dev *eio_i2c; + struct eio_dev *eio_dev =3D dev_get_drvdata(dev->parent); + int ret =3D 0; + enum eio_chan_id ch; + + if (!eio_dev) { + dev_err(dev, "Error contact eio_core\n"); + return -ENODEV; + } + + timeout =3D clamp_t(int, timeout, I2C_TIMEOUT / 100, I2C_TIMEOUT * 100); + dev_info(dev, "Timeout value %d\n", timeout); + + eio_i2c =3D devm_kzalloc(dev, sizeof(*eio_i2c), GFP_KERNEL); + if (!eio_i2c) + return -ENOMEM; + + eio_i2c->dev =3D dev; + eio_i2c->mfd =3D dev->parent; + eio_i2c->regmap =3D dev_get_regmap(dev->parent, NULL); + if (!eio_i2c->regmap) + return dev_err_probe(dev, -ENODEV, "parent regmap not found\n"); + + mutex_init(&eio_i2c->pnp_mutex); + platform_set_drvdata(pdev, eio_i2c); + + for (ch =3D EIO_I2C0; ch < MAX_I2C_SMB; ch++) { + struct eio_i2c_chan *i2c_chan; + + i2c_chan =3D devm_kzalloc(dev, sizeof(*i2c_chan), GFP_KERNEL); + if (!i2c_chan) { + ret =3D -ENOMEM; + break; + } + + i2c_chan->parent =3D eio_i2c; + i2c_chan->freq_override =3D USE_DEFAULT; + mutex_init(&i2c_chan->lock); + + if (load_i2c(dev, ch, i2c_chan)) { + dev_info(dev, "No %s%d!\n", (ch < 2) ? "I2C" : "SMBus", ch & 1); + continue; + } + + i2c_chan->adap.owner =3D THIS_MODULE; + i2c_chan->adap.class =3D I2C_CLASS_HWMON; + i2c_chan->adap.algo =3D &algo; + i2c_chan->adap.dev.parent =3D dev; + snprintf(i2c_chan->adap.name, sizeof(i2c_chan->adap.name), "eio-%s", + names[ch]); + + i2c_set_adapdata(&i2c_chan->adap, i2c_chan); + + ret =3D i2c_add_adapter(&i2c_chan->adap); + dev_info(dev, "Add %s%d %s. %d\n", (ch < 2) ? "I2C" : "SMBus", + ch, ret ? "Error" : "Success", ret); + if (ret) + break; + + eio_i2c->chan[ch] =3D i2c_chan; + } + + if (ret) { + for (ch =3D EIO_I2C0; ch < MAX_I2C_SMB; ch++) { + if (eio_i2c->chan[ch]) { + i2c_del_adapter(&eio_i2c->chan[ch]->adap); + eio_i2c->chan[ch] =3D NULL; + } + } + } + + return ret; +} + +static void eio_i2c_remove(struct platform_device *pdev) +{ + struct eio_i2c_dev *eio_i2c =3D platform_get_drvdata(pdev); + enum eio_chan_id ch; + + for (ch =3D EIO_I2C0; ch < MAX_I2C_SMB; ch++) { + if (eio_i2c->chan[ch]) { + i2c_del_adapter(&eio_i2c->chan[ch]->adap); + eio_i2c->chan[ch] =3D NULL; + } + } +} + +static struct platform_driver eio_i2c_driver =3D { + .probe =3D eio_i2c_probe, + .remove =3D eio_i2c_remove, + .driver =3D { + .name =3D "i2c_eio", + }, +}; + +module_platform_driver(eio_i2c_driver); + +MODULE_AUTHOR("Wenkai Chung "); +MODULE_AUTHOR("Ramiro Oliveira "); +MODULE_DESCRIPTION("I2C driver for Advantech EIO embedded controller"); +MODULE_LICENSE("GPL"); +MODULE_SOFTDEP("pre: eio_core"); --=20 2.43.0 From nobody Sun Dec 14 06:34:44 2025 Received: from OS8PR02CU002.outbound.protection.outlook.com (mail-japanwestazon11022124.outbound.protection.outlook.com [40.107.75.124]) (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 7FC572D24A9; Fri, 12 Dec 2025 16:41:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.75.124 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765557714; cv=fail; b=T2+bkRaZib0Nrm9JUN2fckPCqVQVUtucmlfoNyRbAiEflG37bJxdDcB9DV4Z3J60nNM5AfcjuKN6QImZjaSvOgmhPckEW8yc/+74uvMR4B9mBMrFnhZse7mGByN5QbdVk7V4iyK/jlVH6iDExjIK5w7hWldAdWMNYEOzeSn7P94= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765557714; c=relaxed/simple; bh=Dj3/rfHcj1zxsAAwNGEPn3FYt0XHU4vJvn4M7ol/Uik=; h=From:Date:Subject:Content-Type:Message-Id:References:In-Reply-To: To:Cc:MIME-Version; b=REbLFGkKiUbCjt04aoW1QsQtzZbbr09HlMTy6OENImimJ24xp9XRs6TzI1tmCgIVK0AYfMdvOWZjMEm1UNiF0IJo1psOrvGF5Uy01gxY1RRhvbMNx1oh/9BSUJdk/zbzI4lJpvM8dH/nobisKLgtz2jtI3Tj+sTk9UL3egYo+Ws= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=advantech.com; spf=pass smtp.mailfrom=advantech.de; dkim=pass (2048-bit key) header.d=advantech.com header.i=@advantech.com header.b=VljxvE45; arc=fail smtp.client-ip=40.107.75.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=advantech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=advantech.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=advantech.com header.i=@advantech.com header.b="VljxvE45" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=iU3VHyZC/+4WPj9RIWoLXzdECUGfXcy3fNedqobpjeQxm1taqg/YOu4K7qBoeRQ0doVy1ykZoqIiuesikZ+UDNEcnOjH3AtUQCIzrUJcoykiae/sKD/ZXzqZMUx+cSaoLg9IUgLufjRJG6nWgkfzx+vB/N9APkdnzBRs1lH++Nir254PLBxjts/2rsLVtPhaIfnWX8BIzm37vlhdDmPG71//1IP/8RlPchsGt+BnvQVU5xD2ZwxzUmSn3FFgl062f2PBqN0XAMR0gFHargPsrrx2cYNCKC3b1JqnsFpUYSynIH+wx18viCVxk4eZ/p72UG2on7shmLifQoQnO5n93Q== 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=4vCb2lfUdk7mogpvo75ax/bbGc2r6I6ltkhvum7E7o4=; b=tpb857hBIZqCvbmEPZd/h8TnucY6Y0f/d4eFrkAwgQC/eCvjkXMzcoTHEboLU5G4QmP7O3DSL6MxHaNY699IwzLOKLye4G6bp5mL68esR+1sNCRH+w5e6FhUNK19aOFZh/wAMd+yREMyzia/sYErCpTURNzzKw4B2NjOfO5a1JUk13qjEjzOja1Bg5gDfW6lZWJRGjaJrlMiEnpq5oCNN0jpDpJ0XxoTL/QLpcPOT+HsTu6KWBMU/xsiJE8MUj74qo9ZRmEZSPlVT4npVzHShVB8AIXUS/1YbaRTp0GMNJ2Oamg6worfIvFb+/wEqoO+CumqosckNSnEYd9viG9SWQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=advantech.de; dmarc=pass action=none header.from=advantech.com; dkim=pass header.d=advantech.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=advantech.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=4vCb2lfUdk7mogpvo75ax/bbGc2r6I6ltkhvum7E7o4=; b=VljxvE45cE5GlIZ8PHhUmfM298Xmo4tWUQPBbb+jHzi+rbCw+RV6K6dSmdjYXKo8kMha6S/R465k28ciu5RHtthm5NGPIOnc5RrkQhjAHSyAD4vp4tMurleC16j3udXw/JA+PC6yvGQaAWaciw8HnwVzTzbEny/ieY2aBsEKiCJ5C0UZtgkPMPwUH2b+KfEb7Gs4FWVwhwBZgo0l8dy3QrBaiAzN6CGnP6bU5gPBsP8vWj6EaArIOFvcouINhu0VfUiBshOEbAr+g7ohyRi1L6kKWlduULZqY7AvA3EyVI3aMccTgl8n+cdhzH+bxMV9aI9v3ULI9x2wJJ013aep7w== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=advantech.com; Received: from PSAPR02MB4502.apcprd02.prod.outlook.com (2603:1096:301:21::6) by JH0PR02MB6564.apcprd02.prod.outlook.com (2603:1096:990:3f::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9412.11; Fri, 12 Dec 2025 16:41:47 +0000 Received: from PSAPR02MB4502.apcprd02.prod.outlook.com ([fe80::59c9:fe0:25f6:702b]) by PSAPR02MB4502.apcprd02.prod.outlook.com ([fe80::59c9:fe0:25f6:702b%6]) with mapi id 15.20.9412.005; Fri, 12 Dec 2025 16:41:47 +0000 From: Ramiro Oliveira Date: Fri, 12 Dec 2025 17:40:56 +0100 Subject: [PATCH 5/8] Add Advantech EIO Backlight driver Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251212-upstream-v1-v1-5-d50d40ec8d8a@advantech.com> References: <20251212-upstream-v1-v1-0-d50d40ec8d8a@advantech.com> In-Reply-To: <20251212-upstream-v1-v1-0-d50d40ec8d8a@advantech.com> To: Lee Jones , Linus Walleij , Bartosz Golaszewski , Guenter Roeck , Andi Shyti , Daniel Thompson , Jingoo Han , Helge Deller , Wim Van Sebroeck , "Rafael J. Wysocki" , Daniel Lezcano , Zhang Rui , Lukasz Luba Cc: linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, linux-hwmon@vger.kernel.org, linux-i2c@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-fbdev@vger.kernel.org, linux-watchdog@vger.kernel.org, linux-pm@vger.kernel.org, Wenkai Chung , Francisco Aragon-Trivino , Hongzhi Wang , Mikhail Tsukerman , Thomas Kastner , Ramiro Oliveira X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=8980; i=ramiro.oliveira@advantech.com; h=from:subject:message-id; bh=Dj3/rfHcj1zxsAAwNGEPn3FYt0XHU4vJvn4M7ol/Uik=; b=owEB7QES/pANAwAKAc7t0Ke8vbAJAcsmYgBpPEWhGTwBrjZtOW+Qsh43S9Lq/HZ0OQi6De16n uBGJs2zFaiJAbMEAAEKAB0WIQS1Nkng0ZvJmBKh6GLO7dCnvL2wCQUCaTxFoQAKCRDO7dCnvL2w CbG6C/96VDoxbBFNeZbXYetFgcNCq70/61FezIYfJ9kZ9OH7XfgZ69pdqd5Obf7880/qdai7byE 1tJtSeDHJ7JlTvTF8+PIynGssBva+e3utPDKsrMW0MyHK8ShSMefD6H4v4Bb/kgQH755VAxDvZ9 yA7M5448EY5mZv6dhZQ0anvyubaVtNK1RLCZZz2pTCLEbOaVXP1TwLRSAXhcWy1suQLDAEiIYqC +wqQ3uu1lGOj2rkD5EIxxhL1Jt9Y2X09V5C3YTz6bOXa/yYC2mH/Ao++/EUThNQ5tmxEr99/hKa uOrCRNufqkNoh3Pnz2wI+NpO933NjG28Fv2EsWUg4M9joTGz8oaR4dWoL26qpRuiV5f6174iqid IR56KO0BymZAJH+ZH+lkLpxpz5ZArGMILNPpHrEd1JE8oubwklC7n5FOFoXUR8CRQTXPx/LKPeZ 6iQCFahmE44eU3l4nHvK6W8YnPOos23c+KW7dC92yjAgeD7CPXXFKBNOVxsPfzd+tJjHs= X-Developer-Key: i=ramiro.oliveira@advantech.com; a=openpgp; fpr=B53649E0D19BC99812A1E862CEEDD0A7BCBDB009 X-ClientProxiedBy: FR4P281CA0175.DEUP281.PROD.OUTLOOK.COM (2603:10a6:d10:b7::12) To PSAPR02MB4502.apcprd02.prod.outlook.com (2603:1096:301:21::6) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PSAPR02MB4502:EE_|JH0PR02MB6564:EE_ X-MS-Office365-Filtering-Correlation-Id: 329d28f7-fc78-4ddc-dd32-08de399d57ad X-MS-Exchange-AtpMessageProperties: SA X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|7416014|366016|52116014|376014|38350700014|921020; X-Microsoft-Antispam-Message-Info: =?utf-8?B?TGp2cW9wWjYrRGtJaDRRaHJxd3lPcU83cXdRVGt3YmduTDVNd04ydnN2bDNm?= =?utf-8?B?UTE5V1EvS1ZhWVRsZzQ0TkI0N3FsOHZBWXJCV0FkL2RuOVFoMms0SFVoSkRX?= =?utf-8?B?cVVtaXphMXN1bXFPSzFob3JHZE1WYXo3OEhqWFpwZXdLbmlQaEVYaUFsSTV5?= =?utf-8?B?aFRQdXcyWTRHS3Q5QmpkWlQwcGFYWWxxUHZzbHRXQ3BrODlMZXlHSGFlenli?= =?utf-8?B?dGhYT0E2bW10UHBiTHJPbkNtSnZWVit1MlhZTWFvUmd4WHo3bjFQWEtGKzQy?= =?utf-8?B?NXJHVzVraUJiZ3NMeGFTcG1IazB5NHRoQXZxVjZRUzBIdmo2NjVHZExxbm5h?= =?utf-8?B?dm9YbDJjRVpLU3YxMmVrdlNHZnR6UWJibHdodXFlYlpqVFY3VmZzSXR3cTd1?= =?utf-8?B?MmVIbDVMY0RZOEpIaFNzNmNZaTR0R3RqTHQyc2lUdHJ0ckw5ZHgzQU8vNitk?= =?utf-8?B?K0xicnlvanV1ZUxqM2g2UzdaL0g1TE9sWU5iejdqS0NnQUFmUkZJR1UzT0xP?= =?utf-8?B?cTd5N2FIOUR0ZGpiNVhTZE00Q0tGWERoVDRsNTlNUVVucjBTYzc2bG1lc0tV?= =?utf-8?B?cG03UjFNelg2YUY1b0hBUUFTcTBHVGlFcHg0dTJGeDFnQWRreXNqQVlIWUVn?= =?utf-8?B?SE1qak1paHVPZFpZU3RNUGp0WEVtMC9MWUVFKzZqTmZKTnB3MkZ6UlVFdTcw?= =?utf-8?B?MVZVTlQ3V3pOOUNRWVVRcUcyd0wwZEs2dXZWSFdsUm1hMWx3aXYwQzh2NVl6?= =?utf-8?B?ZjVIM1F6RGVUQVNicCtuTkdOaUUxdkVyWTI5VmlXRmVINXVUbFRZNHh6bUpZ?= =?utf-8?B?Rm9pVkd0UVNvWkVhU21oblVqSjN3cTJNcWx2amM1WmRtelQ3VUR4TUFuTWY4?= =?utf-8?B?QlFOUkVTV2NJS3hpL1dlY2RrZDVCQm1yZkI1Rk9SdXlTUlVGV0g5c2EwakNM?= =?utf-8?B?ejRqVWVEek9HMDRnRFVycXJ5ZHMyU1QvaE1nTm4zZEVhSVpzbyt2T2k0dlpP?= =?utf-8?B?a29RMDFQV1IrZmJpUGNkRFNPVGtGdG11ZlJ4eUl0a1d6VmdQcEV1eUE1VmI0?= =?utf-8?B?SUtqTkdtS3JEV2RNVzkyRGhDU1A0aGFYSkY4d1YvR084bzNMU0RqK2RWbStI?= =?utf-8?B?YUpYaURiU3ZZdWNWV2U2ZkZHVklHeWVOcGdaM05pOXA4WmhDR3Y1c3FUb1JW?= =?utf-8?B?dC9sVFF3WlhUTXlTMXRldDVCVlZWN1dpT25wQ2orVzB6NUFQMGJMTndlMG9P?= =?utf-8?B?blVMOXBvckFuQUtrU2tqaEhRVmFMRnhNd3lNNTgzR0FSUWRsdWxoalAvRHhL?= =?utf-8?B?d2czS2o1akZpL3dBNlI3ZlJkc1BMZk5mR0NVQkNwM1d3RlduQUMxb0VGdmwv?= =?utf-8?B?ci9EYkdORmpXb1RSZUNGOG1qem0vSE1Id3dNSFhVamJtU3J0UVNQbjdiNEZs?= =?utf-8?B?cmhqM2d2UXkvMzhMN29kais3Z2lyOUNMS1MxTmpBVXRJbTdRTnhabkVMUG45?= =?utf-8?B?MWVWak8zc2tsb3dpK3NiUHNGTStudTl2cFcwTjc5YWNwVnVxYmZsMVowS1lU?= =?utf-8?B?VjlndHpQUXhWSm9Ya09Ma09RdlRGYWI1SXVtTnJyYkFrUFNmcU1Ia0QrWFA3?= =?utf-8?B?UVBDK2JsVEFWeTkrNGI0bnZLdndRZXNIVWN5ODk5VW9GQXUrQ3hsUG1LT1ZT?= =?utf-8?B?NnYvMllPa0xVT1lrQ0txWkJodHplMWc5aWMyVWdwNVdtNGdoMys2Q2hmRGU5?= =?utf-8?B?R3FaS29hU3BFMGdqNlIxVUs3RzdIbkVZUDBqMGxaMW5seTBhMWdRNUdrZ1lx?= =?utf-8?B?dDJGQURyVUV1WmVnVUVQTm9WVTlPWkFZUjFQSGVPSjlGRjg0TGdTQk5ibkRm?= =?utf-8?B?WFZVUVpYL2NzMEk2TkRFMFFvWWZRN0hSWmRmbk5GQWM3ZjRZTVY3MFdIR0xU?= =?utf-8?B?YkVaVFI5TUxmbTNzd1FocEd2a21EVEhrNVIyZXhaTWw0T2JHTnEyLytEb3py?= =?utf-8?B?K2dTMDBXYmYvNG9xZVhUQWYydStvMzhKZXB6TDVJRVFwRG5pUzBtUlJXVmw0?= =?utf-8?B?aHE4T2JtSDNpUGRpeXhGKzdiMXV5dWV6RnljQT09?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PSAPR02MB4502.apcprd02.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(7416014)(366016)(52116014)(376014)(38350700014)(921020);DIR:OUT;SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?SmJJckZXWUFFYkMzaTBlaFJjY3U1aEI5SnVhdmE5aHVqMjNPOVMzU2loNlZj?= =?utf-8?B?WmlReExveVpWQW1xVE1SeGZOajVFTmZrZ2dPM1hBeFdkdEF0ZWdGQkhZa3NZ?= =?utf-8?B?V0orMEdmTUVKV2YrTHk5MGhOa29iamtGQ003WTBmOVJrV2F5dnJIVWx3UWl3?= =?utf-8?B?eGlqVmJVMjM3cFkrVnh4VzlqQy9kNGUvVElzVHkxYlJ2YXVKbitZT0ZrWks5?= =?utf-8?B?alBTUVVWSEpOOWlVZUVwT0R1SXZBZnFWUVAyMDdxMTFoaU1peFNvUFRxanAr?= =?utf-8?B?dVdiWXdnRWt5Q1hmNVVHU3FINFF2NmhBdkxOdTJOQXFYWTYwZUUxSDFmZ2RE?= =?utf-8?B?dS9wM25Lcm9Yb0FDRmFtMW5mOXZJZHE4YkNRcDBTZTRickdLUGxlRFlxYnJa?= =?utf-8?B?TkU1ME9FMDA0RmYwSitITWJzK0hMeDh3VUlrM2NQY2RHeDZOcGZIRS9NQXlr?= =?utf-8?B?b0pMUFRERjRjMkFtWk11QVJEU0l1Y1pxTjlEZXBrRHh1cFBweFlZNExHYkNS?= =?utf-8?B?clJnbmgzTTlPUzhPU0hjREIrc2ZyT2NKS09CM1BjbmFuRnQzcmN3azU0d3Y4?= =?utf-8?B?TlEwV013LzlKSVJISFlXcVVVUjBnMkR6MUl4UTlJSUoyeU9VQlluZExMdUNL?= =?utf-8?B?MllFV2NvMUQrc1E5ZUZ1UWR0SmdrSjh3Q2d6L3A0T0RkbnVUTnVMbFlKZkYr?= =?utf-8?B?Tk5QaFVSd25pZW9EbmIrS0piTnFJR0xraCtaVHZCQndpZVBHdCs0Q1ZlK01a?= =?utf-8?B?bEw2eXZyM0RVbnpkWTM3RFpSS1BTQVhqcTM1VTBGNzZ4VkxFajRVTWEybUVK?= =?utf-8?B?NndFcHhsYnRpeDRtOTV0Ryt4NGlrRm5HeUJ2SUVlVTFSdmxON2xMbndOWm9L?= =?utf-8?B?MHVxM0ZHT3MzT3B0OGtEY2hHcjlYaUxTQk92TEg2cUlkeDZ4UytucVFuRjNy?= =?utf-8?B?YUJuM2MzYzFtUGxUaTg3cDdyNGIydUt1RDdLZEkxSVpnWTBmdW9IRHRWa2lt?= =?utf-8?B?TUFmRXUra0VPNWZPRUdWREVFTStjd0Z3RW01K2FONkdGWlBBZG1PY2prWTAr?= =?utf-8?B?aDJIZmtCeXJFdEV0Q2FHOE5hTzJBdkFvRllnV0RBdFI5NHJ3WldQUko5dXov?= =?utf-8?B?SVY4OWEvcjdGRDduRDIyNVZNMzVaL1RwWmFhelMwK3E1bmpZcHduWjZnRkpa?= =?utf-8?B?UUN3UmNTY1Y4cEZjRWZWZjJXVnAydE9FWkFuWEdHckxnazQvU0lFb3NnbHBK?= =?utf-8?B?Rm1zVmlsUFM5eEJlSDdncWNqdVJ3eG4xeHRVSS9uYnk5cWk5THVHQjk0MFBW?= =?utf-8?B?RFVZVnVwU3BVc1NwODgyQ2ZBSjZEODZIV013Qm9sTTB0aEh0MUpucWpGYzBa?= =?utf-8?B?d2NTcEZVYVpla3hvRm9naVhTZlBRU3JGcE9FN2pYbWZRR2c0L2I3QnJHRUIx?= =?utf-8?B?M2R6MEMvSm12RmZDZXAxcWtWQ3NWdmw1VlI1akdaTmRoZGFTdnVLY0dGVGFY?= =?utf-8?B?Tm5Cd0FhUmJLZW8zWmFVajZhRHhKN3pDYzNFcTI3RDBac011TGwrSGVqSzRJ?= =?utf-8?B?WHVabVhieDlzYkVBZFFpKzQwSnkwbWZ0VjlzOWZBVGs1SEp1VnEzc3VTRDFq?= =?utf-8?B?OGFIYjMvTDFYaUduVWJtQnhac0NISjlqeEgwcDlHaVhnSWtiajY1ZVZGbmE2?= =?utf-8?B?RFlldTJsM0xVcHFQdkV1eTBUN2JCY2VpR3ErNnBpeWJpdlIrNDF1V0FjTkpp?= =?utf-8?B?TlhWMHROZFk1ajRLRzhoTGcrZEVCR1kwdWd1LzNNalh0R0s3SFNZazZUSTVP?= =?utf-8?B?MDJCbndvdWZtcnlOaGpXTGowSlpJcHVxaUtxSW11aWFsbXFISmJseHlMS1Yv?= =?utf-8?B?Tm8wMHplK3BQOEFGbS9Za2Z4MDF0Yk9NMiszQnFOSVAwS1ZJSVV3QUROVHVF?= =?utf-8?B?M2szQjZRU2YxblpFSklBc0NyV2V2OTJUTFBUc2FiamhiVjdrTzc2SnpCZlB3?= =?utf-8?B?WStHNnRSUzF4Zm9USVZFWTVnY1BLbnlGeWwzMTUyY3B1R21tenNjUWtYWlVJ?= =?utf-8?B?Y3p0QmgyN0x4RzN4Y3doTnhDNVlIeEo2RTdqd3FkUVNNSVd2dnp6N1phK29U?= =?utf-8?B?d0d2RGN4ZXFrK21jMzVXNkxCWTdnc2piMDE4YU5HNjE0UFhlTEVSUnF1ZThW?= =?utf-8?Q?lKGxVav7SNM4Mbi9Vh8JcvI=3D?= X-OriginatorOrg: advantech.com X-MS-Exchange-CrossTenant-Network-Message-Id: 329d28f7-fc78-4ddc-dd32-08de399d57ad X-MS-Exchange-CrossTenant-AuthSource: PSAPR02MB4502.apcprd02.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 Dec 2025 16:41:47.8085 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: a77d40d9-dcba-4dda-b571-5f18e6da853f X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: O+Ds3j5tNjwZPsAkAxl8xEkUxRAUpO0g/DQ445k0CAZlcVWQMV8HG+nUOZm1e1CIAbAlkFYYQ3Bv/Ed3GiY2UebnL14hqcWivRtUY1G2+gY= X-MS-Exchange-Transport-CrossTenantHeadersStamped: JH0PR02MB6564 This driver controls the Video Backlight block of the Advantech EIO chip. Signed-off-by: Ramiro Oliveira --- MAINTAINERS | 1 + drivers/video/backlight/Kconfig | 6 + drivers/video/backlight/Makefile | 1 + drivers/video/backlight/eio_bl.c | 268 +++++++++++++++++++++++++++++++++++= ++++ 4 files changed, 276 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index be9d3c4e1ce1..df4b4cc31257 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -623,6 +623,7 @@ F: drivers/gpio/gpio-eio.c F: drivers/hwmon/eio-hwmon.c F: drivers/i2c/busses/i2c-eio.c F: drivers/mfd/eio_core.c +F: drivers/video/backlight/eio_bl.c F: include/linux/mfd/eio.h =20 ADXL313 THREE-AXIS DIGITAL ACCELEROMETER DRIVER diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kcon= fig index a1422ddd1c22..ddd3d6922553 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -496,6 +496,12 @@ config BACKLIGHT_RAVE_SP help Support for backlight control on RAVE SP device. =20 +config BACKLIGHT_EIO + tristate "Advantech EIO Backlight" + depends on MFD_EIO && BACKLIGHT_CLASS_DEVICE + help + Backlight driver for Advantech EIO. + config BACKLIGHT_LED tristate "Generic LED based Backlight Driver" depends on LEDS_CLASS && OF diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Mak= efile index a5d62b018102..4601b644b6d4 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_BACKLIGHT_BD6107) +=3D bd6107.o obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) +=3D backlight.o obj-$(CONFIG_BACKLIGHT_DA903X) +=3D da903x_bl.o obj-$(CONFIG_BACKLIGHT_DA9052) +=3D da9052_bl.o +obj-$(CONFIG_BACKLIGHT_EIO) +=3D eio_bl.o obj-$(CONFIG_BACKLIGHT_EP93XX) +=3D ep93xx_bl.o obj-$(CONFIG_BACKLIGHT_GPIO) +=3D gpio_backlight.o obj-$(CONFIG_BACKLIGHT_HP680) +=3D hp680_bl.o diff --git a/drivers/video/backlight/eio_bl.c b/drivers/video/backlight/eio= _bl.c new file mode 100644 index 000000000000..2b9fd4d48d30 --- /dev/null +++ b/drivers/video/backlight/eio_bl.c @@ -0,0 +1,268 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Backlight driver for Advantech EIO Embedded controller. + * + * Copyright (C) 2025 Advantech Corporation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include + +#define PMC_BL_WRITE 0x20 +#define PMC_BL_READ 0x21 + +#define BL_CTRL_STATUS 0x00 +#define BL_CTRL_ENABLE 0x12 +#define BL_CTRL_ENABLE_INVERT 0x13 +#define BL_CTRL_DUTY 0x14 +#define BL_CTRL_INVERT 0x15 +#define BL_CTRL_FREQ 0x16 + +#define BL_MAX 2 + +#define BL_STATUS_AVAIL 0x01 +#define BL_ENABLE_OFF 0x00 +#define BL_ENABLE_ON 0x01 +#define BL_ENABLE_AUTO BIT(1) + +#define USE_DEFAULT -1 +#define THERMAL_MAX 100 + +#define BL_AVAIL BIT(0) +#define BL_PWM_DC BIT(1) +#define BL_PWM_SRC BIT(2) +#define BL_BRI_INVERT BIT(3) +#define BL_ENABLE_PIN_SUPP BIT(4) +#define BL_POWER_INVERT BIT(5) +#define BL_ENABLE_PIN_EN BIT(6) +#define BL_FIRMWARE_ERROR BIT(7) + +static uint bri_freq =3D USE_DEFAULT; +module_param(bri_freq, uint, 0444); +MODULE_PARM_DESC(bri_freq, "Setup backlight PWM frequency.\n"); + +static int bri_invert =3D USE_DEFAULT; +module_param(bri_invert, int, 0444); +MODULE_PARM_DESC(bri_invert, "Setup backlight PWM polarity.\n"); + +static int bl_power_invert =3D USE_DEFAULT; +module_param(bl_power_invert, int, 0444); +MODULE_PARM_DESC(bl_power_invert, "Setup backlight enable pin polarity.\n"= ); + +static int timeout; +module_param(timeout, int, 0444); +MODULE_PARM_DESC(timeout, "Set PMC command timeout value.\n"); + +struct eio_bl_dev { + struct device *mfd; + u8 id; + u8 max; +}; + +static int pmc_write(struct device *mfd, u8 ctrl, u8 dev_id, void *data) +{ + struct pmc_op op =3D { + .cmd =3D PMC_BL_WRITE, + .control =3D ctrl, + .device_id =3D dev_id, + .payload =3D (u8 *)data, + .size =3D (ctrl =3D=3D BL_CTRL_FREQ) ? 4 : 1, + .timeout =3D timeout, + }; + + return eio_core_pmc_operation(mfd, &op); +} + +static int pmc_read(struct device *mfd, u8 ctrl, u8 dev_id, void *data) +{ + struct pmc_op op =3D { + .cmd =3D PMC_BL_READ, + .control =3D ctrl, + .device_id =3D dev_id, + .payload =3D (u8 *)data, + .size =3D (ctrl =3D=3D BL_CTRL_FREQ) ? 4 : 1, + .timeout =3D timeout, + }; + + return eio_core_pmc_operation(mfd, &op); +} + +static int bl_update_status(struct backlight_device *bl) +{ + struct eio_bl_dev *eio_bl =3D bl_get_data(bl); + u32 max =3D bl->props.max_brightness; + u8 duty =3D clamp_val(bl->props.brightness, 0, max); + u8 sw =3D bl->props.power =3D=3D BACKLIGHT_POWER_OFF; + int ret; + + /* Setup PWM duty */ + ret =3D pmc_write(eio_bl->mfd, BL_CTRL_DUTY, eio_bl->id, &duty); + if (ret) + return ret; + + /* Setup backlight enable pin */ + return pmc_write(eio_bl->mfd, BL_CTRL_ENABLE, eio_bl->id, &sw); +} + +static int bl_get_brightness(struct backlight_device *bl) +{ + struct eio_bl_dev *eio_bl =3D bl_get_data(bl); + u8 duty =3D 0; + int ret; + + ret =3D pmc_read(eio_bl->mfd, BL_CTRL_DUTY, eio_bl->id, &duty); + + if (ret) + return ret; + + return duty; +} + +static const struct backlight_ops bl_ops =3D { + .get_brightness =3D bl_get_brightness, + .update_status =3D bl_update_status, + .options =3D BL_CORE_SUSPENDRESUME, +}; + +static int bl_init(struct device *dev, int id, + struct backlight_properties *props) +{ + int ret; + u8 enabled =3D 0; + u8 status =3D 0; + + /* Check EC-supported backlight */ + ret =3D pmc_read(dev, BL_CTRL_STATUS, id, &status); + if (ret) + return ret; + + if (!(status & BL_STATUS_AVAIL)) { + dev_dbg(dev, "eio_bl%d hardware report disabled.\n", id); + return -ENXIO; + } + + ret =3D pmc_read(dev, BL_CTRL_DUTY, id, &props->brightness); + if (ret) + return ret; + + /* Invert PWM */ + dev_dbg(dev, "bri_invert=3D%d\n", bri_invert); + if (bri_invert > USE_DEFAULT) { + ret =3D pmc_write(dev, BL_CTRL_INVERT, id, &bri_invert); + if (ret) + return ret; + } + + bri_invert =3D 0; + ret =3D pmc_read(dev, BL_CTRL_INVERT, id, &bri_invert); + if (ret) + return ret; + + dev_dbg(dev, "bri_freq=3D%u\n", bri_freq); + if (bri_freq !=3D USE_DEFAULT) { + ret =3D pmc_write(dev, BL_CTRL_FREQ, id, &bri_freq); + if (ret) + return ret; + } + + ret =3D pmc_read(dev, BL_CTRL_FREQ, id, &bri_freq); + if (ret) + return ret; + + dev_dbg(dev, "bl_power_invert=3D%d\n", bl_power_invert); + if (bl_power_invert >=3D USE_DEFAULT) { + ret =3D pmc_write(dev, BL_CTRL_ENABLE_INVERT, id, &bl_power_invert); + if (ret) + return ret; + } + + bl_power_invert =3D 0; + ret =3D pmc_read(dev, BL_CTRL_ENABLE_INVERT, id, &bl_power_invert); + if (ret) + return ret; + + /* Read power state */ + ret =3D pmc_read(dev, BL_CTRL_ENABLE, id, &enabled); + if (ret) + return ret; + + props->power =3D enabled ? BACKLIGHT_POWER_OFF : BACKLIGHT_POWER_ON; + + return 0; +} + +static int bl_probe(struct platform_device *pdev) +{ + u8 id; + struct device *dev =3D &pdev->dev; + struct eio_dev *eio_dev =3D dev_get_drvdata(dev->parent); + + if (!eio_dev) { + dev_err(dev, "eio_core not present\n"); + return -ENODEV; + } + + for (id =3D 0; id < BL_MAX; id++) { + char name[32]; + struct backlight_properties props; + struct eio_bl_dev *eio_bl; + struct backlight_device *bl; + int ret; + + memset(&props, 0, sizeof(props)); + props.type =3D BACKLIGHT_RAW; + props.max_brightness =3D THERMAL_MAX; + props.power =3D BACKLIGHT_POWER_OFF; + props.brightness =3D props.max_brightness; + + eio_bl =3D devm_kzalloc(dev, sizeof(*eio_bl), GFP_KERNEL); + if (!eio_bl) + return -ENOMEM; + + eio_bl->mfd =3D dev->parent; + eio_bl->id =3D id; + eio_bl->max =3D props.max_brightness; + + ret =3D bl_init(eio_bl->mfd, id, &props); + if (ret) { + dev_info(dev, "%d No Backlight %u enabled!\n", ret, id); + continue; + } + + snprintf(name, sizeof(name), "%s%u", pdev->name, id); + + bl =3D devm_backlight_device_register(dev, name, dev, eio_bl, + &bl_ops, &props); + + if (IS_ERR(bl)) { + ret =3D PTR_ERR(bl); + if (ret =3D=3D -EPROBE_DEFER) + return ret; + + dev_err(dev, "register %s failed: %d\n", name, ret); + continue; + } + + dev_info(dev, "%s registered (max=3D%u)\n", name, props.max_brightness); + } + + return 0; +} + +static struct platform_driver bl_driver =3D { + .probe =3D bl_probe, + .driver =3D { + .name =3D "eio_bl", + }, +}; + +module_platform_driver(bl_driver); + +MODULE_AUTHOR("Wenkai Chung "); +MODULE_AUTHOR("Ramiro Oliveira "); +MODULE_DESCRIPTION("Backlight driver for Advantech EIO embedded controller= "); +MODULE_LICENSE("GPL"); --=20 2.43.0 From nobody Sun Dec 14 06:34:44 2025 Received: from SEYPR02CU001.outbound.protection.outlook.com (mail-koreacentralazon11023098.outbound.protection.outlook.com [40.107.44.98]) (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 40FEE2D3724; Fri, 12 Dec 2025 16:41:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.44.98 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765557723; cv=fail; b=XNnTudz9eDjdGujTj/JvczEMn4YTS8D4pXbBOtsJee3Gh4BPJYZqpgI9hW4SD85hbJTXuzVSXfLnV1f56Zat+D0nS3Ia28/i1OUPYPOfL/tKbN5N4VG5oA7612RYGZ9FM1YA8qm3mTJhnVucSHRdMou6lACrlVzB1AUtGr5WnJs= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765557723; c=relaxed/simple; bh=s2UIJ7vhMpF4qfu3LEzaEKI+74S2F2KpdlxCIAWFEHY=; h=From:Date:Subject:Content-Type:Message-Id:References:In-Reply-To: To:Cc:MIME-Version; b=lvo4b6N2B5E/gzZMLe1IfA41XsZw8D5s8gfCeVy9WyLNZ1aOt4lOuHnIxlaxLSHY3BEWsEIVoomV4e4DYCWNUWs9q4uEIy7Qyg4MgS4Izg8paISpqVWd0OnDXCICiwlr5Ack4qczvPtl8Bj/knSPQeQIvGPU9yGYAL0/Ojnbojo= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=advantech.com; spf=pass smtp.mailfrom=advantech.de; dkim=pass (2048-bit key) header.d=advantech.com header.i=@advantech.com header.b=RCnRKQv1; arc=fail smtp.client-ip=40.107.44.98 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=advantech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=advantech.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=advantech.com header.i=@advantech.com header.b="RCnRKQv1" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=acjDnKgpRw8Ausi0VzTjC43yyD030IAXRC7cbJAzbSvuHQM8ebVxmRdt5ayl5IipiScojArpFZpb2R3KVGM5HiH9Yi3LTdXcrnoOJCpaZGGTETD8pNWStKwTe/OFPJbbaJQv8ItQTjPfzCdAARqylgnZhVy32c6f5lGhT2RGB/M0zdoPxIjz3G0dVxsUN4DbM6PBclhitVT71Ib0pCYqt9Kh1R0mbr4qfZAWODj/rR81PVr80+OdCK9dq/SJmv9DeVsIssOMREYq2R/WuerRd+f8f7pdrhyi/xpf/gKfqPJuXQHKZUUX1qmURDgWcYEZ73ULcdpgkZpdHl91rQX4eA== 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=oSuHPe15ou/gs6r7qUgitmcEPQDcyG3wtCT7sFq9SgI=; b=uw4AC86Y1mPEX4/h2nYhDWnBk6nL3ZAascMRflLkBnGTvLyElCXT0ABRs9ImxtbaiUkdv1VdcqvyNf3xThqho4B2A3w0+B/2FrjpEle2eyapbyPdrLLLYHV0iRw6tIdd1xqj5evFohmiQo6zHaM1FxxEfZkk9OpfYV0qhPmx8hBJj3UBUKR6vx/7Y0zl+d0pRSUNxe4dFXE6UUmq1LEvB2PKAHfew+cJZ2NIZ01g0FGZmgQ+iG16hdfQneMDrG/GL0YoMRlMJ1+3GnktxXHDulYh98xDt9e8tfp+IoJeigAl+2T/tJnjho/YCoCJATopr/RievOvCWeskUSJzBKxMQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=advantech.de; dmarc=pass action=none header.from=advantech.com; dkim=pass header.d=advantech.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=advantech.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=oSuHPe15ou/gs6r7qUgitmcEPQDcyG3wtCT7sFq9SgI=; b=RCnRKQv1LnoCw8+Pelld6OvQa3/Z6Yfq28KfL7OhXd9qFkKna/woUrbUCCiaRgpg8JgF4zp9yZv1WOOqENrvvYzujLvexqKTY6MpmZGAbKt6T8HzOm2FF7fRRWLvt8kG9/0h0FUAChhCNVL1M0iO74cZXkGMIi63i6UpV+ClSIelQv2Qh+jKNM0yCJoRvXuk/yMqq1FvuLZfKchicsZanpBInvCkPC+MOe5ZFc7RpVm77jUWk7dNOQfZMP7YjOzq2dIn6wjQka5ox/up6zSh6reW15zgQ3Gcg6/LlmCxdKN3S3CTGjQ745B06sMwVEvwdqkm6raI9WFYMnz0bmynQA== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=advantech.com; Received: from PSAPR02MB4502.apcprd02.prod.outlook.com (2603:1096:301:21::6) by JH0PR02MB6564.apcprd02.prod.outlook.com (2603:1096:990:3f::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9412.11; Fri, 12 Dec 2025 16:41:54 +0000 Received: from PSAPR02MB4502.apcprd02.prod.outlook.com ([fe80::59c9:fe0:25f6:702b]) by PSAPR02MB4502.apcprd02.prod.outlook.com ([fe80::59c9:fe0:25f6:702b%6]) with mapi id 15.20.9412.005; Fri, 12 Dec 2025 16:41:54 +0000 From: Ramiro Oliveira Date: Fri, 12 Dec 2025 17:40:57 +0100 Subject: [PATCH 6/8] Add Advantech EIO Watchdog driver Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251212-upstream-v1-v1-6-d50d40ec8d8a@advantech.com> References: <20251212-upstream-v1-v1-0-d50d40ec8d8a@advantech.com> In-Reply-To: <20251212-upstream-v1-v1-0-d50d40ec8d8a@advantech.com> To: Lee Jones , Linus Walleij , Bartosz Golaszewski , Guenter Roeck , Andi Shyti , Daniel Thompson , Jingoo Han , Helge Deller , Wim Van Sebroeck , "Rafael J. Wysocki" , Daniel Lezcano , Zhang Rui , Lukasz Luba Cc: linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, linux-hwmon@vger.kernel.org, linux-i2c@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-fbdev@vger.kernel.org, linux-watchdog@vger.kernel.org, linux-pm@vger.kernel.org, Wenkai Chung , Francisco Aragon-Trivino , Hongzhi Wang , Mikhail Tsukerman , Thomas Kastner , Ramiro Oliveira X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=18326; i=ramiro.oliveira@advantech.com; h=from:subject:message-id; bh=s2UIJ7vhMpF4qfu3LEzaEKI+74S2F2KpdlxCIAWFEHY=; b=owEB7QES/pANAwAKAc7t0Ke8vbAJAcsmYgBpPEWhT0eioszro0oA7czmwIA6zwc29gQFQgvFc RdmEqM8LXWJAbMEAAEKAB0WIQS1Nkng0ZvJmBKh6GLO7dCnvL2wCQUCaTxFoQAKCRDO7dCnvL2w CQ2FDACfzLQTM4TPgJV1m7iYMkzFu/bG//SX4fxikx4BFJhDhDzgeg3C3uaKXoYcQe+ZrkrbhPA lNOoMg9I2efte+6l+iRMWfBRif1R1rN7g6ywdnk9ssDX0mZKM0P2UGlQTG4HAkT3WNillosikxs 9C/1tYnkGcQO8qvqKyqzS8q0SPimK76fJhy06ioJ927yZ+vEA2FR02B+dnItNnV/C/psTekA1k7 L3RVlelK7RcPKEBAm+/1MHdYyPvHxJUBMfeWGrjTdnPWROu8pnUFKRPAhlw1DjNcING5ydTv41D UkT0nQZSAglVPmbnmhYi3cXPB6E3X+51U8l9CEYEAGRun1NdWL8ELRZGO3NzDKpvqhUoiysUI0b xchkFgl9SK6UVARCInyEyVXXxDh5P5wz8mj9GOqw1gI90mFaqclxQ7zKk6DIghyBbPxPPFjDm+n KPvq18J1gB/b6vad0xBk6cAX2h2VtN+w2YowBTbqSL/5s7eyx9uDeUtY3UbvGlVZYomgQ= X-Developer-Key: i=ramiro.oliveira@advantech.com; a=openpgp; fpr=B53649E0D19BC99812A1E862CEEDD0A7BCBDB009 X-ClientProxiedBy: FR4P281CA0175.DEUP281.PROD.OUTLOOK.COM (2603:10a6:d10:b7::12) To PSAPR02MB4502.apcprd02.prod.outlook.com (2603:1096:301:21::6) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PSAPR02MB4502:EE_|JH0PR02MB6564:EE_ X-MS-Office365-Filtering-Correlation-Id: 0b44a3c2-96da-4645-ac7a-08de399d5bc9 X-MS-Exchange-AtpMessageProperties: SA X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|7416014|366016|52116014|376014|38350700014|921020; X-Microsoft-Antispam-Message-Info: =?utf-8?B?M0VoZUFEdkY3NzVPbitmaklsR1dPL0FkVEhmTWYzTlR5OGk4eGFzOWEvbklY?= =?utf-8?B?Y3NSUHd6K0N0bTdUcE1sMytwRUxHVWFUcklRTHduWWxKeFJZRitjYUg2Y2NI?= =?utf-8?B?bkdnQ3VRNEZ0eVhpSGwxcmlhMjM4eklXZVRtbXJmYkxEdTg2UXZwZW1XTHlt?= =?utf-8?B?WXcrbjRrTFdjRmQ0elJvMkdyZkErTnMvUUdoQ0hEM3U2b2J2YXRtVGhrdnNO?= =?utf-8?B?TVJxK0tMb294NE1rVmE5TzNrR0FrVXRaTG9hSVplVjJRMHR6Y0JrRWNWTzRo?= =?utf-8?B?L0ZPbDZ3L1dBWW8zL0JtL0ZhMHNHcC96aTNKcG9SN09RMDVmSnlDYVBoN2Ni?= =?utf-8?B?RS9hVWFObEFBV2FvNVI4U1N2cmIwQU8rcTVnYkF1SmlpQzZrQWFTQ3kzeldn?= =?utf-8?B?K1ZBZjR0OG1BUGpFQVhXMytQTm1HTnhJVnFPT21RSFd6clhUOG1aNTlxaVF3?= =?utf-8?B?alQ2N2ZEUlJ0c0czbVFPQXRzUmZQN2VDM0RoTjh2aXgzT1ZwR3A0K0ZuY3E0?= =?utf-8?B?cEowNzJzNUtrNjRKNkthNWxwS2piVGVTMWd3cTdMMGJHcGFONXRCUVRRY1Jh?= =?utf-8?B?RGlSNVp0bWFmczF3NkRXUmVTU3RuOFArSU9CWDhmTjhvMFlzcGFFVWE1ZERz?= =?utf-8?B?SWk4QWdLNUZOZVlzMStzdEsxelNJbURsNWNCMHhXU2Zmc0c1RWZTVzlVazMw?= =?utf-8?B?UzJOcmZDVnJiRjBVaVBlVFVFa01PL2NXN3NpTmtzeUVtZFRJZFQ5TnFrQTIr?= =?utf-8?B?UFBUMlVjWENqSUU4VWU3YmczNEROWVBnQ2RmQmlqbkRmU05zWVkwcy9pWlla?= =?utf-8?B?TEZIWnFIZ3B3SUFFbTNwcjI2WmpORDNTbVBxbnVDYkQ5UTE4OFVsM3cvS085?= =?utf-8?B?RTZTbldPRUJiVURJcEFvMWJHTWkwaWQxbDV3T2Q4SS9KUFNpYXZNNCt1Q0VD?= =?utf-8?B?VUVEdjVjSmhaS1pENTBRUEVYdGxVWU01WDg2U1NvbmFod1JsbWhUbXJMM2li?= =?utf-8?B?NDgwQzZmUytSazh5QnEvYzhoTkVsU1l0VDZqTThTcmdxR0hNeXpEbHNhKzJL?= =?utf-8?B?V05xT0dWMFJYdEIxayt2aGJYV0NBZGoya3hIU1kwSFI3UlE0Y2JnRDBXM2Nx?= =?utf-8?B?SG1WWk1NblJrRFZseUFXajRreFV1M2gwMnR0dGc5Yk96WVcraWxvTUJIc2tX?= =?utf-8?B?UkhCbnI3aWMzTklRaHptUE0rdVNnWnNmVHd5TTBTTXE0aVZGc0RMMXlYZTUy?= =?utf-8?B?ZVRwbm1VUFFWSHMxb01aQ3hmaTBla1BoM054L0p3Mm1obWpsNzFkTU9OVlY3?= =?utf-8?B?K2VoNm5wUkl5enZUWTlueFBwSlNKRVJzWStrZjJjZjFQY3UyUHdoc3lXR0VY?= =?utf-8?B?UzlUSHcxb0tMUEtKT1VkUEFTbzQrdVhNY0NvUnN2VFFXekZvOWNzQ1pCWnhj?= =?utf-8?B?cDRKM1lFelRzMnVaMlEyVHBzeW0vZ1JQRUNITGVrRGpHbzRmY2NoRjNkY2Y2?= =?utf-8?B?ajlsdjQ3eVh5MjRYKzg3eU05TzF5SjJUdGI4TkVoVTUrV1Z6UlN2Q0ZrVThE?= =?utf-8?B?bzNUQmRrMjZoRzdCWEJnYnh0SWVzdlUrVmpteFRHcVpRTVhiY0RYQkFkRmFa?= =?utf-8?B?R3M4ZExXQ3FZSU1pK3lMMzRwYWo1MndsL04zUzlXdDR2MElnR1RNYXZOSlNN?= =?utf-8?B?K21UOTc4OHpxUzcyK2g1dVFoOUNkdVhjVEFMbWpENE50QkhuYTVKa21SeWEw?= =?utf-8?B?T1BJN3dGbXZXcVhKZ3JzWFEzd1ZhNVJRNG1nUGhlMDFUN1UzZTdsSWZQREFv?= =?utf-8?B?L05ka3FBeDlOblhKUXNFakZFeElWM2RYWXIvZjlwZ0RqY1pMaFpENU96ZTRE?= =?utf-8?B?Z1EwVGdFbUNUTXR6S3ZYc2pLRE1uRG9yRmlUcWE3ejZGL3ZzZ2RtN2QwTTJQ?= =?utf-8?B?WE9TbjlvVEN1dkRNaGZrMGkwVUU1bDNuYXdtYXJTeEZ6WmJlaWhWeDRHNldM?= =?utf-8?B?OElxTU85bmhkRGhNS0V3SmdRcEx1Z21BRFllMnVpWWN5am9BMmlaMTJHam9j?= =?utf-8?B?RlBJRk1ENEVDWTNVUmNnUlJQYm5aU0FlYndVQT09?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PSAPR02MB4502.apcprd02.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(7416014)(366016)(52116014)(376014)(38350700014)(921020);DIR:OUT;SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?bFRmSXBKWmVNTTZBZzM2RVlNcHdsa0I3Z1BVdzlERHN5dVdkdGswY0UrblY4?= =?utf-8?B?QjBKMnpid3h0SnZJYzVpMkxmL2ViWWhxcEhwMm9PYWhuQWYzWXNidyttSFFN?= =?utf-8?B?bG5TZzNOeC9zN2RPZ2xBbWNvbmNVMzBCY0NicS9QVm42NmtTTG92a0drR2k0?= =?utf-8?B?bEJQUXY0QkhSaU1LeUU0b1Bld3hRbGdCVWlCbDUzd0VTY2QzK0Z3MVJXUmQx?= =?utf-8?B?S2NXTDMyeW44Tkx4bTUvUXdWalBtQXFKckJKZXpiWk5Id0pWckNyWTVHUWpI?= =?utf-8?B?STFLVWxLclFlUnFKUXVNZ3ExYmN0U2RqbGZYV2o1cVFJRVcvcWsrdU93dGRC?= =?utf-8?B?ZEpybXNPS0Y5aGNrbE9BRVNIUHpuRTVQV2ltdWprUmhvWVRFZHBYQXpCMith?= =?utf-8?B?VzdEQzRZeTI2SGJta2FrTmt4dUFzVG5TRmdJbHNxNEIvV2dwRlg4T0JmeU1z?= =?utf-8?B?NkhhOVZWSEMwZXlmbDVubTJIWkdXRlJVaEN3SmtwRGJFRitHUE9zcGprNjJ3?= =?utf-8?B?UVlyb01SZVhGb2lvYXFMbXBVU09WeHVNYzR6YVFZUmVJT2JlRW9kaksxRXYz?= =?utf-8?B?SzM5dFdseDJEeEhsOE9QYXhvVWZNVU1ZS3YxUVRrNEFZUHd2TU5wSVBYOEg3?= =?utf-8?B?aXpYeWpxRE5Na0hBT2k4OG81WFMxU2hLSnBYSXNNdXZDRUdFanBKREhUd0NH?= =?utf-8?B?dHlNVEhuc1NLVUQ0eE80dFltT1MzZDhSTmU1MTFjb3pXVWJzSGZ6bXZoZVFF?= =?utf-8?B?WlNiL1FvWWp0cGVtSi9KeXkxTFk5ZlNRQlhRSWpOcTRXRWVPMnBpWGR0NHhZ?= =?utf-8?B?MXZHRG5MenBmekcvNlRsRDNKZGNHcHhJL21SZUVaMEs3V3NDUlNBZzgwbTJ5?= =?utf-8?B?OTd0eURHNkdoamNyU2ltRzZpcTE3THlDT0hQNFJDWFBSWXRNSHdtamczR3lx?= =?utf-8?B?ZW4vOGptVTJ2NVpSV2RSTzNpM2NUV2pwbE9sRDc3UmMxbmRleW9CdUVBYTU1?= =?utf-8?B?UjlpaURNS3RzV1R3eHJzOE9xNklCNFQxQnBvOWFYbklaSXZkRFc5VUNzVzFW?= =?utf-8?B?em5DZTVoOExGc2VnSjlhK1dGTDIxWXdrL0JFNFQ2K3ZreDJjUmVhTWZnZnky?= =?utf-8?B?bVgrSmdVbG5HZFdDWUJrc21RRnpZU2VSampKUnF2eU5JNzU3OEhnblBNdzRF?= =?utf-8?B?Zm95SFpDdm83QkRTcHpZZlY0V1JEVkdWTVFrZXlaUkZBVmIzK1d4Tm9qTVFE?= =?utf-8?B?dWh6dUh6aHB5NmUrVndxTldhMjhTalJXRm43SG5PNDVEQ1p2WnBvUnFwQWF1?= =?utf-8?B?RVk1cWFjdVFHZWtCOXQ5L0FqRzgwcHBzTVdmWGtJZTZITnR0eDl5eW5wWmRk?= =?utf-8?B?S2pWbENwcHcvZGZIakRSYW9kamN5UzVNYnlaT0JLeitPT0poWlk1OWJBUnFq?= =?utf-8?B?bzRQYWFUeTRFUkhLS2g1b0NJSWZrOXJIMGJneFA4WVRXTlNSdXVJKy9Hb2t4?= =?utf-8?B?WVVDcW8yZWpDNGpwSDBhdmh2N1ovQmQ1VTNYaWczTGYyeElMeHNYMllHQmR0?= =?utf-8?B?NXpOUmlHRUJuU0wvSXlKazZtK0lIMWFGOEN4Mzh3SnZZVHFFWk5JSlpMakV5?= =?utf-8?B?Rk1sTjd0b1o2OEpSTnQ3RFhyQVgwaC8vdEpCNzVyQkIzN0tIWHBubUVBLyt4?= =?utf-8?B?RjZjS3pya0J6VWNuK04wSjJPTy8xYmExYWhNZmRlT0FLSG1vSjFabUp5ckV1?= =?utf-8?B?OUIwZHhwTG5qbGNTZCtVQWlJdUN0dGtidWp0L08wVHJFZ1VZMEczcFo0Tmlk?= =?utf-8?B?RXNYdHk3aTJRTHpOM0ZkN2F5eTd1OWRsVjhMSDdDYlBIZ3hyL2VwYjUxdEs1?= =?utf-8?B?Nlp1Vm1peFJ6RDNxd3ZuOXNpOGZONG56d1gzY0ozYzN2RG1pR3FFYXY1M3d4?= =?utf-8?B?OWdkRmZyVW5ha3pLUm1ZbzdTN2gvSDlkZG5EdUJoQ2k2M0dLYlg2NDNrVHFY?= =?utf-8?B?dHhhS25EVlB6M21FVTRPa3c0N20wNnFXeWNOUWpZcjQ3Y0dyZkdDbDZnVlR4?= =?utf-8?B?RU9hVWNGV2hLY0dvTlF2Y1RuRmVMRXFDdVMySlBmNWZXb0dRZGIvNmFXeTB1?= =?utf-8?B?TjFPcnpvNS9LT2k2QXpucUZFZmZ2aGVEY2lIelozZzlBWk1lRndtM0FqdGk4?= =?utf-8?Q?wV+XWix3XGCp4t6lJ9k5+xQ=3D?= X-OriginatorOrg: advantech.com X-MS-Exchange-CrossTenant-Network-Message-Id: 0b44a3c2-96da-4645-ac7a-08de399d5bc9 X-MS-Exchange-CrossTenant-AuthSource: PSAPR02MB4502.apcprd02.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 Dec 2025 16:41:54.6803 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: a77d40d9-dcba-4dda-b571-5f18e6da853f X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: qJvMuV71o9HJmdeS6eJlHwCvIOezaq7BcjM8JXnVltl2DlPHBIVUBKnl+AsJK/3lfA8oV/HInW/RvSvrpq+l/JabmDlmnmMipQmmNiJpjl0= X-MS-Exchange-Transport-CrossTenantHeadersStamped: JH0PR02MB6564 This commit adds the driver to control the Advantech EIO Watchdog block, this block is included in the Advantech EIO Embedded Controller. Signed-off-by: Ramiro Oliveira --- MAINTAINERS | 1 + drivers/watchdog/Kconfig | 7 + drivers/watchdog/Makefile | 1 + drivers/watchdog/eio_wdt.c | 672 +++++++++++++++++++++++++++++++++++++++++= ++++ 4 files changed, 681 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index df4b4cc31257..dfdf4f39c14b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -624,6 +624,7 @@ F: drivers/hwmon/eio-hwmon.c F: drivers/i2c/busses/i2c-eio.c F: drivers/mfd/eio_core.c F: drivers/video/backlight/eio_bl.c +F: drivers/watchdog/eio_wdt.c F: include/linux/mfd/eio.h =20 ADXL313 THREE-AXIS DIGITAL ACCELEROMETER DRIVER diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index d3b9df7d466b..2f8508e51634 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -248,6 +248,13 @@ config DA9062_WATCHDOG =20 This driver can be built as a module. The module name is da9062_wdt. =20 +config EIO_WATCHDOG + tristate "Advantech EIO Watchdog" + depends on MFD_EIO + help + Watchdog timer driver for the Advantech EIO. + If unsure, say N. + config GPIO_WATCHDOG tristate "Watchdog device controlled through GPIO-line" depends on OF_GPIO diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index ba52099b1253..59b5ec0246d6 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -230,6 +230,7 @@ obj-$(CONFIG_DA9052_WATCHDOG) +=3D da9052_wdt.o obj-$(CONFIG_DA9055_WATCHDOG) +=3D da9055_wdt.o obj-$(CONFIG_DA9062_WATCHDOG) +=3D da9062_wdt.o obj-$(CONFIG_DA9063_WATCHDOG) +=3D da9063_wdt.o +obj-$(CONFIG_EIO_WATCHDOG) +=3D eio_wdt.o obj-$(CONFIG_GPIO_WATCHDOG) +=3D gpio_wdt.o obj-$(CONFIG_WDAT_WDT) +=3D wdat_wdt.o obj-$(CONFIG_WM831X_WATCHDOG) +=3D wm831x_wdt.o diff --git a/drivers/watchdog/eio_wdt.c b/drivers/watchdog/eio_wdt.c new file mode 100644 index 000000000000..a81f005d82d2 --- /dev/null +++ b/drivers/watchdog/eio_wdt.c @@ -0,0 +1,672 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Advantech EIO Watchdog Driver + * + * Copyright (C) 2025 Advantech Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define WATCHDOG_TIMEOUT 60 +#define WATCHDOG_PRETIMEOUT 10 + +/* Support Flags */ +#define SUPPORT_AVAILABLE BIT(0) +#define SUPPORT_PWRBTN BIT(3) +#define SUPPORT_IRQ BIT(4) +#define SUPPORT_SCI BIT(5) +#define SUPPORT_PIN BIT(6) +#define SUPPORT_RESET BIT(7) + +/* PMC registers */ +#define REG_STATUS 0x00 +#define REG_CONTROL 0x02 +#define REG_EVENT 0x10 +#define REG_PWR_EVENT_TIME 0x12 +#define REG_IRQ_EVENT_TIME 0x13 +#define REG_RESET_EVENT_TIME 0x14 +#define REG_PIN_EVENT_TIME 0x15 +#define REG_SCI_EVENT_TIME 0x16 +#define REG_IRQ_NUMBER 0x17 + +/* PMC command and control */ +#define CMD_WDT_WRITE 0x2A +#define CMD_WDT_READ 0x2B +#define CTRL_STOP 0x00 +#define CTRL_START 0x01 +#define CTRL_TRIGGER 0x02 + +/* I/O register and its flags */ +#define IOREG_UNLOCK 0x87 +#define IOREG_LOCK 0xAA +#define IOREG_LDN 0x07 +#define IOREG_LDN_PMCIO 0x0F +#define IOREG_IRQ 0x70 +#define IOREG_WDT_STATUS 0x30 + +/* Flags */ +#define FLAG_WDT_ENABLED 0x01 +#define FLAG_TRIGGER_IRQ BIT(4) + +/* Mapping event type to supported bit */ +#define EVENT_BIT(type) BIT(type + 2) + +enum event_type { + EVENT_NONE, + EVENT_PWRBTN, + EVENT_IRQ, + EVENT_SCI, + EVENT_PIN +}; + +struct eio_wdt_dev { + u32 event_type; + u32 support; + int irq; + unsigned long last_time; + struct regmap *iomap; + struct device *mfd; + struct device *dev; + struct watchdog_device wdd; + struct eio_dev *core; +}; + +static char * const type_strs[] =3D { + "NONE", + "PWRBTN", + "IRQ", + "SCI", + "PIN", +}; + +static u32 type_regs[] =3D { + REG_RESET_EVENT_TIME, + REG_PWR_EVENT_TIME, + REG_IRQ_EVENT_TIME, + REG_SCI_EVENT_TIME, + REG_PIN_EVENT_TIME, +}; + +/* Specify the pin triggered on pretimeout or timeout */ +static char *event_type =3D "NONE"; +module_param(event_type, charp, 0); +MODULE_PARM_DESC(event_type, "Watchdog timeout event type (NONE, PWRBTN, I= RQ, SCI, PIN)"); + +/* Specify the IRQ number when the IRQ event is triggered */ +static int irq; +module_param(irq, int, 0); +MODULE_PARM_DESC(irq, "The IRQ number for IRQ event"); + +static int timeout; +module_param(timeout, int, 0444); +MODULE_PARM_DESC(timeout, "Set PMC command timeout value.\n"); + +static int pmc_write(struct device *dev, u8 ctrl, void *data) +{ + struct pmc_op op =3D { + .cmd =3D CMD_WDT_WRITE, + .control =3D ctrl, + .payload =3D data, + .size =3D (ctrl <=3D REG_EVENT) ? 1 : + (ctrl >=3D REG_IRQ_NUMBER) ? 1 : 4, + .timeout =3D timeout, + }; + return eio_core_pmc_operation(dev, &op); +} + +static int pmc_read(struct device *dev, u8 ctrl, void *data) +{ + struct pmc_op op =3D { + .cmd =3D CMD_WDT_READ, + .control =3D ctrl, + .payload =3D data, + .size =3D (ctrl <=3D REG_EVENT) ? 1 : + (ctrl >=3D REG_IRQ_NUMBER) ? 1 : 4, + .timeout =3D timeout, + }; + return eio_core_pmc_operation(dev, &op); +} + +static int wdt_set_timeout(struct watchdog_device *wdd, unsigned int timeo= ut) +{ + struct eio_wdt_dev *eio_wdt =3D watchdog_get_drvdata(wdd); + + wdd->timeout =3D timeout; + dev_info(eio_wdt->dev, "Set timeout: %u\n", timeout); + + return 0; +} + +static int wdt_set_pretimeout(struct watchdog_device *wdd, unsigned int pr= etimeout) +{ + struct eio_wdt_dev *eio_wdt =3D watchdog_get_drvdata(wdd); + + wdd->pretimeout =3D pretimeout; + dev_info(eio_wdt->dev, "Set pretimeout: %u\n", pretimeout); + + return 0; +} + +static int wdt_get_type(struct eio_wdt_dev *eio_wdt) +{ + int i; + + for (i =3D 1; i < ARRAY_SIZE(type_strs); i++) { + if (strcasecmp(event_type, type_strs[i]) =3D=3D 0) { + if ((eio_wdt->support & EVENT_BIT(i)) =3D=3D 0) { + dev_err(eio_wdt->dev, + "This board doesn't support %s trigger type\n", + event_type); + return -EINVAL; + } + + dev_info(eio_wdt->dev, "Trigger type is %d:%s\n", + i, type_strs[i]); + eio_wdt->event_type =3D i; + return 0; + } + } + + dev_info(eio_wdt->dev, "Event type: %s\n", + type_strs[eio_wdt->event_type]); + return 0; +} + +static int get_time(struct eio_wdt_dev *eio_wdt, u8 ctrl, u32 *val) +{ + int ret; + + ret =3D pmc_read(eio_wdt->mfd, ctrl, val); + if (ret) + return ret; + + /* ms to sec */ + *val /=3D 1000; + + return 0; +} + +static int set_time(struct eio_wdt_dev *eio_wdt, u8 ctrl, u32 time) +{ + /* sec to ms */ + time *=3D 1000; + + return pmc_write(eio_wdt->mfd, ctrl, &time); +} + +static int wdt_set_config(struct eio_wdt_dev *eio_wdt) +{ + int ret, type; + u32 event_time =3D 0; + u32 reset_time =3D 0; + + if (eio_wdt->event_type > EVENT_PIN) + return -EFAULT; + + /* Calculate event time and reset time */ + if (eio_wdt->wdd.pretimeout && eio_wdt->wdd.timeout) { + if (eio_wdt->wdd.timeout < eio_wdt->wdd.pretimeout) + return -EINVAL; + + reset_time =3D eio_wdt->wdd.timeout; + event_time =3D eio_wdt->wdd.timeout - eio_wdt->wdd.pretimeout; + + } else if (eio_wdt->wdd.timeout) { + reset_time =3D eio_wdt->event_type ? 0 : eio_wdt->wdd.timeout; + event_time =3D eio_wdt->event_type ? eio_wdt->wdd.timeout : 0; + } + + /* Set reset time */ + ret =3D set_time(eio_wdt, REG_RESET_EVENT_TIME, reset_time); + if (ret) + return ret; + + /* Set every other times */ + for (type =3D 1; type < ARRAY_SIZE(type_regs); type++) { + ret =3D set_time(eio_wdt, type_regs[type], + (eio_wdt->event_type =3D=3D type) ? event_time : 0); + if (ret) + return ret; + } + + dev_dbg(eio_wdt->dev, "Config wdt reset time %u\n", reset_time); + dev_dbg(eio_wdt->dev, "Config wdt event time %u\n", event_time); + dev_dbg(eio_wdt->dev, "Config wdt event type %s\n", + type_strs[eio_wdt->event_type]); + + return 0; +} + +static int wdt_get_config(struct eio_wdt_dev *eio_wdt) +{ + int ret, type; + u32 event_time =3D 0, reset_time =3D 0; + + /* Get Reset Time */ + ret =3D get_time(eio_wdt, REG_RESET_EVENT_TIME, &reset_time); + if (ret) + return ret; + + dev_dbg(eio_wdt->dev, "Timeout H/W default timeout: %u secs\n", reset_tim= e); + + /* Get every other times */ + for (type =3D 1; type < ARRAY_SIZE(type_regs); type++) { + if ((eio_wdt->support & EVENT_BIT(type)) =3D=3D 0) + continue; + + ret =3D get_time(eio_wdt, type_regs[type], &event_time); + if (ret) + return ret; + + if (event_time =3D=3D 0) + continue; + + if (reset_time) { + if (reset_time < event_time) + continue; + + eio_wdt->wdd.timeout =3D reset_time; + eio_wdt->wdd.pretimeout =3D reset_time - event_time; + + dev_dbg(eio_wdt->dev, + "Pretimeout H/W enabled with event %s of %u secs\n", + type_strs[type], eio_wdt->wdd.pretimeout); + } else { + eio_wdt->wdd.timeout =3D event_time; + eio_wdt->wdd.pretimeout =3D 0; + } + + eio_wdt->event_type =3D type; + + dev_dbg(eio_wdt->dev, "Timeout H/W enabled of %u secs\n", + eio_wdt->wdd.timeout); + return 0; + } + + eio_wdt->event_type =3D EVENT_NONE; + eio_wdt->wdd.pretimeout =3D reset_time ? 0 : WATCHDOG_PRETIMEOUT; + eio_wdt->wdd.timeout =3D reset_time ? reset_time : WATCHDOG_TIMEOU= T; + + dev_dbg(eio_wdt->dev, "Pretimeout H/W disabled\n"); + return 0; +} + +static int set_ctrl(struct eio_wdt_dev *eio_wdt, u8 ctrl) +{ + return pmc_write(eio_wdt->mfd, REG_CONTROL, &ctrl); +} + +static int wdt_start(struct watchdog_device *wdd) +{ + struct eio_wdt_dev *eio_wdt =3D watchdog_get_drvdata(wdd); + int ret; + + ret =3D wdt_set_config(eio_wdt); + if (ret) + return ret; + + ret =3D set_ctrl(eio_wdt, CTRL_START); + if (!ret) { + eio_wdt->last_time =3D jiffies; + dev_dbg(eio_wdt->dev, "Watchdog started\n"); + } + + return ret; +} + +static int wdt_stop(struct watchdog_device *wdd) +{ + struct eio_wdt_dev *eio_wdt =3D watchdog_get_drvdata(wdd); + int ret; + + dev_dbg(eio_wdt->dev, "Watchdog stopped\n"); + eio_wdt->last_time =3D 0; + + ret =3D set_ctrl(eio_wdt, CTRL_STOP); + return ret; +} + +static int wdt_ping(struct watchdog_device *wdd) +{ + struct eio_wdt_dev *eio_wdt =3D watchdog_get_drvdata(wdd); + int ret; + + dev_dbg(eio_wdt->dev, "Watchdog ping\n"); + + ret =3D set_ctrl(eio_wdt, CTRL_TRIGGER); + if (!ret) + eio_wdt->last_time =3D jiffies; + + return ret; +} + +static unsigned int wdt_get_timeleft(struct watchdog_device *wdd) +{ + struct eio_wdt_dev *eio_wdt =3D watchdog_get_drvdata(wdd); + unsigned int timeleft =3D 0; + + if (eio_wdt->last_time && wdd->timeout) { + unsigned long delta =3D jiffies - eio_wdt->last_time; + unsigned int elapsed =3D (unsigned int)(delta / HZ); + + if (elapsed < wdd->timeout) + timeleft =3D wdd->timeout - elapsed; + } + return timeleft; +} + +static int wdt_support(struct eio_wdt_dev *eio_wdt) +{ + u8 support; + + if (pmc_read(eio_wdt->mfd, REG_STATUS, &support)) + return -EIO; + + if (!(support & SUPPORT_AVAILABLE)) + return -ENODEV; + + if ((support & SUPPORT_RESET) !=3D SUPPORT_RESET) + return -ENODEV; + + eio_wdt->support =3D support; + + return 0; +} + +static int wdt_get_irq_io(struct eio_wdt_dev *eio_wdt) +{ + int ret =3D 0; + int idx =3D EIO_PNP_INDEX; + int data =3D EIO_PNP_DATA; + struct regmap *map =3D eio_wdt->iomap; + + mutex_lock(&eio_wdt->core->mutex); + + /* Unlock EC IO port */ + ret |=3D regmap_write(map, idx, IOREG_UNLOCK); + ret |=3D regmap_write(map, idx, IOREG_UNLOCK); + + /* Select logical device to PMC */ + ret |=3D regmap_write(map, idx, IOREG_LDN); + ret |=3D regmap_write(map, data, IOREG_LDN_PMCIO); + + /* Get IRQ number */ + ret |=3D regmap_write(map, idx, IOREG_IRQ); + ret |=3D regmap_read(map, data, &eio_wdt->irq); + + /* Lock back */ + ret |=3D regmap_write(map, idx, IOREG_LOCK); + + mutex_unlock(&eio_wdt->core->mutex); + + return ret ? -EIO : 0; +} + +static int wdt_get_irq_pmc(struct eio_wdt_dev *eio_wdt) +{ + return pmc_read(eio_wdt->mfd, REG_IRQ_NUMBER, &eio_wdt->irq); +} + +static int wdt_get_irq(struct eio_wdt_dev *eio_wdt) +{ + int ret; + + if (!(eio_wdt->support & BIT(EVENT_IRQ))) + return -ENODEV; + + ret =3D wdt_get_irq_pmc(eio_wdt); + if (ret) { + dev_err(eio_wdt->dev, "Error get irq by pmc\n"); + return ret; + } + + if (eio_wdt->irq) + return 0; + + /* Fallback: get IRQ number from EC IO space */ + ret =3D wdt_get_irq_io(eio_wdt); + if (ret) { + dev_err(eio_wdt->dev, "Error get irq by io\n"); + return ret; + } + + if (!eio_wdt->irq) { + dev_err(eio_wdt->dev, "Error IRQ number =3D 0\n"); + return -EIO; + } + + return 0; +} + +static int wdt_set_irq_io(struct eio_wdt_dev *eio_wdt) +{ + int ret =3D 0; + int idx =3D EIO_PNP_INDEX; + int data =3D EIO_PNP_DATA; + struct regmap *map =3D eio_wdt->iomap; + + mutex_lock(&eio_wdt->core->mutex); + + /* Unlock EC IO port */ + ret |=3D regmap_write(map, idx, IOREG_UNLOCK); + ret |=3D regmap_write(map, idx, IOREG_UNLOCK); + + /* Select logical device to PMC */ + ret |=3D regmap_write(map, idx, IOREG_LDN); + ret |=3D regmap_write(map, data, IOREG_LDN_PMCIO); + + /* Enable WDT */ + ret |=3D regmap_write(map, idx, IOREG_WDT_STATUS); + ret |=3D regmap_write(map, data, FLAG_WDT_ENABLED); + + /* Set IRQ number */ + ret |=3D regmap_write(map, idx, IOREG_IRQ); + ret |=3D regmap_write(map, data, eio_wdt->irq); + + /* Lock back */ + ret |=3D regmap_write(map, idx, IOREG_LOCK); + + mutex_unlock(&eio_wdt->core->mutex); + + return ret ? -EIO : 0; +} + +static int wdt_set_irq_pmc(struct eio_wdt_dev *eio_wdt) +{ + return pmc_write(eio_wdt->mfd, REG_IRQ_NUMBER, &eio_wdt->irq); +} + +static int wdt_set_irq(struct eio_wdt_dev *eio_wdt) +{ + int ret; + + if (!(eio_wdt->support & BIT(EVENT_IRQ))) + return -ENODEV; + + ret =3D wdt_set_irq_io(eio_wdt); + if (ret) { + dev_err(eio_wdt->dev, "Error set irq by io\n"); + return ret; + } + + ret =3D wdt_set_irq_pmc(eio_wdt); + if (ret) { + dev_err(eio_wdt->dev, "Error set irq by pmc\n"); + return ret; + } + + return 0; +} + +static int wdt_get_irq_event(struct eio_wdt_dev *eio_wdt) +{ + u8 status; + + if (pmc_read(eio_wdt->mfd, REG_EVENT, &status)) + return 0; + + return status; +} + +static irqreturn_t wdt_isr(int irq, void *arg) +{ + return IRQ_WAKE_THREAD; +} + +static irqreturn_t wdt_threaded_isr(int irq, void *arg) +{ + struct eio_wdt_dev *eio_wdt =3D arg; + u8 status =3D wdt_get_irq_event(eio_wdt) & FLAG_TRIGGER_IRQ; + + if (!status) + return IRQ_NONE; + + if (eio_wdt->wdd.pretimeout) { + watchdog_notify_pretimeout(&eio_wdt->wdd); + } else { + dev_crit(eio_wdt->dev, "Watchdog expired, rebooting\n"); + emergency_restart(); + } + + return IRQ_HANDLED; +} + +static int query_irq(struct eio_wdt_dev *eio_wdt) +{ + int ret =3D 0; + + if (irq) { + eio_wdt->irq =3D irq; + } else { + ret =3D wdt_get_irq(eio_wdt); + if (ret) + return ret; + } + + dev_dbg(eio_wdt->dev, "IRQ =3D %d\n", eio_wdt->irq); + + return wdt_set_irq(eio_wdt); +} + +static int wdt_init(struct eio_wdt_dev *eio_wdt) +{ + int ret; + + ret =3D wdt_support(eio_wdt); + if (ret) + return ret; + + ret =3D wdt_get_config(eio_wdt); + if (ret) + return ret; + + ret =3D wdt_get_type(eio_wdt); + if (ret) + return ret; + + if (eio_wdt->event_type =3D=3D EVENT_IRQ) + ret =3D query_irq(eio_wdt); + + return ret; +} + +static const struct watchdog_ops wdt_ops =3D { + .owner =3D THIS_MODULE, + .start =3D wdt_start, + .stop =3D wdt_stop, + .ping =3D wdt_ping, + .set_timeout =3D wdt_set_timeout, + .get_timeleft =3D wdt_get_timeleft, + .set_pretimeout =3D wdt_set_pretimeout, +}; + +static struct watchdog_info wdinfo =3D { + .identity =3D KBUILD_MODNAME, + .options =3D WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | + WDIOF_PRETIMEOUT | WDIOF_MAGICCLOSE, +}; + +static int eio_wdt_probe(struct platform_device *pdev) +{ + struct device *dev =3D &pdev->dev; + struct eio_wdt_dev *eio_wdt; + struct watchdog_device *wdd; + int ret =3D 0; + + eio_wdt =3D devm_kzalloc(dev, sizeof(*eio_wdt), GFP_KERNEL); + if (!eio_wdt) + return -ENOMEM; + + eio_wdt->dev =3D dev; + eio_wdt->mfd =3D dev->parent; + eio_wdt->iomap =3D dev_get_regmap(dev->parent, NULL); + if (!eio_wdt->iomap) + return dev_err_probe(dev, -ENODEV, "parent regmap missing\n"); + + eio_wdt->core =3D dev_get_drvdata(dev->parent); + if (!eio_wdt->core) + return dev_err_probe(dev, -ENODEV, "eio_core not present\n"); + + ret =3D wdt_init(eio_wdt); + if (ret) { + dev_err(dev, "wdt_init fail\n"); + return -EIO; + } + + if (eio_wdt->event_type =3D=3D EVENT_IRQ) { + ret =3D devm_request_threaded_irq(dev, eio_wdt->irq, + wdt_isr, wdt_threaded_isr, + IRQF_SHARED | IRQF_ONESHOT, pdev->name, + eio_wdt); + if (ret) { + dev_err(dev, "IRQ %d request fail:%d. Disabled.\n", + eio_wdt->irq, ret); + return ret; + } + } + + wdd =3D &eio_wdt->wdd; + wdd->info =3D &wdinfo; + wdd->ops =3D &wdt_ops; + wdd->parent =3D dev; + wdd->min_timeout =3D 1; + wdd->max_timeout =3D 0x7FFF; + + ret =3D watchdog_init_timeout(wdd, wdd->timeout, dev); + if (ret) { + dev_err(dev, "Init timeout fail\n"); + return ret; + } + + watchdog_stop_on_reboot(&eio_wdt->wdd); + watchdog_stop_on_unregister(&eio_wdt->wdd); + + watchdog_set_drvdata(&eio_wdt->wdd, eio_wdt); + platform_set_drvdata(pdev, eio_wdt); + + ret =3D devm_watchdog_register_device(dev, &eio_wdt->wdd); + if (ret) + dev_err(dev, "Cannot register watchdog device (err: %d)\n", ret); + + return ret; +} + +static struct platform_driver eio_wdt_driver =3D { + .probe =3D eio_wdt_probe, + .driver =3D { + .name =3D "eio_wdt", + }, +}; +module_platform_driver(eio_wdt_driver); + +MODULE_AUTHOR("Wenkai Chung "); +MODULE_AUTHOR("Ramiro Oliveira "); +MODULE_DESCRIPTION("Watchdog interface for Advantech EIO embedded controll= er"); +MODULE_LICENSE("GPL"); --=20 2.43.0 From nobody Sun Dec 14 06:34:44 2025 Received: from SEYPR02CU001.outbound.protection.outlook.com (mail-koreacentralazon11023098.outbound.protection.outlook.com [40.107.44.98]) (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 BFF252D3EDE; Fri, 12 Dec 2025 16:42:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.44.98 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765557726; cv=fail; b=sv4aBtyJZCznJ2bPZEUTBkrim3lZqCJsQOOdcU/GK88xiy9qwQm6cgyetha8hJqNcGYD8lxlQJPrH8jgsiEWeHxbZqgg0h5DbO1pZYshO971aXgGeT0uy821L4tS6yzbtSfKlpuj0Dl1f2BccF3K7RioduMP2VmZY6yIEtfa2PQ= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765557726; c=relaxed/simple; bh=+mf1e1/Qgjxx4qRc+xcsH1LZBaYAaDUMV9iPWaI+pUM=; h=From:Date:Subject:Content-Type:Message-Id:References:In-Reply-To: To:Cc:MIME-Version; b=cBAliwARUG1mkKP2MpqRuTWLhApSffR67wsHQ4Vt8Y6/OR734JG6HICfY8vX7m51yAvKGdyjQgyXE9RzSdko6WObTSLZzLG2sIEYZxZIIxWimmnZYyqaW5yv6xxiv5Wb76d7c+CHjpbcNb3/vC1q4fkO9foPHu2TLTxDeTN0Lp4= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=advantech.com; spf=pass smtp.mailfrom=advantech.de; dkim=pass (2048-bit key) header.d=advantech.com header.i=@advantech.com header.b=YUSmd7w8; arc=fail smtp.client-ip=40.107.44.98 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=advantech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=advantech.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=advantech.com header.i=@advantech.com header.b="YUSmd7w8" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=E3Kqa+0F0F/9f7Wsl1Uo43gqn66UAFuc7CuwqUstM5t3VVhsB5fZpEP2juDsorQw2gT6zTv+5GzHm8qljgb66Ge425LyI/x293pMfehEZIzyAKheYFtG0JA+0WHP9S8dmEPcAoyE9P7rvByhkSpx80IHpNe00+rlzog4/mVIZD5cVvE7j/UHkDcto4B9mrWTHKlD7ffngTjpEF4SSdwVOE/HQfdZ0GnoG7CpnkLEiNqriA0cdVSHYRdHvRm74WoQIQuujp9g6cDqcVvsqic5p6QL/Iqv0GaDA5Wm7Ut792HMrFUEEVbGwES4UQ0d1fZthgU5jTt9ya/QOlIIsQVPCQ== 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=P7SbUrJTLMWqLniZKK8rb7xUwVoI8860SWHs36/sMyo=; b=qEcxTXZE8vTbAGcLYSn3HBCqnXAzXdWN+j0Cq4toM0hf2vbx5egyC4UpF1moY9aM0oKkOoJ5VRcKvsISFoBXb1BBvSrv9yEzzQjwMaqMjyW3zFSaosglM6i2Fm5gtaMOUHkqCMwJ1BYwg70eFGe2JZTZcDRm8Afspi9G/QkP5e6RGdz7jA+qY/s/K/5Kyufuoq732vfkrm5chPPDrkx6KzfO6pG9o6SmkKnuv/b25HNLS1Ksup+8/FQ6lFnGFOlQmqe7rO38cwUIavp+twW4U3yPPksWhRZvCzsTBKX/Uo3L9Rs8bPqAa6SfHWIX63hGW0BYZfZuqQafJaZP/3jGLg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=advantech.de; dmarc=pass action=none header.from=advantech.com; dkim=pass header.d=advantech.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=advantech.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=P7SbUrJTLMWqLniZKK8rb7xUwVoI8860SWHs36/sMyo=; b=YUSmd7w8Nh5Zruu2ukZM8S0WgxoR5nXmAtMfOppDNQdQ6SOSbrDv873HIEvfZ1xgWC/DbFiom+q01kjqEDL8RhIneWcpYgb2TW9E7wWf2CibYyQEJtGALkwtwzRmP5XzVGCr87WijKWH0OspiYCtJhMXXLN/mgpHNSMvJo6+KPlLYoHT/4mSooZH0F9IWs/m13HLzXLFf760rECVBv9AAcSi1PScxgxfckveBo8tKTiTK73JCs6B0F2kRPev7JZvOe7g9O7so7/FrUud+vbuOIkEfpzwghEZo5t40KOs6UHc3aCb/kWyoXjM3eDYg5tZsYyzUj/He/vYgrJsoScN1g== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=advantech.com; Received: from PSAPR02MB4502.apcprd02.prod.outlook.com (2603:1096:301:21::6) by JH0PR02MB6564.apcprd02.prod.outlook.com (2603:1096:990:3f::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9412.11; Fri, 12 Dec 2025 16:42:01 +0000 Received: from PSAPR02MB4502.apcprd02.prod.outlook.com ([fe80::59c9:fe0:25f6:702b]) by PSAPR02MB4502.apcprd02.prod.outlook.com ([fe80::59c9:fe0:25f6:702b%6]) with mapi id 15.20.9412.005; Fri, 12 Dec 2025 16:42:01 +0000 From: Ramiro Oliveira Date: Fri, 12 Dec 2025 17:40:58 +0100 Subject: [PATCH 7/8] Add Advantech EIO Thermal driver Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251212-upstream-v1-v1-7-d50d40ec8d8a@advantech.com> References: <20251212-upstream-v1-v1-0-d50d40ec8d8a@advantech.com> In-Reply-To: <20251212-upstream-v1-v1-0-d50d40ec8d8a@advantech.com> To: Lee Jones , Linus Walleij , Bartosz Golaszewski , Guenter Roeck , Andi Shyti , Daniel Thompson , Jingoo Han , Helge Deller , Wim Van Sebroeck , "Rafael J. Wysocki" , Daniel Lezcano , Zhang Rui , Lukasz Luba Cc: linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, linux-hwmon@vger.kernel.org, linux-i2c@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-fbdev@vger.kernel.org, linux-watchdog@vger.kernel.org, linux-pm@vger.kernel.org, Wenkai Chung , Francisco Aragon-Trivino , Hongzhi Wang , Mikhail Tsukerman , Thomas Kastner , Ramiro Oliveira X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=13005; i=ramiro.oliveira@advantech.com; h=from:subject:message-id; bh=+mf1e1/Qgjxx4qRc+xcsH1LZBaYAaDUMV9iPWaI+pUM=; b=owEB7QES/pANAwAKAc7t0Ke8vbAJAcsmYgBpPEWhgA/7ELK0PHu90Za6FQEniN27XlsnvalFp 4oogzZggLqJAbMEAAEKAB0WIQS1Nkng0ZvJmBKh6GLO7dCnvL2wCQUCaTxFoQAKCRDO7dCnvL2w CRJzC/9FdOBS39jN7XlxbEb3VdvSWHLW1SwTmvIB/tdhzvY0TDawWy0R0oQZt7wSBFQvzuzqMuL JapylBMLyXc3sn+Tmwxf2Ugm0QR7+L47PDxOV2dTEov9awuYJ6RilBRKLl2LFEExG83pS+Rcd48 6GYDboripJw1qk70igQ4EdwBgKQsh/lrgUG8RGk6bB/5ggzxTXtlpkM2A0fzJ9wcDWcGebKyoy4 c7hg8IGjB+fjhd4TK1XLdcrQOdfXhfVmGhF8kJKM945ASBvL7zUZ0Z85Q/T5Lvj3i926XQJph3M WQAXsg9Jh7vwMcK4vo9GuLUzMpcJOLz+T/Ji2FZoBokKd3ZZYvlsu4z6I7Gz0heZPpSUY05JRGW RRJu4vGICovYbXRcWVxrSkTIvuSY8egel9YVOfBiRocVBAJEn6wTfwCkPyJ69UgT6IYWP8QVGL5 OXpw/FS9K7nt64sl5wAXu14fYzWzpgkUQjdw+5sp+m5ZBUKhY/q24INwOLcYSLUfyDLBY= X-Developer-Key: i=ramiro.oliveira@advantech.com; a=openpgp; fpr=B53649E0D19BC99812A1E862CEEDD0A7BCBDB009 X-ClientProxiedBy: FR4P281CA0175.DEUP281.PROD.OUTLOOK.COM (2603:10a6:d10:b7::12) To PSAPR02MB4502.apcprd02.prod.outlook.com (2603:1096:301:21::6) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PSAPR02MB4502:EE_|JH0PR02MB6564:EE_ X-MS-Office365-Filtering-Correlation-Id: 7b237e26-f301-4c95-4dc3-08de399d5fee X-MS-Exchange-AtpMessageProperties: SA X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|7416014|366016|52116014|376014|38350700014|921020; X-Microsoft-Antispam-Message-Info: =?utf-8?B?dGJpMEJvVHZqcEVwV3RMMHVXcVQ2WjJLL0J0ZFlhQUtRaXF6d1pjSjQrWHB4?= =?utf-8?B?ZmN1bW82OU0xWnU1eVdkdjAvcmI2QTJDdDV4NUh5TWpVY1lIY3hiMUZEdEhS?= =?utf-8?B?bEdmei9HU3RubGF5NVcvOUxmSHhIYUwvbFRFTmFrZWxwb2hndGl4WGNLNmNr?= =?utf-8?B?WEdUUjFHYzRMeFlHNEpYR0FBS2I1RzR4MXNzb08zeEl5elUyczBNL2FnSGFK?= =?utf-8?B?YWxIVndJVWRLTGNyM2lSNXV6Rk4vMVVPY254KzhIek1MOElaQ1g0WGwrZ2tm?= =?utf-8?B?MlFpeFVmTVJmTmNlWnExalRtV3pSUzV5Yk92aEZ2REhkc1hBM0M1THRkRHdB?= =?utf-8?B?RDliYUZURVhlUGVGZFV4YzY2cEhXbFNrcG1hdy9EQ0JDY3VjYmJ0Z0ZScWhP?= =?utf-8?B?ZEQrL1k1cm1GOUJkZzZPRzJYWUV5VDM0VmJGY2JhSk01U2F3M1kvcWs1WElk?= =?utf-8?B?YzFtL0tRRzVJM2ZGenFhZ0dRYmtuZXJhdGRIMmJKc0lOc3FaMzNhd3NJNWhC?= =?utf-8?B?V0lnU2taZ1o2UnBQWUVSUkNpU2ZVSTQxWXU1WE1TYlB3aXdzcm5YaWhiOExR?= =?utf-8?B?N2hva1I0SHpEZ1hXSDNuMlVsTVUxNXFQUWEyNFRNRHhEd3JCbkdsUFB0Q3pG?= =?utf-8?B?bktSL3NEcEYrbTd5YW14Q3J1eTVYYWZUd0YvcTJYVW9kSUpYNHVVc3c4U3d2?= =?utf-8?B?SkVZMXgxbDhqWjN4eEZPdG01VjNXSVROeEl3NFNuV3BaU09nTEFidkt0Znl6?= =?utf-8?B?S1BaZ1JUYkVheFozRExrVFJMdDdjN244NWl5Y0pCMVdlYXM4SVkzZnhuR09z?= =?utf-8?B?MCtpaXVvVVlkOFJpZnBhaFhqdlUxcW1JQS9EOC91L1pMaVNMTURyMDBsZnBB?= =?utf-8?B?enVtaExqWXNiVk5SZnJyckpPTWNzS0RBZG9QMXpGbXJQaThQNldubDhZRXNu?= =?utf-8?B?MEJwbEdQcG9hZXlLYkVZRGRkTDhJWitnMWhxSUo2WWRFWFJxbGEzOTdXaWUx?= =?utf-8?B?VHhHdFRxRWxTVHhSOGZGM3BMU0pmaFhQWTBmWTNYRm9RRnNrSUV0d2R3RkhR?= =?utf-8?B?R1VsSWpwMG9Fem9FSU03YU1MMTBkK2JBMGEyWDR2QWp2UU0vYUp5WmxsV2lM?= =?utf-8?B?QzVCY1prM2Q4NFlNV2dhMGoza05yUDVsRUR6OVVSM1l5eitUaXlzYVZMSXpO?= =?utf-8?B?Y2RYRUwwTkkxdU43ZmRORjFaSnNhV3p0cW5CTmRLLzRDZklaYTFBNjdldzJ3?= =?utf-8?B?Y080ZFZkS1RUZDF6SHhCL3ZKdTBjZGdOc2hLaTU0c3Q4UDVYZDZoMEtBeVk4?= =?utf-8?B?Z2d1cFZVOGlNcHBPSHo3WnhneHNlQ2crZWZPOXlIQW54VEpkZmR3TGxkcklz?= =?utf-8?B?ZWlmK3d0S0lGNC9QN2VYa1FSdG43elV0T2xxdHdIVmgvNGk5eVdybUdCQ1Bi?= =?utf-8?B?Q25MVWljbkcwL0xram5nZ0NURGxBTTNadG9Cc1pXNEpaZlM4ZHR2TDZNdTN0?= =?utf-8?B?aVlsd0phcExPS3dkemk4REFyTnNEK0FKUTkzY3pxdWNzeXJsRWsrbzh6bW43?= =?utf-8?B?Y3RzWXkrZ0JranZYS1h3UVl3K0I0TkNhRWNRUmJLNUhNb2h2OXhCTGFZcitF?= =?utf-8?B?LzljaWUxT05qRk1QR1l2RVBhTk5wdTVkTUdxNDRkWVlpT0RnZ29aNmZkU0dX?= =?utf-8?B?RFUzOER0d1hmM1dlVFNLT2Yvc2pJa1hVTmdmRDRiQVp3a2FFZE0xMHdsd0Vr?= =?utf-8?B?c1JXRGE4KzFwZzVVNk5iWTNmUThjVGl5Qzk0Ykt0bXZjeFNzdnkvRmpKSzY2?= =?utf-8?B?ODFzM1IxVTM5KzVpTU1jemVzR0IxZEpCTXlCbU9TeHNGZlE4T1U1N2xZWVZ2?= =?utf-8?B?a1dXczRtQjRuTGFBbTlnUHlBN1p2SUt3THh3d2pCUEYySUowMDNiTWVXM1Nv?= =?utf-8?B?SWk3YTJ6N1YzM3NKd2Q4eUpVUjEwYVBLRzQ4TXJOeWZzc1pLWnhUc0NnaXJN?= =?utf-8?B?QXpUeTI1c0ZOcExMSkhjTkh3K1JVSCtPYmN5MWtVcHhSMWVvc1ZhOWlDYXdu?= =?utf-8?B?QU9BNnZpcmpwQ0pKeWZmNUd3bFRxTjZHaVpVZz09?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PSAPR02MB4502.apcprd02.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(7416014)(366016)(52116014)(376014)(38350700014)(921020);DIR:OUT;SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?SG1xM2wxYjNJL21NOW9KS29MQ1lhc1VyQ2FTUlRyTXVUSE56b1diT3MzYTMv?= =?utf-8?B?dU5oTzZjWFZOTDdHYUdFZ3lYT0wwMUdoS1UvUkJIVlloYnozSW5ESFdqWERI?= =?utf-8?B?TlN0TFliVlhtZDRnTFkwTTdNQUVzOUJyWW44b0RIRlg4R3pRekVIcXpkTW5W?= =?utf-8?B?WmwyQ0kwbkd6eWhXTjRseVFnK3ZOMnRrR05lZ25QaVhmK1BjRlloVW1QQnRx?= =?utf-8?B?cUpQaVQwUUFSOCs2b1NjN2R0YmN3UVg0VVZIcTE3bFR2RTFNRCtOZlFzK3pR?= =?utf-8?B?VWFCMkVYTFN6cmxUZ0FGS01ZUVMrWTVaUlhDaXhuR3lUVE1zOVRjeCs0Tk1k?= =?utf-8?B?aFRHZjVQSTNkbkJKc2hFc1lYL1RFdkJzbTlTOGhOVDZQRXFxREtRTXYrY2Fz?= =?utf-8?B?SStiOURSamx0bnQ1SHdWcmhYS204UFh6cTc2OUttQVZIbE9TUmdaS1huU3Qy?= =?utf-8?B?bkt1N01CSEpwUzVjUTlVMHpwWHNQVTJkSytyU3hBVjhnVGQ5NXZ5djdSMWhh?= =?utf-8?B?dnRlSkZCTlJXRFVSaFh4UGlzRWtrKzZRRFhrcEtRQmFMVmc5anFSUjJTME9z?= =?utf-8?B?Sm41RUs5VUZ5V2E5WXVKc20yckg4VThYd0lDbnJTdk9oVjhFdDhLRFBaOWdB?= =?utf-8?B?NjR2YXVVT1ZZSVhMckZCUUN3ZVk3dE5ZRWlGZk4xM003QjloYmdjdnIvcGln?= =?utf-8?B?K1FTVElIRGU3c0ozNklKazZUNVJBeXJKby9PV0ZFZXlPU1dCM0F5WmVWMWp4?= =?utf-8?B?TTZRV2RVOEMwMUNpbS8xSXFGdll0T0pjM1lJb3BVQm1PUDlVcVdBMW8xemNC?= =?utf-8?B?QWxCUVo4bDYvT2g5V0JzdXN1d04rVlJyYXVpcTBEOWJiSHBLU2Judjg1Qlkx?= =?utf-8?B?UXBIS2VKRFJSazJ1TnU2S3dyYTJ4czREbTF3b1NTbDUxZzA2MGJCN2MrRFBF?= =?utf-8?B?UTBhNmwvb0g4T1dqYzQzU1EzeWhKSTBXS1R6RjdFaTBzZHpobEJ4UjB2UkpT?= =?utf-8?B?aGtYMmgzcFVYL0lIeDc3YzJJbW1oRFNzVnFqM3lLanNvT3ZQdVRWWis3Mkxp?= =?utf-8?B?cU8xdW9nbkNCMktNakZxZnhXejBoVGpTd2kwWDFFaWdNMjZSM3RCZVlwMFdR?= =?utf-8?B?aTBwbExEZmNWNkVkU0wrUStkOXlXcGpjVXMwTlpSRC9oTlk5aEpOTmZCMmdC?= =?utf-8?B?TDVqVlRNK3Q1OFBwMkV2Sm5xQ2VCbnJjUVpseCtkbkRVSEI5UklUbkFjd2NR?= =?utf-8?B?T0x5b1dweEJ6V2FsckRPalpCSjBGSUE5dVVUOEtLQkRPVzhkZHdrQmppeTR0?= =?utf-8?B?cGNvRmloc3Q5ODVXaW1RaWhPVWNjUSt0VktwSEZsRFh0dHgrUm1JNWdld0NH?= =?utf-8?B?dXc1NkdMU2FLeXdTdFo0bnMxUUhPK0lBeEZycVk1K0xkdlBVZTdzc1QyN1pr?= =?utf-8?B?Y3VHZStUVm8zaSs3VHBNZC9STENYTDVsSE83QlA2bUJnNXJwN2d1WnViTnhm?= =?utf-8?B?S3RneUxoMW9HSTRJZDZTR3htcGdNR1p2NUFyaUllQlFUaCtxckRSMUZ0ZUNq?= =?utf-8?B?eDlKdE1EL2FXQTFRenF6dVl2M2hveHpnUFV5anVCdE9CdWdxc1lXVFozZG53?= =?utf-8?B?blU0TUVXTGZGOEhkRWlGcnB4SnZuTzBSZ2YvbW5LYjVEck95MmMwNndXSXgz?= =?utf-8?B?QnYzTkVZa1lQSmtaRStCUFM1anQxNGZkbWtFVUZnRG56Y292WjUvREl4aEk3?= =?utf-8?B?aUZ2RkFlc0l0MFR1SUNsRjNoaWRIYlRYblV1QXVHZGJKd0h4THZUNFlpUnNF?= =?utf-8?B?a2FYcDhWQTNCbWdaZzJqa1RHZTBoYXRhT2tXeFF6Zkl2czgwbXVXNklwNklh?= =?utf-8?B?b3hERkpCYnNmUmNkaDVwMnNObHh3bEdXQzdrSi9SY2lEeUxna3E4LzVWOVdL?= =?utf-8?B?YlFSZHVBUVhCSC9mNFRZVHhZZURzcEcrWWxCMm14ZUJHS0FXSGVKcUZlZmZK?= =?utf-8?B?WnQvS25xdHN6aVl4T3lnZUJ3d0hEYVB5OUF1TE8zOWZnS1lteHNEMFpQdkZq?= =?utf-8?B?OE9yRUJJU1dwU2dJQVVxTzZlT1JFQ25jVzBlSFNTUnhldC9jR2JCK1AxdlRl?= =?utf-8?B?TlpqNVJOTnZSeHBKYkhpZEZ2Ykt1MWFoSVhJQWtOaGs1ZHJ4NUxWYWVyQ3pY?= =?utf-8?Q?qhDf5hQGPYuIQkBPqvtxZtg=3D?= X-OriginatorOrg: advantech.com X-MS-Exchange-CrossTenant-Network-Message-Id: 7b237e26-f301-4c95-4dc3-08de399d5fee X-MS-Exchange-CrossTenant-AuthSource: PSAPR02MB4502.apcprd02.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 Dec 2025 16:42:01.6281 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: a77d40d9-dcba-4dda-b571-5f18e6da853f X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: FwkQ9R6sxE0jQlWQNg8ANKC0sXvRhEBNv31usJU3hbJOpgmGsa4R2Tz2NIJkAhhuvNeg/EJ0aQMRp+DaETM4vh9PTJ7bkJepB+CkJGLqiy8= X-MS-Exchange-Transport-CrossTenantHeadersStamped: JH0PR02MB6564 This commit adds the driver to control the Advantech EIO Thermal block, this block is included in the Advantech EIO Embedded Controller. Signed-off-by: Ramiro Oliveira --- MAINTAINERS | 1 + drivers/thermal/Kconfig | 9 ++ drivers/thermal/Makefile | 1 + drivers/thermal/eio_thermal.c | 352 ++++++++++++++++++++++++++++++++++++++= ++++ 4 files changed, 363 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index dfdf4f39c14b..770b2f82d01a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -623,6 +623,7 @@ F: drivers/gpio/gpio-eio.c F: drivers/hwmon/eio-hwmon.c F: drivers/i2c/busses/i2c-eio.c F: drivers/mfd/eio_core.c +F: drivers/thermal/eio_thermal.c F: drivers/video/backlight/eio_bl.c F: drivers/watchdog/eio_wdt.c F: include/linux/mfd/eio.h diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index b10080d61860..7309f7e7a1c1 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -427,6 +427,15 @@ config DA9062_THERMAL zone. Compatible with the DA9062 and DA9061 PMICs. =20 +config EIO_THERMAL + tristate "Advantech EIO Thermal zone" + depends on MFD_EIO && THERMAL + help + Thermal zone support for the Advantech EIO. This driver exposes + temperature readings, trip points and protection enable/disable via + the Linux thermal framework. It communicates with the EC through the + EIO MFD core. + menu "Mediatek thermal drivers" depends on ARCH_MEDIATEK || COMPILE_TEST source "drivers/thermal/mediatek/Kconfig" diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index bb21e7ea7fc6..3740540d8a18 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_IMX91_THERMAL) +=3D imx91_thermal.o obj-$(CONFIG_MAX77620_THERMAL) +=3D max77620_thermal.o obj-$(CONFIG_QORIQ_THERMAL) +=3D qoriq_thermal.o obj-$(CONFIG_DA9062_THERMAL) +=3D da9062-thermal.o +obj-$(CONFIG_EIO_THERMAL) +=3D eio_thermal.o obj-y +=3D intel/ obj-$(CONFIG_TI_SOC_THERMAL) +=3D ti-soc-thermal/ obj-y +=3D st/ diff --git a/drivers/thermal/eio_thermal.c b/drivers/thermal/eio_thermal.c new file mode 100644 index 000000000000..2d82bd9d7855 --- /dev/null +++ b/drivers/thermal/eio_thermal.c @@ -0,0 +1,352 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * eio_thermal + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * Thermal zone driver for Advantech EIO embedded controller's thermal + * protect mechanism. + * + * In EIO chip. The smart fan has 3 trips. While the temperature: + * - Touch Trip0: Shutdown --> Cut off the power. + * - Touch Trip1: Poweroff --> Send the power button signal. + * - between Trip2 and Trip1: Throttle --> Intermittently hold the CPU. + * + * PowerOff Shutdown + * ^ ^ + * Throttle | | + * | | | + * +--------+------------+----------+--------- + * 0 trip2 trip1 trip0 (Temp) + * + * Copyright (C) 2025 Advantech Corporation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CMD_THERM_WRITE 0x10 +#define CMD_THERM_READ 0x11 +#define THERM_NUM 0x04 +#define UNIT_PER_TEMP 100 + +#define CTRL_STATE 0x00 +#define CTRL_TYPE 0x01 +#define CTRL_ERROR 0x04 +#define CTRL_VALUE 0x10 +#define CTRL_MAX 0x11 +#define CTRL_MIN 0x12 +#define CTRL_THROTTLE 0x20 +#define CTRL_THROTTLE_HI 0x21 +#define CTRL_THROTTLE_LO 0x22 +#define CTRL_THROTTLE_DEFAULT 0x28 +#define CTRL_THROTTLE_HI_DEFAULT 0x29 +#define CTRL_THROTTLE_LO_DEFAULT 0x2A +#define CTRL_POWEROFF 0x30 +#define CTRL_POWEROFF_HI 0x31 +#define CTRL_POWEROFF_LO 0x32 +#define CTRL_POWEROFF_DEFAULT 0x38 +#define CTRL_POWEROFF_HI_DEFAULT 0x39 +#define CTRL_POWEROFF_LO_DEFAULT 0x3A +#define CTRL_SHUTDOWN 0x40 +#define CTRL_SHUTDOWN_HI 0x41 +#define CTRL_SHUTDOWN_LO 0x42 +#define CTRL_SHUTDOWN_DEFAULT 0x48 +#define CTRL_SHUTDOWN_HI_DEFAULT 0x49 +#define CTRL_SHUTDOWN_LO_DEFAULT 0x4A +#define CTRL_SB_TSI_STATUS 0x80 +#define CTRL_SB_TSI_ACCESS 0x81 +#define CTRL_WARN_STATUS 0x90 +#define CTRL_WARN_BEEP 0x91 +#define CTRL_WARN_TEMP 0x92 + +#define THERM_ERR_NO 0x00 +#define THERM_ERR_CHANNEL 0x01 +#define THERM_ERR_HI 0x02 +#define THERM_ERR_LO 0x03 + +#define NAME_SIZE 5 + +#define TRIP_NUM 3 +#define TRIP_SHUTDOWN 0 +#define TRIP_POWEROFF 1 +#define TRIP_THROTTLE 2 +/* Beep mechanism no stable. Not supported, yet. */ +#define TRIP_BEEP 3 + +#define THERMAL_POLLING_DELAY 2000 /* millisecond */ +#define THERMAL_PASSIVE_DELAY 1000 + +#define DECI_KELVIN_TO_MILLI_CELSIUS(t) (((t) - 2731) * 100) +#define MILLI_CELSIUS_TO_DECI_KELVIN(t) (((t) / 100) + 2731) + +#define THERM_STS_AVAIL BIT(0) +#define THERM_STS_THROTTLE_AVAIL BIT(1) +#define THERM_STS_POWEROFF_AVAIL BIT(2) +#define THERM_STS_SHUTDOWN_AVAIL BIT(3) +#define THERM_STS_THROTTLE_EVT BIT(4) +#define THERM_STS_POWEROFF_EVT BIT(5) +#define THERM_STS_SHUTDOWN_EVT BIT(6) +/* BIT(7) reserved */ +#define THERM_STS_THROTTLE_ON BIT(8) +#define THERM_STS_POWEROFF_ON BIT(9) +#define THERM_STS_SHUTDOWN_ON BIT(10) +/* BIT(11) reserved */ +#define THERM_STS_THROTTLE_LOG BIT(12) +#define THERM_STS_POWEROFF_LOG BIT(13) +#define THERM_STS_SHUTDOWN_LOG BIT(14) + +static u8 pmc_len[] =3D { +/* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f */ +/* 0 */ 2, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* 1 */ 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* 2 */ 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* 3 */ 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* 4 */ 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* 5 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* 6 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* 7 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* 8 */ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* 9 */ 2, 1, 2, +}; + +static char therm_name[0x20][NAME_SIZE + 1] =3D { + "CPU0", "CPU1", "CPU2", "CPU3", "SYS0", "SYS1", "SYS2", "SYS3", + "AUX0", "AUX1", "AUX2", "AUX3", "DIMM0", "DIMM1", "DIMM2", "DIMM3", + "PCH", "VGA", "", "", "", "", "", "", + "", "", "", "", "OEM0", "OEM1", "OEM2", "OEM3", +}; + +static const u8 ctrl_map[] =3D { + CTRL_SHUTDOWN, CTRL_POWEROFF, CTRL_THROTTLE +}; + +struct eio_thermal_dev { + struct device *mfd; + struct device *dev; + u8 ch; + u8 name; +}; + +struct eio_trip_dev { + struct device *mfd; + u8 ch; + u8 idx; +}; + +static int timeout; +module_param(timeout, int, 0444); +MODULE_PARM_DESC(timeout, "Set PMC command timeout value.\n"); + +static int pmc_write(struct device *mfd, u8 ctrl, u8 dev_id, void *data) +{ + if (ctrl >=3D ARRAY_SIZE(pmc_len)) + return -EINVAL; + + struct pmc_op op =3D { + .cmd =3D CMD_THERM_WRITE, + .control =3D ctrl, + .device_id =3D dev_id, + .payload =3D (u8 *)data, + .size =3D pmc_len[ctrl], + .timeout =3D timeout, + }; + + return eio_core_pmc_operation(mfd, &op); +} + +static int pmc_read(struct device *mfd, u8 ctrl, u8 dev_id, void *data) +{ + if (ctrl >=3D ARRAY_SIZE(pmc_len)) + return -EINVAL; + + struct pmc_op op =3D { + .cmd =3D CMD_THERM_READ, + .control =3D ctrl, + .device_id =3D dev_id, + .payload =3D (u8 *)data, + .size =3D pmc_len[ctrl], + .timeout =3D timeout, + }; + + return eio_core_pmc_operation(mfd, &op); +} + +static int eio_tz_get_temp(struct thermal_zone_device *tzd, int *temp) +{ + struct eio_thermal_dev *eio_thermal =3D thermal_zone_device_priv(tzd); + u16 val =3D 0; + int ret; + + ret =3D pmc_read(eio_thermal->mfd, CTRL_VALUE, eio_thermal->ch, &val); + if (ret) + return ret; + + *temp =3D DECI_KELVIN_TO_MILLI_CELSIUS(val); + return 0; +} + +static int eio_tz_set_trip_temp(struct thermal_zone_device *tzd, + const struct thermal_trip *trip, int temp) +{ + struct eio_thermal_dev *eio_thermal =3D thermal_zone_device_priv(tzd); + const u8 ctl =3D (uintptr_t)trip->priv; + u16 val; + + if (temp < 1000) + return -EINVAL; + + val =3D MILLI_CELSIUS_TO_DECI_KELVIN(temp); + return pmc_write(eio_thermal->mfd, ctl, eio_thermal->ch, &val); +} + +static int eio_tz_change_mode(struct thermal_zone_device *tzd, + enum thermal_device_mode mode) +{ + struct eio_thermal_dev *eio_thermal =3D thermal_zone_device_priv(tzd); + int trip; + int ret =3D 0; + + for (trip =3D 0; trip < TRIP_NUM; trip++) { + ret =3D pmc_write(eio_thermal->mfd, ctrl_map[trip], eio_thermal->ch, &mo= de); + if (ret) + dev_err(eio_thermal->dev, "Error when %s trip num %d\n", + mode =3D=3D THERMAL_DEVICE_ENABLED ? "enabling" : "disabling", + trip); + } + + return ret; +} + +static struct thermal_zone_device_ops zone_ops =3D { + .get_temp =3D eio_tz_get_temp, + .set_trip_temp =3D eio_tz_set_trip_temp, + .change_mode =3D eio_tz_change_mode, +}; + +static struct thermal_zone_params zone_params =3D { + .no_hwmon =3D true, +}; + +static int eio_thermal_probe(struct platform_device *pdev) +{ + struct device *dev =3D &pdev->dev; + int ch; + + if (!dev_get_drvdata(dev->parent)) { + dev_err(dev, "eio_core not present\n"); + return -ENODEV; + } + + for (ch =3D 0; ch < THERM_NUM; ch++) { + u16 state =3D 0; + u8 name =3D 0; + u16 hi_shutdown =3D 0, hi_poweroff =3D 0, hi_throttle =3D 0; + int t_shutdown =3D 0, t_poweroff =3D 0, t_throttle =3D 0; + struct thermal_trip trips[TRIP_NUM]; + int ntrips =3D 0; + struct eio_thermal_dev *eio_th; + struct thermal_zone_device *tzd; + + if (pmc_read(dev->parent, CTRL_STATE, (u8)ch, &state) || + pmc_read(dev->parent, CTRL_TYPE, (u8)ch, &name)) { + dev_info(dev, "thermal%d: PMC read error\n", ch); + continue; + } + + if (!(state & THERM_STS_AVAIL) || + !((state & THERM_STS_THROTTLE_AVAIL) || + (state & THERM_STS_POWEROFF_AVAIL) || + (state & THERM_STS_SHUTDOWN_AVAIL))) { + dev_info(dev, "thermal%d: firmware not activated\n", ch); + continue; + } + + if (name >=3D ARRAY_SIZE(therm_name) || !therm_name[name][0]) { + dev_info(dev, "thermal%d: unknown sensor name idx=3D%u\n", ch, name); + continue; + } + + /* Throttle starts a 1C increase it */ + int throttle_temp =3D MILLI_CELSIUS_TO_DECI_KELVIN(60000); + + pmc_write(dev->parent, CTRL_THROTTLE_HI, (u8)ch, &throttle_temp); + + pmc_read(dev->parent, CTRL_SHUTDOWN_HI, (u8)ch, &hi_shutdown); + pmc_read(dev->parent, CTRL_POWEROFF_HI, (u8)ch, &hi_poweroff); + pmc_read(dev->parent, CTRL_THROTTLE_HI, (u8)ch, &hi_throttle); + + t_shutdown =3D DECI_KELVIN_TO_MILLI_CELSIUS(hi_shutdown); + t_poweroff =3D DECI_KELVIN_TO_MILLI_CELSIUS(hi_poweroff); + t_throttle =3D DECI_KELVIN_TO_MILLI_CELSIUS(hi_throttle); + + ntrips =3D 0; + if (hi_shutdown) { + trips[ntrips].type =3D THERMAL_TRIP_CRITICAL; + trips[ntrips].temperature =3D t_shutdown; + trips[ntrips].flags =3D THERMAL_TRIP_FLAG_RW_TEMP; + trips[ntrips].priv =3D THERMAL_INT_TO_TRIP_PRIV(TRIP_SHUTDOWN), + ntrips++; + } + if (hi_poweroff) { + trips[ntrips].type =3D THERMAL_TRIP_HOT; + trips[ntrips].temperature =3D t_poweroff; + trips[ntrips].flags =3D THERMAL_TRIP_FLAG_RW_TEMP; + trips[ntrips].priv =3D THERMAL_INT_TO_TRIP_PRIV(TRIP_POWEROFF), + ntrips++; + } + if (hi_throttle) { + trips[ntrips].type =3D THERMAL_TRIP_PASSIVE; + trips[ntrips].temperature =3D t_throttle; + trips[ntrips].flags =3D THERMAL_TRIP_FLAG_RW_TEMP; + trips[ntrips].priv =3D THERMAL_INT_TO_TRIP_PRIV(TRIP_THROTTLE), + ntrips++; + } + if (!ntrips) { + dev_info(dev, "thermal%d: no valid trips\n", ch); + continue; + } + + eio_th =3D devm_kzalloc(dev, sizeof(*eio_th), GFP_KERNEL); + if (!eio_th) + return -ENOMEM; + eio_th->ch =3D (u8)ch; + eio_th->mfd =3D dev->parent; + eio_th->dev =3D dev; + + tzd =3D thermal_zone_device_register_with_trips(therm_name[name], + trips, + ntrips, + eio_th, + &zone_ops, + &zone_params, + THERMAL_PASSIVE_DELAY, + THERMAL_POLLING_DELAY); + if (IS_ERR(tzd)) + return PTR_ERR(tzd); + /* Make sure zones start disabled */ + thermal_zone_device_disable(tzd); + + dev_info(dev, "%s thermal up (ch=3D%d)\n", therm_name[name], ch); + } + + return 0; +} + +static struct platform_driver eio_thermal_driver =3D { + .probe =3D eio_thermal_probe, + .driver =3D { + .name =3D "eio_thermal", + }, +}; +module_platform_driver(eio_thermal_driver); + +MODULE_AUTHOR("Wenkai Chung "); +MODULE_AUTHOR("Ramiro Oliveira "); +MODULE_DESCRIPTION("Thermal driver for Advantech EIO embedded controller"); +MODULE_LICENSE("GPL"); --=20 2.43.0 From nobody Sun Dec 14 06:34:44 2025 Received: from SEYPR02CU001.outbound.protection.outlook.com (mail-koreacentralazon11023084.outbound.protection.outlook.com [40.107.44.84]) (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 87C6C2D63FF; Fri, 12 Dec 2025 16:42:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.107.44.84 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765557733; cv=fail; b=XGbQvrpXDtV1SfEOZ45vuUB+OKMNlxhBxTZXcs+mMK4l7kvQjUvUu5n4J3tjm0aL1Qlxo4cTrXXiKB81rQ7zEcv28clEn9vvqCT6ghxgA6wq3+gVrdaXtN6eYsNNvFPYTNgWwSiZPyxzY9Ooz3MhVa/cRF0At/kmfLl/r6Wg4mY= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1765557733; c=relaxed/simple; bh=51WajgEbvqgEKRA7RH5RVOmoSeCUuZhN+95x5loIgvM=; h=From:Date:Subject:Content-Type:Message-Id:References:In-Reply-To: To:Cc:MIME-Version; b=lUKQVpLj3ZWOclKRfWFHJ89nk8TYS9H2sTo9pNU1SjK2eeEHJzniFETgd9PJV4wl1EW8GOX5qerppI7yGfaPKhYUHl0HhRvf9L/CVvpc6wHu0qPYcGM/3wybaEAIhlZyHSf48xExd+2SaXQTUYxF0UJxWvovZiJqNmdWrBIiFHY= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=advantech.com; spf=pass smtp.mailfrom=advantech.de; dkim=pass (2048-bit key) header.d=advantech.com header.i=@advantech.com header.b=qCE/Oxdb; arc=fail smtp.client-ip=40.107.44.84 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=advantech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=advantech.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=advantech.com header.i=@advantech.com header.b="qCE/Oxdb" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=tqtISj+e6XS4/DYgwWo7vfg1YgQg00mFZl/lclaX09NftgjfLeM481Uu0zMnfyOIe0vfvWzRoXuQw0UkRMCmE/cTil84ipgV726UisHoymAC2tbydEU13/z7TQ/tupGc5rI3iptFQNozsZmJkXd+xOeIXkwMbjr1rLOW6wnaCil37Bw3KsSdaJGltaYEDIHQ1yMbcNnR+DkkxADUVfdGoRDqlWjSw8VwcKvlNmmO+ZLQR/wgLz/NrrMI18P9pUI6WQF0WrWxIlAetNRQSj3TESHbWKHKpGuA72rQpmudiTZXSx+LKiPHajjXMXO/+o3I0P1bb//VKptpQVYx/3oTtA== 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=QqUN7pptFhqfCYONenv7vxAeHgv1MtZwOFVyEjl+bHw=; b=je2i8779o50Mk3XAk4c2NbsYFFpwfvx2bOdtBxYi5zVbNzlma7Ba1PnoVajYK1LoxgutM/AMm4qdDwSY5dEkMBErnFny4Vn6SJuCWSvqd7z8QLyaqqLS268jesE7YzLoIOL21fXDzp9FZFexv+ObbQMHx/TRjzwmaSDa5wA287wI6Nq7N9efsM4uKCSAhvQYiDSERMsF0mwUfukCr0rH9jJ6xKuuxb+l2Qi9VaMEvLh8wiCMq/+US95yi1yVTpUa13rKHkQEJTHsPKUpd7QqluaMt48/N4fAZ/w1BwNtpeFXaWVNfngVQtNyXEwsHKG+DO4Kw7pc0r5dR7U8wQN5HQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=advantech.de; dmarc=pass action=none header.from=advantech.com; dkim=pass header.d=advantech.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=advantech.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=QqUN7pptFhqfCYONenv7vxAeHgv1MtZwOFVyEjl+bHw=; b=qCE/OxdbWc0F9LGZgEm6Y+goJ4bRIgAj/z7a0sae2rquuiveugiJFov3TQhqyrOsz9GopHuBTFRy3uXCaiJ9VoVWbkCcKS/0wu1bHkFLkx0TqbsCKu4nis7FVIHWjVMgCfB4mcfi4fJZ2p1a5agkthjCmJH4Girn09UOVOWW9qXHeQuh+BvkStO5R/rXnrGiaBez5xHCvKTeX/NlRoCxQf9sPF3RRLE9XQEGAGZwSSXilTn0oK74tCAmTuvecFFi7bHXYotfhAKDKLVPZ8/bbboceQKY32Jr+6IADZr2hRcueldEg51yqBiL1Kgg8wAm3OLoTrk7V0PumZsQSpsEDg== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=advantech.com; Received: from PSAPR02MB4502.apcprd02.prod.outlook.com (2603:1096:301:21::6) by JH0PR02MB6564.apcprd02.prod.outlook.com (2603:1096:990:3f::9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9412.11; Fri, 12 Dec 2025 16:42:08 +0000 Received: from PSAPR02MB4502.apcprd02.prod.outlook.com ([fe80::59c9:fe0:25f6:702b]) by PSAPR02MB4502.apcprd02.prod.outlook.com ([fe80::59c9:fe0:25f6:702b%6]) with mapi id 15.20.9412.005; Fri, 12 Dec 2025 16:42:08 +0000 From: Ramiro Oliveira Date: Fri, 12 Dec 2025 17:40:59 +0100 Subject: [PATCH 8/8] Add Advantech EIO Fan driver Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251212-upstream-v1-v1-8-d50d40ec8d8a@advantech.com> References: <20251212-upstream-v1-v1-0-d50d40ec8d8a@advantech.com> In-Reply-To: <20251212-upstream-v1-v1-0-d50d40ec8d8a@advantech.com> To: Lee Jones , Linus Walleij , Bartosz Golaszewski , Guenter Roeck , Andi Shyti , Daniel Thompson , Jingoo Han , Helge Deller , Wim Van Sebroeck , "Rafael J. Wysocki" , Daniel Lezcano , Zhang Rui , Lukasz Luba Cc: linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, linux-hwmon@vger.kernel.org, linux-i2c@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-fbdev@vger.kernel.org, linux-watchdog@vger.kernel.org, linux-pm@vger.kernel.org, Wenkai Chung , Francisco Aragon-Trivino , Hongzhi Wang , Mikhail Tsukerman , Thomas Kastner , Ramiro Oliveira X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=15819; i=ramiro.oliveira@advantech.com; h=from:subject:message-id; bh=51WajgEbvqgEKRA7RH5RVOmoSeCUuZhN+95x5loIgvM=; b=owEB7QES/pANAwAKAc7t0Ke8vbAJAcsmYgBpPEWhfKRACF01ila+8eGN3kwcftzUzGOqanEwl QQ3waIjsCeJAbMEAAEKAB0WIQS1Nkng0ZvJmBKh6GLO7dCnvL2wCQUCaTxFoQAKCRDO7dCnvL2w CbGCDADD/LewB6FT+KGC0hVpUy8uIvHVcyhhscDZ2EP5xHd/VWhdRX/SM+RTbQBGxERWuzR92JP sC5vUWkNp7sIlsAFp9hWhMS/TggAmmWWyLgdaQSGsM6LHjcN9+XkTDCVO+85Ph+sTG7ifpEk9wV iKOOSFTkm3uyWQBWMcF5zEvHODSVEe3INiy94PD328yL7BMLcJvSr+R1Sc0v+EmcRXg5MRNibh2 zL3qqxYkLBAn6IW3WMgh8Lp0vrDCMc50zWSJOU75jTfdGaXcBmyj59T7Y5ul0+18ZDHTZL9z0BT Z+W4/ZSBUFmhHRNzXBdsg8ll6ay+9azR6SgzAjLbID1rbM5LMH2gtuFV8ogf1W5Tjnr6y9T5X+w lAejA5sdE1ilWeiGr/h1V5lW/YUVCEcIGG3KrRJyUcMegD2Y+GFsj8vKtmKqdfbAlX+4rTDx/yw Tn4VoCQ0KLfGLkOKS+dLJ3JpGmi9c+mULw6S+uh3UGSRSszpp7+3Cu1HyFQDchgi+H0ao= X-Developer-Key: i=ramiro.oliveira@advantech.com; a=openpgp; fpr=B53649E0D19BC99812A1E862CEEDD0A7BCBDB009 X-ClientProxiedBy: FR4P281CA0175.DEUP281.PROD.OUTLOOK.COM (2603:10a6:d10:b7::12) To PSAPR02MB4502.apcprd02.prod.outlook.com (2603:1096:301:21::6) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PSAPR02MB4502:EE_|JH0PR02MB6564:EE_ X-MS-Office365-Filtering-Correlation-Id: db491152-3f02-4021-5de5-08de399d640d X-MS-Exchange-AtpMessageProperties: SA X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|1800799024|7416014|366016|52116014|376014|38350700014|921020; X-Microsoft-Antispam-Message-Info: =?utf-8?B?M0NaekpJU3g0cUlJenUwdW5qQ0Fld2E3TVIrTC9iYk0vZXVOeG9NM3duREJ5?= =?utf-8?B?UkxtR0ErN05pU1ZVKzBsTm40cFplVDI3b3RjOUhNbjJHcFRvVEhhb05iTmZG?= =?utf-8?B?ZWZvZ0E3TlNxVnJ1OVBKK0ZKb2tTbjkzSWhicnRMM0JOMkNrZEtkT01rbzFP?= =?utf-8?B?OTRxNW05UFJiL3RYMUszVWZJcWt6bkFrbHNEbGRtTzZ2MlZVakN4NVplOG93?= =?utf-8?B?QS9rWXlxSDdhTE8ycExMUnNGVGRTK3haRTFjVUNobEhla0tVVkFzV0N1ZFFk?= =?utf-8?B?RXVRS2VBUFQ5S0tIMFJEVUluc2ZQc3FMRDBuWHNPTUowM0NKY0pwZlppcm1N?= =?utf-8?B?OW5ydlUyV3A2RmJsbUcwamlNcWFlNThFMXYwd3VTWFZUenFEaWxsMG1tTEJK?= =?utf-8?B?OUxVM0c0VEdRckJJZWEzSGNrWHJUMHErZTErMWhRUW1KcTFIZnRBdUF4a2lP?= =?utf-8?B?eVk4RmpFZzltcm4xOG0vdlNTcDcvMTlZK2dqWENoamRTNTFWYjQ1U2p0Qkw1?= =?utf-8?B?c0Zpc044RmNINHUxRnlKdVQ0UVJtM2VpNGg5RENEMk5uRVpPd2dQUHkrd2Ex?= =?utf-8?B?SThicWQ1KzNqUmsxVnhQcnU5UW1UblJoQ1NwdjlCN2FpWU56OUpiWWZ0bysz?= =?utf-8?B?aVVTREs4Qk5tSC82UDV6Tno2VmZlZjN4Q2FhYngvTERpdUVCbjh0NjZFZDNl?= =?utf-8?B?ZjdKUk56dGJ3eWMzWUpqRWo2Wk4wSmZydXNnaGhtQVRsa0U1YzB1NjZjeFdZ?= =?utf-8?B?L2twcHFUYlp6WDU5dER1emVtWDJZcDI5WmdqVnd1Qmwweks3ODdYZFFJVWpK?= =?utf-8?B?RStPcFBFM2R3QlNYbjdMNGR3amlnNnY1NXJSaFFsNVFSajNBZVJOWjczVkNq?= =?utf-8?B?MFZpK0ZWaEQyalNXd2owZjdJNHIxcXZHTkdLRjdmS0xYdER0OHliVXBsSmlY?= =?utf-8?B?MFArME05eStncDU2SGE0c3g5QjVGMXpxRlFqRU04UFJtcmRweGhNTExOU1VD?= =?utf-8?B?NUZkQk9NRjB1OGlkRWg5RmtQSkZETWlXUFRRNHJSb3VJZXUvL216ejM5SmMx?= =?utf-8?B?VVJPQjFMRncvSzFzaFlFL0FpMUJjR2FhYzlPeGV0anJncjRRWk5vQkcwc3Bt?= =?utf-8?B?Yi9yekYxUWdTS0VudUpRM1lhRlFzZ3BpeDBqU05uV3Q3R2IwL1VjTk55L0Fk?= =?utf-8?B?elFsVFkvbm54MWhXK0V2c2hJYjh3NDlpOVRjNnBhc0c2ZlNUUXBFM3hqYlVP?= =?utf-8?B?eUlLWHc5KzhuTnpzNDJkWm00anJZd2VjRkNsZGtKUFBkS2pZbVZBbTRCeVd0?= =?utf-8?B?YlRWVWdqN05tMFMwTWdIM21PUTFad1A4czB5RUNlT25MTmduMFBxQmFYZXRK?= =?utf-8?B?a0RCczVGZXM3NVdwc2NTWkE0OTNMOFJXM0FkL2wvcHNia3Y5Yk9FSW4wT3FX?= =?utf-8?B?cWFtM3lHSzhJU1dzQWwrSFp0QkdFYzh5ZlQ5aFgxRkpCYmVCTk1NNGUxOFVS?= =?utf-8?B?SHlXMXBMcWdGanp0NExhQlBHUGkrYW80TkJxRm9FcHQ2YUE2dllyZmpxc09N?= =?utf-8?B?cVZraVI0UE5KaVV3dnpzR0Fpc3RFengxdTBZdXQrZDVyUzFURVIxV2MxalY5?= =?utf-8?B?TmNaQUcrekdSU0REODNwQnVIV0pFZ2xXa1FLNXdiOVFKVW5uYkVOazZlL05M?= =?utf-8?B?VFI5MnVWbHJPdXhGV1A4WkpIM2ZSSGgxTVpiWnhMMkM0VW8wOGIyYlpPZ2l6?= =?utf-8?B?akdLK2tnOGR3d05JN0FhcVA0dTlhOWxSRXlEYXgrWThJUlRpNmkrOElJYkJo?= =?utf-8?B?eDZGYWdZdmpMUjVmTFBKanFwbUU3QXY3c1dlZDBGZGZBQktaSEh5blBJUHNY?= =?utf-8?B?TTloS3N4TEUxTy9teTh3aUJhQnBsRXhsOGxCQlRBV0t3Slp4L0lXOXlhUFVK?= =?utf-8?B?RU90MUhXaER0Njg5VUZobFlyN01jeEgxQUVDeEMxeGFCUkZZdk14OENCVW83?= =?utf-8?B?Snk5L1ArWnpHcGVsM1E3aWY1R1pEd1pEUk1KTUNkaTkxQ3I4eXpES3dIVDJY?= =?utf-8?B?VHJ0OFoydHpmV0pWT2I1aW5HTkxQMXYrUnk5UT09?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PSAPR02MB4502.apcprd02.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(1800799024)(7416014)(366016)(52116014)(376014)(38350700014)(921020);DIR:OUT;SFP:1102; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?ZFJHYi93R25NdXpDbDVVL0prR280WW9VSGpXQnJIc0FPWEsrRVlwY1ZaeUpJ?= =?utf-8?B?UWxpSFl0ZGh1V3RTMWJCaFhrNUNTTHovaS9zWjFKVnVURVNWZSt1TlBRcGo4?= =?utf-8?B?S0dqK1hrdnhLL3VOUjFEcG1kVUdyY2VKR29wR2xaN2cwNkhtOFJxNis4TXpr?= =?utf-8?B?WEUxVEhReTdPSWJEelo5ZmFpSGQ3S2tBQThlZSt2elBKMHpKaVQxOUdJTHFO?= =?utf-8?B?RG5hZTBSNzJzdnJsbGl3T3ROeTVHaEZUSWFCOWp4cUZ5QTY2clJsL0VaRXhN?= =?utf-8?B?QlROak1FYytiaHRDb2dOKzk3SGthaE9Pd0RTOHVoY0RDTHB1Y0hvOVExaEVz?= =?utf-8?B?VWtTQ1VjaWlZSFpubkw3OXlCcFZ1bm1sSDJZR1pOZ0dLaVYxZ0tlTTVjd2sv?= =?utf-8?B?UlBKSFlxNk1Kd3NKcGZsQTFKVTJsR3Z4VWdvckFJY2kvL0F2OHorSzJnYVdB?= =?utf-8?B?cGIwYUtlNlN2YjZqd0VmR0thZG9aRTBseUsyaTBpVGhJVE5scWZrWjRHTW1V?= =?utf-8?B?cFFRSTNIYVdKSXdrL2tmczRaazhndjV5bHNRR3d6SStJYU1mTktlT2NuTnM1?= =?utf-8?B?WHZDUjduM3hnZktMYThhYjdpNkx6c0Z3azZ5cVAwUzNIM25OVzFHdHRiL3FX?= =?utf-8?B?ampCc3dLSFZQY1hidm5EK04rNURTbUs5MjVtMUpQRnhQekhGYk12YTN1SWtw?= =?utf-8?B?Nk5KOUlpZzVXYmtFTlI0RnQxOWxXZ0xnLzNPMDdQL1NZYW5jYnhpcWQ4Znli?= =?utf-8?B?ZFZKYis4WDF3TXY5UW5lWEp4VTQwM0krajJOQklIcHpUR0YwdmR2SW5WVWg0?= =?utf-8?B?aFY5Z0hDeGc4WVczSXF1SE5WVXQxL3JRUXNMNmpnaWlCcWJEVVRUVkZwREs0?= =?utf-8?B?V1JKQjhqRittWGdSa1hXVXUxMlAvUUdCRFJxNEdzSXpPZXdTclRGL0w2YzI3?= =?utf-8?B?dmE4VDNsZm9SZXhLWnN6YStadGVTeUNhY282cmVjR0I4ejJha3dmNHlUaWV2?= =?utf-8?B?L20waExwZ1ZBUkNOdlg5UFd2THluV1QzakZtb2g0RCt0cllJR2RRWk5HVlN1?= =?utf-8?B?SjJ6OHY4MkxGQWlZUW9sRFZuZFlpT05nTlVsS1c1NTYrd0dkT0ZQQndVUDlQ?= =?utf-8?B?MlFmc3gyVjNxei9tNXJzYlFkUkNGdXA5b2ZJNjlHeUkyUmdJR0xJbWZWaWw3?= =?utf-8?B?RDVha0QzSHRxL2RQbTA5a1JWdFVMUDhHRnlsQkZvcDNlS2Y4anNTQkNJdGRL?= =?utf-8?B?bGYxVzlZRi85TmhXSnhDM0xuZklScFhyRFpNeS94dERtRDFidGtNSWVsWTZ0?= =?utf-8?B?ZzZXekQ2bnZUK0VYRnRzbldqbGZpdHpWcXlwckZveXZncEVPdFgxY3drbzlM?= =?utf-8?B?MHlBOHNweWRJWWttYW1EQWVhRmxSSmZhdDRqZkJEaDQrd0p4TnV2ZUQwaTNp?= =?utf-8?B?R2xoTkFPRjFld2lLQjhaZFQ1cUZ2ck9iUVV1bjJmV2tBeVpSQzhOWmxNTmJ3?= =?utf-8?B?UlRUNE5WZHF5TWhkTUxaQW1Cc25CZE1kRGs2STlRd21HbzQ2OU1NczVHV3Z2?= =?utf-8?B?RmhOcFR3Y3pFT1ZBdHVreTBrdS9xdzNCVkQraC83VUw3T0tyazdScGY3T1BS?= =?utf-8?B?eXRuQlhLbkFUcFVJTlNJeXVkdUw5bHRTa0xVTFFtWlRhaWVRaW95OG41dDM5?= =?utf-8?B?UTJZMHY3QTJJM0lWbE41NW1xL1NXVGFURVdvNXo2VUhheTRhV1FxMzZwNDVs?= =?utf-8?B?MWJGQ0NDd05ZRlk5Y0FKdGZHM3VaM1ovcUhCYXBXSEx1S2dNVXh6c2YyZ0FU?= =?utf-8?B?cjZSeEM3eDFOLy9QOVFLSVc4NUhLSGY1R3hCVGZOQWtMQ2ZhYzRHNk9FYWNY?= =?utf-8?B?ZTVISU5GeGtBaFBwZnF2cW9mMlozYS9QVXgwTVJ4bk9uTnBwZUZpd3JJVytl?= =?utf-8?B?MUczV1pHRFptMkQwZ21XWHM3YThxL2piN3VWdkxOSUkrNU56Z3RoMlJIRWVT?= =?utf-8?B?TDE5bDZFK3RiV0lTVjZxbFlPR2drdzIxTzJMZlpneER2TjFSZG1QbzJvd1g3?= =?utf-8?B?THBCZmprRGRRSTVTcnB0LzhGQ0Z1MlNtSUlJeXBLSkpRQUozVkdqZm9DVUtH?= =?utf-8?B?SlJrZUpKNWFRWmhPRmNUMnFNVDJXRk81cEw3WHk5YlNQMDlQYnZZT2NUd1Rp?= =?utf-8?Q?+J6z8aIll8HmvB56YBPieFg=3D?= X-OriginatorOrg: advantech.com X-MS-Exchange-CrossTenant-Network-Message-Id: db491152-3f02-4021-5de5-08de399d640d X-MS-Exchange-CrossTenant-AuthSource: PSAPR02MB4502.apcprd02.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 12 Dec 2025 16:42:08.5609 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: a77d40d9-dcba-4dda-b571-5f18e6da853f X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: EYglerHuhbCiuJqTykCDb9EmBur234FsFi/x4ej3Hf4nIx+nb7VEt+X/S7wdKKEiGvYu+4D9eNVE0AaQGJtOlZIoVKmxwCxzf2k7c9Ehdc8= X-MS-Exchange-Transport-CrossTenantHeadersStamped: JH0PR02MB6564 This commit adds the driver to control the Advantech EIO Fan block, which is included in the Advantech EIO Embedded Controller. Signed-off-by: Ramiro Oliveira --- MAINTAINERS | 1 + drivers/thermal/Kconfig | 8 + drivers/thermal/Makefile | 1 + drivers/thermal/eio_fan.c | 490 ++++++++++++++++++++++++++++++++++++++++++= ++++ 4 files changed, 500 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 770b2f82d01a..b227a9d28191 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -623,6 +623,7 @@ F: drivers/gpio/gpio-eio.c F: drivers/hwmon/eio-hwmon.c F: drivers/i2c/busses/i2c-eio.c F: drivers/mfd/eio_core.c +F: drivers/thermal/eio_fan.c F: drivers/thermal/eio_thermal.c F: drivers/video/backlight/eio_bl.c F: drivers/watchdog/eio_wdt.c diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 7309f7e7a1c1..ba4958ff0962 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -436,6 +436,14 @@ config EIO_THERMAL the Linux thermal framework. It communicates with the EC through the EIO MFD core. =20 +config EIO_FAN + tristate "Advantech EIO Fan cooling device" + depends on MFD_EIO && THERMAL + help + Fan cooling device for the Advantech EIO. This driver exposes a + thermal cooling device with controllable states (e.g. Auto/Manua= l/PWM). + It communicates with the EC through the EIO MFD core. + menu "Mediatek thermal drivers" depends on ARCH_MEDIATEK || COMPILE_TEST source "drivers/thermal/mediatek/Kconfig" diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 3740540d8a18..2633e8ed9fdc 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_IMX91_THERMAL) +=3D imx91_thermal.o obj-$(CONFIG_MAX77620_THERMAL) +=3D max77620_thermal.o obj-$(CONFIG_QORIQ_THERMAL) +=3D qoriq_thermal.o obj-$(CONFIG_DA9062_THERMAL) +=3D da9062-thermal.o +obj-$(CONFIG_EIO_FAN) +=3D eio_fan.o obj-$(CONFIG_EIO_THERMAL) +=3D eio_thermal.o obj-y +=3D intel/ obj-$(CONFIG_TI_SOC_THERMAL) +=3D ti-soc-thermal/ diff --git a/drivers/thermal/eio_fan.c b/drivers/thermal/eio_fan.c new file mode 100644 index 000000000000..7f0529a1907e --- /dev/null +++ b/drivers/thermal/eio_fan.c @@ -0,0 +1,490 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * eio_fan + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * Thermal zone driver for Advantech EIO embedded controller's smart + * fan mechanism. + * + * We create a sysfs 'name' of the zone, point out where the fan is. Such = as + * CPU0, SYS3, etc. + * + * The sysfs 'fan_mode' can be one of 'Stop', 'Full', 'Manual' or 'Auto'. + * If 'Manual'. You can control fan speed via sysfs 'PWM'. + * If it is 'Auto'. It enables the smart fan mechanism as below. + * + * In EIO chip. The smart fan has 3 trips. When the temperature is: + * - Over Temp High(trip0), the Fan runs at the fan PWM High. + * - Between Temp Low and Temp High(trip1 - trip0), the fan PWM value slop= es + * from PWM Low to PWM High. + * - Between Temp Stop and Temp Low(trip2 - trip1), the fan PWM is PWM low. + * - Below Temp Stop, the fan stopped. + * + * (PWM)| + * | + * High |............................. ______________ + * (Max)| /: + * | / : + * | / : + * | / : + * | / : + * | / : + * | / : + * | / : + * Low |.......... __________/ : + * | | : : + * | | : : + * 0 +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D+---------+--------+------------- + * 0 Stop Low High (Temp) + * + * Copyright (C) 2025 Advantech Corporation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define CMD_FAN_WRITE 0x24 +#define CMD_FAN_READ 0x25 +#define FAN_MAX 0x04 + +#define CMD_THERM_WRITE 0x10 +#define CMD_THERM_READ 0x11 +#define THERM_MAX 0x04 +#define THERM_MULTI 100 + +#define CTRL_STATE 0x00 +#define CTRL_TYPE 0x01 +#define CTRL_CTRL 0x02 +#define CTRL_ERROR 0x04 +#define CTRL_VALUE 0x10 +#define CTRL_INVERT 0x11 +#define CTRL_FREQ 0x12 +#define CTRL_THERM_HIGH 0x13 +#define CTRL_THERM_LOW 0x14 +#define CTRL_THERM_STOP 0x15 +#define CTRL_PWM_HIGH 0x16 +#define CTRL_PWM_LOW 0x17 +#define CTRL_THERM_SRC 0x20 + +#define CTRLMODE_STOP 0x00 +#define CTRLMODE_FULL 0x01 +#define CTRLMODE_MANUAL 0x02 +#define CTRLMODE_AUTO 0x03 + +#define DUTY_MAX 100 +#define UNIT_PER_TEMP 10 +#define NAME_SIZE 4 + +#define TRIP_HIGH 0 +#define TRIP_LOW 1 +#define TRIP_STOP 2 +#define TRIP_NUM 3 + +/* Bitfields inside CTRL_CTRL */ +#define FAN_MODE_MASK GENMASK(1, 0) +#define FAN_SCM_BIT BIT(2) +#define FAN_FRAME_BIT BIT(3) +#define FAN_SRC_MASK GENMASK(7, 4) + +#define FAN_SRC(val) (((int)(val)) >> 4) + +#ifndef DECI_KELVIN_TO_MILLI_CELSIUS +#define DECI_KELVIN_TO_MILLI_CELSIUS(t) ((((t) - 2731) * 100)) +#endif + +#ifndef MILLI_CELSIUS_TO_DECI_KELVIN +#define MILLI_CELSIUS_TO_DECI_KELVIN(t) ((((t) / 100) + 2731)) +#endif + +static const u8 pmc_len[CTRL_THERM_SRC + 1] =3D { +/* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f */ + 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 4, 2, 2, 2, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, + 1, +}; + +static const char fan_name[0x20][NAME_SIZE + 1] =3D { + "CPU0", "CPU1", "CPU2", "CPU3", "SYS0", "SYS1", "SYS2", "SYS3", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "OEM0", "OEM1", "OEM2", "OEM3", +}; + +struct eio_fan_trip { + u8 trip_ctl; +}; + +struct eio_fan_dev { + struct device *mfd; + struct device *dev; + u8 id; + struct thermal_zone_device *tzd; + struct eio_fan_trip trip_priv[TRIP_NUM]; +}; + +static int timeout; +module_param(timeout, int, 0444); +MODULE_PARM_DESC(timeout, "Set PMC command timeout value.\n"); + +static int pmc_write(struct device *mfd, u8 ctrl, u8 id, void *data) +{ + if (ctrl >=3D ARRAY_SIZE(pmc_len)) + return -EINVAL; + + struct pmc_op op =3D { + .cmd =3D CMD_FAN_WRITE, + .control =3D ctrl, + .device_id =3D id, + .size =3D pmc_len[ctrl], + .payload =3D (u8 *)data, + .timeout =3D timeout, + }; + return eio_core_pmc_operation(mfd, &op); +} + +static int pmc_read(struct device *mfd, u8 ctrl, u8 id, void *data) +{ + struct pmc_op op =3D { + .cmd =3D CMD_FAN_READ, + .control =3D ctrl, + .device_id =3D id, + .size =3D pmc_len[ctrl], + .payload =3D (u8 *)data, + .timeout =3D timeout, + }; + return eio_core_pmc_operation(mfd, &op); +} + +static int pmc_read_therm(struct device *mfd, u8 ctrl, u8 id, void *data) +{ + struct pmc_op op =3D { + .cmd =3D CMD_THERM_READ, + .control =3D ctrl, + .device_id =3D id, + .size =3D 2, + .payload =3D (u8 *)data, + .timeout =3D timeout, + }; + return eio_core_pmc_operation(mfd, &op); +} + +static int eio_fan_get_temp(struct thermal_zone_device *tzd, int *temp) +{ + struct eio_fan_dev *fan =3D thermal_zone_device_priv(tzd); + struct device *mfd =3D fan->mfd; + u8 ch =3D fan->id; + int sensor =3D 0; + u16 val =3D 0; + int ret; + + ret =3D pmc_read(mfd, CTRL_CTRL, ch, &sensor); + if (ret) + return ret; + + ret =3D pmc_read_therm(mfd, CTRL_VALUE, (u8)FAN_SRC(sensor), &val); + if (ret) + return ret; + + *temp =3D DECI_KELVIN_TO_MILLI_CELSIUS(val); + return 0; +} + +static int eio_fan_set_trip_temp(struct thermal_zone_device *tzd, + const struct thermal_trip *trip, int temp) +{ + struct eio_fan_dev *fan =3D thermal_zone_device_priv(tzd); + const struct eio_fan_trip *fan_trip =3D trip->priv; + u8 ctl =3D CTRL_THERM_HIGH + fan_trip->trip_ctl; + u16 val; + + if (temp < 1000) + return -EINVAL; + + val =3D MILLI_CELSIUS_TO_DECI_KELVIN(temp); + return pmc_write(fan->mfd, ctl, fan->id, &val); +} + +static bool eio_fan_should_bind(struct thermal_zone_device *tzd, + const struct thermal_trip *trip, + struct thermal_cooling_device *cdev, + struct cooling_spec *spec) +{ + struct eio_fan_dev *tz_fan =3D thermal_zone_device_priv(tzd); + struct eio_fan_dev *cd_fan =3D cdev->devdata; + + if (!tz_fan || !cd_fan) + return false; + + if (tz_fan->mfd !=3D cd_fan->mfd || tz_fan->id !=3D cd_fan->id) + return false; + + return true; +} + +static const struct thermal_zone_device_ops zone_ops =3D { + .get_temp =3D eio_fan_get_temp, + .set_trip_temp =3D eio_fan_set_trip_temp, + .should_bind =3D eio_fan_should_bind, +}; + +static int eio_fan_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + *state =3D 100; + return 0; +} + +static int eio_fan_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct eio_fan_dev *fan =3D cdev->devdata; + int fan_mode =3D 0; + u8 duty =3D 0; + int ret =3D 0; + + *state =3D 0; + ret =3D pmc_read(fan->mfd, CTRL_CTRL, fan->id, &fan_mode); + if (ret) + return ret; + + switch (fan_mode & FAN_MODE_MASK) { + case CTRLMODE_STOP: + *state =3D 0; + break; + case CTRLMODE_FULL: + *state =3D 100; + break; + case CTRLMODE_AUTO: + *state =3D 0; + ret =3D 0; + break; + case CTRLMODE_MANUAL: + ret =3D pmc_read(fan->mfd, CTRL_VALUE, fan->id, &duty); + if (ret) + return ret; + duty =3D (u8)clamp_val(duty, 0, 100); + *state =3D duty; + break; + default: + *state =3D 0; + return -EINVAL; + } + return 0; +} + +static int eio_fan_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) +{ + struct eio_fan_dev *fan =3D cdev->devdata; + u8 ctrl =3D 0; + u8 duty; + int ret; + + ret =3D pmc_read(fan->mfd, CTRL_CTRL, fan->id, &ctrl); + if (ret) + return ret; + + if ((ctrl & FAN_MODE_MASK) !=3D CTRLMODE_MANUAL) + return -EOPNOTSUPP; + + duty =3D (u8)clamp_val(state, 0, 100); + + ret =3D pmc_write(fan->mfd, CTRL_VALUE, fan->id, &duty); + + return ret; +} + +static const struct thermal_cooling_device_ops cooling_ops =3D { + .get_max_state =3D eio_fan_get_max_state, + .get_cur_state =3D eio_fan_get_cur_state, + .set_cur_state =3D eio_fan_set_cur_state, +}; + +static ssize_t fan_mode_show(struct device *dev, struct device_attribute *= attr, + char *buf) +{ + static const char * const names[] =3D { "Stop", "Full", "Manual", "Auto" = }; + struct thermal_zone_device *tzd =3D dev_get_drvdata(dev); + struct eio_fan_dev *fan =3D thermal_zone_device_priv(tzd); + u8 mode =3D 0; + + int ret =3D pmc_read(fan->mfd, CTRL_CTRL, fan->id, &mode); + + if (ret) + return ret; + + return sysfs_emit(buf, "%s\n", names[mode & 0x03]); +} + +static ssize_t fan_mode_store(struct device *dev, struct device_attribute = *attr, + const char *buf, size_t count) +{ + static const char * const names[] =3D { "Stop", "Full", "Manual", "Auto" = }; + struct thermal_zone_device *tzd =3D dev_get_drvdata(dev); + struct eio_fan_dev *fan =3D thermal_zone_device_priv(tzd); + u8 ctrl, newc; + int mode_idx, ret; + + for (mode_idx =3D 0; mode_idx < ARRAY_SIZE(names); mode_idx++) { + if (strncasecmp(buf, names[mode_idx], strlen(names[mode_idx]))) + continue; + + ret =3D pmc_read(fan->mfd, CTRL_CTRL, fan->id, &ctrl); + if (ret) + return -EIO; + + newc =3D ctrl & FAN_SRC_MASK; + + switch (mode_idx) { + case CTRLMODE_AUTO: + newc |=3D FAN_FRAME_BIT; + newc &=3D ~FAN_SCM_BIT; + newc |=3D CTRLMODE_AUTO; + break; + case CTRLMODE_MANUAL: + newc &=3D ~FAN_FRAME_BIT; + newc &=3D ~FAN_SCM_BIT; + newc |=3D CTRLMODE_MANUAL; + break; + case CTRLMODE_FULL: + newc &=3D ~FAN_FRAME_BIT; + newc &=3D ~FAN_SCM_BIT; + newc |=3D CTRLMODE_FULL; + break; + case CTRLMODE_STOP: + default: + newc &=3D ~FAN_FRAME_BIT; + newc &=3D ~FAN_SCM_BIT; + newc |=3D CTRLMODE_STOP; + break; + } + + ret =3D pmc_write(fan->mfd, CTRL_CTRL, fan->id, &newc); + return ret ? ret : count; + } + + return -EINVAL; +} + +static DEVICE_ATTR_RW(fan_mode); + +static int eio_fan_probe(struct platform_device *pdev) +{ + struct device *dev =3D &pdev->dev; + unsigned int fan_id; + int ret; + + if (!dev_get_drvdata(dev->parent)) { + dev_err(dev, "eio_core not present\n"); + return -ENODEV; + } + + for (fan_id =3D 0; fan_id < FAN_MAX; fan_id++) { + u8 state =3D 0, name =3D 0; + int trip_hi =3D 0, trip_lo =3D 0, trip_stop =3D 0; + int pwm_hi =3D 0, pwm_lo =3D 0; + int temps_mc[TRIP_NUM]; + struct eio_fan_dev *fan; + struct thermal_zone_device *tzd; + struct thermal_cooling_device *cdev; + + if (pmc_read(dev->parent, CTRL_STATE, fan_id, &state) || + pmc_read(dev->parent, CTRL_TYPE, fan_id, &name) || + pmc_read(dev->parent, CTRL_THERM_HIGH, fan_id, &trip_hi) || + pmc_read(dev->parent, CTRL_THERM_LOW, fan_id, &trip_lo) || + pmc_read(dev->parent, CTRL_THERM_STOP, fan_id, &trip_stop) || + pmc_read(dev->parent, CTRL_PWM_HIGH, fan_id, &pwm_hi) || + pmc_read(dev->parent, CTRL_PWM_LOW, fan_id, &pwm_lo)) { + dev_info(dev, "fan%u: pmc read error, skipping\n", fan_id); + continue; + } + + if (!(state & 0x1)) { + dev_info(dev, "fan%u: firmware reports disabled\n", fan_id); + continue; + } + + if (!fan_name[name][0]) { + dev_info(dev, "fan%u: unknown name index %u\n", fan_id, name); + continue; + } + + temps_mc[TRIP_HIGH] =3D DECI_KELVIN_TO_MILLI_CELSIUS(trip_hi); + temps_mc[TRIP_LOW] =3D DECI_KELVIN_TO_MILLI_CELSIUS(trip_lo); + temps_mc[TRIP_STOP] =3D DECI_KELVIN_TO_MILLI_CELSIUS(trip_stop); + + fan =3D devm_kzalloc(dev, sizeof(*fan), GFP_KERNEL); + if (!fan) + return -ENOMEM; + + fan->mfd =3D dev->parent; + fan->id =3D (u8)fan_id; + + fan->trip_priv[TRIP_HIGH].trip_ctl =3D CTRL_THERM_HIGH; + fan->trip_priv[TRIP_LOW].trip_ctl =3D CTRL_THERM_LOW; + fan->trip_priv[TRIP_STOP].trip_ctl =3D CTRL_THERM_STOP; + + struct thermal_trip trips[TRIP_NUM] =3D { + [TRIP_HIGH] =3D { + .type =3D THERMAL_TRIP_ACTIVE, + .temperature =3D DECI_KELVIN_TO_MILLI_CELSIUS(trip_hi), + .flags =3D THERMAL_TRIP_FLAG_RW_TEMP, + .priv =3D &fan->trip_priv[TRIP_HIGH], + }, + [TRIP_LOW] =3D { + .type =3D THERMAL_TRIP_ACTIVE, + .temperature =3D DECI_KELVIN_TO_MILLI_CELSIUS(trip_lo), + .flags =3D THERMAL_TRIP_FLAG_RW_TEMP, + .priv =3D &fan->trip_priv[TRIP_LOW], + }, + [TRIP_STOP] =3D { + .type =3D THERMAL_TRIP_ACTIVE, + .temperature =3D DECI_KELVIN_TO_MILLI_CELSIUS(trip_stop), + .flags =3D THERMAL_TRIP_FLAG_RW_TEMP, + .priv =3D &fan->trip_priv[TRIP_STOP], + }, + }; + + tzd =3D thermal_zone_device_register_with_trips(fan_name[name], + trips, TRIP_NUM, + fan, + &zone_ops, + NULL, + 0, 0); + if (IS_ERR(tzd)) + return PTR_ERR(tzd); + + cdev =3D thermal_cooling_device_register(fan_name[name], fan, &cooling_o= ps); + if (IS_ERR(cdev)) { + thermal_zone_device_unregister(tzd); + dev_err(dev, "fan%u: cdev register failed: %ld\n", + fan_id, PTR_ERR(cdev)); + return PTR_ERR(cdev); + } + + dev_set_drvdata(thermal_zone_device(tzd), tzd); + ret =3D device_create_file(thermal_zone_device(tzd), &dev_attr_fan_mode); + if (ret) + dev_warn(dev, "Error create thermal zone fan_mode sysfs\n"); + } + return 0; +} + +static struct platform_driver eio_fan_driver =3D { + .probe =3D eio_fan_probe, + .driver =3D { + .name =3D "eio_fan", + }, +}; + +module_platform_driver(eio_fan_driver); + +MODULE_AUTHOR("Wenkai Chung "); +MODULE_AUTHOR("Ramiro Oliveira "); +MODULE_DESCRIPTION("Fan driver for Advantech EIO embedded controller"); +MODULE_LICENSE("GPL"); --=20 2.43.0