From nobody Wed Nov 27 12:34:19 2024 Received: from mx08lb.world4you.com (mx08lb.world4you.com [81.19.149.118]) (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 60AAB18E775 for ; Wed, 9 Oct 2024 21:01:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=81.19.149.118 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1728507706; cv=none; b=Dbi8S/FyfP71JN1L1XlmQdbAE+VVRHxmSvRle5tlz/JBdEqbU2Rk5ZVkesejCZtEu8nL+hM16brC2c9+lRLf9wUFc9IYPc7V+0Mt4zdMYLIwhttvsj8ZeOGvwQ3hKgGN7t9QFPEqoH9J6MYGk/6zbQL36ELC6mcCSnrLENVuf5Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1728507706; c=relaxed/simple; bh=8rRssPa4NmPHUObCdqarK2uu6tG9wGRvVJp0EgS8w20=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=Rah2jCXzgJCIOAxnQruPi/TWha/FjWb5frok7hzOv+xvk4Ht2k6iL3lPlG/RYNY3sp4h3D33SQNSHQlC5Ny7Ggu636YRNEhIAomdrd+E5+nUsg5A9KUN1R4mLWCdcCYXZBQFhXw37omVyouwU/LI/m3wKeqD115KmbFVNH9SZac= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=engleder-embedded.com; spf=pass smtp.mailfrom=engleder-embedded.com; dkim=pass (1024-bit key) header.d=engleder-embedded.com header.i=@engleder-embedded.com header.b=gQ//6uS5; arc=none smtp.client-ip=81.19.149.118 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=engleder-embedded.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=engleder-embedded.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=engleder-embedded.com header.i=@engleder-embedded.com header.b="gQ//6uS5" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=engleder-embedded.com; s=dkim11; h=Content-Transfer-Encoding:MIME-Version: References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To: Content-Type:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help: List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive; bh=5GXUPncpZ9Bbrotfu0G3IW9V00gB6RyXfU2fRGHIojU=; b=gQ//6uS5eQfmR/7W7EwQL1pkBF 40D6Tm1kIayn4kAzXtKGyEOy6bh7+dGx1CNEmNU/AixOTdTIUnyD6Qj77K806KQlBNDPc6DN/jrg6 r7lzJSfdl96ELh2Ht0gRCT+5t/Q9Z5QfAX469jLH5EKQ0Z0CzMwITWQsJFzQH1jXzqi4=; Received: from 88-117-56-173.adsl.highway.telekom.at ([88.117.56.173] helo=hornet.engleder.at) by mx08lb.world4you.com with esmtpsa (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.97.1) (envelope-from ) id 1sydJq-000000002pc-0DB0; Wed, 09 Oct 2024 22:30:02 +0200 From: Gerhard Engleder To: linux-kernel@vger.kernel.org Cc: arnd@arndb.de, gregkh@linuxfoundation.org, Gerhard Engleder , Gerhard Engleder Subject: [PATCH 1/6] misc: keba: Add SPI controller device Date: Wed, 9 Oct 2024 22:29:44 +0200 Message-Id: <20241009202949.20164-2-gerhard@engleder-embedded.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20241009202949.20164-1-gerhard@engleder-embedded.com> References: <20241009202949.20164-1-gerhard@engleder-embedded.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-AV-Do-Run: Yes X-ACL-Warn: X-W4Y-Internal Content-Type: text/plain; charset="utf-8" From: Gerhard Engleder Add support for the SPI controller auxiliary device. This enables access to the SPI flash of the FPGA and some other SPI devices. The actual list of SPI devices is detected by reading some bits out of the previously registered I2C EEPROM. Signed-off-by: Gerhard Engleder --- drivers/misc/keba/cp500.c | 235 ++++++++++++++++++++++++++++++++++---- include/linux/misc/keba.h | 15 +++ 2 files changed, 227 insertions(+), 23 deletions(-) diff --git a/drivers/misc/keba/cp500.c b/drivers/misc/keba/cp500.c index ae0922817881..7cebf2929390 100644 --- a/drivers/misc/keba/cp500.c +++ b/drivers/misc/keba/cp500.c @@ -12,7 +12,11 @@ #include #include #include +#include +#include #include +#include +#include =20 #define CP500 "cp500" =20 @@ -43,6 +47,16 @@ =20 /* EEPROM */ #define CP500_HW_CPU_EEPROM_NAME "cp500_cpu_eeprom" +#define CP500_EEPROM_DA_OFFSET 0x016F +#define CP500_EEPROM_DA_ESC_TYPE_MASK 0x01 +#define CP500_EEPROM_ESC_LAN9252 0x00 +#define CP500_EEPROM_ESC_ET1100 0x01 + +/* SPI flash running at full speed */ +#define CP500_FLASH_HZ (33 * 1000 * 1000) + +/* LAN9252 */ +#define CP500_LAN9252_HZ (10 * 1000 * 1000) =20 #define CP500_IS_CP035(dev) ((dev)->pci_dev->device =3D=3D PCI_DEVICE_ID_K= EBA_CP035) #define CP500_IS_CP505(dev) ((dev)->pci_dev->device =3D=3D PCI_DEVICE_ID_K= EBA_CP505) @@ -55,25 +69,29 @@ struct cp500_dev_info { =20 struct cp500_devs { struct cp500_dev_info startup; + struct cp500_dev_info spi; struct cp500_dev_info i2c; }; =20 /* list of devices within FPGA of CP035 family (CP035, CP056, CP057) */ static struct cp500_devs cp035_devices =3D { - .startup =3D { 0x0000, SZ_4K }, - .i2c =3D { 0x4000, SZ_4K }, + .startup =3D { 0x0000, SZ_4K }, + .spi =3D { 0x1000, SZ_4K }, + .i2c =3D { 0x4000, SZ_4K }, }; =20 /* list of devices within FPGA of CP505 family (CP503, CP505, CP507) */ static struct cp500_devs cp505_devices =3D { - .startup =3D { 0x0000, SZ_4K }, - .i2c =3D { 0x5000, SZ_4K }, + .startup =3D { 0x0000, SZ_4K }, + .spi =3D { 0x4000, SZ_4K }, + .i2c =3D { 0x5000, SZ_4K }, }; =20 /* list of devices within FPGA of CP520 family (CP520, CP530) */ static struct cp500_devs cp520_devices =3D { - .startup =3D { 0x0000, SZ_4K }, - .i2c =3D { 0x5000, SZ_4K }, + .startup =3D { 0x0000, SZ_4K }, + .spi =3D { 0x4000, SZ_4K }, + .i2c =3D { 0x5000, SZ_4K }, }; =20 struct cp500 { @@ -85,9 +103,12 @@ struct cp500 { int minor; int build; } version; + struct notifier_block nvmem_notifier; + atomic_t nvmem_notified; =20 /* system FPGA BAR */ resource_size_t sys_hwbase; + struct keba_spi_auxdev *spi; struct keba_i2c_auxdev *i2c; =20 /* ECM EtherCAT BAR */ @@ -97,6 +118,7 @@ struct cp500 { }; =20 /* I2C devices */ +#define CP500_EEPROM_ADDR 0x50 static struct i2c_board_info cp500_i2c_info[] =3D { { /* temperature sensor */ I2C_BOARD_INFO("emc1403", 0x4c), @@ -107,30 +129,67 @@ static struct i2c_board_info cp500_i2c_info[] =3D { * CP505 family: bridge board * CP520 family: carrier board */ - I2C_BOARD_INFO("24c32", 0x50), + I2C_BOARD_INFO("24c32", CP500_EEPROM_ADDR), .dev_name =3D CP500_HW_CPU_EEPROM_NAME, }, { /* interface board EEPROM */ - I2C_BOARD_INFO("24c32", 0x51), + I2C_BOARD_INFO("24c32", CP500_EEPROM_ADDR + 1), }, { /* * EEPROM (optional) * CP505 family: CPU board * CP520 family: MMI board */ - I2C_BOARD_INFO("24c32", 0x52), + I2C_BOARD_INFO("24c32", CP500_EEPROM_ADDR + 2), }, { /* extension module 0 EEPROM (optional) */ - I2C_BOARD_INFO("24c32", 0x53), + I2C_BOARD_INFO("24c32", CP500_EEPROM_ADDR + 3), }, { /* extension module 1 EEPROM (optional) */ - I2C_BOARD_INFO("24c32", 0x54), + I2C_BOARD_INFO("24c32", CP500_EEPROM_ADDR + 4), }, { /* extension module 2 EEPROM (optional) */ - I2C_BOARD_INFO("24c32", 0x55), + I2C_BOARD_INFO("24c32", CP500_EEPROM_ADDR + 5), }, { /* extension module 3 EEPROM (optional) */ - I2C_BOARD_INFO("24c32", 0x56), + I2C_BOARD_INFO("24c32", CP500_EEPROM_ADDR + 6), + } +}; + +/* SPI devices */ +static struct mtd_partition cp500_partitions[] =3D { + { + .name =3D "system-flash-parts", + .size =3D MTDPART_SIZ_FULL, + .offset =3D 0, + .mask_flags =3D 0 + } +}; +static const struct flash_platform_data cp500_w25q32 =3D { + .type =3D "w25q32", + .name =3D "system-flash", + .parts =3D cp500_partitions, + .nr_parts =3D ARRAY_SIZE(cp500_partitions), +}; +static const struct flash_platform_data cp500_m25p16 =3D { + .type =3D "m25p16", + .name =3D "system-flash", + .parts =3D cp500_partitions, + .nr_parts =3D ARRAY_SIZE(cp500_partitions), +}; +static struct spi_board_info cp500_spi_info[] =3D { + { /* system FPGA configuration bitstream flash */ + .modalias =3D "m25p80", + .platform_data =3D &cp500_m25p16, + .max_speed_hz =3D CP500_FLASH_HZ, + .chip_select =3D 0, + .mode =3D SPI_MODE_3, + }, { /* LAN9252 EtherCAT slave controller */ + .modalias =3D "lan9252", + .platform_data =3D NULL, + .max_speed_hz =3D CP500_LAN9252_HZ, + .chip_select =3D 1, + .mode =3D SPI_MODE_3, } }; =20 @@ -229,7 +288,7 @@ static void cp500_i2c_release(struct device *dev) =20 static int cp500_register_i2c(struct cp500 *cp500) { - int retval; + int ret; =20 cp500->i2c =3D kzalloc(sizeof(*cp500->i2c), GFP_KERNEL); if (!cp500->i2c) @@ -251,30 +310,149 @@ static int cp500_register_i2c(struct cp500 *cp500) cp500->i2c->info_size =3D ARRAY_SIZE(cp500_i2c_info); cp500->i2c->info =3D cp500_i2c_info; =20 - retval =3D auxiliary_device_init(&cp500->i2c->auxdev); - if (retval) { + ret =3D auxiliary_device_init(&cp500->i2c->auxdev); + if (ret) { kfree(cp500->i2c); cp500->i2c =3D NULL; =20 - return retval; + return ret; } - retval =3D __auxiliary_device_add(&cp500->i2c->auxdev, "keba"); - if (retval) { + ret =3D __auxiliary_device_add(&cp500->i2c->auxdev, "keba"); + if (ret) { auxiliary_device_uninit(&cp500->i2c->auxdev); cp500->i2c =3D NULL; =20 - return retval; + return ret; } =20 return 0; } =20 +static void cp500_spi_release(struct device *dev) +{ + struct keba_spi_auxdev *spi =3D + container_of(dev, struct keba_spi_auxdev, auxdev.dev); + + kfree(spi); +} + +static int cp500_register_spi(struct cp500 *cp500, u8 esc_type) +{ + int info_size; + int ret; + + cp500->spi =3D kzalloc(sizeof(*cp500->spi), GFP_KERNEL); + if (!cp500->spi) + return -ENOMEM; + + if (CP500_IS_CP035(cp500)) + cp500_spi_info[0].platform_data =3D &cp500_w25q32; + if (esc_type =3D=3D CP500_EEPROM_ESC_LAN9252) + info_size =3D ARRAY_SIZE(cp500_spi_info); + else + info_size =3D ARRAY_SIZE(cp500_spi_info) - 1; + + cp500->spi->auxdev.name =3D "spi"; + cp500->spi->auxdev.id =3D 0; + cp500->spi->auxdev.dev.release =3D cp500_spi_release; + cp500->spi->auxdev.dev.parent =3D &cp500->pci_dev->dev; + cp500->spi->io =3D (struct resource) { + /* SPI register area */ + .start =3D (resource_size_t) cp500->sys_hwbase + + cp500->devs->spi.offset, + .end =3D (resource_size_t) cp500->sys_hwbase + + cp500->devs->spi.offset + + cp500->devs->spi.size - 1, + .flags =3D IORESOURCE_MEM, + }; + cp500->spi->info_size =3D info_size; + cp500->spi->info =3D cp500_spi_info; + + ret =3D auxiliary_device_init(&cp500->spi->auxdev); + if (ret) { + kfree(cp500->spi); + cp500->spi =3D NULL; + + return ret; + } + ret =3D __auxiliary_device_add(&cp500->spi->auxdev, "keba"); + if (ret) { + auxiliary_device_uninit(&cp500->spi->auxdev); + cp500->spi =3D NULL; + + return ret; + } + + return 0; +} + +static int cp500_nvmem_match(struct device *dev, const void *data) +{ + const struct cp500 *cp500 =3D data; + struct i2c_client *client; + + /* match only CPU EEPROM below the cp500 device */ + dev =3D dev->parent; + client =3D i2c_verify_client(dev); + if (!client || client->addr !=3D CP500_EEPROM_ADDR) + return 0; + while ((dev =3D dev->parent)) + if (dev =3D=3D &cp500->pci_dev->dev) + return 1; + + return 0; +} + +static int cp500_nvmem(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct nvmem_device *nvmem; + struct cp500 *cp500; + struct device *dev; + int notified; + u8 esc_type; + int ret; + + if (action !=3D NVMEM_ADD) + return NOTIFY_DONE; + cp500 =3D container_of(nb, struct cp500, nvmem_notifier); + dev =3D &cp500->pci_dev->dev; + + /* process CPU EEPROM content only once */ + notified =3D atomic_read(&cp500->nvmem_notified); + if (notified) + return NOTIFY_DONE; + nvmem =3D nvmem_device_find(cp500, cp500_nvmem_match); + if (IS_ERR_OR_NULL(nvmem)) + return NOTIFY_DONE; + if (!atomic_try_cmpxchg_relaxed(&cp500->nvmem_notified, ¬ified, 1)) { + nvmem_device_put(nvmem); + + return NOTIFY_DONE; + } + + ret =3D nvmem_device_read(nvmem, CP500_EEPROM_DA_OFFSET, sizeof(esc_type), + (void *)&esc_type); + nvmem_device_put(nvmem); + if (ret !=3D sizeof(esc_type)) { + dev_warn(dev, "Failed to read device assembly!\n"); + + return NOTIFY_DONE; + } + esc_type &=3D CP500_EEPROM_DA_ESC_TYPE_MASK; + + if (cp500_register_spi(cp500, esc_type)) + dev_warn(dev, "Failed to register SPI!\n"); + + return NOTIFY_OK; +} + static void cp500_register_auxiliary_devs(struct cp500 *cp500) { struct device *dev =3D &cp500->pci_dev->dev; =20 if (cp500_register_i2c(cp500)) - dev_warn(dev, "Failed to register i2c!\n"); + dev_warn(dev, "Failed to register I2C!\n"); } =20 static void cp500_unregister_dev(struct auxiliary_device *auxdev) @@ -285,7 +463,10 @@ static void cp500_unregister_dev(struct auxiliary_devi= ce *auxdev) =20 static void cp500_unregister_auxiliary_devs(struct cp500 *cp500) { - + if (cp500->spi) { + cp500_unregister_dev(&cp500->spi->auxdev); + cp500->spi =3D NULL; + } if (cp500->i2c) { cp500_unregister_dev(&cp500->i2c->auxdev); cp500->i2c =3D NULL; @@ -396,15 +577,21 @@ static int cp500_probe(struct pci_dev *pci_dev, const= struct pci_device_id *id) =20 pci_set_drvdata(pci_dev, cp500); =20 + cp500->nvmem_notifier.notifier_call =3D cp500_nvmem; + ret =3D nvmem_register_notifier(&cp500->nvmem_notifier); + if (ret !=3D 0) + goto out_free_irq; =20 ret =3D cp500_enable(cp500); if (ret !=3D 0) - goto out_free_irq; + goto out_unregister_nvmem; =20 cp500_register_auxiliary_devs(cp500); =20 return 0; =20 +out_unregister_nvmem: + nvmem_unregister_notifier(&cp500->nvmem_notifier); out_free_irq: pci_free_irq_vectors(pci_dev); out_disable: @@ -422,6 +609,8 @@ static void cp500_remove(struct pci_dev *pci_dev) =20 cp500_disable(cp500); =20 + nvmem_unregister_notifier(&cp500->nvmem_notifier); + pci_set_drvdata(pci_dev, 0); =20 pci_free_irq_vectors(pci_dev); diff --git a/include/linux/misc/keba.h b/include/linux/misc/keba.h index 323b31a847c5..1bd5409c6f6f 100644 --- a/include/linux/misc/keba.h +++ b/include/linux/misc/keba.h @@ -7,6 +7,7 @@ #include =20 struct i2c_board_info; +struct spi_board_info; =20 /** * struct keba_i2c_auxdev - KEBA I2C auxiliary device @@ -22,4 +23,18 @@ struct keba_i2c_auxdev { struct i2c_board_info *info; }; =20 +/** + * struct keba_spi_auxdev - KEBA SPI auxiliary device + * @auxdev: auxiliary device object + * @io: address range of SPI controller IO memory + * @info_size: number of SPI devices to be probed + * @info: SPI devices to be probed + */ +struct keba_spi_auxdev { + struct auxiliary_device auxdev; + struct resource io; + int info_size; + struct spi_board_info *info; +}; + #endif /* _LINUX_MISC_KEBA_H */ --=20 2.39.2 From nobody Wed Nov 27 12:34:19 2024 Received: from mx08lb.world4you.com (mx08lb.world4you.com [81.19.149.118]) (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 CEAEC13A409 for ; Wed, 9 Oct 2024 21:08:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=81.19.149.118 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1728508141; cv=none; b=ZYAOacYMYv283MC+faqbbPxwKsD8A65kGEnYfGUEA/YyA6Eh9VdxRHBOibiI1t0DWWOd6BSUk8ZyFm2WX0KqB7vtFqE2vMLv4/34OjGnr2g9KqsMQfTXtLKzfx1vQhEW5euimHVLcyjDxZaFR9xyoBrqyZgLCTfxhmGDGcIU9wE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1728508141; c=relaxed/simple; bh=4GAGFNmfPUMbRPPbf3s5liSC0t4/0p0lLBq6/+BH8eQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=UcGVgEvJKph28ozpmV+FmMnH3zOf70WhHkPGtSXvIDoBSiBs1BlJEOF/bkVNzTX+xUy2VXRQQiDQe/pJoBwrIrMbQyIlKhrkZ2P7EhWDE3cGSlYXtCyLKV90KsSMbVb4Rzj56xSZtWEKsW0nGqImuthsw4H+p14XUMz3p02Lx+E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=engleder-embedded.com; spf=pass smtp.mailfrom=engleder-embedded.com; dkim=pass (1024-bit key) header.d=engleder-embedded.com header.i=@engleder-embedded.com header.b=Z+L42VEn; arc=none smtp.client-ip=81.19.149.118 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=engleder-embedded.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=engleder-embedded.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=engleder-embedded.com header.i=@engleder-embedded.com header.b="Z+L42VEn" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=engleder-embedded.com; s=dkim11; h=Content-Transfer-Encoding:MIME-Version: References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To: Content-Type:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help: List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive; bh=Kz+WQD1c6yjDYCQcqtwueNjgejWZ8hRZEweMwb4+0bY=; b=Z+L42VEn06wPdd4d3uelnNPqWb sXlcXsGjglX1Ll888/qV8vpeEZoelh9OEKFbpjjGxRBcjUbflpCPJeCw7VB4SyR9bGjecup5yZxku PBsPpfwHXU59gZi3DCuAPKPfKZXoGFHCQKtm7n2xRYtcRv3vjGULcL17yss8nrt3L4+U=; Received: from 88-117-56-173.adsl.highway.telekom.at ([88.117.56.173] helo=hornet.engleder.at) by mx08lb.world4you.com with esmtpsa (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.97.1) (envelope-from ) id 1sydJr-000000002pc-1McE; Wed, 09 Oct 2024 22:30:03 +0200 From: Gerhard Engleder To: linux-kernel@vger.kernel.org Cc: arnd@arndb.de, gregkh@linuxfoundation.org, Gerhard Engleder , Gerhard Engleder Subject: [PATCH 2/6] misc: keba: Add LAN9252 driver Date: Wed, 9 Oct 2024 22:29:45 +0200 Message-Id: <20241009202949.20164-3-gerhard@engleder-embedded.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20241009202949.20164-1-gerhard@engleder-embedded.com> References: <20241009202949.20164-1-gerhard@engleder-embedded.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-AV-Do-Run: Yes X-ACL-Warn: X-W4Y-Internal Content-Type: text/plain; charset="utf-8" From: Gerhard Engleder KEBA CP500 devices use the LAN9252 controller for EtherCAT communication. For a stable Ethernet link the PHY registers of the controller need to be configured correctly. This driver configures these PHY registers as required. Signed-off-by: Gerhard Engleder --- drivers/misc/keba/Kconfig | 11 ++ drivers/misc/keba/Makefile | 1 + drivers/misc/keba/lan9252.c | 359 ++++++++++++++++++++++++++++++++++++ 3 files changed, 371 insertions(+) create mode 100644 drivers/misc/keba/lan9252.c diff --git a/drivers/misc/keba/Kconfig b/drivers/misc/keba/Kconfig index 5fbcbc2252ac..dc27b902f34e 100644 --- a/drivers/misc/keba/Kconfig +++ b/drivers/misc/keba/Kconfig @@ -11,3 +11,14 @@ config KEBA_CP500 =20 This driver can also be built as a module. If so, the module will be called cp500. + +config KEBA_LAN9252 + tristate "KEBA CP500 LAN9252 configuration" + depends on SPI + depends on KEBA_CP500 || COMPILE_TEST + help + This driver is used for updating the configuration of the LAN9252 + controller on KEBA CP500 devices. + + This driver can also be built as a module. If so, the module will be + called lan9252. diff --git a/drivers/misc/keba/Makefile b/drivers/misc/keba/Makefile index 0a8b846cda7d..05e9efcad54f 100644 --- a/drivers/misc/keba/Makefile +++ b/drivers/misc/keba/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 =20 obj-$(CONFIG_KEBA_CP500) +=3D cp500.o +obj-$(CONFIG_KEBA_LAN9252) +=3D lan9252.o diff --git a/drivers/misc/keba/lan9252.c b/drivers/misc/keba/lan9252.c new file mode 100644 index 000000000000..d2e734239b70 --- /dev/null +++ b/drivers/misc/keba/lan9252.c @@ -0,0 +1,359 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) KEBA Industrial Automation Gmbh 2024 + * + * Driver for LAN9252 on KEBA CP500 devices + * + * This driver is used for updating the configuration of the LAN9252 contr= oller + * on KEBA CP500 devices. The LAN9252 is connected over SPI, which is also= named + * PDI. + */ + +#include +#include + +/* SPI commands */ +#define LAN9252_SPI_READ 0x3 +#define LAN9252_SPI_WRITE 0x2 + +struct lan9252_read_cmd { + u8 cmd; + u8 addr_0; + u8 addr_1; +} __packed; + +struct lan9252_write_cmd { + u8 cmd; + u8 addr_0; + u8 addr_1; + u32 data; +} __packed; + +/* byte test register */ +#define LAN9252_BYTE_TEST 0x64 +#define LAN9252_BYTE_TEST_VALUE 0x87654321 + +/* hardware configuration register */ +#define LAN9252_HW_CFG 0x74 +#define LAN9252_HW_CFG_READY 0x08000000 + +/* EtherCAT CSR interface data register */ +#define LAN9252_ECAT_CSR_DATA 0x300 + +/* EtherCAT CSR interface command register */ +#define LAN9252_ECAT_CSR_CMD 0x304 +#define LAN9252_ECAT_CSR_BUSY 0x80000000 +#define LAN9252_ECAT_CSR_READ 0x40000000 + +/* EtherCAT slave controller MII register */ +#define LAN9252_ESC_MII 0x510 +#define LAN9252_ESC_MII_BUSY 0x8000 +#define LAN9252_ESC_MII_CMD_ERR 0x4000 +#define LAN9252_ESC_MII_READ_ERR 0x2000 +#define LAN9252_ESC_MII_ERR_MASK (LAN9252_ESC_MII_CMD_ERR | \ + LAN9252_ESC_MII_READ_ERR) +#define LAN9252_ESC_MII_WRITE 0x0200 +#define LAN9252_ESC_MII_READ 0x0100 + +/* EtherCAT slave controller PHY address register */ +#define LAN9252_ESC_PHY_ADDR 0x512 + +/* EtherCAT slave controller PHY register address register */ +#define LAN9252_ESC_PHY_REG_ADDR 0x513 + +/* EtherCAT slave controller PHY data register */ +#define LAN9252_ESC_PHY_DATA 0x514 + +/* EtherCAT slave controller PDI access state register */ +#define LAN9252_ESC_MII_PDI 0x517 +#define LAN9252_ESC_MII_ACCESS_PDI 0x01 +#define LAN9252_ESC_MII_ACCESS_ECAT 0x00 + +/* PHY address */ +#define PHY_ADDRESS 2 + +#define SPI_RETRY_COUNT 10 +#define SPI_WAIT_US 100 +#define SPI_CSR_WAIT_US 500 + +static int lan9252_spi_read(struct spi_device *spi, u16 addr, u32 *data) +{ + struct lan9252_read_cmd cmd; + + cmd.cmd =3D LAN9252_SPI_READ; + cmd.addr_0 =3D (addr >> 8) & 0xFF; + cmd.addr_1 =3D addr & 0xFF; + + return spi_write_then_read(spi, (u8 *)&cmd, + sizeof(struct lan9252_read_cmd), + (u8 *)data, sizeof(u32)); +} + +static int lan9252_spi_write(struct spi_device *spi, u16 addr, u32 data) +{ + struct lan9252_write_cmd cmd; + + cmd.cmd =3D LAN9252_SPI_WRITE; + cmd.addr_0 =3D (addr >> 8) & 0xFF; + cmd.addr_1 =3D addr & 0xFF; + cmd.data =3D data; + + return spi_write(spi, (u8 *)&cmd, sizeof(struct lan9252_write_cmd)); +} + +static bool lan9252_init(struct spi_device *spi) +{ + u32 data; + int ret; + + ret =3D lan9252_spi_read(spi, LAN9252_BYTE_TEST, &data); + if (ret || data !=3D LAN9252_BYTE_TEST_VALUE) + return false; + + ret =3D lan9252_spi_read(spi, LAN9252_HW_CFG, &data); + if (ret || !(data & LAN9252_HW_CFG_READY)) + return false; + + return true; +} + +static u8 lan9252_esc_get_size(u16 addr) +{ + if (addr =3D=3D LAN9252_ESC_MII || addr =3D=3D LAN9252_ESC_PHY_DATA) + return 2; + + return 1; +} + +static int lan9252_esc_wait(struct spi_device *spi) +{ + ktime_t timeout =3D ktime_add_us(ktime_get(), SPI_WAIT_US); + u32 data; + int ret; + + /* wait while CSR command is busy */ + for (;;) { + ret =3D lan9252_spi_read(spi, LAN9252_ECAT_CSR_CMD, &data); + if (ret) + return ret; + if (!(data & LAN9252_ECAT_CSR_BUSY)) + return 0; + + if (ktime_compare(ktime_get(), timeout) > 0) { + ret =3D lan9252_spi_read(spi, LAN9252_ECAT_CSR_CMD, &data); + if (ret) + return ret; + break; + } + } + + return (!(data & LAN9252_ECAT_CSR_BUSY)) ? 0 : -ETIMEDOUT; +} + +static int lan9252_esc_read(struct spi_device *spi, u16 addr, u32 *data) +{ + u32 csr_cmd; + u8 size; + int ret; + + size =3D lan9252_esc_get_size(addr); + csr_cmd =3D LAN9252_ECAT_CSR_BUSY | LAN9252_ECAT_CSR_READ; + csr_cmd |=3D (size << 16) | addr; + ret =3D lan9252_spi_write(spi, LAN9252_ECAT_CSR_CMD, csr_cmd); + if (ret) + return ret; + + ret =3D lan9252_esc_wait(spi); + if (ret) + return ret; + + ret =3D lan9252_spi_read(spi, LAN9252_ECAT_CSR_DATA, data); + if (ret) + return ret; + + return 0; +} + +static int lan9252_esc_write(struct spi_device *spi, u16 addr, u32 data) +{ + u32 csr_cmd; + u8 size; + int ret; + + ret =3D lan9252_spi_write(spi, LAN9252_ECAT_CSR_DATA, data); + if (ret) + return ret; + + size =3D lan9252_esc_get_size(addr); + csr_cmd =3D LAN9252_ECAT_CSR_BUSY; + csr_cmd |=3D (size << 16) | addr; + ret =3D lan9252_spi_write(spi, LAN9252_ECAT_CSR_CMD, csr_cmd); + if (ret) + return ret; + + ret =3D lan9252_esc_wait(spi); + if (ret) + return ret; + + return 0; +} + +static int lan9252_access_mii(struct spi_device *spi, bool access) +{ + u32 data; + + if (access) + data =3D LAN9252_ESC_MII_ACCESS_PDI; + else + data =3D LAN9252_ESC_MII_ACCESS_ECAT; + + return lan9252_esc_write(spi, LAN9252_ESC_MII_PDI, data); +} + +static int lan9252_mii_wait(struct spi_device *spi) +{ + ktime_t timeout =3D ktime_add_us(ktime_get(), SPI_CSR_WAIT_US); + u32 data; + int ret; + + /* wait while MII control state machine is busy */ + for (;;) { + ret =3D lan9252_esc_read(spi, LAN9252_ESC_MII, &data); + if (ret) + return ret; + if (data & LAN9252_ESC_MII_ERR_MASK) + return -EIO; + if (!(data & LAN9252_ESC_MII_BUSY)) + return 0; + + if (ktime_compare(ktime_get(), timeout) > 0) { + ret =3D lan9252_esc_read(spi, LAN9252_ESC_MII, &data); + if (ret) + return ret; + if (data & LAN9252_ESC_MII_ERR_MASK) + return -EIO; + break; + } + } + + return (!(data & LAN9252_ESC_MII_BUSY)) ? 0 : -ETIMEDOUT; +} + +static int lan9252_mii_read(struct spi_device *spi, u8 phy_addr, u8 reg_ad= dr, + u32 *data) +{ + int ret; + + ret =3D lan9252_esc_write(spi, LAN9252_ESC_PHY_ADDR, phy_addr); + if (ret) + return ret; + ret =3D lan9252_esc_write(spi, LAN9252_ESC_PHY_REG_ADDR, reg_addr); + if (ret) + return ret; + + ret =3D lan9252_esc_write(spi, LAN9252_ESC_MII, LAN9252_ESC_MII_READ); + if (ret) + return ret; + + ret =3D lan9252_mii_wait(spi); + if (ret) + return ret; + + return lan9252_esc_read(spi, LAN9252_ESC_PHY_DATA, data); +} + +static int lan9252_mii_write(struct spi_device *spi, u8 phy_addr, u8 reg_a= ddr, + u32 data) +{ + int ret; + + ret =3D lan9252_esc_write(spi, LAN9252_ESC_PHY_ADDR, phy_addr); + if (ret) + return ret; + ret =3D lan9252_esc_write(spi, LAN9252_ESC_PHY_REG_ADDR, reg_addr); + if (ret) + return ret; + ret =3D lan9252_esc_write(spi, LAN9252_ESC_PHY_DATA, data); + if (ret) + return ret; + + ret =3D lan9252_esc_write(spi, LAN9252_ESC_MII, LAN9252_ESC_MII_WRITE); + if (ret) + return ret; + + return lan9252_mii_wait(spi); +} + +static int lan9252_probe(struct spi_device *spi) +{ + u32 data; + int retry =3D SPI_RETRY_COUNT; + int ret; + + /* execute specified initialization sequence */ + while (retry && !lan9252_init(spi)) + retry--; + if (retry =3D=3D 0) { + dev_err(&spi->dev, + "Can't initialize LAN9252 SPI communication!"); + return ret; + } + + /* enable access to MII management for PDI */ + ret =3D lan9252_access_mii(spi, true); + if (ret) { + dev_err(&spi->dev, "Can't enable access to MII management!"); + return ret; + } + + /* + * check PHY configuration and configure if necessary + * - full duplex + * - auto negotiation disabled + * - 100 Mbps + */ + ret =3D lan9252_mii_read(spi, PHY_ADDRESS, MII_BMCR, &data); + if (ret) { + dev_err(&spi->dev, "Can't read LAN9252 configuration!"); + goto out; + } + if (!(data & BMCR_FULLDPLX) || (data & BMCR_ANENABLE) || + !(data & BMCR_SPEED100)) { + /* + */ + data &=3D ~(BMCR_ANENABLE); + data |=3D (BMCR_FULLDPLX | BMCR_SPEED100); + ret =3D lan9252_mii_write(spi, PHY_ADDRESS, MII_BMCR, data); + if (ret) + dev_err(&spi->dev, + "Can't write LAN9252 configuration!"); + } + + dev_info(&spi->dev, "LAN9252 PHY configuration"); + +out: + /* disable access to MII management for PDI */ + lan9252_access_mii(spi, false); + + return ret; +} + +static const struct spi_device_id lan9252_id[] =3D { + {"lan9252"}, + {} +}; +MODULE_DEVICE_TABLE(spi, lan9252_id); + +static struct spi_driver lan9252_driver =3D { + .driver =3D { + .name =3D "lan9252", + }, + .probe =3D lan9252_probe, + .id_table =3D lan9252_id, +}; +module_spi_driver(lan9252_driver); + +MODULE_AUTHOR("Petar Bojanic "); +MODULE_AUTHOR("Gerhard Engleder "); +MODULE_DESCRIPTION("KEBA LAN9252 driver"); +MODULE_LICENSE("GPL"); --=20 2.39.2 From nobody Wed Nov 27 12:34:19 2024 Received: from mx08lb.world4you.com (mx08lb.world4you.com [81.19.149.118]) (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 07C1713A409 for ; Wed, 9 Oct 2024 21:09:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=81.19.149.118 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1728508156; cv=none; b=hhPYO0F0jB7ctNFtrF/dnGoGxUQrAkVMgB1Ka0ByQ594cGQNUO1oquF+sh06ZOYQbvPeICvti1Wvv0F4oL80FOWXsum/wLdtkwyJulbNPlLvCJone1Vav5Rl1ZdrnfasbWRg7Lwsa8ch6STMRBIuatJwDG8GYl5P2QPbnERU35g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1728508156; c=relaxed/simple; bh=d2sNCfav+nTxuA6wt8xfOE24xC0xQX6o4T+3lnRBK+g=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=MBcHk5ODIpgyz6FYZz0nK/rtIQNC3G545HDoN1vHNvBJNgEaa+Kgyoj7izKaqG3bZaoO/o139pEEwsPQlAIDu9jbei5VzNvp/ygfNCZi9WKHe2M5dp8iCZW3PtOYNCYtWWKEKmIV82R6wLeOxk1DLH3LEZn8fs7dEmCWNLvzFzk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=engleder-embedded.com; spf=pass smtp.mailfrom=engleder-embedded.com; dkim=pass (1024-bit key) header.d=engleder-embedded.com header.i=@engleder-embedded.com header.b=H44TLiFp; arc=none smtp.client-ip=81.19.149.118 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=engleder-embedded.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=engleder-embedded.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=engleder-embedded.com header.i=@engleder-embedded.com header.b="H44TLiFp" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=engleder-embedded.com; s=dkim11; h=Content-Transfer-Encoding:MIME-Version: References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To: Content-Type:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help: List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive; bh=u4yjrgdQaJF97lO8xIY17x9JYCnO6tJwOpp0Opp/mJo=; b=H44TLiFpPcsN60fY6q/8LwoPxy 38UQ45jCSo5riQl78NxYaJ8fS9l/J7OK/5vFIvoPf9dCOy+evFXKvjIYHpCd1JubNlfzEh91ST/k6 ZaT4VfBwqXoP9inbb+Me+GybBG2s9XXAFUz1V8EpwXCNeYLT1eKAg20f0Q+cEIZf8jO0=; Received: from 88-117-56-173.adsl.highway.telekom.at ([88.117.56.173] helo=hornet.engleder.at) by mx08lb.world4you.com with esmtpsa (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.97.1) (envelope-from ) id 1sydJs-000000002pc-2rK5; Wed, 09 Oct 2024 22:30:04 +0200 From: Gerhard Engleder To: linux-kernel@vger.kernel.org Cc: arnd@arndb.de, gregkh@linuxfoundation.org, Gerhard Engleder , Gerhard Engleder Subject: [PATCH 3/6] misc: keba: Support EEPROM sections as separate devices Date: Wed, 9 Oct 2024 22:29:46 +0200 Message-Id: <20241009202949.20164-4-gerhard@engleder-embedded.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20241009202949.20164-1-gerhard@engleder-embedded.com> References: <20241009202949.20164-1-gerhard@engleder-embedded.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-AV-Do-Run: Yes X-ACL-Warn: X-W4Y-Internal Content-Type: text/plain; charset="utf-8" From: Gerhard Engleder The main EEPROM of KEBA CP500 devices is divided into two sections. One section for type label data like device name, order number, serial number and so on. The second section is reserved for arbitrary data stored by the user. The two sections have a defined range. The first 3 kB for the type label and the remaining 1 kB for user data. The type label is written during production and can fill up the whole 3 kB. Thus, the range is fixed and cannot be changed dynamically. The two sections cannot be presented as NVMEM cells. A NVMEM cell is always read and written at once, because the data presented to the user can differ from the data stored in the physical NVMEM cell. Thus, NVMEM cells would lead to reading 3 kB for every type label access, even if only the device name is read. So performance would suffer. But it is also an indication that NVMEM cells are designed for small data cells within NVMEM devices. Register separate NVMEM devices for every section. This enables safe access to every section. Also different access rights are then possible. Signed-off-by: Gerhard Engleder --- drivers/misc/keba/cp500.c | 105 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 102 insertions(+), 3 deletions(-) diff --git a/drivers/misc/keba/cp500.c b/drivers/misc/keba/cp500.c index 7cebf2929390..3cf99eaf45c4 100644 --- a/drivers/misc/keba/cp500.c +++ b/drivers/misc/keba/cp500.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -46,11 +47,16 @@ #define CP500_NUM_MSIX_NO_AXI 3 =20 /* EEPROM */ -#define CP500_HW_CPU_EEPROM_NAME "cp500_cpu_eeprom" #define CP500_EEPROM_DA_OFFSET 0x016F #define CP500_EEPROM_DA_ESC_TYPE_MASK 0x01 #define CP500_EEPROM_ESC_LAN9252 0x00 #define CP500_EEPROM_ESC_ET1100 0x01 +#define CP500_EEPROM_CPU_NAME "cpu_eeprom" +#define CP500_EEPROM_CPU_OFFSET 0 +#define CP500_EEPROM_CPU_SIZE 3072 +#define CP500_EEPROM_USER_NAME "user_eeprom" +#define CP500_EEPROM_USER_OFFSET 3072 +#define CP500_EEPROM_USER_SIZE 1024 =20 /* SPI flash running at full speed */ #define CP500_FLASH_HZ (33 * 1000 * 1000) @@ -94,6 +100,11 @@ static struct cp500_devs cp520_devices =3D { .i2c =3D { 0x5000, SZ_4K }, }; =20 +struct cp500_nvmem { + struct nvmem_device *nvmem; + unsigned int offset; +}; + struct cp500 { struct pci_dev *pci_dev; struct cp500_devs *devs; @@ -114,6 +125,10 @@ struct cp500 { /* ECM EtherCAT BAR */ resource_size_t ecm_hwbase; =20 + /* NVMEM devices */ + struct cp500_nvmem nvmem_cpu; + struct cp500_nvmem nvmem_user; + void __iomem *system_startup_addr; }; =20 @@ -130,7 +145,6 @@ static struct i2c_board_info cp500_i2c_info[] =3D { * CP520 family: carrier board */ I2C_BOARD_INFO("24c32", CP500_EEPROM_ADDR), - .dev_name =3D CP500_HW_CPU_EEPROM_NAME, }, { /* interface board EEPROM */ I2C_BOARD_INFO("24c32", CP500_EEPROM_ADDR + 1), @@ -386,6 +400,77 @@ static int cp500_register_spi(struct cp500 *cp500, u8 = esc_type) return 0; } =20 +static int cp500_nvmem_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct cp500_nvmem *nvmem =3D priv; + int ret; + + ret =3D nvmem_device_read(nvmem->nvmem, nvmem->offset + offset, bytes, + val); + if (ret !=3D bytes) + return ret; + + return 0; +} + +static int cp500_nvmem_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct cp500_nvmem *nvmem =3D priv; + int ret; + + ret =3D nvmem_device_write(nvmem->nvmem, nvmem->offset + offset, bytes, + val); + if (ret !=3D bytes) + return ret; + + return 0; +} + +static int cp500_nvmem_register(struct cp500 *cp500, struct nvmem_device *= nvmem) +{ + struct device *dev =3D &cp500->pci_dev->dev; + struct nvmem_config nvmem_config =3D {}; + struct nvmem_device *tmp; + + /* + * The main EEPROM of CP500 devices is logically split into two EEPROMs. + * The first logical EEPROM with 3 kB contains the type label which is + * programmed during production of the device. The second logical EEPROM + * with 1 kB is not programmed during production and can be used for + * arbitrary user data. + */ + + nvmem_config.dev =3D dev; + nvmem_config.owner =3D THIS_MODULE; + nvmem_config.id =3D NVMEM_DEVID_NONE; + nvmem_config.type =3D NVMEM_TYPE_EEPROM; + nvmem_config.root_only =3D true; + nvmem_config.reg_read =3D cp500_nvmem_read; + nvmem_config.reg_write =3D cp500_nvmem_write; + + cp500->nvmem_cpu.nvmem =3D nvmem; + cp500->nvmem_cpu.offset =3D CP500_EEPROM_CPU_OFFSET; + nvmem_config.name =3D CP500_EEPROM_CPU_NAME; + nvmem_config.size =3D CP500_EEPROM_CPU_SIZE; + nvmem_config.priv =3D &cp500->nvmem_cpu; + tmp =3D devm_nvmem_register(dev, &nvmem_config); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + + cp500->nvmem_user.nvmem =3D nvmem; + cp500->nvmem_user.offset =3D CP500_EEPROM_USER_OFFSET; + nvmem_config.name =3D CP500_EEPROM_USER_NAME; + nvmem_config.size =3D CP500_EEPROM_USER_SIZE; + nvmem_config.priv =3D &cp500->nvmem_user; + tmp =3D devm_nvmem_register(dev, &nvmem_config); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + + return 0; +} + static int cp500_nvmem_match(struct device *dev, const void *data) { const struct cp500 *cp500 =3D data; @@ -403,6 +488,13 @@ static int cp500_nvmem_match(struct device *dev, const= void *data) return 0; } =20 +static void cp500_devm_nvmem_put(void *data) +{ + struct nvmem_device *nvmem =3D data; + + nvmem_device_put(nvmem); +} + static int cp500_nvmem(struct notifier_block *nb, unsigned long action, void *data) { @@ -431,9 +523,16 @@ static int cp500_nvmem(struct notifier_block *nb, unsi= gned long action, return NOTIFY_DONE; } =20 + ret =3D devm_add_action_or_reset(dev, cp500_devm_nvmem_put, nvmem); + if (ret) + return ret; + + ret =3D cp500_nvmem_register(cp500, nvmem); + if (ret) + return ret; + ret =3D nvmem_device_read(nvmem, CP500_EEPROM_DA_OFFSET, sizeof(esc_type), (void *)&esc_type); - nvmem_device_put(nvmem); if (ret !=3D sizeof(esc_type)) { dev_warn(dev, "Failed to read device assembly!\n"); =20 --=20 2.39.2 From nobody Wed Nov 27 12:34:19 2024 Received: from mx08lb.world4you.com (mx08lb.world4you.com [81.19.149.118]) (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 20E101514CC for ; Wed, 9 Oct 2024 20:30:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=81.19.149.118 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1728505813; cv=none; b=YW8Rmz9q1caa2dHdAZXOOUF23Vmir5ao55atrw6AgE81gXePLRey/hGrpQS/Ke11wJzc5pdeNN5RuPupJWUIDzpHbt8KM/xn1XGnfFPgjJppsfD3KGocx3Vri+3I5+UAy3ZYDBJWg8Pj+cltMUb4bIWV+8/7Yc5mFaNwMKEF4YE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1728505813; c=relaxed/simple; bh=xR4sur8STC0wLQxhuyWYlwsSz4HbVC3KFWZ0Q79HE9w=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=LSKwlEuZ1sECMNdhcpqi4BotGplSwoGosxoMkVvwC58Hti3Kj5pKpl+EBEXhKdF/Ns+YRQntsDAy2vJV/DPW8ANQTm8IP4E06KgILgGe2C10KupSRgqHxfYZX1gH5lfvRClsngeymHwJ8LmBNLtTmteDGErB+NPgGivky1uOsSQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=engleder-embedded.com; spf=pass smtp.mailfrom=engleder-embedded.com; dkim=pass (1024-bit key) header.d=engleder-embedded.com header.i=@engleder-embedded.com header.b=G61gRm/n; arc=none smtp.client-ip=81.19.149.118 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=engleder-embedded.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=engleder-embedded.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=engleder-embedded.com header.i=@engleder-embedded.com header.b="G61gRm/n" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=engleder-embedded.com; s=dkim11; h=Content-Transfer-Encoding:MIME-Version: References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To: Content-Type:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help: List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive; bh=BBjkiwbvKjgDMiC2y9fkDU85s2dm10o08sOLbxl1400=; b=G61gRm/noQgYU2fyBZbLCMaZYK BNqqXOpsp+OU/eGf4KjzHvZoYUk5UcL3xWmY7COwP4m45pIq7drc0N27gbCI8wLFe231o+7W3Ul6C FLUcOnNDcbi3RiJe9/NBTqAU2AtC/dRDDoG5mrZp4hGU+qjy5N8ZrRfQv4N270JxgoEM=; Received: from 88-117-56-173.adsl.highway.telekom.at ([88.117.56.173] helo=hornet.engleder.at) by mx08lb.world4you.com with esmtpsa (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.97.1) (envelope-from ) id 1sydJu-000000002pc-0AjL; Wed, 09 Oct 2024 22:30:06 +0200 From: Gerhard Engleder To: linux-kernel@vger.kernel.org Cc: arnd@arndb.de, gregkh@linuxfoundation.org, Gerhard Engleder , Gerhard Engleder Subject: [PATCH 4/6] misc: keba: Add fan device Date: Wed, 9 Oct 2024 22:29:47 +0200 Message-Id: <20241009202949.20164-5-gerhard@engleder-embedded.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20241009202949.20164-1-gerhard@engleder-embedded.com> References: <20241009202949.20164-1-gerhard@engleder-embedded.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-AV-Do-Run: Yes X-ACL-Warn: X-W4Y-Internal Content-Type: text/plain; charset="utf-8" From: Gerhard Engleder Add support for the fan auxiliary device. This enables monitoring of the fan. Signed-off-by: Gerhard Engleder --- drivers/misc/keba/cp500.c | 83 ++++++++++++++++++++++++++++++++++----- include/linux/misc/keba.h | 10 +++++ 2 files changed, 84 insertions(+), 9 deletions(-) diff --git a/drivers/misc/keba/cp500.c b/drivers/misc/keba/cp500.c index 3cf99eaf45c4..ae3ed1cece32 100644 --- a/drivers/misc/keba/cp500.c +++ b/drivers/misc/keba/cp500.c @@ -32,6 +32,7 @@ /* BAR 0 registers */ #define CP500_VERSION_REG 0x00 #define CP500_RECONFIG_REG 0x11 /* upper 8-bits of STARTUP register */ +#define CP500_PRESENT_REG 0x20 #define CP500_AXI_REG 0x40 =20 /* Bits in BUILD_REG */ @@ -40,6 +41,9 @@ /* Bits in RECONFIG_REG */ #define CP500_RECFG_REQ 0x01 /* reconfigure FPGA on next reset */ =20 +/* Bits in PRESENT_REG */ +#define CP500_PRESENT_FAN0 0x01 + /* MSIX */ #define CP500_AXI_MSIX 3 #define CP500_NUM_MSIX 8 @@ -77,27 +81,31 @@ struct cp500_devs { struct cp500_dev_info startup; struct cp500_dev_info spi; struct cp500_dev_info i2c; + struct cp500_dev_info fan; }; =20 /* list of devices within FPGA of CP035 family (CP035, CP056, CP057) */ static struct cp500_devs cp035_devices =3D { - .startup =3D { 0x0000, SZ_4K }, - .spi =3D { 0x1000, SZ_4K }, - .i2c =3D { 0x4000, SZ_4K }, + .startup =3D { 0x0000, SZ_4K }, + .spi =3D { 0x1000, SZ_4K }, + .i2c =3D { 0x4000, SZ_4K }, + .fan =3D { 0x9000, SZ_4K }, }; =20 /* list of devices within FPGA of CP505 family (CP503, CP505, CP507) */ static struct cp500_devs cp505_devices =3D { - .startup =3D { 0x0000, SZ_4K }, - .spi =3D { 0x4000, SZ_4K }, - .i2c =3D { 0x5000, SZ_4K }, + .startup =3D { 0x0000, SZ_4K }, + .spi =3D { 0x4000, SZ_4K }, + .i2c =3D { 0x5000, SZ_4K }, + .fan =3D { 0x9000, SZ_4K }, }; =20 /* list of devices within FPGA of CP520 family (CP520, CP530) */ static struct cp500_devs cp520_devices =3D { - .startup =3D { 0x0000, SZ_4K }, - .spi =3D { 0x4000, SZ_4K }, - .i2c =3D { 0x5000, SZ_4K }, + .startup =3D { 0x0000, SZ_4K }, + .spi =3D { 0x4000, SZ_4K }, + .i2c =3D { 0x5000, SZ_4K }, + .fan =3D { 0x8000, SZ_4K }, }; =20 struct cp500_nvmem { @@ -121,6 +129,7 @@ struct cp500 { resource_size_t sys_hwbase; struct keba_spi_auxdev *spi; struct keba_i2c_auxdev *i2c; + struct keba_fan_auxdev *fan; =20 /* ECM EtherCAT BAR */ resource_size_t ecm_hwbase; @@ -400,6 +409,54 @@ static int cp500_register_spi(struct cp500 *cp500, u8 = esc_type) return 0; } =20 +static void cp500_fan_release(struct device *dev) +{ + struct keba_fan_auxdev *fan =3D + container_of(dev, struct keba_fan_auxdev, auxdev.dev); + + kfree(fan); +} + +static int cp500_register_fan(struct cp500 *cp500) +{ + int ret; + + cp500->fan =3D kzalloc(sizeof(*cp500->fan), GFP_KERNEL); + if (!cp500->fan) + return -ENOMEM; + + cp500->fan->auxdev.name =3D "fan"; + cp500->fan->auxdev.id =3D 0; + cp500->fan->auxdev.dev.release =3D cp500_fan_release; + cp500->fan->auxdev.dev.parent =3D &cp500->pci_dev->dev; + cp500->fan->io =3D (struct resource) { + /* fan register area */ + .start =3D (resource_size_t) cp500->sys_hwbase + + cp500->devs->fan.offset, + .end =3D (resource_size_t) cp500->sys_hwbase + + cp500->devs->fan.offset + + cp500->devs->fan.size - 1, + .flags =3D IORESOURCE_MEM, + }; + + ret =3D auxiliary_device_init(&cp500->fan->auxdev); + if (ret) { + kfree(cp500->fan); + cp500->fan =3D NULL; + + return ret; + } + ret =3D __auxiliary_device_add(&cp500->fan->auxdev, "keba"); + if (ret) { + auxiliary_device_uninit(&cp500->fan->auxdev); + cp500->fan =3D NULL; + + return ret; + } + + return 0; +} + static int cp500_nvmem_read(void *priv, unsigned int offset, void *val, size_t bytes) { @@ -549,9 +606,13 @@ static int cp500_nvmem(struct notifier_block *nb, unsi= gned long action, static void cp500_register_auxiliary_devs(struct cp500 *cp500) { struct device *dev =3D &cp500->pci_dev->dev; + u8 present =3D ioread8(cp500->system_startup_addr + CP500_PRESENT_REG); =20 if (cp500_register_i2c(cp500)) dev_warn(dev, "Failed to register I2C!\n"); + if (present & CP500_PRESENT_FAN0) + if (cp500_register_fan(cp500)) + dev_warn(dev, "Failed to register fan!\n"); } =20 static void cp500_unregister_dev(struct auxiliary_device *auxdev) @@ -570,6 +631,10 @@ static void cp500_unregister_auxiliary_devs(struct cp5= 00 *cp500) cp500_unregister_dev(&cp500->i2c->auxdev); cp500->i2c =3D NULL; } + if (cp500->fan) { + cp500_unregister_dev(&cp500->fan->auxdev); + cp500->fan =3D NULL; + } } =20 static irqreturn_t cp500_axi_handler(int irq, void *dev) diff --git a/include/linux/misc/keba.h b/include/linux/misc/keba.h index 1bd5409c6f6f..451777acc262 100644 --- a/include/linux/misc/keba.h +++ b/include/linux/misc/keba.h @@ -37,4 +37,14 @@ struct keba_spi_auxdev { struct spi_board_info *info; }; =20 +/** + * struct keba_fan_auxdev - KEBA fan auxiliary device + * @auxdev: auxiliary device object + * @io: address range of fan controller IO memory + */ +struct keba_fan_auxdev { + struct auxiliary_device auxdev; + struct resource io; +}; + #endif /* _LINUX_MISC_KEBA_H */ --=20 2.39.2 From nobody Wed Nov 27 12:34:19 2024 Received: from mx08lb.world4you.com (mx08lb.world4you.com [81.19.149.118]) (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 B0F741714A4 for ; Wed, 9 Oct 2024 20:30:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=81.19.149.118 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1728505814; cv=none; b=ZsCdc7BH0UdFd875ww3tvGUsnmFpbxcNKo2pe+9RqdnRRAHgSdZczqKKvKtTtgbrc7c1lhd0H/ozAsLoCoay5Rl4DEnmJajP6rL4u1dEI8Y25dL6AZ7HAcOdxMH6rhAUpBTVxZremRF2ouNB0HNVBorO9Mk967/hB6qir+gJ0Gg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1728505814; c=relaxed/simple; bh=9d627FDTOIkVX66beegSDFYO5t9zTrTs4nuv5e8i32s=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=k5k/aunI21UQ1wkr2mbKHn6TGWvLCMdrhwYeK5cAwDOFGbyh1wYWRT6KegvbDRau7NAVH25eFp6lblJ4Cq7W1Z++cbNtvNnbBV/tbDuUzhwgroXs8lfrBqIs1s1U0JfBiYq8FaY3EF74jobHQvehlNGRl0hJVJ48+JuCqDVgyl0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=engleder-embedded.com; spf=pass smtp.mailfrom=engleder-embedded.com; dkim=pass (1024-bit key) header.d=engleder-embedded.com header.i=@engleder-embedded.com header.b=bvUNqKAH; arc=none smtp.client-ip=81.19.149.118 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=engleder-embedded.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=engleder-embedded.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=engleder-embedded.com header.i=@engleder-embedded.com header.b="bvUNqKAH" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=engleder-embedded.com; s=dkim11; h=Content-Transfer-Encoding:MIME-Version: References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To: Content-Type:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help: List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive; bh=lfm5KoQta1gf45olzsRtGjm8PknulBKLLAsOuXP43tM=; b=bvUNqKAHk+2LS2tfMdawjo+fFU e8VVtEM0KzhlfDaCqlauFUWWHxrDfrQy3ReBh8QpQ8c32/yOKwkvWDyrkDLC+2odD45yOj+y89n6O PiFJ5MIXbDVBkv4oTlpLlVa4picu9tBsbBmw4Hy865CQv9qxw4tCBe4miFijaXgnLrzQ=; Received: from 88-117-56-173.adsl.highway.telekom.at ([88.117.56.173] helo=hornet.engleder.at) by mx08lb.world4you.com with esmtpsa (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.97.1) (envelope-from ) id 1sydJv-000000002pc-0wci; Wed, 09 Oct 2024 22:30:07 +0200 From: Gerhard Engleder To: linux-kernel@vger.kernel.org Cc: arnd@arndb.de, gregkh@linuxfoundation.org, Gerhard Engleder , Gerhard Engleder Subject: [PATCH 5/6] misc: keba: Add battery device Date: Wed, 9 Oct 2024 22:29:48 +0200 Message-Id: <20241009202949.20164-6-gerhard@engleder-embedded.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20241009202949.20164-1-gerhard@engleder-embedded.com> References: <20241009202949.20164-1-gerhard@engleder-embedded.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-AV-Do-Run: Yes X-ACL-Warn: X-W4Y-Internal Content-Type: text/plain; charset="utf-8" From: Gerhard Engleder Add support for the battery auxiliary device. This enables monitoring of the battery. Signed-off-by: Gerhard Engleder --- drivers/misc/keba/cp500.c | 59 +++++++++++++++++++++++++++++++++++++++ include/linux/misc/keba.h | 10 +++++++ 2 files changed, 69 insertions(+) diff --git a/drivers/misc/keba/cp500.c b/drivers/misc/keba/cp500.c index ae3ed1cece32..afd4d7c06cee 100644 --- a/drivers/misc/keba/cp500.c +++ b/drivers/misc/keba/cp500.c @@ -82,6 +82,7 @@ struct cp500_devs { struct cp500_dev_info spi; struct cp500_dev_info i2c; struct cp500_dev_info fan; + struct cp500_dev_info batt; }; =20 /* list of devices within FPGA of CP035 family (CP035, CP056, CP057) */ @@ -90,6 +91,7 @@ static struct cp500_devs cp035_devices =3D { .spi =3D { 0x1000, SZ_4K }, .i2c =3D { 0x4000, SZ_4K }, .fan =3D { 0x9000, SZ_4K }, + .batt =3D { 0xA000, SZ_4K }, }; =20 /* list of devices within FPGA of CP505 family (CP503, CP505, CP507) */ @@ -98,6 +100,7 @@ static struct cp500_devs cp505_devices =3D { .spi =3D { 0x4000, SZ_4K }, .i2c =3D { 0x5000, SZ_4K }, .fan =3D { 0x9000, SZ_4K }, + .batt =3D { 0xA000, SZ_4K }, }; =20 /* list of devices within FPGA of CP520 family (CP520, CP530) */ @@ -106,6 +109,7 @@ static struct cp500_devs cp520_devices =3D { .spi =3D { 0x4000, SZ_4K }, .i2c =3D { 0x5000, SZ_4K }, .fan =3D { 0x8000, SZ_4K }, + .batt =3D { 0x9000, SZ_4K }, }; =20 struct cp500_nvmem { @@ -130,6 +134,7 @@ struct cp500 { struct keba_spi_auxdev *spi; struct keba_i2c_auxdev *i2c; struct keba_fan_auxdev *fan; + struct keba_batt_auxdev *batt; =20 /* ECM EtherCAT BAR */ resource_size_t ecm_hwbase; @@ -457,6 +462,54 @@ static int cp500_register_fan(struct cp500 *cp500) return 0; } =20 +static void cp500_batt_release(struct device *dev) +{ + struct keba_batt_auxdev *fan =3D + container_of(dev, struct keba_batt_auxdev, auxdev.dev); + + kfree(fan); +} + +static int cp500_register_batt(struct cp500 *cp500) +{ + int ret; + + cp500->batt =3D kzalloc(sizeof(*cp500->batt), GFP_KERNEL); + if (!cp500->batt) + return -ENOMEM; + + cp500->batt->auxdev.name =3D "batt"; + cp500->batt->auxdev.id =3D 0; + cp500->batt->auxdev.dev.release =3D cp500_batt_release; + cp500->batt->auxdev.dev.parent =3D &cp500->pci_dev->dev; + cp500->batt->io =3D (struct resource) { + /* battery register area */ + .start =3D (resource_size_t) cp500->sys_hwbase + + cp500->devs->batt.offset, + .end =3D (resource_size_t) cp500->sys_hwbase + + cp500->devs->batt.offset + + cp500->devs->batt.size - 1, + .flags =3D IORESOURCE_MEM, + }; + + ret =3D auxiliary_device_init(&cp500->batt->auxdev); + if (ret) { + kfree(cp500->batt); + cp500->batt =3D NULL; + + return ret; + } + ret =3D __auxiliary_device_add(&cp500->batt->auxdev, "keba"); + if (ret) { + auxiliary_device_uninit(&cp500->batt->auxdev); + cp500->batt =3D NULL; + + return ret; + } + + return 0; +} + static int cp500_nvmem_read(void *priv, unsigned int offset, void *val, size_t bytes) { @@ -613,6 +666,8 @@ static void cp500_register_auxiliary_devs(struct cp500 = *cp500) if (present & CP500_PRESENT_FAN0) if (cp500_register_fan(cp500)) dev_warn(dev, "Failed to register fan!\n"); + if (cp500_register_batt(cp500)) + dev_warn(dev, "Failed to register battery!\n"); } =20 static void cp500_unregister_dev(struct auxiliary_device *auxdev) @@ -635,6 +690,10 @@ static void cp500_unregister_auxiliary_devs(struct cp5= 00 *cp500) cp500_unregister_dev(&cp500->fan->auxdev); cp500->fan =3D NULL; } + if (cp500->batt) { + cp500_unregister_dev(&cp500->batt->auxdev); + cp500->batt =3D NULL; + } } =20 static irqreturn_t cp500_axi_handler(int irq, void *dev) diff --git a/include/linux/misc/keba.h b/include/linux/misc/keba.h index 451777acc262..ca52716f8437 100644 --- a/include/linux/misc/keba.h +++ b/include/linux/misc/keba.h @@ -47,4 +47,14 @@ struct keba_fan_auxdev { struct resource io; }; =20 +/** + * struct keba_batt_auxdev - KEBA battery auxiliary device + * @auxdev: auxiliary device object + * @io: address range of battery controller IO memory + */ +struct keba_batt_auxdev { + struct auxiliary_device auxdev; + struct resource io; +}; + #endif /* _LINUX_MISC_KEBA_H */ --=20 2.39.2 From nobody Wed Nov 27 12:34:19 2024 Received: from mx08lb.world4you.com (mx08lb.world4you.com [81.19.149.118]) (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 C7230183CAA for ; Wed, 9 Oct 2024 20:30:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=81.19.149.118 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1728505813; cv=none; b=pN9W9GZZPLcmKK0rnsrOoujejnkvTH+vUoFtP9E8iqdZDXx+1OWU6/Eu4aeh6Ou95FPqIv3zZ9t5YKcmwd0Da2/momZdychoFWAlgO5+vc+G9vMJMTKUrrnvFTmYyR2mmgm/LaNBAH6vdnIDNyck/8OfRTfRYioqjqsgoh8kV+s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1728505813; c=relaxed/simple; bh=oBVp6xCJ46lhFLoKzzje44//wjdEV2iRH1V8pvZ1rV8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=pYqsTyqprN/UnbMZkOteQLe79Szasq4I+o2cQ+HSgfFcHZ1oz1knQRs+coVwSRmxT838VYIhPmc71vFAsAcz7FpOLLsg3IO2WiupIl3TeB+KPCwsfkIyi5UmNySGEq2b2Q2QhvS2glvDVMRPKYPG+GjHPTCvoKRdi7X6lvdw3NY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=engleder-embedded.com; spf=pass smtp.mailfrom=engleder-embedded.com; dkim=pass (1024-bit key) header.d=engleder-embedded.com header.i=@engleder-embedded.com header.b=xA6Spyj+; arc=none smtp.client-ip=81.19.149.118 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=engleder-embedded.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=engleder-embedded.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=engleder-embedded.com header.i=@engleder-embedded.com header.b="xA6Spyj+" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=engleder-embedded.com; s=dkim11; h=Content-Transfer-Encoding:MIME-Version: References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To: Content-Type:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help: List-Unsubscribe:List-Subscribe:List-Post:List-Owner:List-Archive; bh=z5YHM9D01vA222n9g5hi9rEul/bQJQMaAl6cSatx4Xo=; b=xA6Spyj+q61DOH5jJ9Cjm2YyUD cdlJKJmOjIiT66qKSph5tIfC2r8uuhgMiAvd9zhYE7WB0YTBbgTKytSfwovfj4vL0JJyn/Pfyu0ef Wuy/KWIMOKOgKhrSsYHgjHuHxVi9mOBti1hQOqMG4eE12rBCdq5TSaMQ7iAdpSX5yeZQ=; Received: from 88-117-56-173.adsl.highway.telekom.at ([88.117.56.173] helo=hornet.engleder.at) by mx08lb.world4you.com with esmtpsa (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.97.1) (envelope-from ) id 1sydJw-000000002pc-1aOK; Wed, 09 Oct 2024 22:30:08 +0200 From: Gerhard Engleder To: linux-kernel@vger.kernel.org Cc: arnd@arndb.de, gregkh@linuxfoundation.org, Gerhard Engleder , Gerhard Engleder Subject: [PATCH 6/6] misc: keba: Add UART devices Date: Wed, 9 Oct 2024 22:29:49 +0200 Message-Id: <20241009202949.20164-7-gerhard@engleder-embedded.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20241009202949.20164-1-gerhard@engleder-embedded.com> References: <20241009202949.20164-1-gerhard@engleder-embedded.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-AV-Do-Run: Yes X-ACL-Warn: X-W4Y-Internal Content-Type: text/plain; charset="utf-8" From: Gerhard Engleder Add support for the UART auxiliary devices. This enables access to up to 3 different UARTs, which are implemented in the FPGA. Signed-off-by: Gerhard Engleder --- drivers/misc/keba/cp500.c | 104 ++++++++++++++++++++++++++++++++++++++ include/linux/misc/keba.h | 12 +++++ 2 files changed, 116 insertions(+) diff --git a/drivers/misc/keba/cp500.c b/drivers/misc/keba/cp500.c index afd4d7c06cee..255d3022dae8 100644 --- a/drivers/misc/keba/cp500.c +++ b/drivers/misc/keba/cp500.c @@ -46,6 +46,9 @@ =20 /* MSIX */ #define CP500_AXI_MSIX 3 +#define CP500_RFB_UART_MSIX 4 +#define CP500_DEBUG_UART_MSIX 5 +#define CP500_SI1_UART_MSIX 6 #define CP500_NUM_MSIX 8 #define CP500_NUM_MSIX_NO_MMI 2 #define CP500_NUM_MSIX_NO_AXI 3 @@ -75,6 +78,7 @@ struct cp500_dev_info { off_t offset; size_t size; + unsigned int msix; }; =20 struct cp500_devs { @@ -83,6 +87,9 @@ struct cp500_devs { struct cp500_dev_info i2c; struct cp500_dev_info fan; struct cp500_dev_info batt; + struct cp500_dev_info uart0_rfb; + struct cp500_dev_info uart1_dbg; + struct cp500_dev_info uart2_si1; }; =20 /* list of devices within FPGA of CP035 family (CP035, CP056, CP057) */ @@ -92,6 +99,8 @@ static struct cp500_devs cp035_devices =3D { .i2c =3D { 0x4000, SZ_4K }, .fan =3D { 0x9000, SZ_4K }, .batt =3D { 0xA000, SZ_4K }, + .uart0_rfb =3D { 0xB000, SZ_4K, CP500_RFB_UART_MSIX }, + .uart2_si1 =3D { 0xD000, SZ_4K, CP500_SI1_UART_MSIX }, }; =20 /* list of devices within FPGA of CP505 family (CP503, CP505, CP507) */ @@ -101,6 +110,8 @@ static struct cp500_devs cp505_devices =3D { .i2c =3D { 0x5000, SZ_4K }, .fan =3D { 0x9000, SZ_4K }, .batt =3D { 0xA000, SZ_4K }, + .uart0_rfb =3D { 0xB000, SZ_4K, CP500_RFB_UART_MSIX }, + .uart2_si1 =3D { 0xD000, SZ_4K, CP500_SI1_UART_MSIX }, }; =20 /* list of devices within FPGA of CP520 family (CP520, CP530) */ @@ -110,6 +121,8 @@ static struct cp500_devs cp520_devices =3D { .i2c =3D { 0x5000, SZ_4K }, .fan =3D { 0x8000, SZ_4K }, .batt =3D { 0x9000, SZ_4K }, + .uart0_rfb =3D { 0xC000, SZ_4K, CP500_RFB_UART_MSIX }, + .uart1_dbg =3D { 0xD000, SZ_4K, CP500_DEBUG_UART_MSIX }, }; =20 struct cp500_nvmem { @@ -135,6 +148,9 @@ struct cp500 { struct keba_i2c_auxdev *i2c; struct keba_fan_auxdev *fan; struct keba_batt_auxdev *batt; + struct keba_uart_auxdev *uart0_rfb; + struct keba_uart_auxdev *uart1_dbg; + struct keba_uart_auxdev *uart2_si1; =20 /* ECM EtherCAT BAR */ resource_size_t ecm_hwbase; @@ -510,6 +526,55 @@ static int cp500_register_batt(struct cp500 *cp500) return 0; } =20 +static void cp500_uart_release(struct device *dev) +{ + struct keba_uart_auxdev *uart =3D + container_of(dev, struct keba_uart_auxdev, auxdev.dev); + + kfree(uart); +} + +static int cp500_register_uart(struct cp500 *cp500, + struct keba_uart_auxdev **uart, const char *name, + struct cp500_dev_info *info, unsigned int irq) +{ + int ret; + + *uart =3D kzalloc(sizeof(**uart), GFP_KERNEL); + if (!*uart) + return -ENOMEM; + + (*uart)->auxdev.name =3D name; + (*uart)->auxdev.id =3D 0; + (*uart)->auxdev.dev.release =3D cp500_uart_release; + (*uart)->auxdev.dev.parent =3D &cp500->pci_dev->dev; + (*uart)->io =3D (struct resource) { + /* UART register area */ + .start =3D (resource_size_t) cp500->sys_hwbase + info->offset, + .end =3D (resource_size_t) cp500->sys_hwbase + info->offset + + info->size - 1, + .flags =3D IORESOURCE_MEM, + }; + (*uart)->irq =3D irq; + + ret =3D auxiliary_device_init(&(*uart)->auxdev); + if (ret) { + kfree(*uart); + *uart =3D NULL; + + return ret; + } + ret =3D __auxiliary_device_add(&(*uart)->auxdev, "keba"); + if (ret) { + auxiliary_device_uninit(&(*uart)->auxdev); + *uart =3D NULL; + + return ret; + } + + return 0; +} + static int cp500_nvmem_read(void *priv, unsigned int offset, void *val, size_t bytes) { @@ -668,6 +733,33 @@ static void cp500_register_auxiliary_devs(struct cp500= *cp500) dev_warn(dev, "Failed to register fan!\n"); if (cp500_register_batt(cp500)) dev_warn(dev, "Failed to register battery!\n"); + if (cp500->devs->uart0_rfb.size && + cp500->devs->uart0_rfb.msix < cp500->msix_num) { + int irq =3D pci_irq_vector(cp500->pci_dev, + cp500->devs->uart0_rfb.msix); + + if (cp500_register_uart(cp500, &cp500->uart0_rfb, "rs485-uart", + &cp500->devs->uart0_rfb, irq)) + dev_warn(dev, "Failed to register RFB UART!\n"); + } + if (cp500->devs->uart1_dbg.size && + cp500->devs->uart1_dbg.msix < cp500->msix_num) { + int irq =3D pci_irq_vector(cp500->pci_dev, + cp500->devs->uart1_dbg.msix); + + if (cp500_register_uart(cp500, &cp500->uart1_dbg, "rs232-uart", + &cp500->devs->uart1_dbg, irq)) + dev_warn(dev, "Failed to register debug UART!\n"); + } + if (cp500->devs->uart2_si1.size && + cp500->devs->uart2_si1.msix < cp500->msix_num) { + int irq =3D pci_irq_vector(cp500->pci_dev, + cp500->devs->uart2_si1.msix); + + if (cp500_register_uart(cp500, &cp500->uart2_si1, "uart", + &cp500->devs->uart2_si1, irq)) + dev_warn(dev, "Failed to register SI1 UART!\n"); + } } =20 static void cp500_unregister_dev(struct auxiliary_device *auxdev) @@ -694,6 +786,18 @@ static void cp500_unregister_auxiliary_devs(struct cp5= 00 *cp500) cp500_unregister_dev(&cp500->batt->auxdev); cp500->batt =3D NULL; } + if (cp500->uart0_rfb) { + cp500_unregister_dev(&cp500->uart0_rfb->auxdev); + cp500->uart0_rfb =3D NULL; + } + if (cp500->uart1_dbg) { + cp500_unregister_dev(&cp500->uart1_dbg->auxdev); + cp500->uart1_dbg =3D NULL; + } + if (cp500->uart2_si1) { + cp500_unregister_dev(&cp500->uart2_si1->auxdev); + cp500->uart2_si1 =3D NULL; + } } =20 static irqreturn_t cp500_axi_handler(int irq, void *dev) diff --git a/include/linux/misc/keba.h b/include/linux/misc/keba.h index ca52716f8437..a81d6fa70851 100644 --- a/include/linux/misc/keba.h +++ b/include/linux/misc/keba.h @@ -57,4 +57,16 @@ struct keba_batt_auxdev { struct resource io; }; =20 +/** + * struct keba_uart_auxdev - KEBA UART auxiliary device + * @auxdev: auxiliary device object + * @io: address range of UART controller IO memory + * @irq: number of UART controller interrupt + */ +struct keba_uart_auxdev { + struct auxiliary_device auxdev; + struct resource io; + unsigned int irq; +}; + #endif /* _LINUX_MISC_KEBA_H */ --=20 2.39.2