From patchwork Fri Aug 9 14:52:05 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Richard X-Patchwork-Id: 1970974 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.a=rsa-sha256 header.s=gm1 header.b=H42b/MAA; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=2604:1380:45e3:2400::1; helo=sv.mirrors.kernel.org; envelope-from=linux-gpio+bounces-8685-incoming=patchwork.ozlabs.org@vger.kernel.org; receiver=patchwork.ozlabs.org) Received: from sv.mirrors.kernel.org (sv.mirrors.kernel.org [IPv6:2604:1380:45e3:2400::1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WgRjT3BjGz1yYl for ; Sat, 10 Aug 2024 00:53:01 +1000 (AEST) Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by sv.mirrors.kernel.org (Postfix) with ESMTPS id 2CCB028280A for ; Fri, 9 Aug 2024 14:53:00 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 1EB1F19882B; Fri, 9 Aug 2024 14:52:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="H42b/MAA" X-Original-To: linux-gpio@vger.kernel.org Received: from relay2-d.mail.gandi.net (relay2-d.mail.gandi.net [217.70.183.194]) (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 75238197A66; Fri, 9 Aug 2024 14:52:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.70.183.194 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1723215165; cv=none; b=jI4uQa6XIPJeRv9Pi/ODGZkNg3/dMTxRocX+Em13mnd7qS6Pq+5IGZEFJ5CksOav1ytCmm+MASwy0pp1QAfGmIkbHncUV0QuJpMSBokIASOLLM4wvQYYVrYWjRNW/rTh6BpyBFxiqqWMVeqKHJMUg5aKBOngRylTYiWf9t0XDEU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1723215165; c=relaxed/simple; bh=2oc4eoTmRukHT9v6YNPh6vSAVagn6XjQPVEFY36Tqsg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ZFJWv+32OuuFV60e2fuAPpIlMdpoTartQoq5Jv80wgaOfOGyAFTX0/cfpWsUyee6mr2fDPlXP03WgJ7dm0W6JL15JzIaauvuPBOZ/wUf9BUdygcHKiD/siTc8oUSjQBd4NmM0yrmu/L+R4cX7wpLO9oPOHKmclqBxqIh6rscJ1s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=H42b/MAA; arc=none smtp.client-ip=217.70.183.194 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Received: by mail.gandi.net (Postfix) with ESMTPSA id 1854640002; Fri, 9 Aug 2024 14:52:34 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1723215155; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ty3oPC7+oNO4UtElrrdmnYePAOYrfKEvmMwMbWoHneA=; b=H42b/MAA3V/dFAt4egHD6neUwfE7r+/14Rib3P6zaWzKtPXJX9m4wcXy46lwfVzN/5tui2 54O4MsKSQI+Mk4dIwYIJZa2mRixzFtdAQLl+np8VPeLXvbkyXLmjI3ENT4WmK6uTXR9lYC Up/wjOuXbZ4F6WtwXhw8HiNRU4LQEUj/hYRHxqTz5Qe6LjjEkEtEdD2DHnrPh5E3+oRZ3Z F9+1Gf5D5GN6bO/YZ+TE43Li8j4BqOD9hLHIspvGjmCoQ88b6Y4NPa5nErWhlfAEPcL6y5 rfAjqTjb8TPRRhSMyZ3qJ73q3IPjipqcmXSjagC0GTTEotKLwLGSaU3Xrvdd5A== From: Thomas Richard Date: Fri, 09 Aug 2024 16:52:05 +0200 Subject: [PATCH 1/5] mfd: add Congatec Board Controller mfd driver Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240503-congatec-board-controller-v1-1-fec5236270e7@bootlin.com> References: <20240503-congatec-board-controller-v1-0-fec5236270e7@bootlin.com> In-Reply-To: <20240503-congatec-board-controller-v1-0-fec5236270e7@bootlin.com> To: Lee Jones , Linus Walleij , Bartosz Golaszewski , Andi Shyti , Wim Van Sebroeck , Guenter Roeck Cc: linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, linux-i2c@vger.kernel.org, linux-watchdog@vger.kernel.org, thomas.petazzoni@bootlin.com, blake.vermeer@keysight.com, Thomas Richard X-Mailer: b4 0.12.0 X-GND-Sasl: thomas.richard@bootlin.com Add core MFD driver for the Board Controller found on some Congatec SMARC module. This Board Controller provides functions like watchdog, GPIO, and I2C busses. This commit add support only for the conga-SA7 module. Signed-off-by: Thomas Richard --- drivers/mfd/Kconfig | 12 ++ drivers/mfd/Makefile | 1 + drivers/mfd/cgbc-core.c | 453 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/cgbc.h | 44 +++++ 4 files changed, 510 insertions(+) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index bc8be2e593b6..3e0530f30267 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -224,6 +224,18 @@ config MFD_AXP20X_RSB components like regulators or the PEK (Power Enable Key) under the corresponding menus. +config MFD_CGBC + tristate "Congatec Board Controller" + select MFD_CORE + depends on X86 + help + This is the core driver of the Board Controller found on some Congatec + SMARC modules. The Board Controller provides functions like watchdog, + I2C busses, and GPIO controller. + + To compile this driver as a module, choose M here: the module will be + called cgbc-core. + config MFD_CROS_EC_DEV tristate "ChromeOS Embedded Controller multifunction device" select MFD_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 02b651cd7535..d5da3fcd691c 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_MFD_SM501) += sm501.o obj-$(CONFIG_ARCH_BCM2835) += bcm2835-pm.o obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o obj-$(CONFIG_MFD_BD9571MWV) += bd9571mwv.o +obj-$(CONFIG_MFD_CGBC) += cgbc-core.o obj-$(CONFIG_MFD_CROS_EC_DEV) += cros_ec_dev.o obj-$(CONFIG_MFD_CS42L43) += cs42l43.o obj-$(CONFIG_MFD_CS42L43_I2C) += cs42l43-i2c.o diff --git a/drivers/mfd/cgbc-core.c b/drivers/mfd/cgbc-core.c new file mode 100644 index 000000000000..cca9b1170cc9 --- /dev/null +++ b/drivers/mfd/cgbc-core.c @@ -0,0 +1,453 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Congatec Board Controller MFD core driver. + * + * The x86 Congatec modules have an embedded micro controller named Board + * Controller. + * This Board Controller have a watchdog timer, some GPIOs, and two i2c busses. + * + * Copyright (C) 2024 Bootlin + * Author: Thomas Richard + */ + +#include +#include +#include +#include +#include +#include +#include + +#define CGBC_MASK_STATUS (BIT(6) | BIT(7)) +#define CGBC_MASK_DATA_COUNT 0x1F +#define CGBC_MASK_ERROR_CODE 0x1F + +#define CGBC_STATUS_DATA_READY 0x00 +#define CGBC_STATUS_CMD_READY BIT(6) +#define CGBC_STATUS_ERROR (BIT(6) | BIT(7)) + +#define CGBC_CMD_GET_FW_REV 0x21 + +#define CGBC_IO_SESSION_BASE 0x0E20 +#define CGBC_IO_SESSION_END 0x0E30 +#define CGBC_IO_CMD_BASE 0x0E00 +#define CGBC_IO_CMD_END 0x0E10 + +#define CGBC_SESSION_CMD 0x00 +#define CGBC_SESSION_CMD_IDLE 0x00 +#define CGBC_SESSION_CMD_REQUEST 0x01 +#define CGBC_SESSION_DATA 0x01 +#define CGBC_SESSION_STATUS 0x02 +#define CGBC_SESSION_STATUS_FREE 0x03 +#define CGBC_SESSION_ACCESS 0x04 +#define CGBC_SESSION_ACCESS_GAINED 0x00 + +#define CGBC_SESSION_VALID_MIN 0x02 +#define CGBC_SESSION_VALID_MAX 0xFE + +#define CGBC_CMD_STROBE 0x00 +#define CGBC_CMD_INDEX 0x02 +#define CGBC_CMD_INDEX_CBM_MAN8 0x00 +#define CGBC_CMD_INDEX_CBM_AUTO32 0x03 +#define CGBC_CMD_DATA 0x04 +#define CGBC_CMD_ACCESS 0x0C + +struct cgbc_platform_data { + const struct resource *ioresource; + unsigned int num_ioresource; +}; + +static struct platform_device *cgbc_pdev; + +static int cgbc_detect_device(struct cgbc_device_data *cgbc) +{ + u16 status; + int ret; + + ret = readx_poll_timeout(ioread16, cgbc->io_session + CGBC_SESSION_STATUS, status, + status == CGBC_SESSION_STATUS_FREE, 0, 500000); + + if (ret || ioread32(cgbc->io_session + CGBC_SESSION_ACCESS)) + ret = -ENODEV; + + return ret; +} + +static int cgbc_session_command(struct cgbc_device_data *cgbc, u8 cmd) +{ + int ret; + u8 val; + + ret = readx_poll_timeout(ioread8, cgbc->io_session + CGBC_SESSION_CMD, val, + val == CGBC_SESSION_CMD_IDLE, 0, 100000); + if (ret) + return ret; + + iowrite8(cmd, cgbc->io_session + CGBC_SESSION_CMD); + + ret = readx_poll_timeout(ioread8, cgbc->io_session + CGBC_SESSION_CMD, val, + val == CGBC_SESSION_CMD_IDLE, 0, 100000); + if (ret) + return ret; + + ret = (int)ioread8(cgbc->io_session + CGBC_SESSION_DATA); + + iowrite8(CGBC_SESSION_STATUS_FREE, + cgbc->io_session + CGBC_SESSION_STATUS); + + return ret; +} + +static int cgbc_session_request(struct cgbc_device_data *cgbc) +{ + unsigned int ret = cgbc_detect_device(cgbc); + + if (ret) + return dev_err_probe(cgbc->dev, ret, "device not found\n"); + + cgbc->session = cgbc_session_command(cgbc, CGBC_SESSION_CMD_REQUEST); + + /* the Board Controller sent us a wrong session handle, we cannot + * communicate with it. + */ + if (cgbc->session < CGBC_SESSION_VALID_MIN || + cgbc->session > CGBC_SESSION_VALID_MAX) { + cgbc->session = 0; + return dev_err_probe(cgbc->dev, (cgbc->session < 0) ? cgbc->session : -ECONNREFUSED, + "failed to get a valid session handle\n"); + } + + return 0; +} + +static void cgbc_session_release(struct cgbc_device_data *cgbc) +{ + if (cgbc_session_command(cgbc, cgbc->session) != cgbc->session) + dev_err(cgbc->dev, "failed to release session\n"); +} + +static bool cgbc_command_lock(struct cgbc_device_data *cgbc) +{ + iowrite8(cgbc->session, cgbc->io_cmd + CGBC_CMD_ACCESS); + + return ioread8(cgbc->io_cmd + CGBC_CMD_ACCESS) == cgbc->session; +} + +static void cgbc_command_unlock(struct cgbc_device_data *cgbc) +{ + iowrite8(cgbc->session, cgbc->io_cmd + CGBC_CMD_ACCESS); +} + +static int __cgbc_command(struct cgbc_device_data *cgbc, u8 *cmd, u8 cmd_size, + u8 *data, u8 data_size, u8 *status) +{ + u8 checksum = 0, data_checksum = 0, istatus = 0, val; + int mode_change = -1; + bool lock; + int ret, i; + + mutex_lock(&cgbc->lock); + + /* request access */ + ret = readx_poll_timeout(cgbc_command_lock, cgbc, lock, lock, 0, 100000); + if (ret) + goto out; + + /* wait board controller is ready */ + ret = readx_poll_timeout(ioread8, cgbc->io_cmd + CGBC_CMD_STROBE, val, + val == CGBC_CMD_STROBE, 0, 100000); + if (ret) + goto release; + + /* write command packet */ + if (cmd_size <= 2) { + iowrite8(CGBC_CMD_INDEX_CBM_MAN8, + cgbc->io_cmd + CGBC_CMD_INDEX); + } else { + iowrite8(CGBC_CMD_INDEX_CBM_AUTO32, + cgbc->io_cmd + CGBC_CMD_INDEX); + if ((cmd_size % 4) != 0x03) + mode_change = (cmd_size & 0xFFFC) - 1; + } + + for (i = 0; i < cmd_size; i++) { + iowrite8(cmd[i], cgbc->io_cmd + CGBC_CMD_DATA + (i % 4)); + checksum ^= cmd[i]; + if (mode_change == i) + iowrite8((i + 1) | CGBC_CMD_INDEX_CBM_MAN8, + cgbc->io_cmd + CGBC_CMD_INDEX); + } + + /* append checksum byte */ + iowrite8(checksum, cgbc->io_cmd + CGBC_CMD_DATA + (i % 4)); + + /* perform command strobe */ + iowrite8(cgbc->session, cgbc->io_cmd + CGBC_CMD_STROBE); + + /* rewind cmd buffer index */ + iowrite8(CGBC_CMD_INDEX_CBM_AUTO32, + cgbc->io_cmd + CGBC_CMD_INDEX); + + /* wait command completion */ + ret = read_poll_timeout(ioread8, val, val == CGBC_CMD_STROBE, 0, + 100000, false, + cgbc->io_cmd + CGBC_CMD_STROBE); + if (ret) + goto release; + + istatus = ioread8(cgbc->io_cmd + CGBC_CMD_DATA); + checksum = istatus; + + /* check command status */ + switch (istatus & CGBC_MASK_STATUS) { + case CGBC_STATUS_DATA_READY: + if (istatus > data_size) + istatus = data_size; + for (i = 0; i < istatus; i++) { + data[i] = ioread8(cgbc->io_cmd + + CGBC_CMD_DATA + ((i + 1) % 4)); + checksum ^= data[i]; + } + data_checksum = ioread8(cgbc->io_cmd + + CGBC_CMD_DATA + ((i + 1) % 4)); + istatus &= CGBC_MASK_DATA_COUNT; + break; + case CGBC_STATUS_ERROR: + case CGBC_STATUS_CMD_READY: + data_checksum = ioread8(cgbc->io_cmd + + CGBC_CMD_DATA + 1); + if ((istatus & CGBC_MASK_STATUS) == CGBC_STATUS_ERROR) + ret = -EIO; + istatus = istatus & CGBC_MASK_ERROR_CODE; + break; + default: + data_checksum = ioread8(cgbc->io_cmd + CGBC_CMD_DATA + 1); + istatus &= CGBC_MASK_ERROR_CODE; + ret = -EIO; + break; + } + + /* checksum verification */ + if (ret == 0 && data_checksum != checksum) + ret = -EIO; + +release: + cgbc_command_unlock(cgbc); + +out: + mutex_unlock(&cgbc->lock); + + if (status) + *status = istatus; + + return ret; +} + +int cgbc_command(struct cgbc_device_data *cgbc, void *cmd, unsigned int cmd_size, + void *data, unsigned int data_size, u8 *status) +{ + return __cgbc_command(cgbc, (u8 *)cmd, cmd_size, (u8 *)data, data_size, status); +} +EXPORT_SYMBOL_GPL(cgbc_command); + +static struct mfd_cell cgbc_devs[] = { + { .name = "cgbc-wdt" }, + { .name = "cgbc-gpio" }, + { .name = "cgbc-i2c", .id = 1 }, + { .name = "cgbc-i2c", .id = 2 }, +}; + +static int cgbc_map(struct cgbc_device_data *cgbc) +{ + struct device *dev = cgbc->dev; + struct platform_device *pdev = to_platform_device(dev); + struct resource *ioport; + + ioport = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!ioport) + return -EINVAL; + + cgbc->io_session = devm_ioport_map(dev, ioport->start, + resource_size(ioport)); + if (!cgbc->io_session) + return -ENOMEM; + + ioport = platform_get_resource(pdev, IORESOURCE_IO, 1); + if (!ioport) + return -EINVAL; + + cgbc->io_cmd = devm_ioport_map(dev, ioport->start, + resource_size(ioport)); + if (!cgbc->io_cmd) + return -ENOMEM; + + return 0; +} + +static struct resource cgbc_ioresource[] = { + { + .start = CGBC_IO_SESSION_BASE, + .end = CGBC_IO_SESSION_END, + .flags = IORESOURCE_IO, + }, + { + .start = CGBC_IO_CMD_BASE, + .end = CGBC_IO_CMD_END, + .flags = IORESOURCE_IO, + }, +}; + +static const struct cgbc_platform_data cgbc_platform_data = { + .ioresource = &cgbc_ioresource[0], + .num_ioresource = ARRAY_SIZE(cgbc_ioresource), +}; + +static int cgbc_create_platform_device(const struct cgbc_platform_data *pdata) +{ + const struct platform_device_info pdevinfo = { + .name = "cgbc", + .id = PLATFORM_DEVID_NONE, + .res = pdata->ioresource, + .num_res = pdata->num_ioresource, + }; + + cgbc_pdev = platform_device_register_full(&pdevinfo); + if (IS_ERR(cgbc_pdev)) + return PTR_ERR(cgbc_pdev); + + return 0; +} + +static ssize_t cgbc_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cgbc_device_data *cgbc = dev_get_drvdata(dev); + + return sysfs_emit(buf, "CGBCP%c%c%c\n", cgbc->version.feature, + cgbc->version.major, cgbc->version.minor); +} + +static DEVICE_ATTR_RO(cgbc_version); + +static struct attribute *cgbc_attrs[] = { + &dev_attr_cgbc_version.attr, + NULL +}; + +ATTRIBUTE_GROUPS(cgbc); + +static int cgbc_get_version(struct cgbc_device_data *cgbc) +{ + u8 cmd = CGBC_CMD_GET_FW_REV; + u8 data[4]; + int ret; + + ret = cgbc_command(cgbc, &cmd, 1, &data, sizeof(data), NULL); + if (ret) + return ret; + + cgbc->version.feature = data[0]; + cgbc->version.major = data[1]; + cgbc->version.minor = data[2]; + + return 0; +} + +static int cgbc_init_device(struct cgbc_device_data *cgbc) +{ + int ret; + + ret = cgbc_session_request(cgbc); + if (ret) + return ret; + + ret = cgbc_get_version(cgbc); + if (ret) + return ret; + + return mfd_add_devices(cgbc->dev, -1, cgbc_devs, + ARRAY_SIZE(cgbc_devs), NULL, 0, NULL); +} + +static int cgbc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cgbc_device_data *cgbc; + int ret; + + cgbc = devm_kzalloc(dev, sizeof(*cgbc), GFP_KERNEL); + if (!cgbc) + return -ENOMEM; + + cgbc->dev = dev; + + ret = cgbc_map(cgbc); + if (ret) + return ret; + + mutex_init(&cgbc->lock); + + platform_set_drvdata(pdev, cgbc); + + return cgbc_init_device(cgbc); +} + +static void cgbc_remove(struct platform_device *pdev) +{ + struct cgbc_device_data *cgbc = platform_get_drvdata(pdev); + + cgbc_session_release(cgbc); + + mfd_remove_devices(&pdev->dev); +} + +static struct platform_driver cgbc_driver = { + .driver = { + .name = "cgbc", + .dev_groups = cgbc_groups, + }, + .probe = cgbc_probe, + .remove_new = cgbc_remove, +}; + +static const struct dmi_system_id cgbc_dmi_table[] __initconst = { + { + .ident = "SA7", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "congatec"), + DMI_MATCH(DMI_BOARD_NAME, "conga-SA7"), + }, + }, + {} +}; +MODULE_DEVICE_TABLE(dmi, cgbc_dmi_table); + +static int __init cgbc_init(void) +{ + const struct dmi_system_id *id; + int ret = -ENODEV; + + id = dmi_first_match(cgbc_dmi_table); + if (IS_ERR_OR_NULL(id)) + return ret; + + ret = cgbc_create_platform_device(&cgbc_platform_data); + if (ret) + return ret; + + return platform_driver_register(&cgbc_driver); +} + +static void __exit cgbc_exit(void) +{ + platform_device_unregister(cgbc_pdev); + platform_driver_unregister(&cgbc_driver); +} + +module_init(cgbc_init); +module_exit(cgbc_exit); + +MODULE_DESCRIPTION("Congatec Board Controller Core Driver"); +MODULE_AUTHOR("Thomas Richard "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:cgbc-core"); diff --git a/include/linux/mfd/cgbc.h b/include/linux/mfd/cgbc.h new file mode 100644 index 000000000000..badbec4c7033 --- /dev/null +++ b/include/linux/mfd/cgbc.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Congatec Board Controller driver definitions + * + * Copyright (C) 2024 Bootlin + * Author: Thomas Richard + */ + +#ifndef _LINUX_MFD_CGBC_H_ + +/** + * struct cgbc_version - Board Controller device version structure + * @feature: Board Controller feature number + * @major: Board Controller major revision + * @minor: Board Controller minor revision + */ +struct cgbc_version { + unsigned char feature; + unsigned char major; + unsigned char minor; +}; + +/** + * struct cgbc_device_data - Internal representation of the Board Controller device + * @io_session: Pointer to the session IO memory + * @io_cmd: Pointer to the command IO memory + * @session: Session id returned by the Board Controller + * @dev: Pointer to kernel device structure + * @cgbc_version: Board Controller version structure + * @mutex: Board Controller mutex + */ +struct cgbc_device_data { + void __iomem *io_session; + void __iomem *io_cmd; + u8 session; + struct device *dev; + struct cgbc_version version; + struct mutex lock; +}; + +int cgbc_command(struct cgbc_device_data *cgbc, void *cmd, unsigned int cmd_size, + void *data, unsigned int data_size, u8 *status); + +#endif /*_LINUX_MFD_CGBC_H_*/ From patchwork Fri Aug 9 14:52:06 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Richard X-Patchwork-Id: 1970977 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.a=rsa-sha256 header.s=gm1 header.b=SHvNo+Cr; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=2604:1380:4601:e00::3; helo=am.mirrors.kernel.org; envelope-from=linux-gpio+bounces-8686-incoming=patchwork.ozlabs.org@vger.kernel.org; receiver=patchwork.ozlabs.org) Received: from am.mirrors.kernel.org (am.mirrors.kernel.org [IPv6:2604:1380:4601:e00::3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WgRjd11yfz1yYl for ; Sat, 10 Aug 2024 00:53:09 +1000 (AEST) Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by am.mirrors.kernel.org (Postfix) with ESMTPS id 8496A1F2136C for ; Fri, 9 Aug 2024 14:53:05 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 58CCC198842; Fri, 9 Aug 2024 14:52:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="SHvNo+Cr" X-Original-To: linux-gpio@vger.kernel.org Received: from relay2-d.mail.gandi.net (relay2-d.mail.gandi.net [217.70.183.194]) (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 93482197512; Fri, 9 Aug 2024 14:52:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.70.183.194 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1723215166; cv=none; b=uYfSCin+QyvABPQW9MQIjsFHT+HH8xotH/owaBgbJMEpqOPXSpWfN3P18FQvPdXGsz1TCUyMDSehKTch2uWvhMo9SrCnLykGapv2p6Ej6q+z2UqMRNmCFkcVwGM1hKdSAyZHuV4Ao2mbwy2GUQpbiavVGo5Hg5anxbI0/cg4Q/E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1723215166; c=relaxed/simple; bh=MgX4Y9doq3nduiqv3nXn3S/Sl6ElRTZrwO72vQB8sv4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=NH/aelJWq8GHgmIyoWCv5KnbGxbV6kQEPzqzpHx1pTlBs7hzcyuHQr1VW1JhvVE1+OUnHkvKKiZvAWBNXKECrGLihJ3+UF70t+RdYval7iGDUiSNKywBRAyhYYuE8g8iTVa7Lja37nsbtd6UfhYuxRzFEFct23uonM8xBKzmH/w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=SHvNo+Cr; arc=none smtp.client-ip=217.70.183.194 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Received: by mail.gandi.net (Postfix) with ESMTPSA id 4AE6B40004; Fri, 9 Aug 2024 14:52:35 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1723215156; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=cEe5eWxop6igHf93vf56/fh3/U5CavtxVjxV45rsRGU=; b=SHvNo+Crih9WvCXb8V+Ys95YA9X7D5AMbdPcyF97lk16FxPnlP4tohPKDDxmQk9bqZAJ8E QUbRUHNnpx4dkyBU7ZF2m1+yXCR0iuPpO0Xw508a3PbOMFG81tTbHpU6xIDHhO5OgDVkzN W+F7ojJZvsV7Qj0zpNMR21BGsk1gzshNMU5tLhprFOifsp92m9g3viPVIjLYqBFOKPqgMa gsoIEE3uKVwdH7Rgs4fb9aYlR0vwVFEOINVxaFEHJyTtSWUrNSgzuQieRJJ3j3VrJNJDnL FqW4K+weWxiEPkTD5wZuBiqRnROKroXCQDsHHxL4SdTYEHDRhTxktrbOa67bcA== From: Thomas Richard Date: Fri, 09 Aug 2024 16:52:06 +0200 Subject: [PATCH 2/5] gpio: Congatec Board Controller gpio driver Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240503-congatec-board-controller-v1-2-fec5236270e7@bootlin.com> References: <20240503-congatec-board-controller-v1-0-fec5236270e7@bootlin.com> In-Reply-To: <20240503-congatec-board-controller-v1-0-fec5236270e7@bootlin.com> To: Lee Jones , Linus Walleij , Bartosz Golaszewski , Andi Shyti , Wim Van Sebroeck , Guenter Roeck Cc: linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, linux-i2c@vger.kernel.org, linux-watchdog@vger.kernel.org, thomas.petazzoni@bootlin.com, blake.vermeer@keysight.com, Thomas Richard X-Mailer: b4 0.12.0 X-GND-Sasl: thomas.richard@bootlin.com Add gpio support for the Congatec Board Controller. Signed-off-by: Thomas Richard --- drivers/gpio/Kconfig | 10 +++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-cgbc.c | 203 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 214 insertions(+) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 58f43bcced7c..ce77bad40087 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -233,6 +233,16 @@ config GPIO_CADENCE help Say yes here to enable support for Cadence GPIO controller. +config GPIO_CGBC + tristate "Congatec Board Controller GPIO support" + depends on MFD_CGBC + help + Select this option to enable GPIO support for the Congatec Board + Controller. + + This driver can also be built as a module. If so, the module will be + called gpio-cgbc. + config GPIO_CLPS711X tristate "CLPS711X GPIO support" depends on ARCH_CLPS711X || COMPILE_TEST diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 64dd6d9d730d..3a96e3c27a2d 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o obj-$(CONFIG_GPIO_CADENCE) += gpio-cadence.o +obj-$(CONFIG_GPIO_CGBC) += gpio-cgbc.o obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o obj-$(CONFIG_GPIO_SNPS_CREG) += gpio-creg-snps.o obj-$(CONFIG_GPIO_CROS_EC) += gpio-cros-ec.o diff --git a/drivers/gpio/gpio-cgbc.c b/drivers/gpio/gpio-cgbc.c new file mode 100644 index 000000000000..6da50c794872 --- /dev/null +++ b/drivers/gpio/gpio-cgbc.c @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Congatec Board Controller GPIO driver + * + * Copyright (C) 2024 Bootlin + * Author: Thomas Richard + */ + +#include +#include +#include +#include +#include + +#define CGBC_GPIO_NGPIO 14 + +#define CGBC_GPIO_CMD_GET 0x64 +#define CGBC_GPIO_CMD_SET 0x65 +#define CGBC_GPIO_CMD_DIR_GET 0x66 +#define CGBC_GPIO_CMD_DIR_SET 0x67 + +struct cgbc_gpio_data { + struct gpio_chip chip; + struct cgbc_device_data *cgbc; + struct mutex lock; +}; + +static int cgbc_gpio_cmd(struct cgbc_device_data *cgbc, + u8 cmd0, u8 cmd1, u8 cmd2, u8 *value) +{ + u8 cmd[3] = {cmd0, cmd1, cmd2}; + + return cgbc_command(cgbc, cmd, sizeof(cmd), value, 1, NULL); +} + +static int cgbc_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); + struct cgbc_device_data *cgbc = gpio->cgbc; + int ret; + u8 val; + + mutex_lock(&gpio->lock); + + ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_GET, (offset > 7) ? 1 : 0, 0, &val); + + mutex_unlock(&gpio->lock); + + offset %= 8; + + if (ret) + return ret; + else + return (int)(val & (u8)BIT(offset)); +} + +static void __cgbc_gpio_set(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); + struct cgbc_device_data *cgbc = gpio->cgbc; + u8 val; + int ret; + + ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_GET, (offset > 7) ? 1 : 0, 0, &val); + if (ret) + return; + + if (value) + val |= BIT(offset % 8); + else + val &= ~((u8)BIT(offset % 8)); + + cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_SET, (offset > 7) ? 1 : 0, val, &val); +} + +static void cgbc_gpio_set(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); + + mutex_lock(&gpio->lock); + __cgbc_gpio_set(chip, offset, value); + mutex_unlock(&gpio->lock); +} + +static int cgbc_gpio_direction_set(struct gpio_chip *chip, + unsigned int offset, int direction) +{ + struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); + struct cgbc_device_data *cgbc = gpio->cgbc; + int ret; + u8 val; + + ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_DIR_GET, (offset > 7) ? 1 : 0, 0, &val); + if (ret) + goto end; + + if (direction == GPIO_LINE_DIRECTION_IN) + val &= ~((u8)BIT(offset % 8)); + else + val |= BIT(offset % 8); + + ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_DIR_SET, (offset > 7) ? 1 : 0, val, &val); + +end: + return ret; +} + +static int cgbc_gpio_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); + int ret; + + mutex_lock(&gpio->lock); + ret = cgbc_gpio_direction_set(chip, offset, GPIO_LINE_DIRECTION_IN); + mutex_unlock(&gpio->lock); + + return ret; +} + +static int cgbc_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); + int ret; + + mutex_lock(&gpio->lock); + __cgbc_gpio_set(chip, offset, value); + ret = cgbc_gpio_direction_set(chip, offset, GPIO_LINE_DIRECTION_OUT); + mutex_unlock(&gpio->lock); + + return ret; +} + +static int cgbc_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); + struct cgbc_device_data *cgbc = gpio->cgbc; + int ret; + u8 val; + + ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_DIR_GET, (offset > 7) ? 1 : 0, 0, &val); + if (ret) + return ret; + + if (val & BIT(offset % 8)) + return GPIO_LINE_DIRECTION_OUT; + else + return GPIO_LINE_DIRECTION_IN; +} + +static int cgbc_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cgbc_device_data *cgbc = dev_get_drvdata(dev->parent); + struct cgbc_gpio_data *gpio; + struct gpio_chip *chip; + int ret; + + gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + gpio->cgbc = cgbc; + + platform_set_drvdata(pdev, gpio); + + chip = &gpio->chip; + chip->label = dev_name(&pdev->dev); + chip->owner = THIS_MODULE; + chip->parent = dev; + chip->base = -1; + chip->direction_input = cgbc_gpio_direction_input; + chip->direction_output = cgbc_gpio_direction_output; + chip->get_direction = cgbc_gpio_get_direction; + chip->get = cgbc_gpio_get; + chip->set = cgbc_gpio_set; + chip->ngpio = CGBC_GPIO_NGPIO; + + mutex_init(&gpio->lock); + + ret = devm_gpiochip_add_data(dev, chip, gpio); + if (ret) + return dev_err_probe(dev, ret, "Could not register GPIO chip\n"); + + return 0; +} + +static struct platform_driver cgbc_gpio_driver = { + .driver = { + .name = "cgbc-gpio", + }, + .probe = cgbc_gpio_probe, +}; + +module_platform_driver(cgbc_gpio_driver); + +MODULE_DESCRIPTION("Congatec Board Controller GPIO Driver"); +MODULE_AUTHOR("Thomas Richard "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:cgbc-gpio"); From patchwork Fri Aug 9 14:52:07 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Richard X-Patchwork-Id: 1970978 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.a=rsa-sha256 header.s=gm1 header.b=P8arfPb1; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=2604:1380:45d1:ec00::1; helo=ny.mirrors.kernel.org; envelope-from=linux-gpio+bounces-8687-incoming=patchwork.ozlabs.org@vger.kernel.org; receiver=patchwork.ozlabs.org) Received: from ny.mirrors.kernel.org (ny.mirrors.kernel.org [IPv6:2604:1380:45d1:ec00::1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WgRjp36f3z1yYl for ; Sat, 10 Aug 2024 00:53:18 +1000 (AEST) Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ny.mirrors.kernel.org (Postfix) with ESMTPS id 97CF81C2082C for ; Fri, 9 Aug 2024 14:53:16 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 6C46A198A25; Fri, 9 Aug 2024 14:52:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="P8arfPb1" X-Original-To: linux-gpio@vger.kernel.org Received: from relay2-d.mail.gandi.net (relay2-d.mail.gandi.net [217.70.183.194]) (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 3069E197558; Fri, 9 Aug 2024 14:52:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.70.183.194 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1723215167; cv=none; b=mNneOWNqSyen8Fent6MD4TORzk3Y3mjrVYYUMoeCPq5KnmFbaEYNJzIKZjbhcYu+Jc+jqYaoskndLVqGBTnZdoEt7s0SDyHGYk9HUd0/Th8bUVkLQqh/hhfy0Ir69EK2BGOUqEDaNHakXzn/xey0oVA0xw0mCSSoXFoQY+KepRM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1723215167; c=relaxed/simple; bh=tgLTqHTnvrMFnnJJn94AtfapaHFtIN5/ZWkItwIlph0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=iAdk20xA5jLGgMpNcqhsuVDjP8ANrTq0wakY1UB23JJ5xL6IBoYDP71l9ouv3qtMpqddmnPQJZ06khGo+k7leIFqmJzfN4NPwkqpGzVZ0ySriNcVIk2ZimaQ0rahr7sgojFt4Tb/Auoo8qduuR1KyDpxk8nZaXMlhB2cw2epHNM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=P8arfPb1; arc=none smtp.client-ip=217.70.183.194 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Received: by mail.gandi.net (Postfix) with ESMTPSA id 563964000D; Fri, 9 Aug 2024 14:52:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1723215157; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=b3gLO614SplJM4SKH0LBRRio4fHKmgoCPIWyKO1NRp4=; b=P8arfPb1VrIiMNscPkGh+TUhc+MWNG5c4d5eGkeEt2DQUJTlZZNZdKTZdKrwsW66Nr+CMM WBaJ6jnJsT70M95PLzKsq8+nD0DgFQrtXtrVPi4n90VLV8THqa2UNu2rE40HhVdsayB0ft 5dk4EvH87buMUeJFbMfaK5096Ur4RvlDg5OP+38T1ldjLVDZUm5KUPe/pgYSA4uzQ6BpMM DmVUgcnW/9B+EsRd+ghlmVxTJcjhdDuWdjwVvG17W87t/wko9ZYCRnUwocKBoMO5Uc+RIC J5bMNhUYWr9YCBgC98nSr1Brh0Hn1brAGuTdvmM7wVhNO2Ask0DL2mh+eKDXnQ== From: Thomas Richard Date: Fri, 09 Aug 2024 16:52:07 +0200 Subject: [PATCH 3/5] i2c: Congatec Board Controller i2c bus driver Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240503-congatec-board-controller-v1-3-fec5236270e7@bootlin.com> References: <20240503-congatec-board-controller-v1-0-fec5236270e7@bootlin.com> In-Reply-To: <20240503-congatec-board-controller-v1-0-fec5236270e7@bootlin.com> To: Lee Jones , Linus Walleij , Bartosz Golaszewski , Andi Shyti , Wim Van Sebroeck , Guenter Roeck Cc: linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, linux-i2c@vger.kernel.org, linux-watchdog@vger.kernel.org, thomas.petazzoni@bootlin.com, blake.vermeer@keysight.com, Thomas Richard X-Mailer: b4 0.12.0 X-GND-Sasl: thomas.richard@bootlin.com Add i2c support for the Congatec Board Controller. Signed-off-by: Thomas Richard --- drivers/i2c/busses/Kconfig | 10 ++ drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-cgbc.c | 407 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 418 insertions(+) diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index a22f9125322a..3657338d0346 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -535,6 +535,16 @@ config I2C_CBUS_GPIO This driver can also be built as a module. If so, the module will be called i2c-cbus-gpio. +config I2C_CGBC + tristate "Congatec I2C Controller" + depends on MFD_CGBC + help + This enables the I2C bus interfaces for the Congatec Board + Controller. + + This driver can also be built as a module. If so, the module will + be called i2c-cgbc.ko. + config I2C_CPM tristate "Freescale CPM1 or CPM2 (MPC8xx/826x)" depends on CPM1 || CPM2 diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 78d0561339e5..3e6bb569c546 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o obj-$(CONFIG_I2C_BCM_IPROC) += i2c-bcm-iproc.o obj-$(CONFIG_I2C_CADENCE) += i2c-cadence.o obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o +obj-$(CONFIG_I2C_CGBC) += i2c-cgbc.o obj-$(CONFIG_I2C_CPM) += i2c-cpm.o obj-$(CONFIG_I2C_DAVINCI) += i2c-davinci.o obj-$(CONFIG_I2C_DESIGNWARE_CORE) += i2c-designware-core.o diff --git a/drivers/i2c/busses/i2c-cgbc.c b/drivers/i2c/busses/i2c-cgbc.c new file mode 100644 index 000000000000..5fffe07c40e6 --- /dev/null +++ b/drivers/i2c/busses/i2c-cgbc.c @@ -0,0 +1,407 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Congatec Board Controller I2C busses driver + * + * Copyright (C) 2024 Bootlin + * Author: Thomas Richard + */ + +#include +#include +#include +#include + +#include + +#define CGBC_I2C_PRIMARY_BUS_ID 0 +#define CGBC_I2C_PM_BUS_ID 4 + +#define CGBC_I2C_CMD_START 0x40 +#define CGBC_I2C_CMD_STAT 0x48 +#define CGBC_I2C_CMD_DATA 0x50 +#define CGBC_I2C_CMD_SPEED 0x58 + +#define CGBC_I2C_STAT_IDL 0x00 +#define CGBC_I2C_STAT_DAT 0x01 +#define CGBC_I2C_STAT_BUSY 0x02 + +#define CGBC_I2C_START 0x80 +#define CGBC_I2C_STOP 0x40 + +#define CGBC_I2C_LAST_ACK 0x80 /* send ACK on last read byte */ + +/* + * Reference code defines 1kHz as min freq and 6.1MHz as max freq. + * But in practice, the board controller limits the frequency to 1MHz, and the + * 1kHz is not functional (minimal working freq is 50kHz). + * So use these values as limits. + */ +#define CGBC_I2C_FREQ_MIN_HZ 50000 /* 50 kHz */ +#define CGBC_I2C_FREQ_MAX_HZ 1000000 /* 1 MHz */ + +#define CGBC_I2C_FREQ_UNIT_1KHZ 0x40 +#define CGBC_I2C_FREQ_UNIT_10KHZ 0x80 +#define CGBC_I2C_FREQ_UNIT_100KHZ 0xC0 + +#define CGBC_I2C_FREQ_UNIT_MASK 0xC0 +#define CGBC_I2C_FREQ_VALUE_MASK 0x3F + +#define CGBC_I2C_READ_MAX_LEN 31 +#define CGBC_I2C_WRITE_MAX_LEN 32 + +#define CGBC_I2C_CMD_HEADER_SIZE 4 +#define CGBC_I2C_CMD_SIZE (CGBC_I2C_CMD_HEADER_SIZE + CGBC_I2C_WRITE_MAX_LEN) + +enum i2c_state { + STATE_DONE = 0, + STATE_INIT, + STATE_START, + STATE_READ, + STATE_WRITE, + STATE_ERROR, +}; + +struct i2c_algo_cgbc_data { + u8 bus_id; + unsigned long read_maxtime_us; +}; + +struct cgbc_i2c_data { + struct device *dev; + struct cgbc_device_data *cgbc; + struct i2c_adapter adap; + struct i2c_msg *msg; + int nmsgs; + int pos; + enum i2c_state state; +}; + +struct cgbc_i2c_transfer { + u8 bus_id; + bool start; + bool stop; + bool last_ack; + u8 read; + u8 write; + u8 addr; + u8 data[CGBC_I2C_WRITE_MAX_LEN]; +}; + +static u8 cgbc_i2c_freq_to_reg(unsigned int bus_frequency) +{ + u8 reg; + + if (bus_frequency <= 10000) + reg = CGBC_I2C_FREQ_UNIT_1KHZ | (bus_frequency / 1000); + else if (bus_frequency <= 100000) + reg = CGBC_I2C_FREQ_UNIT_10KHZ | (bus_frequency / 10000); + else + reg = CGBC_I2C_FREQ_UNIT_100KHZ | (bus_frequency / 100000); + + return reg; +} + +static unsigned int cgbc_i2c_reg_to_freq(u8 reg) +{ + unsigned int freq = reg & CGBC_I2C_FREQ_VALUE_MASK; + u8 unit = reg & CGBC_I2C_FREQ_UNIT_MASK; + + if (unit == CGBC_I2C_FREQ_UNIT_100KHZ) + return freq * 100000; + else if (unit == CGBC_I2C_FREQ_UNIT_10KHZ) + return freq * 10000; + else + return freq * 1000; +} + +static int cgbc_i2c_get_status(struct i2c_adapter *adap) +{ + struct i2c_algo_cgbc_data *algo_data = adap->algo_data; + struct cgbc_i2c_data *i2c = i2c_get_adapdata(adap); + struct cgbc_device_data *cgbc = i2c->cgbc; + u8 cmd = CGBC_I2C_CMD_STAT | algo_data->bus_id; + u8 status; + int ret; + + ret = cgbc_command(cgbc, &cmd, sizeof(cmd), NULL, 0, &status); + if (ret) + return ret; + + return status; +} + +static int cgbc_i2c_set_frequency(struct i2c_adapter *adap, + unsigned int bus_frequency) +{ + struct i2c_algo_cgbc_data *algo_data = adap->algo_data; + struct cgbc_i2c_data *i2c = i2c_get_adapdata(adap); + struct cgbc_device_data *cgbc = i2c->cgbc; + u8 cmd[2], data; + int ret; + + if (bus_frequency > CGBC_I2C_FREQ_MAX_HZ || + bus_frequency < CGBC_I2C_FREQ_MIN_HZ) { + dev_warn(i2c->dev, "invalid frequency %u, using default\n", bus_frequency); + bus_frequency = I2C_MAX_STANDARD_MODE_FREQ; + } + + cmd[0] = CGBC_I2C_CMD_SPEED | algo_data->bus_id; + cmd[1] = cgbc_i2c_freq_to_reg(bus_frequency); + + ret = cgbc_command(cgbc, &cmd, sizeof(cmd), &data, 1, NULL); + if (ret) + return dev_err_probe(i2c->dev, ret, + "Failed to initialize I2C bus %s", + adap->name); + + cmd[1] = 0x00; + + ret = cgbc_command(cgbc, &cmd, sizeof(cmd), &data, 1, NULL); + if (ret) + return dev_err_probe(i2c->dev, ret, + "Failed to get I2C bus frequency"); + + bus_frequency = cgbc_i2c_reg_to_freq(data); + + dev_dbg(i2c->dev, "%s is running at %d Hz\n", adap->name, bus_frequency); + + /* + * The read_maxtime_us is the maximum time to wait during a read to get + * data. At maximum CGBC_I2C_READ_MAX_LEN can be read by command. + * So calculate the max time to size correctly the timeout. + */ + algo_data->read_maxtime_us = (BITS_PER_BYTE + 1) * CGBC_I2C_READ_MAX_LEN + * USEC_PER_SEC / bus_frequency; + + return 0; +} + +static unsigned int cgbc_i2c_xfer_to_cmd(struct cgbc_i2c_transfer xfer, u8 *cmd) +{ + int i = 0; + + cmd[i++] = CGBC_I2C_CMD_START | xfer.bus_id; + + cmd[i] = (xfer.start) ? CGBC_I2C_START : 0x00; + if (xfer.stop) + cmd[i] |= CGBC_I2C_STOP; + cmd[i++] |= (xfer.start) ? xfer.write + 1 : xfer.write; + + cmd[i++] = (xfer.last_ack) ? (xfer.read | CGBC_I2C_LAST_ACK) : xfer.read; + + if (xfer.start) + cmd[i++] = xfer.addr; + + if (xfer.write > 0) + memcpy(&cmd[i], &xfer.data, xfer.write); + + return i + xfer.write; +} + +static int cgbc_i2c_xfer_msg(struct i2c_adapter *adap) +{ + struct i2c_algo_cgbc_data *algo_data = adap->algo_data; + struct cgbc_i2c_data *i2c = i2c_get_adapdata(adap); + struct cgbc_device_data *cgbc = i2c->cgbc; + struct i2c_msg *msg = i2c->msg; + u8 cmd[CGBC_I2C_CMD_SIZE]; + int ret, max_len, len, i; + unsigned int cmd_len; + u8 cmd_data; + + struct cgbc_i2c_transfer xfer = { + .bus_id = algo_data->bus_id, + .addr = i2c_8bit_addr_from_msg(msg), + }; + + if (i2c->state == STATE_DONE) + return 0; + + ret = cgbc_i2c_get_status(adap); + + if (ret == CGBC_I2C_STAT_BUSY) + return -EBUSY; + else if (ret < 0) + goto err; + + if (i2c->state == STATE_INIT || + (i2c->state == STATE_WRITE && msg->flags & I2C_M_RD)) + xfer.start = true; + + i2c->state = (msg->flags & I2C_M_RD) ? STATE_READ : STATE_WRITE; + + max_len = (i2c->state == STATE_READ) ? + CGBC_I2C_READ_MAX_LEN : CGBC_I2C_WRITE_MAX_LEN; + + if (msg->len - i2c->pos > max_len) { + len = max_len; + } else { + len = msg->len - i2c->pos; + + if (i2c->nmsgs == 1) + xfer.stop = true; + } + + if (i2c->state == STATE_WRITE) { + xfer.write = len; + xfer.read = 0; + + for (i = 0; i < len; i++) + xfer.data[i] = msg->buf[i2c->pos + i]; + + cmd_len = cgbc_i2c_xfer_to_cmd(xfer, &cmd[0]); + + ret = cgbc_command(cgbc, &cmd, cmd_len, NULL, 0, NULL); + if (ret) + goto err; + } else if (i2c->state == STATE_READ) { + xfer.write = 0; + xfer.read = len; + + if (i2c->nmsgs > 1 || msg->len - i2c->pos > max_len) + xfer.read |= CGBC_I2C_LAST_ACK; + + cmd_len = cgbc_i2c_xfer_to_cmd(xfer, &cmd[0]); + ret = cgbc_command(cgbc, &cmd, cmd_len, NULL, 0, NULL); + if (ret) + goto err; + + ret = read_poll_timeout(cgbc_i2c_get_status, ret, + ret != CGBC_I2C_STAT_BUSY, 0, + 2 * algo_data->read_maxtime_us, false, adap); + if (ret < 0) + goto err; + + cmd_data = CGBC_I2C_CMD_DATA | algo_data->bus_id; + ret = cgbc_command(cgbc, &cmd_data, sizeof(cmd_data), + msg->buf + i2c->pos, len, NULL); + if (ret) + goto err; + } + + if (len == (msg->len - i2c->pos)) { + i2c->msg++; + i2c->nmsgs--; + i2c->pos = 0; + } else { + i2c->pos += len; + } + + if (i2c->nmsgs == 0) + i2c->state = STATE_DONE; + + return 0; + +err: + i2c->state = STATE_ERROR; + return ret; +} + +static int cgbc_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + struct cgbc_i2c_data *i2c = i2c_get_adapdata(adap); + unsigned long timeout = jiffies + HZ; + int ret; + + i2c->state = STATE_INIT; + i2c->msg = msgs; + i2c->nmsgs = num; + i2c->pos = 0; + + while (time_before(jiffies, timeout)) { + ret = cgbc_i2c_xfer_msg(adap); + + if (i2c->state == STATE_DONE) + return num; + + if (i2c->state == STATE_ERROR) + return ret; + + if (ret == 0) + timeout = jiffies + HZ; + } + + i2c->state = STATE_ERROR; + return -ETIMEDOUT; +} + +static u32 cgbc_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm cgbc_i2c_algorithm = { + .master_xfer = cgbc_i2c_xfer, + .functionality = cgbc_i2c_func, +}; + +static struct i2c_algo_cgbc_data cgbc_i2c_algo_data[] = { + { .bus_id = CGBC_I2C_PRIMARY_BUS_ID }, + { .bus_id = CGBC_I2C_PM_BUS_ID }, +}; + +static const struct i2c_adapter cgbc_i2c_adapter[] = { + { + .owner = THIS_MODULE, + .name = "Congatec Primary I2C adapter", + .class = I2C_CLASS_DEPRECATED, + .algo = &cgbc_i2c_algorithm, + .algo_data = &cgbc_i2c_algo_data[0], + .nr = -1, + }, + { + .owner = THIS_MODULE, + .name = "Congatec Power Management I2C adapter", + .class = I2C_CLASS_DEPRECATED, + .algo = &cgbc_i2c_algorithm, + .algo_data = &cgbc_i2c_algo_data[1], + .nr = -1, + }, +}; + +static int cgbc_i2c_probe(struct platform_device *pdev) +{ + struct cgbc_device_data *cgbc = dev_get_drvdata(pdev->dev.parent); + struct cgbc_i2c_data *i2c; + int ret; + + i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + i2c->cgbc = cgbc; + i2c->dev = &pdev->dev; + i2c->adap = cgbc_i2c_adapter[pdev->id]; + i2c->adap.dev.parent = i2c->dev; + i2c_set_adapdata(&i2c->adap, i2c); + platform_set_drvdata(pdev, i2c); + + ret = cgbc_i2c_set_frequency(&i2c->adap, I2C_MAX_STANDARD_MODE_FREQ); + if (ret) + return ret; + + return i2c_add_numbered_adapter(&i2c->adap); +} + +static void cgbc_i2c_remove(struct platform_device *pdev) +{ + struct cgbc_i2c_data *i2c = platform_get_drvdata(pdev); + + i2c_del_adapter(&i2c->adap); +} + +static struct platform_driver cgbc_i2c_driver = { + .driver = { + .name = "cgbc-i2c", + }, + .probe = cgbc_i2c_probe, + .remove_new = cgbc_i2c_remove, +}; + +module_platform_driver(cgbc_i2c_driver); + +MODULE_DESCRIPTION("Congatec Board Controller I2C Driver"); +MODULE_AUTHOR("Thomas Richard "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:cgbc_i2c"); From patchwork Fri Aug 9 14:52:08 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Richard X-Patchwork-Id: 1970981 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.a=rsa-sha256 header.s=gm1 header.b=k2bcOd5z; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=2604:1380:45d1:ec00::1; helo=ny.mirrors.kernel.org; envelope-from=linux-gpio+bounces-8688-incoming=patchwork.ozlabs.org@vger.kernel.org; receiver=patchwork.ozlabs.org) Received: from ny.mirrors.kernel.org (ny.mirrors.kernel.org [IPv6:2604:1380:45d1:ec00::1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WgRkB152bz1yYl for ; Sat, 10 Aug 2024 00:53:38 +1000 (AEST) Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ny.mirrors.kernel.org (Postfix) with ESMTPS id 9CD0F1C21285 for ; Fri, 9 Aug 2024 14:53:35 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id A0161198E96; Fri, 9 Aug 2024 14:52:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="k2bcOd5z" X-Original-To: linux-gpio@vger.kernel.org Received: from relay2-d.mail.gandi.net (relay2-d.mail.gandi.net [217.70.183.194]) (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 BC789197A97; Fri, 9 Aug 2024 14:52:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.70.183.194 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1723215168; cv=none; b=UKyvUtjSVyzG+1XTjkLSBYo6mLBBr32kjO8ZH3po3dVddzOfaJxdjhGNfzITcxNVZKVmIDd5TVt6+bjEqrm7OygeeM4w6fia25BfKDdE+UGutIA/1cP/7kTwmKbgBTojQkBxbNJcoc8a8m5r4No6oSYyfBw1rtUy3GMRH/K81pU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1723215168; c=relaxed/simple; bh=fS9lG9YSu0Zt2OhD8Yt6Ql3M20X5tLBA7w7sg8lrLKg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=NCVj9cyUJgVNUhXmLrB9x45ZKwylSKSKuiX2i0V/TQmepQPYZ5WHVo+/1d8h2FchjPwPwTMgFTk7NTKAkPz+sWkv+Vq3hniKOfcxYaY12fsRcopAIRNNTjcIFQJe0fTUalBqfY51ujKcFi5a542ax0VSbBenN6qfaG9Bq+2S4BQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=k2bcOd5z; arc=none smtp.client-ip=217.70.183.194 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Received: by mail.gandi.net (Postfix) with ESMTPSA id 74EEE40009; Fri, 9 Aug 2024 14:52:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1723215158; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=pgf/cI0G0sPCEdY5s9qG8ueGUF4HU9d1QAfvNExVzAo=; b=k2bcOd5zkZK/cHj4MRSaTxsXO5QmsMYoelCoXDD/ne0EX+LIUZ8ftGwsWh6DijB1HJBnhp fPRWur2esvIZllvlzHZO8+33HhAzNXxsoTHNStuo7KVbiDFbIe7gRlNOxJtnBpcrOLkuiv MmzZSuTrImYD85Xrle6r509lA2TLKfrNjR1vP8T5w/4m8vpiHIYRaauxLbrFuinGAKNSdw 5CV+QXy225Kz2fEcbf1Q2Hz803T1JlkXY01XfT3lf4EHY4LIL6uKNEVqgOppEEM4k7CZfq wC2F50BPGMGSpr71QHV9Lhtiw0MYSHEoLyzrBK1s+VDk955iv5fkdK3xR3JaEQ== From: Thomas Richard Date: Fri, 09 Aug 2024 16:52:08 +0200 Subject: [PATCH 4/5] watchdog: Congatec Board Controller watchdog timer driver Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240503-congatec-board-controller-v1-4-fec5236270e7@bootlin.com> References: <20240503-congatec-board-controller-v1-0-fec5236270e7@bootlin.com> In-Reply-To: <20240503-congatec-board-controller-v1-0-fec5236270e7@bootlin.com> To: Lee Jones , Linus Walleij , Bartosz Golaszewski , Andi Shyti , Wim Van Sebroeck , Guenter Roeck Cc: linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, linux-i2c@vger.kernel.org, linux-watchdog@vger.kernel.org, thomas.petazzoni@bootlin.com, blake.vermeer@keysight.com, Thomas Richard X-Mailer: b4 0.12.0 X-GND-Sasl: thomas.richard@bootlin.com Add watchdog timer support for the Congatec Board Controller. Signed-off-by: Thomas Richard --- drivers/watchdog/Kconfig | 10 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/cgbc_wdt.c | 217 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 228 insertions(+) diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index bae1d97cce89..07b711fc8bb2 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1142,6 +1142,16 @@ config ALIM7101_WDT Most people will say N. +config CGBC_WDT + tristate "Congatec Board Controller Watchdog Timer" + depends on MFD_CGBC + select WATCHDOG_CORE + help + Enables watchdog timer support for the Congatec Board Controller. + + This driver can also be built as a module. If so, the module will be + called cgbc_wdt. + config EBC_C384_WDT tristate "WinSystems EBC-C384 Watchdog Timer" depends on (X86 || COMPILE_TEST) && HAS_IOPORT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index b51030f035a6..5aa66ba91346 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -106,6 +106,7 @@ obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o obj-$(CONFIG_ADVANTECH_EC_WDT) += advantech_ec_wdt.o obj-$(CONFIG_ALIM1535_WDT) += alim1535_wdt.o obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o +obj-$(CONFIG_CGBC_WDT) += cgbc_wdt.o obj-$(CONFIG_EBC_C384_WDT) += ebc-c384_wdt.o obj-$(CONFIG_EXAR_WDT) += exar_wdt.o obj-$(CONFIG_F71808E_WDT) += f71808e_wdt.o diff --git a/drivers/watchdog/cgbc_wdt.c b/drivers/watchdog/cgbc_wdt.c new file mode 100644 index 000000000000..9327e87b52e8 --- /dev/null +++ b/drivers/watchdog/cgbc_wdt.c @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Congatec Board Controller watchdog driver + * + * Copyright (C) 2024 Bootlin + * Author: Thomas Richard + */ + +#include +#include +#include + +#include + +#define CGBC_WDT_CMD_TRIGGER 0x27 +#define CGBC_WDT_CMD_INIT 0x28 +#define CGBC_WDT_DISABLE 0x00 + +#define CGBC_WDT_MODE_SINGLE_EVENT 0x02 + +#define DEFAULT_TIMEOUT_SEC 30 +#define DEFAULT_PRETIMEOUT_SEC 0 + +enum action { + ACTION_INT = 0, + ACTION_SMI, + ACTION_RESET, + ACTION_BUTTON, +}; + +static unsigned int timeout = DEFAULT_TIMEOUT_SEC; +module_param(timeout, uint, 0); +MODULE_PARM_DESC(timeout, + "Watchdog timeout in seconds. (>=0, default=" + __MODULE_STRING(DEFAULT_TIMEOUT_SEC) ")"); + +static unsigned int pretimeout = DEFAULT_PRETIMEOUT_SEC; +module_param(pretimeout, uint, 0); +MODULE_PARM_DESC(pretimeout, + "Watchdog pretimeout in seconds. (>=0, default=" + __MODULE_STRING(DEFAULT_PRETIMEOUT_SEC) ")"); + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +struct cgbc_wdt_data { + struct cgbc_device_data *cgbc; + struct watchdog_device wdd; + enum action timeout_action; + enum action pretimeout_action; +}; + +struct cgbc_wdt_cmd_cfg { + u8 cmd; + u8 mode; + u8 action; + u8 timeout1[3]; + u8 timeout2[3]; + u8 reserved[3]; + u8 delay[3]; +} __packed; + +static_assert(sizeof(struct cgbc_wdt_cmd_cfg) == 15); + +static int cgbc_wdt_start(struct watchdog_device *wdd) +{ + struct cgbc_wdt_data *wdt_data = watchdog_get_drvdata(wdd); + struct cgbc_device_data *cgbc = wdt_data->cgbc; + unsigned int timeout1 = (wdd->timeout - wdd->pretimeout) * 1000; + unsigned int timeout2 = wdd->pretimeout * 1000; + u8 action; + + struct cgbc_wdt_cmd_cfg cmd_start = { + .cmd = CGBC_WDT_CMD_INIT, + .mode = CGBC_WDT_MODE_SINGLE_EVENT, + .timeout1[0] = (u8)timeout1, + .timeout1[1] = (u8)(timeout1 >> 8), + .timeout1[2] = (u8)(timeout1 >> 16), + .timeout2[0] = (u8)timeout2, + .timeout2[1] = (u8)(timeout2 >> 8), + .timeout2[2] = (u8)(timeout2 >> 16), + }; + + if (wdd->pretimeout) { + action = 2; + action |= wdt_data->pretimeout_action << 2; + action |= wdt_data->timeout_action << 4; + } else { + action = 1; + action |= wdt_data->timeout_action << 2; + } + + cmd_start.action = action; + + return cgbc_command(cgbc, &cmd_start, sizeof(cmd_start), NULL, 0, NULL); +} + +static int cgbc_wdt_stop(struct watchdog_device *wdd) +{ + struct cgbc_wdt_data *wdt_data = watchdog_get_drvdata(wdd); + struct cgbc_device_data *cgbc = wdt_data->cgbc; + struct cgbc_wdt_cmd_cfg cmd_stop = { + .cmd = CGBC_WDT_CMD_INIT, + .mode = CGBC_WDT_DISABLE, + }; + + return cgbc_command(cgbc, &cmd_stop, sizeof(cmd_stop), NULL, 0, NULL); +} + +static int cgbc_wdt_keepalive(struct watchdog_device *wdd) +{ + struct cgbc_wdt_data *wdt_data = watchdog_get_drvdata(wdd); + struct cgbc_device_data *cgbc = wdt_data->cgbc; + u8 cmd_ping = CGBC_WDT_CMD_TRIGGER; + + return cgbc_command(cgbc, &cmd_ping, sizeof(cmd_ping), NULL, 0, NULL); +} + +static int cgbc_wdt_set_pretimeout(struct watchdog_device *wdd, + unsigned int pretimeout) +{ + struct cgbc_wdt_data *wdt_data = watchdog_get_drvdata(wdd); + + wdd->pretimeout = pretimeout; + wdt_data->pretimeout_action = ACTION_SMI; + + if (watchdog_active(wdd)) + return cgbc_wdt_start(wdd); + + return 0; +} + +static int cgbc_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + struct cgbc_wdt_data *wdt_data = watchdog_get_drvdata(wdd); + + if (timeout < wdd->pretimeout) { + dev_warn(wdd->parent, "timeout <= pretimeout. Setting pretimeout to zero\n"); + wdd->pretimeout = 0; + } + + wdd->timeout = timeout; + wdt_data->timeout_action = ACTION_RESET; + + if (watchdog_active(wdd)) + return cgbc_wdt_start(wdd); + + return 0; +} + +static const struct watchdog_info cgbc_wdt_info = { + .identity = "CGBC Watchdog", + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE | WDIOF_PRETIMEOUT +}; + +static const struct watchdog_ops cgbc_wdt_ops = { + .owner = THIS_MODULE, + .start = cgbc_wdt_start, + .stop = cgbc_wdt_stop, + .ping = cgbc_wdt_keepalive, + .set_timeout = cgbc_wdt_set_timeout, + .set_pretimeout = cgbc_wdt_set_pretimeout, +}; + +static int cgbc_wdt_probe(struct platform_device *pdev) +{ + struct cgbc_device_data *cgbc = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct cgbc_wdt_data *wdt_data; + struct watchdog_device *wdd; + int ret; + + wdt_data = devm_kzalloc(dev, sizeof(*wdt_data), GFP_KERNEL); + if (!wdt_data) + return -ENOMEM; + + wdt_data->cgbc = cgbc; + wdd = &wdt_data->wdd; + wdd->parent = dev; + + wdd->info = &cgbc_wdt_info; + wdd->ops = &cgbc_wdt_ops; + + watchdog_set_drvdata(wdd, wdt_data); + watchdog_set_nowayout(wdd, nowayout); + + cgbc_wdt_set_timeout(wdd, timeout); + cgbc_wdt_set_pretimeout(wdd, pretimeout); + + platform_set_drvdata(pdev, wdt_data); + watchdog_stop_on_reboot(wdd); + watchdog_stop_on_unregister(wdd); + + ret = devm_watchdog_register_device(dev, wdd); + if (ret) + return ret; + + return 0; +} + +static struct platform_driver cgbc_wdt_driver = { + .driver = { + .name = "cgbc-wdt", + }, + .probe = cgbc_wdt_probe, +}; + +module_platform_driver(cgbc_wdt_driver); + +MODULE_DESCRIPTION("Congatec Board Controller Watchdog Driver"); +MODULE_AUTHOR("Thomas Richard "); +MODULE_LICENSE("GPL"); From patchwork Fri Aug 9 14:52:09 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Richard X-Patchwork-Id: 1970980 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=bootlin.com header.i=@bootlin.com header.a=rsa-sha256 header.s=gm1 header.b=e4YddIaj; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=139.178.88.99; helo=sv.mirrors.kernel.org; envelope-from=linux-gpio+bounces-8689-incoming=patchwork.ozlabs.org@vger.kernel.org; receiver=patchwork.ozlabs.org) Received: from sv.mirrors.kernel.org (sv.mirrors.kernel.org [139.178.88.99]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1)) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WgRk91SP0z1yYl for ; Sat, 10 Aug 2024 00:53:37 +1000 (AEST) Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by sv.mirrors.kernel.org (Postfix) with ESMTPS id F3D7B282961 for ; Fri, 9 Aug 2024 14:53:35 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id B5774198E9B; Fri, 9 Aug 2024 14:52:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="e4YddIaj" X-Original-To: linux-gpio@vger.kernel.org Received: from relay2-d.mail.gandi.net (relay2-d.mail.gandi.net [217.70.183.194]) (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 5A3E8198848; Fri, 9 Aug 2024 14:52:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.70.183.194 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1723215168; cv=none; b=GjTv4/313sUQHBBVKw5NFZu/jR4vYxwmYXmOAkZKDvH8314g9AZ8Jyk7C/8iolgfrPnf8w4iJi8y/G8tzfE7Q5aRz0qxF9vTeZLkb+qgQq4GFHMpHHqyqyBHB+Ec6SjzH7N/mC2ObzW1FIaVfuUfY9Yt9yg3rVgrhz9+1jYwqj0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1723215168; c=relaxed/simple; bh=Sw+oLlc7TSxf7TiSdLT9axbvrep8wB4Fgzlbr7MroUA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=pWkpTxu/8CbYHcnQsjmEnjvOdJzqEQpJbibxCHgAebibOR+omw3Af1gwiV3Rg1prgPe6JfflfpQZTb4XViRlBwt3deWQATgYVSqRqgdvSNlYzBVbVSHtD9WQ7GIVjIq/riFeazWFSq5qkPvSJBysokR94cPYlVrHD0dKL9df0gA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=e4YddIaj; arc=none smtp.client-ip=217.70.183.194 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Received: by mail.gandi.net (Postfix) with ESMTPSA id 8E61340006; Fri, 9 Aug 2024 14:52:38 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1723215159; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Bof5JX2qIGglUu1nJZwnxlc2Jg4ND61QWz8uaqbeqT0=; b=e4YddIajyCvvmE4aJz0bnfme8ANROPwDQr+9iFGRe0A24GDNKFZ1rB77DUS8Xd3aTEFi8b s4qkeony1oh5VBMDU/eDa2A/CIxW/ssRJfUh4v4IPhMfUAMycNsTCWT/ptSQrATU56KTke ttUdKlkW1cbfwIUzF8RPWAP0FO60my2P9dK5xsxJiLnHxI143dgIUhf+bVPY6V+p1Agwu/ UK+uXPj/6JfIoudYlW7z7hHJDbIgBTiB5cJ+Ftb9D1se9YuyF1LFcdTvSSx88P9CtSQEgW yhoPEIalfsiIamkjP5bO6nfJ4MN3ruHwNhtaHlCmosN0AVJSi/53vTDlsHAjsg== From: Thomas Richard Date: Fri, 09 Aug 2024 16:52:09 +0200 Subject: [PATCH 5/5] MAINTAINERS: Add entry for Congatec Board Controller Precedence: bulk X-Mailing-List: linux-gpio@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-Id: <20240503-congatec-board-controller-v1-5-fec5236270e7@bootlin.com> References: <20240503-congatec-board-controller-v1-0-fec5236270e7@bootlin.com> In-Reply-To: <20240503-congatec-board-controller-v1-0-fec5236270e7@bootlin.com> To: Lee Jones , Linus Walleij , Bartosz Golaszewski , Andi Shyti , Wim Van Sebroeck , Guenter Roeck Cc: linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, linux-i2c@vger.kernel.org, linux-watchdog@vger.kernel.org, thomas.petazzoni@bootlin.com, blake.vermeer@keysight.com, Thomas Richard X-Mailer: b4 0.12.0 X-GND-Sasl: thomas.richard@bootlin.com Add the Congatec Board Controller drivers and header as Maintained by myself. Signed-off-by: Thomas Richard Acked-by: Andi Shyti --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 8766f3e5e87e..781803bb5d0b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5648,6 +5648,15 @@ F: fs/configfs/ F: include/linux/configfs.h F: samples/configfs/ +CONGATEC BOARD CONTROLLER MFD DRIVER +M: Thomas Richard +S: Maintained +F: drivers/gpio/gpio-cgbc.c +F: drivers/i2c/busses/i2c-cgbc.c +F: drivers/mfd/cgbc-core.c +F: drivers/watchdog/cgbc_wdt.c +F: include/linux/mfd/cgbc.h + CONSOLE SUBSYSTEM M: Greg Kroah-Hartman S: Supported