From nobody Wed Nov 27 08:52:04 2024 Received: from mx01lb.world4you.com (mx01lb.world4you.com [81.19.149.111]) (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 794D71D1300 for ; Fri, 11 Oct 2024 20:02:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=81.19.149.111 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1728676981; cv=none; b=R42uFUEnIhJvS/V6gedL3kpwcKSsEzdz3EheP1PeMKUwBYqFLx/C0WiQac5bWw7+MtpOpE00s2s93kCMJOaNRnI1eTZceaFAslECsBL0BxaqWbRE+bkFPwmt8Cy8wVLgjSWbUAyxWnN7Y2jlEP93paqXlZbuoNEG2iaKCPsPQd8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1728676981; c=relaxed/simple; bh=fXr42gKXes1KmAoKmx/JlkMJFZGlzs6bR1Nefux2S7k=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=AVmIKpLPb1ro4B7zT72PjCDKpwanPVS36Eb0zua2BHitOGWmf7D29UDkzyZXSnB8tAoojxp9qwImRi2ieApts20z6Ewn4GqwXVIp3wMZdoys5q7Hp/lbEO5N7CsyWscjYA44p3CXUYxPKL3hqGKr/2CSan33ET5ETqrhrb/nhD0= 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=DF9YIywj; arc=none smtp.client-ip=81.19.149.111 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="DF9YIywj" 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=jfPO5c43H2tWIIBxZfAMbiDUrWRQQsXA86hvuylfUek=; b=DF9YIywjgawTHYwn7ynzr0DfpM xYyN/oA+5uUKxu6fytHacaUYfanJ00HYrgxuCETXAk7EQyA3ieCKuoXjhN21YQOfndHEW2Q8TDYey kIgJj0fUAVj7GjuTeNwLhgqXYnjlnrRrmMmqtY64kJPcI/mXAd7cS8vD6CbmZbWFTlSw=; Received: from 88-117-56-173.adsl.highway.telekom.at ([88.117.56.173] helo=hornet.engleder.at) by mx01lb.world4you.com with esmtpsa (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.97.1) (envelope-from ) id 1szL4R-0000000039T-3rww; Fri, 11 Oct 2024 21:13:04 +0200 From: Gerhard Engleder To: linux-kernel@vger.kernel.org Cc: arnd@arndb.de, gregkh@linuxfoundation.org, Gerhard Engleder , Gerhard Engleder Subject: [PATCH v2 3/8] misc: keba: Add SPI controller device Date: Fri, 11 Oct 2024 21:12:52 +0200 Message-Id: <20241011191257.19702-4-gerhard@engleder-embedded.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20241011191257.19702-1-gerhard@engleder-embedded.com> References: <20241011191257.19702-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 | 219 +++++++++++++++++++++++++++++++++++--- include/linux/misc/keba.h | 15 +++ 2 files changed, 219 insertions(+), 15 deletions(-) diff --git a/drivers/misc/keba/cp500.c b/drivers/misc/keba/cp500.c index 1eee130c3a7f..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 @@ -269,6 +328,125 @@ static int cp500_register_i2c(struct cp500 *cp500) 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; @@ -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