From 71c07ee53b2223517f1740ac487410062bddba63 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Fri, 15 Sep 2023 13:06:51 -0500 Subject: [PATCH] linxu: add cfg80211: Loading compiled-in X.509 certificates for regulatory database Signed-off-by: Robert Nelson --- 04_build_linux.sh | 15 +- build_linux_menuconfig.sh | 15 +- ...-controller-and-bridge-base-address.patch} | 12 +- ...-GPIO-Add-Microchip-CoreGPIO-driver.patch} | 12 +- ...04-ADC-Add-Microchip-MCP356X-driver.patch} | 12 +- ...icrochip-QSPI-Add-regular-transfers.patch} | 12 +- ...printk-to-IM219-driver-for-board-te.patch} | 8 +- ...to-support-non-DMA-capable-SPI-ctrl.patch} | 12 +- ...eless-regdb-regulatory-database-file.patch | 100 ++ .../0001-Add-BeagleV-Fire-device-tree.patch | 21 +- ...e-controller-and-bridge-base-address.patch | 27 + ...3-GPIO-Add-Microchip-CoreGPIO-driver.patch | 371 +++++ ...004-ADC-Add-Microchip-MCP356X-driver.patch | 1450 +++++++++++++++++ ...Microchip-QSPI-Add-regular-transfers.patch | 275 ++++ ...-printk-to-IM219-driver-for-board-te.patch | 45 + ...-to-support-non-DMA-capable-SPI-ctrl.patch | 66 + ...eless-regdb-regulatory-database-file.patch | 100 ++ patches/linux/mpfs_defconfig | 3 +- 18 files changed, 2496 insertions(+), 60 deletions(-) rename patches/{linux/0001-PCIe-Change-controller-and-bridge-base-address.patch => fix/0002-PCIe-Change-controller-and-bridge-base-address.patch} (75%) rename patches/{linux/0001-GPIO-Add-Microchip-CoreGPIO-driver.patch => fix/0003-GPIO-Add-Microchip-CoreGPIO-driver.patch} (97%) rename patches/{linux/0001-ADC-Add-Microchip-MCP356X-driver.patch => fix/0004-ADC-Add-Microchip-MCP356X-driver.patch} (99%) rename patches/{linux/0001-Microchip-QSPI-Add-regular-transfers.patch => fix/0005-Microchip-QSPI-Add-regular-transfers.patch} (97%) rename patches/{linux/0001-BeagleV-Fire-Add-printk-to-IM219-driver-for-board-te.patch => fix/0006-BeagleV-Fire-Add-printk-to-IM219-driver-for-board-te.patch} (84%) rename patches/{linux/0001-MMC-SPI-Hack-to-support-non-DMA-capable-SPI-ctrl.patch => fix/0007-MMC-SPI-Hack-to-support-non-DMA-capable-SPI-ctrl.patch} (85%) create mode 100644 patches/fix/0008-Add-wireless-regdb-regulatory-database-file.patch create mode 100644 patches/linux/0002-PCIe-Change-controller-and-bridge-base-address.patch create mode 100644 patches/linux/0003-GPIO-Add-Microchip-CoreGPIO-driver.patch create mode 100644 patches/linux/0004-ADC-Add-Microchip-MCP356X-driver.patch create mode 100644 patches/linux/0005-Microchip-QSPI-Add-regular-transfers.patch create mode 100644 patches/linux/0006-BeagleV-Fire-Add-printk-to-IM219-driver-for-board-te.patch create mode 100644 patches/linux/0007-MMC-SPI-Hack-to-support-non-DMA-capable-SPI-ctrl.patch create mode 100644 patches/linux/0008-Add-wireless-regdb-regulatory-database-file.patch diff --git a/04_build_linux.sh b/04_build_linux.sh index 2ef79c4..8472d53 100755 --- a/04_build_linux.sh +++ b/04_build_linux.sh @@ -8,13 +8,14 @@ cd ./linux/ if [ ! -f ./.patched ] ; then if [ -f arch/riscv/configs/mpfs_defconfig ] ; then - patch -p1 < ../patches/linux/0001-Add-BeagleV-Fire-device-tree.patch - patch -p1 < ../patches/linux/0001-PCIe-Change-controller-and-bridge-base-address.patch - patch -p1 < ../patches/linux/0001-GPIO-Add-Microchip-CoreGPIO-driver.patch - patch -p1 < ../patches/linux/0001-ADC-Add-Microchip-MCP356X-driver.patch - patch -p1 < ../patches/linux/0001-Microchip-QSPI-Add-regular-transfers.patch - patch -p1 < ../patches/linux/0001-BeagleV-Fire-Add-printk-to-IM219-driver-for-board-te.patch - patch -p1 < ../patches/linux/0001-MMC-SPI-Hack-to-support-non-DMA-capable-SPI-ctrl.patch + git am ../patches/linux/0001-Add-BeagleV-Fire-device-tree.patch + git am ../patches/linux/0002-PCIe-Change-controller-and-bridge-base-address.patch + git am ../patches/linux/0003-GPIO-Add-Microchip-CoreGPIO-driver.patch + git am ../patches/linux/0004-ADC-Add-Microchip-MCP356X-driver.patch + git am ../patches/linux/0005-Microchip-QSPI-Add-regular-transfers.patch + git am ../patches/linux/0006-BeagleV-Fire-Add-printk-to-IM219-driver-for-board-te.patch + git am ../patches/linux/0007-MMC-SPI-Hack-to-support-non-DMA-capable-SPI-ctrl.patch + git am ../patches/linux/0008-Add-wireless-regdb-regulatory-database-file.patch fi touch .patched fi diff --git a/build_linux_menuconfig.sh b/build_linux_menuconfig.sh index aa3d740..3f82f19 100755 --- a/build_linux_menuconfig.sh +++ b/build_linux_menuconfig.sh @@ -8,13 +8,14 @@ cd ./linux/ if [ ! -f ./.patched ] ; then if [ -f arch/riscv/configs/mpfs_defconfig ] ; then - patch -p1 < ../patches/linux/0001-Add-BeagleV-Fire-device-tree.patch - patch -p1 < ../patches/linux/0001-PCIe-Change-controller-and-bridge-base-address.patch - patch -p1 < ../patches/linux/0001-GPIO-Add-Microchip-CoreGPIO-driver.patch - patch -p1 < ../patches/linux/0001-ADC-Add-Microchip-MCP356X-driver.patch - patch -p1 < ../patches/linux/0001-Microchip-QSPI-Add-regular-transfers.patch - patch -p1 < ../patches/linux/0001-BeagleV-Fire-Add-printk-to-IM219-driver-for-board-te.patch - patch -p1 < ../patches/linux/0001-MMC-SPI-Hack-to-support-non-DMA-capable-SPI-ctrl.patch + git am ../patches/linux/0001-Add-BeagleV-Fire-device-tree.patch + git am ../patches/linux/0002-PCIe-Change-controller-and-bridge-base-address.patch + git am ../patches/linux/0003-GPIO-Add-Microchip-CoreGPIO-driver.patch + git am ../patches/linux/0004-ADC-Add-Microchip-MCP356X-driver.patch + git am ../patches/linux/0005-Microchip-QSPI-Add-regular-transfers.patch + git am ../patches/linux/0006-BeagleV-Fire-Add-printk-to-IM219-driver-for-board-te.patch + git am ../patches/linux/0007-MMC-SPI-Hack-to-support-non-DMA-capable-SPI-ctrl.patch + git am ../patches/linux/0008-Add-wireless-regdb-regulatory-database-file.patch fi touch .patched fi diff --git a/patches/linux/0001-PCIe-Change-controller-and-bridge-base-address.patch b/patches/fix/0002-PCIe-Change-controller-and-bridge-base-address.patch similarity index 75% rename from patches/linux/0001-PCIe-Change-controller-and-bridge-base-address.patch rename to patches/fix/0002-PCIe-Change-controller-and-bridge-base-address.patch index d890e2b..9fb4752 100644 --- a/patches/linux/0001-PCIe-Change-controller-and-bridge-base-address.patch +++ b/patches/fix/0002-PCIe-Change-controller-and-bridge-base-address.patch @@ -1,18 +1,18 @@ -From a9ba71fb483fdacbc4f0d1d3ab2ec3013cc69f17 Mon Sep 17 00:00:00 2001 +From 8911855f7b07ba7f592f73850e551802ae40601d Mon Sep 17 00:00:00 2001 From: vauban353 Date: Sat, 5 Nov 2022 17:47:50 +0000 -Subject: [PATCH] PCIe: Change controller and bridge base address. +Subject: [PATCH 2/8] PCIe: Change controller and bridge base address. --- drivers/pci/controller/pcie-microchip-host.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pci/controller/pcie-microchip-host.c b/drivers/pci/controller/pcie-microchip-host.c -index 4b37b3403b85..04ca330f643d 100644 +index 56306f514..b1b3b7820 100644 --- a/drivers/pci/controller/pcie-microchip-host.c +++ b/drivers/pci/controller/pcie-microchip-host.c -@@ -24,8 +24,8 @@ - #define MC_NUM_MSI_IRQS_CODED 5 +@@ -26,8 +26,8 @@ + #define MC_ATT_MASK GENMASK_ULL(63, 31) /* PCIe Bridge Phy and Controller Phy offsets */ -#define MC_PCIE1_BRIDGE_ADDR 0x00008000u @@ -23,5 +23,5 @@ index 4b37b3403b85..04ca330f643d 100644 #define MC_PCIE_BRIDGE_ADDR (MC_PCIE1_BRIDGE_ADDR) #define MC_PCIE_CTRL_ADDR (MC_PCIE1_CTRL_ADDR) -- -2.25.1 +2.39.2 diff --git a/patches/linux/0001-GPIO-Add-Microchip-CoreGPIO-driver.patch b/patches/fix/0003-GPIO-Add-Microchip-CoreGPIO-driver.patch similarity index 97% rename from patches/linux/0001-GPIO-Add-Microchip-CoreGPIO-driver.patch rename to patches/fix/0003-GPIO-Add-Microchip-CoreGPIO-driver.patch index 41c5b26..eccc9a5 100644 --- a/patches/linux/0001-GPIO-Add-Microchip-CoreGPIO-driver.patch +++ b/patches/fix/0003-GPIO-Add-Microchip-CoreGPIO-driver.patch @@ -1,7 +1,7 @@ -From 49e37dfd8c7070958960ba23d0e3e005b6604940 Mon Sep 17 00:00:00 2001 +From 37ff4eff8ff033c48aa73526fec7291127326dcb Mon Sep 17 00:00:00 2001 From: vauban353 Date: Fri, 21 Jul 2023 19:33:28 +0100 -Subject: [PATCH] GPIO: Add Microchip CoreGPIO driver. +Subject: [PATCH 3/8] GPIO: Add Microchip CoreGPIO driver. --- drivers/gpio/Kconfig | 8 + @@ -11,7 +11,7 @@ Subject: [PATCH] GPIO: Add Microchip CoreGPIO driver. create mode 100644 drivers/gpio/gpio-microchip-core.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig -index d71f5bd6f532..8e3858e34cc7 100644 +index d71f5bd6f..8e3858e34 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -501,6 +501,14 @@ config GPIO_POLARFIRE_SOC @@ -30,7 +30,7 @@ index d71f5bd6f532..8e3858e34cc7 100644 bool "PXA GPIO support" depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile -index 9be0691d9660..05a65ff04b23 100644 +index 9be0691d9..05a65ff04 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -121,6 +121,7 @@ obj-$(CONFIG_GPIO_PISOSR) += gpio-pisosr.o @@ -43,7 +43,7 @@ index 9be0691d9660..05a65ff04b23 100644 obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o diff --git a/drivers/gpio/gpio-microchip-core.c b/drivers/gpio/gpio-microchip-core.c new file mode 100644 -index 000000000000..fd630cac4fe5 +index 000000000..fd630cac4 --- /dev/null +++ b/drivers/gpio/gpio-microchip-core.c @@ -0,0 +1,319 @@ @@ -367,5 +367,5 @@ index 000000000000..fd630cac4fe5 +}; +builtin_platform_driver(mchp_core_gpio_driver); -- -2.25.1 +2.39.2 diff --git a/patches/linux/0001-ADC-Add-Microchip-MCP356X-driver.patch b/patches/fix/0004-ADC-Add-Microchip-MCP356X-driver.patch similarity index 99% rename from patches/linux/0001-ADC-Add-Microchip-MCP356X-driver.patch rename to patches/fix/0004-ADC-Add-Microchip-MCP356X-driver.patch index 65e59bb..32fa0f1 100644 --- a/patches/linux/0001-ADC-Add-Microchip-MCP356X-driver.patch +++ b/patches/fix/0004-ADC-Add-Microchip-MCP356X-driver.patch @@ -1,7 +1,7 @@ -From 51e3b82d70fd95e72035cfc393b50a9b5410cd35 Mon Sep 17 00:00:00 2001 +From deadef387eed1bc60c8d2570e86a9bd0b4b196b4 Mon Sep 17 00:00:00 2001 From: vauban353 Date: Fri, 16 Jun 2023 16:05:01 +0100 -Subject: [PATCH] ADC: Add Microchip MCP356X driver. +Subject: [PATCH 4/8] ADC: Add Microchip MCP356X driver. --- drivers/iio/adc/Kconfig | 10 + @@ -11,7 +11,7 @@ Subject: [PATCH] ADC: Add Microchip MCP356X driver. create mode 100644 drivers/iio/adc/mcp356x.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig -index 48763d6a89a4..6c6b62a7f3e6 100644 +index 48763d6a8..6c6b62a7f 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -741,6 +741,16 @@ config MCP3911 @@ -32,7 +32,7 @@ index 48763d6a89a4..6c6b62a7f3e6 100644 tristate "Mediatek MT6360 ADC driver" depends on MFD_MT6360 diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile -index d5ca372050cb..5b3f027ac74c 100644 +index d5ca37205..5b3f027ac 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -68,6 +68,7 @@ obj-$(CONFIG_MAX9611) += max9611.o @@ -45,7 +45,7 @@ index d5ca372050cb..5b3f027ac74c 100644 obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o diff --git a/drivers/iio/adc/mcp356x.c b/drivers/iio/adc/mcp356x.c new file mode 100644 -index 000000000000..22d59413d342 +index 000000000..22d59413d --- /dev/null +++ b/drivers/iio/adc/mcp356x.c @@ -0,0 +1,1396 @@ @@ -1446,5 +1446,5 @@ index 000000000000..22d59413d342 +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("0.1.2"); -- -2.25.1 +2.39.2 diff --git a/patches/linux/0001-Microchip-QSPI-Add-regular-transfers.patch b/patches/fix/0005-Microchip-QSPI-Add-regular-transfers.patch similarity index 97% rename from patches/linux/0001-Microchip-QSPI-Add-regular-transfers.patch rename to patches/fix/0005-Microchip-QSPI-Add-regular-transfers.patch index 92a17be..b691b57 100644 --- a/patches/linux/0001-Microchip-QSPI-Add-regular-transfers.patch +++ b/patches/fix/0005-Microchip-QSPI-Add-regular-transfers.patch @@ -1,14 +1,14 @@ -From 0777b8ed4d5994b906dcfd06fa9c626b6905d655 Mon Sep 17 00:00:00 2001 +From 0eeed461b7b86e3b822984b1c266316ac70ccf69 Mon Sep 17 00:00:00 2001 From: vauban353 Date: Sun, 6 Aug 2023 09:37:40 +0100 -Subject: [PATCH] Microchip QSPI: Add regular transfers. +Subject: [PATCH 5/8] Microchip QSPI: Add regular transfers. --- drivers/spi/spi-microchip-core-qspi.c | 226 +++++++++++++++++++++++++- 1 file changed, 223 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-microchip-core-qspi.c b/drivers/spi/spi-microchip-core-qspi.c -index 33c19b98b9e2..2cce6526a507 100644 +index 33c19b98b..5f1623ac4 100644 --- a/drivers/spi/spi-microchip-core-qspi.c +++ b/drivers/spi/spi-microchip-core-qspi.c @@ -1,3 +1,4 @@ @@ -59,7 +59,7 @@ index 33c19b98b9e2..2cce6526a507 100644 + qspi->rxbuf += 4; + qspi->rx_len -= 4; + } -+ } ++ } + } + + if (!last ) { @@ -73,7 +73,7 @@ index 33c19b98b9e2..2cce6526a507 100644 + } + } + -+ if (qspi->tx_len) { ++ if (qspi->tx_len) { + control &= ~CONTROL_FLAGSX4; + writel_relaxed(control, qspi->regs + REG_CONTROL); + @@ -271,5 +271,5 @@ index 33c19b98b9e2..2cce6526a507 100644 ret = devm_spi_register_controller(&pdev->dev, ctlr); if (ret) { -- -2.25.1 +2.39.2 diff --git a/patches/linux/0001-BeagleV-Fire-Add-printk-to-IM219-driver-for-board-te.patch b/patches/fix/0006-BeagleV-Fire-Add-printk-to-IM219-driver-for-board-te.patch similarity index 84% rename from patches/linux/0001-BeagleV-Fire-Add-printk-to-IM219-driver-for-board-te.patch rename to patches/fix/0006-BeagleV-Fire-Add-printk-to-IM219-driver-for-board-te.patch index a3dd8d7..b2aad86 100644 --- a/patches/linux/0001-BeagleV-Fire-Add-printk-to-IM219-driver-for-board-te.patch +++ b/patches/fix/0006-BeagleV-Fire-Add-printk-to-IM219-driver-for-board-te.patch @@ -1,14 +1,14 @@ -From fa73238dfe5e29d9d77ecb2166fbae46215d59b6 Mon Sep 17 00:00:00 2001 +From 747c6d5984cccf7c3e48c2cdb4dd1d626889096e Mon Sep 17 00:00:00 2001 From: vauban353 Date: Sat, 12 Aug 2023 18:14:01 +0100 -Subject: [PATCH] BeagleV-Fire: Add printk to IM219 driver for board tests. +Subject: [PATCH 6/8] BeagleV-Fire: Add printk to IM219 driver for board tests. --- drivers/media/i2c/imx219.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c -index 7a14688f8c22..effb399b143f 100644 +index 7a14688f8..effb399b1 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -1181,6 +1181,9 @@ static int imx219_identify_module(struct imx219 *imx219) @@ -41,5 +41,5 @@ index 7a14688f8c22..effb399b143f 100644 if (!imx219) return -ENOMEM; -- -2.25.1 +2.39.2 diff --git a/patches/linux/0001-MMC-SPI-Hack-to-support-non-DMA-capable-SPI-ctrl.patch b/patches/fix/0007-MMC-SPI-Hack-to-support-non-DMA-capable-SPI-ctrl.patch similarity index 85% rename from patches/linux/0001-MMC-SPI-Hack-to-support-non-DMA-capable-SPI-ctrl.patch rename to patches/fix/0007-MMC-SPI-Hack-to-support-non-DMA-capable-SPI-ctrl.patch index 96e219c..7953390 100644 --- a/patches/linux/0001-MMC-SPI-Hack-to-support-non-DMA-capable-SPI-ctrl.patch +++ b/patches/fix/0007-MMC-SPI-Hack-to-support-non-DMA-capable-SPI-ctrl.patch @@ -1,7 +1,7 @@ -From 5252a0a75c390e01fc9b402660c1a28f7f4d172f Mon Sep 17 00:00:00 2001 +From e9aea028119afd0858e77989dd1a4ecc21d30dc1 Mon Sep 17 00:00:00 2001 From: vauban353 Date: Sun, 6 Aug 2023 11:13:27 +0100 -Subject: [PATCH] MMC SPI: Hack to support non DMA capable SPI ctrl. +Subject: [PATCH 7/8] MMC SPI: Hack to support non DMA capable SPI ctrl. --- drivers/mmc/host/mmc_spi.c | 6 +++++- @@ -10,7 +10,7 @@ Subject: [PATCH] MMC SPI: Hack to support non DMA capable SPI ctrl. 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c -index cc333ad67cac..fb45c7fa431d 100644 +index cc333ad67..fb45c7fa4 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -29,6 +29,9 @@ @@ -34,7 +34,7 @@ index cc333ad67cac..fb45c7fa431d 100644 host = mmc_priv(mmc); diff --git a/drivers/spi/internals.h b/drivers/spi/internals.h -index 4a28a8395552..c49350240978 100644 +index 4a28a8395..c49350240 100644 --- a/drivers/spi/internals.h +++ b/drivers/spi/internals.h @@ -12,6 +12,9 @@ @@ -48,7 +48,7 @@ index 4a28a8395552..c49350240978 100644 #include #include diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c -index a221478e528e..e198049cc80c 100644 +index a221478e5..e198049cc 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -42,6 +42,9 @@ EXPORT_TRACEPOINT_SYMBOL(spi_transfer_stop); @@ -62,5 +62,5 @@ index a221478e528e..e198049cc80c 100644 static void spidev_release(struct device *dev) -- -2.25.1 +2.39.2 diff --git a/patches/fix/0008-Add-wireless-regdb-regulatory-database-file.patch b/patches/fix/0008-Add-wireless-regdb-regulatory-database-file.patch new file mode 100644 index 0000000..cd33500 --- /dev/null +++ b/patches/fix/0008-Add-wireless-regdb-regulatory-database-file.patch @@ -0,0 +1,100 @@ +From 17558b89548b694599af7d6144426f30fe83e2c7 Mon Sep 17 00:00:00 2001 +From: Robert Nelson +Date: Sun, 10 Sep 2023 19:07:40 -0500 +Subject: [PATCH 8/8] Add wireless-regdb regulatory database file + +https://git.kernel.org/pub/scm/linux/kernel/git/sforshee/wireless-regdb.git/commit/?id=991b1ef696b7a034a5bf001cf31ab7735888c6e1 +Signed-off-by: Robert Nelson +--- + firmware/regulatory.db | Bin 0 -> 4896 bytes + firmware/regulatory.db.p7s | Bin 0 -> 1182 bytes + 2 files changed, 0 insertions(+), 0 deletions(-) + create mode 100644 firmware/regulatory.db + create mode 100644 firmware/regulatory.db.p7s + +diff --git a/firmware/regulatory.db b/firmware/regulatory.db +new file mode 100644 +index 0000000000000000000000000000000000000000..ebdc7e354e9c2317f9b862300bdc9381def4f0e0 +GIT binary patch +literal 4896 +zcmZvfe~eUD702(r=j>0)_OTf^7As;4Bqd5#YLK8s-kY7BnVnyc`L#P^e~FO@Dt6lR +zM?_azsUoYjXbH8g6tS%|!Glth5V7H5c)=ZkOKt)i?kG;WX`FJiaMsP??QWj9Nc$yp-7+-Y +z3jE3)r@cDf?YVHt^Kg$hg3oygXnLdA@RIa5g`e`WIO*lk_42sGE8w$U5tn!+-0PKT +zzeb-rz7%)yL-A4E7*D~)cp4k=EbfTsp%pLCx`;Q&D>xLNz`gMrG~;z>j!=$~41IFA +zW~4x$IzEz!4rvQOZ49gbPUz-b#+(P00k_mmI~ZWC|O}EdBNA +zQ-o%+jBAn=T$QZhkyHk6NaeASD#9zNGPG}9w^O`7!6S@hF+Je)4#bLkRXO4BUH +za@ZIv;I1(rGnoWFlu6-aCXd@QJ|;3noSzxTt(h9`&D7yU*26|Nj@z?oxRlM{&Dj!8 +zW=SJUIkHt~=3LyGi^JL62z?Utp;~h(baNTFl*{AZTmcW|eB$-AU&P5A#pWusUxm{- +zI*;50HgYw5ET6<@@@aJQS!m`9a53-Wj(i=b3pt$hUHqU=XY7-DF2uXnMpn82xG0h&eXxmZD29LGk<#mN%Qq?E+L63x0qXHufMm8h;# +z6>Fs$rps~MUmn59auPR}Y3AiTZY>w#WVuZ1aa>%kK!lP=2`;NwK7iCUA(_eJ=5J4*DIkutOSmM=pO44-PcQE +zDR4Akr?+QT%zJRP7`TDFduAOmZ(7WM=?*dfckAYu)&qSrLag0p>{rO2`RaX|d9CB$ +zw@&ktCxuu#P4Q0QduDd79*(y; +zwEx)`BEQ(w%XVh2pX;+Zj1TQ$e9+&!iI;x)fQctP$voqTWdp-nzpsN?f9!9EM0N?; +zDW5}}-#sh}#}8?ab??{gzh_p+)!Sl&6wmg6g%HoTV?v)$!}b{rbVHvG4s=?!cFdu( +z=*%@3-HUaw)*0;orX8p6gr(y+bY?cUH8#D$JvytJ(!R2SbHK4b11sV-ixFZ%({Mw#wJ#7;RPOf|Nd=76@HIA-aG=N9^N +z-+Yc1bB(c~ckVgYM)#bURTt~pd37GE4xJ~;dKO5}sg+ybDb4%!otmr7u#Rch7Wj0q +z_KEcl8(rfwS8KpSVeNrNjP9zH*W`)r&Gi>*hW&(^^gLM_juZA4+Iwiu^z4Ms2hXc? +z@35%cCG_)y^bWO|`_lZFqwT$Yw`d;!M9TOFc4mH_x}zNHU^8!;3u}fkVGRMt!WshR +znAlKIm$m00^=j!*T!hUTMECUj#fou|&Oq~)SzUj-7Q=J37&i5XnqdshxOCJP<<_j3 +zNx+r!^q%Hti)T#s>MGyl;GE3c%{xffO+3EXyvJ>(nVP-=JF%{TzuQ=YV|ea-ha(*1 +z9H$!X8Z-ybr}ig1KPOl5xv^f@Q&3}@Ej?XRl+9ei8jMCvvBs%WVng2nu2K8&_e6#6 +z5}#xEyz*Q)k7hb&oB4gsT#G-f`{egK$4;C*ul@Nq12fw>p8aj+@60Hh__lwb7qXzf +zBL38q&HOysEUx-%lYA7y=y>pwjZ*1#IHM|q=rj&P2_r)So|?88DVzD%=)^O`TlwrN(~UWDnc +zMfvJ|5mp@>>W6yto0|Rcqqof%edlKf?@WB +zww>Z>4X=M(>q8%P{(|_2oG;%c_sEykAy^B~lIA=q#LvZx;y2>BT&@@G{| +z9`$$FXnb`2x2pfb)9^g}1iENGC*)@JN2tT@K;xubO7)CVKi|-rCuKqnDG&N#H}rxK +zr{#O)LF#C&dKEqk-_tWWEtk{IE$RyQOga7}H>x%20s#4+l^fK{q}@gPP5FM+r84RY +z=hDox+@j-pH0RZIYNPrOokiHwYw|`lUu}hjP}MQ7$*NkWHiLrufnzR{eUVzBeh*{t +zg&@ymxkVmPPr*uJuIIA+nmj{xu7TIDs%aQ&`~Gl_sosCfd*osDRai!Lau+T7%YC=x +z0(p&mNFGy%;Z}H1pIuAOW&#Gtr$_tL)pE5;{T2rG`Eu?S)iMn$b#7jVbpf4yOWrBz +z8x&B-QeIdQ_%Z|`#- +z-wm{R{Y20YulK?7c1^^vCh+@?oK<^*S|3+mf$Q}7w&dgBwf&!zXVjFQc}vndn0ZU` +zjK2<}QgROUqL~H#9PE-swNMpc +jkn<>J>#Vh%{SRw@IvcW^8c9=jktWSr_Q@6UU$E$Y57IP8L6oT +z3gWzm=7t6aW`>p~2F9jQ68uJnhUNyAhDJ~U?An?bm5_}Gx`?@nk)HwR0xqT|Mn;CM +z4v*((v{*8Kvb>(A<7gqerfz9-p=#ZPnvA>o_BXFbTFkG>3OdBBrCXQwMQ*cDfkO0^ +z=|;AN-PY0^;v(6JN5Z?OiOKv`xysG?GefZD_o+ahi>{Kl1YEjq{wrJgH$hi|TVv`b +z-B%qmc3cco{17bvktzSB+w95l8WZ@}nElF7E({O*cINW^TCJPX#jdY+skgqbs$Y7# +z;kbd>rQmmO*2q0K{AjjlPV%wT=z7)CjD@c**D=J*io2m8VN}kpDU>E>R{VTAi=+9* +zjYZmD7HTTgOsnM9zHsB-i5&aMzmLAF2oe;PuKM;UW9IYUZelHuk1u@o;F8AX=M0{IyM6Wwa{mIf7OHQT7tSL%AoG*M>*R=9_*YmGe6b-ztep`IAGVBw- +z!1vkYwQ2f*qmwdu%Ti1qxCzl8GMutX;+>(KaknSwU#Nh +zc=g}&Q_k>OCH%D!(HApbsVILsU}Hb?y9H@0#VSP%n;5qNQ^_V^qBc;#nzqnVFjAsl +zkZB+RPtu&shRAuqKn5YhYRE6nYh-L-X<%q*X=-L_9%Z19kYzDcVo~7o+FSLAmwk6Z +zil+IQO`29k%L3dD-YtpB>}ZOdACa7a9Cg6N&C Date: Sun, 6 Aug 2023 09:42:33 +0100 -Subject: [PATCH] Add: BeagleV-Fire device tree. +Subject: [PATCH 1/8] Add: BeagleV-Fire device tree. --- - .../microchip/mpfs-beaglev-fire-fabric.dtsi | 156 +++++++ + .../microchip/mpfs-beaglev-fire-fabric.dtsi | 155 +++++++ .../boot/dts/microchip/mpfs-beaglev-fire.dts | 380 ++++++++++++++++++ - 2 files changed, 536 insertions(+) + 2 files changed, 535 insertions(+) create mode 100644 arch/riscv/boot/dts/microchip/mpfs-beaglev-fire-fabric.dtsi create mode 100644 arch/riscv/boot/dts/microchip/mpfs-beaglev-fire.dts diff --git a/arch/riscv/boot/dts/microchip/mpfs-beaglev-fire-fabric.dtsi b/arch/riscv/boot/dts/microchip/mpfs-beaglev-fire-fabric.dtsi new file mode 100644 -index 000000000000..c6f2c7ed9bc2 +index 000000000..60ba0883a --- /dev/null +++ b/arch/riscv/boot/dts/microchip/mpfs-beaglev-fire-fabric.dtsi -@@ -0,0 +1,156 @@ +@@ -0,0 +1,155 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* Copyright (c) 2020-2021 Microchip Technology Inc */ + @@ -93,7 +93,7 @@ index 000000000000..c6f2c7ed9bc2 + "B0_HSIO70P", "B0_HSIO71P", "B0_HSIO83P", "B0_HSIO73N_C2P_CLKP", + "XCVR1_RX_VALID", "XCVR1_LOCK", "XCVR1_ERROR", + "XCVR2_RX_VALID", "XCVR2_LOCK", "XCVR2_ERROR", -+ "XCVR3_RX_VALID", "XCVR3_LOCK", "XCVR3_ERROR", ++ "XCVR3_RX_VALID", "XCVR3_LOCK", "XCVR3_ERROR", + "XCVR_0B_REF_CLK_PLL_LOCK", "XCVR_0C_REF_CLK_PLL_LOCK", "B0_HSIO81N"; + }; + }; @@ -171,10 +171,9 @@ index 000000000000..c6f2c7ed9bc2 + "dll0_ref", "dll1_ref"; + status = "okay"; +}; -+ diff --git a/arch/riscv/boot/dts/microchip/mpfs-beaglev-fire.dts b/arch/riscv/boot/dts/microchip/mpfs-beaglev-fire.dts new file mode 100644 -index 000000000000..0c608922e54e +index 000000000..743da3845 --- /dev/null +++ b/arch/riscv/boot/dts/microchip/mpfs-beaglev-fire.dts @@ -0,0 +1,380 @@ @@ -325,7 +324,7 @@ index 000000000000..0c608922e54e + +&gpio1 { + ngpios=<24>; -+ gpio-line-names = "", "", "", "", "", "", "", "", "", "", ++ gpio-line-names = "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "ADC_IRQn", "", "", "USB_OCn"; + status = "okay"; @@ -559,5 +558,5 @@ index 000000000000..0c608922e54e + dr_mode = "otg"; +}; -- -2.25.1 +2.39.2 diff --git a/patches/linux/0002-PCIe-Change-controller-and-bridge-base-address.patch b/patches/linux/0002-PCIe-Change-controller-and-bridge-base-address.patch new file mode 100644 index 0000000..9fb4752 --- /dev/null +++ b/patches/linux/0002-PCIe-Change-controller-and-bridge-base-address.patch @@ -0,0 +1,27 @@ +From 8911855f7b07ba7f592f73850e551802ae40601d Mon Sep 17 00:00:00 2001 +From: vauban353 +Date: Sat, 5 Nov 2022 17:47:50 +0000 +Subject: [PATCH 2/8] PCIe: Change controller and bridge base address. + +--- + drivers/pci/controller/pcie-microchip-host.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/pci/controller/pcie-microchip-host.c b/drivers/pci/controller/pcie-microchip-host.c +index 56306f514..b1b3b7820 100644 +--- a/drivers/pci/controller/pcie-microchip-host.c ++++ b/drivers/pci/controller/pcie-microchip-host.c +@@ -26,8 +26,8 @@ + #define MC_ATT_MASK GENMASK_ULL(63, 31) + + /* PCIe Bridge Phy and Controller Phy offsets */ +-#define MC_PCIE1_BRIDGE_ADDR 0x00008000u +-#define MC_PCIE1_CTRL_ADDR 0x0000a000u ++#define MC_PCIE1_BRIDGE_ADDR 0x00004000u ++#define MC_PCIE1_CTRL_ADDR 0x00006000u + + #define MC_PCIE_BRIDGE_ADDR (MC_PCIE1_BRIDGE_ADDR) + #define MC_PCIE_CTRL_ADDR (MC_PCIE1_CTRL_ADDR) +-- +2.39.2 + diff --git a/patches/linux/0003-GPIO-Add-Microchip-CoreGPIO-driver.patch b/patches/linux/0003-GPIO-Add-Microchip-CoreGPIO-driver.patch new file mode 100644 index 0000000..eccc9a5 --- /dev/null +++ b/patches/linux/0003-GPIO-Add-Microchip-CoreGPIO-driver.patch @@ -0,0 +1,371 @@ +From 37ff4eff8ff033c48aa73526fec7291127326dcb Mon Sep 17 00:00:00 2001 +From: vauban353 +Date: Fri, 21 Jul 2023 19:33:28 +0100 +Subject: [PATCH 3/8] GPIO: Add Microchip CoreGPIO driver. + +--- + drivers/gpio/Kconfig | 8 + + drivers/gpio/Makefile | 1 + + drivers/gpio/gpio-microchip-core.c | 319 +++++++++++++++++++++++++++++ + 3 files changed, 328 insertions(+) + create mode 100644 drivers/gpio/gpio-microchip-core.c + +diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig +index d71f5bd6f..8e3858e34 100644 +--- a/drivers/gpio/Kconfig ++++ b/drivers/gpio/Kconfig +@@ -501,6 +501,14 @@ config GPIO_POLARFIRE_SOC + help + Say yes here to support the GPIO device on Microchip FPGAs. + ++config GPIO_MICROCHIP_CORE ++ bool "Microchip FPGA soft-IP GPIO support" ++ depends on OF_GPIO ++ select GPIOLIB_IRQCHIP ++ help ++ Say yes here to support the soft-IP GPIO device on Microchip FPGAs ++ ++ + config GPIO_PXA + bool "PXA GPIO support" + depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST +diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile +index 9be0691d9..05a65ff04 100644 +--- a/drivers/gpio/Makefile ++++ b/drivers/gpio/Makefile +@@ -121,6 +121,7 @@ obj-$(CONFIG_GPIO_PISOSR) += gpio-pisosr.o + obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o + obj-$(CONFIG_GPIO_PMIC_EIC_SPRD) += gpio-pmic-eic-sprd.o + obj-$(CONFIG_GPIO_POLARFIRE_SOC) += gpio-mpfs.o ++obj-$(CONFIG_GPIO_MICROCHIP_CORE) += gpio-microchip-core.o + obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o + obj-$(CONFIG_GPIO_RASPBERRYPI_EXP) += gpio-raspberrypi-exp.o + obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o +diff --git a/drivers/gpio/gpio-microchip-core.c b/drivers/gpio/gpio-microchip-core.c +new file mode 100644 +index 000000000..fd630cac4 +--- /dev/null ++++ b/drivers/gpio/gpio-microchip-core.c +@@ -0,0 +1,319 @@ ++// SPDX-License-Identifier: (GPL-2.0) ++/* ++ * Microchip CoreGPIO FPGA soft-IP GPIO controller driver ++ * ++ * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries ++ * ++ * Author: Lewis Hanly ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MPFS_GPIO_CTRL(i) (0x4 * (i)) ++#define MAX_NUM_GPIO 32 ++#define MPFS_GPIO_EN_INT 3 ++#define MPFS_GPIO_EN_OUT_BUF BIT(2) ++#define MPFS_GPIO_EN_IN BIT(1) ++#define MPFS_GPIO_EN_OUT BIT(0) ++ ++#define MPFS_GPIO_TYPE_INT_EDGE_BOTH 0x80 ++#define MPFS_GPIO_TYPE_INT_EDGE_NEG 0x60 ++#define MPFS_GPIO_TYPE_INT_EDGE_POS 0x40 ++#define MPFS_GPIO_TYPE_INT_LEVEL_LOW 0x20 ++#define MPFS_GPIO_TYPE_INT_LEVEL_HIGH 0x00 ++#define MPFS_GPIO_TYPE_INT_MASK GENMASK(7, 5) ++#define MPFS_IRQ_REG 0x80 ++#define MPFS_INP_REG 0x90 ++#define MPFS_OUTP_REG 0xA0 ++ ++struct mpfs_gpio_chip { ++ void __iomem *base; ++ struct clk *clk; ++ raw_spinlock_t lock; ++ struct gpio_chip gc; ++}; ++ ++static void mpfs_gpio_assign_bit(void __iomem *addr, unsigned int bit_offset, bool value) ++{ ++ unsigned long reg = readl(addr); ++ ++ __assign_bit(bit_offset, ®, value); ++ writel(reg, addr); ++} ++ ++static int mpfs_gpio_direction_input(struct gpio_chip *gc, unsigned int gpio_index) ++{ ++ struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); ++ u32 gpio_cfg; ++ unsigned long flags; ++ ++ raw_spin_lock_irqsave(&mpfs_gpio->lock, flags); ++ ++ gpio_cfg = readl(mpfs_gpio->base + MPFS_GPIO_CTRL(gpio_index)); ++ gpio_cfg |= MPFS_GPIO_EN_IN; ++ gpio_cfg &= ~(MPFS_GPIO_EN_OUT | MPFS_GPIO_EN_OUT_BUF); ++ writel(gpio_cfg, mpfs_gpio->base + MPFS_GPIO_CTRL(gpio_index)); ++ ++ raw_spin_unlock_irqrestore(&mpfs_gpio->lock, flags); ++ ++ return 0; ++} ++ ++static int mpfs_gpio_direction_output(struct gpio_chip *gc, unsigned int gpio_index, int value) ++{ ++ struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); ++ u32 gpio_cfg; ++ unsigned long flags; ++ ++ raw_spin_lock_irqsave(&mpfs_gpio->lock, flags); ++ ++ gpio_cfg = readl(mpfs_gpio->base + MPFS_GPIO_CTRL(gpio_index)); ++ gpio_cfg |= MPFS_GPIO_EN_OUT | MPFS_GPIO_EN_OUT_BUF; ++ gpio_cfg &= ~MPFS_GPIO_EN_IN; ++ writel(gpio_cfg, mpfs_gpio->base + MPFS_GPIO_CTRL(gpio_index)); ++ ++ mpfs_gpio_assign_bit(mpfs_gpio->base + MPFS_OUTP_REG, gpio_index, value); ++ ++ raw_spin_unlock_irqrestore(&mpfs_gpio->lock, flags); ++ ++ return 0; ++} ++ ++static int mpfs_gpio_get_direction(struct gpio_chip *gc, ++ unsigned int gpio_index) ++{ ++ struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); ++ u32 gpio_cfg; ++ ++ gpio_cfg = readl(mpfs_gpio->base + MPFS_GPIO_CTRL(gpio_index)); ++ if (gpio_cfg & MPFS_GPIO_EN_IN) ++ return GPIO_LINE_DIRECTION_IN; ++ ++ return GPIO_LINE_DIRECTION_OUT; ++} ++ ++static int mpfs_gpio_get(struct gpio_chip *gc, ++ unsigned int gpio_index) ++{ ++ struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); ++ ++ return !!(readl(mpfs_gpio->base + MPFS_INP_REG) & BIT(gpio_index)); ++} ++ ++static void mpfs_gpio_set(struct gpio_chip *gc, unsigned int gpio_index, int value) ++{ ++ struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); ++ unsigned long flags; ++ ++ raw_spin_lock_irqsave(&mpfs_gpio->lock, flags); ++ ++ mpfs_gpio_assign_bit(mpfs_gpio->base + MPFS_OUTP_REG, ++ gpio_index, value); ++ ++ raw_spin_unlock_irqrestore(&mpfs_gpio->lock, flags); ++} ++ ++static int mpfs_gpio_irq_set_type(struct irq_data *data, unsigned int type) ++{ ++ struct gpio_chip *gc = irq_data_get_irq_chip_data(data); ++ struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); ++ int gpio_index = irqd_to_hwirq(data); ++ u32 interrupt_type; ++ u32 gpio_cfg; ++ unsigned long flags; ++ ++ switch (type) { ++ case IRQ_TYPE_EDGE_BOTH: ++ interrupt_type = MPFS_GPIO_TYPE_INT_EDGE_BOTH; ++ break; ++ case IRQ_TYPE_EDGE_FALLING: ++ interrupt_type = MPFS_GPIO_TYPE_INT_EDGE_NEG; ++ break; ++ case IRQ_TYPE_EDGE_RISING: ++ interrupt_type = MPFS_GPIO_TYPE_INT_EDGE_POS; ++ break; ++ case IRQ_TYPE_LEVEL_HIGH: ++ interrupt_type = MPFS_GPIO_TYPE_INT_LEVEL_HIGH; ++ break; ++ case IRQ_TYPE_LEVEL_LOW: ++ interrupt_type = MPFS_GPIO_TYPE_INT_LEVEL_LOW; ++ break; ++ } ++ ++ raw_spin_lock_irqsave(&mpfs_gpio->lock, flags); ++ ++ gpio_cfg = readl(mpfs_gpio->base + MPFS_GPIO_CTRL(gpio_index)); ++ gpio_cfg &= ~MPFS_GPIO_TYPE_INT_MASK; ++ gpio_cfg |= interrupt_type; ++ writel(gpio_cfg, mpfs_gpio->base + MPFS_GPIO_CTRL(gpio_index)); ++ ++ raw_spin_unlock_irqrestore(&mpfs_gpio->lock, flags); ++ ++ return 0; ++} ++ ++static void mpfs_gpio_irq_unmask(struct irq_data *data) ++{ ++ struct gpio_chip *gc = irq_data_get_irq_chip_data(data); ++ struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); ++ int gpio_index = irqd_to_hwirq(data) % MAX_NUM_GPIO; ++ ++ mpfs_gpio_direction_input(gc, gpio_index); ++ mpfs_gpio_assign_bit(mpfs_gpio->base + MPFS_IRQ_REG, gpio_index, 1); ++ mpfs_gpio_assign_bit(mpfs_gpio->base + MPFS_GPIO_CTRL(gpio_index), ++ MPFS_GPIO_EN_INT, 1); ++} ++ ++static void mpfs_gpio_irq_mask(struct irq_data *data) ++{ ++ struct gpio_chip *gc = irq_data_get_irq_chip_data(data); ++ struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); ++ int gpio_index = irqd_to_hwirq(data) % MAX_NUM_GPIO; ++ ++ mpfs_gpio_assign_bit(mpfs_gpio->base + MPFS_IRQ_REG, gpio_index, 1); ++ mpfs_gpio_assign_bit(mpfs_gpio->base + MPFS_GPIO_CTRL(gpio_index), ++ MPFS_GPIO_EN_INT, 0); ++} ++ ++static struct irq_chip mpfs_gpio_irqchip = { ++ .name = "mpfs", ++ .irq_set_type = mpfs_gpio_irq_set_type, ++ .irq_mask = mpfs_gpio_irq_mask, ++ .irq_unmask = mpfs_gpio_irq_unmask, ++ .flags = IRQCHIP_MASK_ON_SUSPEND, ++}; ++ ++static void mpfs_gpio_irq_handler(struct irq_desc *desc) ++{ ++ struct irq_chip *irqchip = irq_desc_get_chip(desc); ++ struct mpfs_gpio_chip *mpfs_gpio = ++ gpiochip_get_data(irq_desc_get_handler_data(desc)); ++ unsigned long status; ++ int offset; ++ ++ chained_irq_enter(irqchip, desc); ++ ++ status = readl(mpfs_gpio->base + MPFS_IRQ_REG); ++ for_each_set_bit(offset, &status, mpfs_gpio->gc.ngpio) { ++ mpfs_gpio_assign_bit(mpfs_gpio->base + MPFS_IRQ_REG, offset, 1); ++ generic_handle_irq(irq_find_mapping(mpfs_gpio->gc.irq.domain, offset)); ++ } ++ ++ chained_irq_exit(irqchip, desc); ++} ++ ++static int mpfs_gpio_probe(struct platform_device *pdev) ++{ ++ struct clk *clk; ++ struct device *dev = &pdev->dev; ++ struct device_node *node = pdev->dev.of_node; ++ struct mpfs_gpio_chip *mpfs_gpio; ++ struct gpio_irq_chip *girq; ++ int i, ret, ngpios, nirqs; ++ ++ mpfs_gpio = devm_kzalloc(dev, sizeof(*mpfs_gpio), GFP_KERNEL); ++ if (!mpfs_gpio) ++ return -ENOMEM; ++ ++ mpfs_gpio->base = devm_platform_ioremap_resource(pdev, 0); ++ if (IS_ERR(mpfs_gpio->base)) ++ return dev_err_probe(dev, PTR_ERR(mpfs_gpio->base), "memory allocation failure\n"); ++ ++ clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(clk)) ++ return dev_err_probe(dev, PTR_ERR(clk), "devm_clk_get failed\n"); ++ ++ ret = clk_prepare_enable(clk); ++ if (ret) ++ return dev_err_probe(dev, ret, "failed to enable clock\n"); ++ ++ mpfs_gpio->clk = clk; ++ ++ raw_spin_lock_init(&mpfs_gpio->lock); ++ ++ ngpios = MAX_NUM_GPIO; ++ device_property_read_u32(dev, "ngpios", &ngpios); ++ if (ngpios > MAX_NUM_GPIO) ++ ngpios = MAX_NUM_GPIO; ++ ++ mpfs_gpio->gc.direction_input = mpfs_gpio_direction_input; ++ mpfs_gpio->gc.direction_output = mpfs_gpio_direction_output; ++ mpfs_gpio->gc.get_direction = mpfs_gpio_get_direction; ++ mpfs_gpio->gc.get = mpfs_gpio_get; ++ mpfs_gpio->gc.set = mpfs_gpio_set; ++ mpfs_gpio->gc.base = -1; ++ mpfs_gpio->gc.ngpio = ngpios; ++ mpfs_gpio->gc.label = dev_name(dev); ++ mpfs_gpio->gc.parent = dev; ++ mpfs_gpio->gc.owner = THIS_MODULE; ++ ++ nirqs = of_irq_count(node); ++ if (nirqs > MAX_NUM_GPIO) { ++ ret = -ENXIO; ++ goto cleanup_clock; ++ } ++ girq = &mpfs_gpio->gc.irq; ++ girq->chip = &mpfs_gpio_irqchip; ++ girq->handler = handle_simple_irq; ++ girq->parent_handler = mpfs_gpio_irq_handler; ++ girq->default_type = IRQ_TYPE_NONE; ++ girq->num_parents = nirqs; ++ girq->parents = devm_kcalloc(&pdev->dev, nirqs, ++ sizeof(*girq->parents), GFP_KERNEL); ++ if (!girq->parents) { ++ ret = -ENOMEM; ++ goto cleanup_clock; ++ } ++ for (i = 0; i < nirqs; i++) ++ girq->parents[i] = platform_get_irq(pdev, i); ++ ++ ret = gpiochip_add_data(&mpfs_gpio->gc, mpfs_gpio); ++ if (ret) ++ goto cleanup_clock; ++ ++ platform_set_drvdata(pdev, mpfs_gpio); ++ ++ return 0; ++ ++cleanup_clock: ++ clk_disable_unprepare(mpfs_gpio->clk); ++ return ret; ++} ++ ++static int mpfs_gpio_remove(struct platform_device *pdev) ++{ ++ struct mpfs_gpio_chip *mpfs_gpio = platform_get_drvdata(pdev); ++ ++ gpiochip_remove(&mpfs_gpio->gc); ++ clk_disable_unprepare(mpfs_gpio->clk); ++ ++ return 0; ++} ++ ++static const struct of_device_id mpfs_of_ids[] = { ++ { .compatible = "microchip,core-gpio", }, ++ { /* end of list */ } ++}; ++ ++static struct platform_driver mchp_core_gpio_driver = { ++ .probe = mpfs_gpio_probe, ++ .driver = { ++ .name = "microchip,core-gpio", ++ .of_match_table = mpfs_of_ids, ++ }, ++ .remove = mpfs_gpio_remove, ++}; ++builtin_platform_driver(mchp_core_gpio_driver); +-- +2.39.2 + diff --git a/patches/linux/0004-ADC-Add-Microchip-MCP356X-driver.patch b/patches/linux/0004-ADC-Add-Microchip-MCP356X-driver.patch new file mode 100644 index 0000000..32fa0f1 --- /dev/null +++ b/patches/linux/0004-ADC-Add-Microchip-MCP356X-driver.patch @@ -0,0 +1,1450 @@ +From deadef387eed1bc60c8d2570e86a9bd0b4b196b4 Mon Sep 17 00:00:00 2001 +From: vauban353 +Date: Fri, 16 Jun 2023 16:05:01 +0100 +Subject: [PATCH 4/8] ADC: Add Microchip MCP356X driver. + +--- + drivers/iio/adc/Kconfig | 10 + + drivers/iio/adc/Makefile | 1 + + drivers/iio/adc/mcp356x.c | 1396 +++++++++++++++++++++++++++++++++++++ + 3 files changed, 1407 insertions(+) + create mode 100644 drivers/iio/adc/mcp356x.c + +diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig +index 48763d6a8..6c6b62a7f 100644 +--- a/drivers/iio/adc/Kconfig ++++ b/drivers/iio/adc/Kconfig +@@ -741,6 +741,16 @@ config MCP3911 + This driver can also be built as a module. If so, the module will be + called mcp3911. + ++config MCP356X ++ tristate "Microchip Technology MCP356X driver" ++ depends on SPI ++ help ++ Say yes here to build support for Microchip Technology's MCP356X ++ analog to digital converter. ++ ++ This driver can also be built as a module. If so, the module will be ++ called mcp356x. ++ + config MEDIATEK_MT6360_ADC + tristate "Mediatek MT6360 ADC driver" + depends on MFD_MT6360 +diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile +index d5ca37205..5b3f027ac 100644 +--- a/drivers/iio/adc/Makefile ++++ b/drivers/iio/adc/Makefile +@@ -68,6 +68,7 @@ obj-$(CONFIG_MAX9611) += max9611.o + obj-$(CONFIG_MCP320X) += mcp320x.o + obj-$(CONFIG_MCP3422) += mcp3422.o + obj-$(CONFIG_MCP3911) += mcp3911.o ++obj-$(CONFIG_MCP356X) += mcp356x.o + obj-$(CONFIG_MEDIATEK_MT6360_ADC) += mt6360-adc.o + obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o + obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o +diff --git a/drivers/iio/adc/mcp356x.c b/drivers/iio/adc/mcp356x.c +new file mode 100644 +index 000000000..22d59413d +--- /dev/null ++++ b/drivers/iio/adc/mcp356x.c +@@ -0,0 +1,1396 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * IIO driver for MCP356X/MCP356XR and MCP346X/MCP346XR series ADC chip family ++ * ++ * Copyright (C) 2022-2023 Microchip Technology Inc. and its subsidiaries ++ * ++ * Author: Marius Cristea ++ * ++ * Datasheet for MCP3561, MCP3562, MCP3564 can be found here: ++ * https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/MCP3561-2-4-Family-Data-Sheet-DS20006181C.pdf ++ * Datasheet for MCP3561R, MCP3562R, MCP3564R can be found here: ++ * https://ww1.microchip.com/downloads/aemDocuments/documents/APID/ProductDocuments/DataSheets/MCP3561_2_4R-Data-Sheet-DS200006391C.pdf ++ * Datasheet for MCP3461, MCP3462, MCP3464 can be found here: ++ * https://ww1.microchip.com/downloads/aemDocuments/documents/APID/ProductDocuments/DataSheets/MCP3461-2-4-Two-Four-Eight-Channel-153.6-ksps-Low-Noise-16-Bit-Delta-Sigma-ADC-Data-Sheet-20006180D.pdf ++ * Datasheet for MCP3461R, MCP3462R, MCP3464R can be found here: ++ * https://ww1.microchip.com/downloads/aemDocuments/documents/APID/ProductDocuments/DataSheets/MCP3461-2-4R-Family-Data-Sheet-DS20006404C.pdf ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define MCP356X_ADCDATA 0x00 ++#define MCP356X_CONFIG0 0x01 ++#define MCP356X_CONFIG1 0x02 ++#define MCP356X_CONFIG2 0x03 ++#define MCP356X_CONFIG3 0x04 ++#define MCP356X_IRQ 0x05 ++#define MCP356X_MUX 0x06 ++#define MCP356X_SCAN 0x07 ++#define MCP356X_TIMER 0x08 ++#define MCP356X_OFFSETCAL 0x09 ++#define MCP356X_GAINCAL 0x0A ++#define MCP356X_RESERVED_B 0x0B ++#define MCP356X_RESERVED_C 0x0C ++#define MCP356X_LOCK 0x0D ++#define MCP356X_RESERVED_E 0x0E ++#define MCP356X_CRCCFG 0x0F ++ ++#define MCP356X_FAST_CMD_CTRL 0 ++#define MCP356X_RD_CTRL BIT(0) ++#define MCP356X_WRT_CTRL BIT(1) ++ ++#define MCP356X_FULL_RESET_CMD GENMASK(3, 1) ++ ++#define MCP3461_HW_ID BIT(3) ++#define MCP3462_HW_ID 0x0009 ++#define MCP3464_HW_ID 0x000B ++ ++#define MCP3561_HW_ID GENMASK(3, 2) ++#define MCP3562_HW_ID 0x000D ++#define MCP3564_HW_ID GENMASK(3, 0) ++#define MCP356X_HW_ID_MASK GENMASK(3, 0) ++ ++#define MCP356XR_INT_VREF_MV 2400 ++ ++/* MUX_VIN Input Selection ++ */ ++#define MCP356X_INTERNAL_VCM GENMASK(3, 0) ++#define MCP356X_TEMP_DIODE_M GENMASK(3, 1) ++#define MCP356X_TEMP_DIODE_P 0b1101 ++#define MCP356X_REFIN_NEG GENMASK(3, 2) ++#define MCP356X_REFIN_POZ 0b1011 ++#define MCP356X_RESERVED 0b1010 /* do not use */ ++#define MCP356X_AVDD 0b1001 ++#define MCP356X_AGND BIT(3) ++#define MCP356X_CH7 GENMASK(2, 0) ++#define MCP356X_CH6 GENMASK(2, 1) ++#define MCP356X_CH5 0b0101 ++#define MCP356X_CH4 BIT(2) ++#define MCP356X_CH3 GENMASK(1, 0) ++#define MCP356X_CH2 BIT(1) ++#define MCP356X_CH1 BIT(0) ++#define MCP356X_CH0 0 ++ ++#define MCP356X_ADC_MODE_MASK GENMASK(1, 0) ++ ++#define MCP356X_ADC_DEFAULT_MODE 0 ++#define MCP356X_ADC_SHUTDOWN_MODE BIT(0) ++#define MCP356X_ADC_STANDBY BIT(1) ++#define MCP356X_ADC_CONVERSION_MODE GENMASK(1, 0) ++ ++#define MCP356X_DATA_READY_MASK BIT(6) ++ ++#define MCP356X_OVERSAMPLING_RATIO_32 0 ++#define MCP356X_OVERSAMPLING_RATIO_64 BIT(0) ++#define MCP356X_OVERSAMPLING_RATIO_128 BIT(1) ++#define MCP356X_OVERSAMPLING_RATIO_256 GENMASK(1, 0) ++#define MCP356X_OVERSAMPLING_RATIO_512 BIT(2) ++#define MCP356X_OVERSAMPLING_RATIO_1024 0x05 ++#define MCP356X_OVERSAMPLING_RATIO_2048 GENMASK(2, 1) ++#define MCP356X_OVERSAMPLING_RATIO_4096 GENMASK(2, 0) ++#define MCP356X_OVERSAMPLING_RATIO_8192 BIT(3) ++#define MCP356X_OVERSAMPLING_RATIO_16384 0x09 ++#define MCP356X_OVERSAMPLING_RATIO_20480 0x0A ++#define MCP356X_OVERSAMPLING_RATIO_24576 0x0B ++#define MCP356X_OVERSAMPLING_RATIO_40960 0x0C ++#define MCP356X_OVERSAMPLING_RATIO_49152 0x0D ++#define MCP356X_OVERSAMPLING_RATIO_81920 GENMASK(3, 1) ++#define MCP356X_OVERSAMPLING_RATIO_98304 GENMASK(3, 0) ++ ++#define MCP356X_OVERSAMPLING_RATIO_MASK GENMASK(5, 2) ++#define MCP356X_OVERSAMPLING_RATIO_SHIFT 0x02 ++ ++#define MCP356X_HARDWARE_GAIN_MASK GENMASK(5, 3) ++#define MCP356X_HARDWARE_GAIN_SHIFT 0x03 ++#define MCP356X_DEFAULT_HARDWARE_GAIN BIT(1) ++ ++#define MCP356X_CS_SEL_0_0_uA 0x0 ++#define MCP356X_CS_SEL_0_9_uA BIT(0) ++#define MCP356X_CS_SEL_3_7_uA BIT(1) ++#define MCP356X_CS_SEL_15_uA GENMASK(1, 0) ++ ++#define MCP356X_CS_SEL_MASK GENMASK(3, 2) ++ ++#define MCP356X_BOOST_CURRENT_x0_50 0 ++#define MCP356X_BOOST_CURRENT_x0_66 BIT(0) ++#define MCP356X_BOOST_CURRENT_x1_00 BIT(1) ++#define MCP356X_BOOST_CURRENT_x2_00 GENMASK(1, 0) ++ ++#define MCP356X_BOOST_CURRENT_MASK GENMASK(7, 6) ++ ++/* Auto-Zeroing MUX Setting */ ++#define MCP356X_AZ_MUX_MASK BIT(2) ++/* Auto-Zeroing REF Setting */ ++#define MCP356X_AZ_REF_MASK BIT(1) ++ ++#define MCP356X_SHARED_DEVATTRS_COUNT 1 ++#define MCP356X_PARTICULAR_DEVATTRS_COUNT 1 ++ ++#define MAX_HWGAIN 64000 ++ ++#define MCP356X_DATA_READY_TIMEOUT_MS 2000 ++ ++enum mcp356x_ids { ++ mcp3461, ++ mcp3462, ++ mcp3464, ++ mcp3461r, ++ mcp3462r, ++ mcp3464r, ++ mcp3561, ++ mcp3562, ++ mcp3564, ++ mcp3561r, ++ mcp3562r, ++ mcp3564r, ++}; ++ ++static const unsigned int mcp356x_oversampling_avail[16] = { ++ [MCP356X_OVERSAMPLING_RATIO_32] = 32, ++ [MCP356X_OVERSAMPLING_RATIO_64] = 64, ++ [MCP356X_OVERSAMPLING_RATIO_128] = 128, ++ [MCP356X_OVERSAMPLING_RATIO_256] = 256, ++ [MCP356X_OVERSAMPLING_RATIO_512] = 512, ++ [MCP356X_OVERSAMPLING_RATIO_1024] = 1024, ++ [MCP356X_OVERSAMPLING_RATIO_2048] = 2048, ++ [MCP356X_OVERSAMPLING_RATIO_4096] = 4096, ++ [MCP356X_OVERSAMPLING_RATIO_8192] = 8192, ++ [MCP356X_OVERSAMPLING_RATIO_16384] = 16384, ++ [MCP356X_OVERSAMPLING_RATIO_20480] = 20480, ++ [MCP356X_OVERSAMPLING_RATIO_24576] = 24576, ++ [MCP356X_OVERSAMPLING_RATIO_40960] = 40960, ++ [MCP356X_OVERSAMPLING_RATIO_49152] = 49152, ++ [MCP356X_OVERSAMPLING_RATIO_81920] = 81920, ++ [MCP356X_OVERSAMPLING_RATIO_98304] = 98304 ++}; ++ ++/* ++ * Current Source/Sink Selection Bits for Sensor Bias (source on VIN+/sink on VIN-) ++ */ ++static const char * const mcp356x_current_bias_avail[] = { ++ [MCP356X_CS_SEL_0_0_uA] = "no_current(default)", ++ [MCP356X_CS_SEL_0_9_uA] = "0.9_uA", ++ [MCP356X_CS_SEL_3_7_uA] = "3.7_uA", ++ [MCP356X_CS_SEL_15_uA] = "15_uA", ++}; ++ ++/* ++ * BOOST[1:0]: ADC Bias Current Selection ++ */ ++static const char * const mcp356x_boost_current_avail[] = { ++ [MCP356X_BOOST_CURRENT_x0_50] = "x0.5", ++ [MCP356X_BOOST_CURRENT_x0_66] = "x0.66", ++ [MCP356X_BOOST_CURRENT_x1_00] = "x1_(default)", ++ [MCP356X_BOOST_CURRENT_x2_00] = "x2", ++}; ++ ++/* ++ * Calibration bias values ++ */ ++static const int mcp356x_calib_bias[] = { ++ -8388608, /* min: -2^23 */ ++ 1, /* step: 1 */ ++ 8388607 /* max: 2^23 - 1 */ ++}; ++ ++/* ++ * Calibration scale values ++ * The Gain Error Calibration register (GAINCAL) is an ++ * unsigned 24-bit register that holds the digital gain error ++ * calibration value, GAINCAL which could be calculated by ++ * GAINCAL (V/V) = (GAINCAL[23:0])/8388608 ++ * The gain error calibration value range in equivalent voltage is [0; 2-2^(-23)] ++ */ ++static const unsigned int mcp356x_calib_scale[] = { ++ 0, /* min: 0 */ ++ 1, /* step: 1/8388608 */ ++ 16777215 /* max: 2 - 2^(-23) */ ++}; ++ ++/* Programmable hardware gain x1/3, x1, x2, x4, x8, x16, x32, x64 */ ++static const int mcp356x_hwgain_frac[] = { ++ 3, ++ 10, ++ 1, ++ 1, ++ 2, ++ 1, ++ 4, ++ 1, ++ 8, ++ 1, ++ 16, ++ 1, ++ 32, ++ 1, ++ 64, ++ 1 ++}; ++ ++static const int mcp356x_hwgain[] = { ++ 300, ++ 1000, ++ 2000, ++ 4000, ++ 8000, ++ 16000, ++ 32000, ++ 64000 ++}; ++ ++/** ++ * struct mcp356x_chip_info - chip specific data ++ * @channels: struct iio_chan_spec matching the device's capabilities ++ * @num_channels: number of channels ++ * @int_vref_uv: internal voltage reference value in microVolts ++ * @has_vref: Does the ADC has an internal voltage reference? ++ */ ++struct mcp356x_chip_info { ++ const struct iio_chan_spec *channels; ++ unsigned int num_channels; ++ unsigned int int_vref_uv; ++ bool has_vref; ++}; ++ ++/** ++ * struct mcp356x_state - working data for a ADC device ++ * @chip_info: chip specific data ++ * @mcp356x_info: information about iio device ++ * @spi: SPI device structure ++ * @vref: The regulator device used as a voltage reference in case ++ * external voltage reference is used ++ * @vref_mv: voltage reference value in miliVolts ++ * @lock: mutex to prevent concurrent reads/writes ++ * @dev_addr: hardware device address ++ * @oversampling: the index inside oversampling list of the ADC ++ * @hwgain: the index inside hardware gain list of the ADC ++ * @calib_bias: calibration bias value ++ * @calib_scale: calibration scale value ++ * @current_boost_mode: the index inside current boost list of the ADC ++ * @current_bias_mode: the index inside current bias list of the ADC ++ * @auto_zeroing_mux: set if ADC auto-zeroing algorithm is enabled ++ * @auto_zeroing_ref: set if ADC auto-Zeroing Reference Buffer Setting is enabled ++ */ ++struct mcp356x_state { ++ const struct mcp356x_chip_info *chip_info; ++ struct iio_info mcp356x_info; ++ struct spi_device *spi; ++ struct regulator *vref; ++ unsigned short vref_mv; ++ struct mutex lock; /*lock to prevent concurrent reads/writes */ ++ u8 dev_addr; ++ unsigned int oversampling; ++ unsigned int hwgain; ++ int calib_bias; ++ int calib_scale; ++ unsigned int current_boost_mode; ++ unsigned int current_bias_mode; ++ bool auto_zeroing_mux; ++ bool auto_zeroing_ref; ++}; ++ ++static inline u8 mcp356x_reg_write(u8 chip_addr, u8 reg) ++{ ++ return ((chip_addr << 6) | (reg << 2) | MCP356X_WRT_CTRL); ++} ++ ++static inline u8 mcp356x_reg_read(u8 chip_addr, u8 reg) ++{ ++ return ((chip_addr << 6) | (reg << 2) | MCP356X_RD_CTRL); ++} ++ ++static inline u8 mcp356x_reg_fast_cmd(u8 chip_addr, u8 cmd) ++{ ++ return ((chip_addr << 6) | (cmd << 2)); ++} ++ ++static int mcp356x_read(struct mcp356x_state *adc, u8 reg, u32 *val, u8 len) ++{ ++ int ret; ++ u8 tmp_reg; ++ ++ tmp_reg = mcp356x_reg_read(adc->dev_addr, reg); ++ ++ ret = spi_write_then_read(adc->spi, &tmp_reg, 1, val, len); ++ ++ be32_to_cpus(val); ++ *val >>= ((4 - len) * 8); ++ ++ return ret; ++} ++ ++static int mcp356x_write(struct mcp356x_state *adc, u8 reg, u32 val, u8 len) ++{ ++ val |= (mcp356x_reg_write(adc->dev_addr, reg) << (len * 8)); ++ val <<= (3 - len) * 8; ++ cpu_to_be32s(&val); ++ ++ return spi_write(adc->spi, &val, len + 1); ++} ++ ++static int mcp356x_fast_cmd(struct mcp356x_state *adc, u8 fast_cmd) ++{ ++ u8 val; ++ ++ val = mcp356x_reg_fast_cmd(adc->dev_addr, fast_cmd); ++ ++ return spi_write(adc->spi, &val, 1); ++} ++ ++static int mcp356x_update(struct mcp356x_state *adc, u8 reg, u32 mask, u32 val, ++ u8 len) ++{ ++ u32 tmp; ++ int ret; ++ ++ ret = mcp356x_read(adc, reg, &tmp, len); ++ ++ if (ret == 0) { ++ val &= mask; ++ val |= tmp & ~mask; ++ ret = mcp356x_write(adc, reg, val, len); ++ } ++ ++ return ret; ++} ++ ++/* Custom IIO Device Attributes */ ++static int mcp356x_set_current_boost_mode(struct iio_dev *indio_dev, ++ const struct iio_chan_spec *chan, ++ unsigned int mode) ++{ ++ struct mcp356x_state *adc = iio_priv(indio_dev); ++ int ret; ++ ++ dev_dbg(&indio_dev->dev, "%s: %d\n", __func__, mode); ++ ++ mutex_lock(&adc->lock); ++ ret = mcp356x_update(adc, MCP356X_CONFIG2, MCP356X_BOOST_CURRENT_MASK, ++ mode, 1); ++ ++ if (ret) ++ dev_err(&indio_dev->dev, "Failed to configure CONFIG2 register\n"); ++ else ++ adc->current_boost_mode = mode; ++ ++ mutex_unlock(&adc->lock); ++ ++ return ret; ++} ++ ++static int mcp356x_get_current_boost_mode(struct iio_dev *indio_dev, ++ const struct iio_chan_spec *chan) ++{ ++ struct mcp356x_state *adc = iio_priv(indio_dev); ++ ++ return adc->current_boost_mode; ++} ++ ++static int mcp356x_set_current_bias_mode(struct iio_dev *indio_dev, ++ const struct iio_chan_spec *chan, ++ unsigned int mode) ++{ ++ struct mcp356x_state *adc = iio_priv(indio_dev); ++ int ret; ++ ++ dev_dbg(&indio_dev->dev, "%s: %d\n", __func__, mode); ++ ++ mutex_lock(&adc->lock); ++ ret = mcp356x_update(adc, MCP356X_CONFIG0, MCP356X_CS_SEL_MASK, mode, 1); ++ ++ if (ret) ++ dev_err(&indio_dev->dev, "Failed to configure CONFIG0 register\n"); ++ else ++ adc->current_bias_mode = mode; ++ ++ mutex_unlock(&adc->lock); ++ ++ return ret; ++} ++ ++static int mcp356x_get_current_bias_mode(struct iio_dev *indio_dev, ++ const struct iio_chan_spec *chan) ++{ ++ struct mcp356x_state *adc = iio_priv(indio_dev); ++ ++ return adc->current_bias_mode; ++} ++ ++static const struct iio_enum mcp356x_current_boost_mode_enum = { ++ .items = mcp356x_boost_current_avail, ++ .num_items = ARRAY_SIZE(mcp356x_boost_current_avail), ++ .set = mcp356x_set_current_boost_mode, ++ .get = mcp356x_get_current_boost_mode, ++}; ++ ++static const struct iio_enum mcp356x_current_bias_mode_enum = { ++ .items = mcp356x_current_bias_avail, ++ .num_items = ARRAY_SIZE(mcp356x_current_bias_avail), ++ .set = mcp356x_set_current_bias_mode, ++ .get = mcp356x_get_current_bias_mode, ++}; ++ ++static const struct iio_chan_spec_ext_info mcp356x_ext_info[] = { ++ IIO_ENUM("boost_current", IIO_SHARED_BY_ALL, &mcp356x_current_boost_mode_enum), ++ { ++ .name = "boost_current_available", ++ .shared = IIO_SHARED_BY_ALL, ++ .read = iio_enum_available_read, ++ .private = (uintptr_t)&mcp356x_current_boost_mode_enum, ++ }, ++ IIO_ENUM("current_bias", IIO_SHARED_BY_ALL, &mcp356x_current_bias_mode_enum), ++ { ++ .name = "current_bias_available", ++ .shared = IIO_SHARED_BY_ALL, ++ .read = iio_enum_available_read, ++ .private = (uintptr_t)&mcp356x_current_bias_mode_enum, ++ }, ++ {} ++}; ++ ++static ssize_t mcp356x_auto_zeroing_mux_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct iio_dev *indio_dev = dev_to_iio_dev(dev); ++ struct mcp356x_state *adc = iio_priv(indio_dev); ++ ++ return sysfs_emit(buf, "%d\n", adc->auto_zeroing_mux); ++} ++ ++static ssize_t mcp356x_auto_zeroing_mux_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t len) ++{ ++ struct iio_dev *indio_dev = dev_to_iio_dev(dev); ++ struct mcp356x_state *adc = iio_priv(indio_dev); ++ bool auto_zero; ++ int ret; ++ ++ ret = kstrtobool(buf, &auto_zero); ++ if (ret) ++ return ret; ++ ++ mutex_lock(&adc->lock); ++ ret = mcp356x_update(adc, MCP356X_CONFIG2, MCP356X_AZ_MUX_MASK, ++ (u32)auto_zero, 1); ++ ++ if (ret) ++ dev_err(&indio_dev->dev, "Failed to update CONFIG2 register\n"); ++ else ++ adc->auto_zeroing_mux = auto_zero; ++ ++ mutex_unlock(&adc->lock); ++ ++ return ret ? ret : len; ++} ++ ++static ssize_t mcp356x_auto_zeroing_ref_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct iio_dev *indio_dev = dev_to_iio_dev(dev); ++ struct mcp356x_state *adc = iio_priv(indio_dev); ++ ++ return sysfs_emit(buf, "%d\n", adc->auto_zeroing_ref); ++} ++ ++static ssize_t mcp356x_auto_zeroing_ref_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t len) ++{ ++ struct iio_dev *indio_dev = dev_to_iio_dev(dev); ++ struct mcp356x_state *adc = iio_priv(indio_dev); ++ bool auto_zero; ++ int ret; ++ ++ ret = kstrtobool(buf, &auto_zero); ++ if (ret) ++ return ret; ++ ++ mutex_lock(&adc->lock); ++ ret = mcp356x_update(adc, MCP356X_CONFIG2, MCP356X_AZ_REF_MASK, ++ (u32)auto_zero, 1); ++ ++ if (ret) ++ dev_err(&indio_dev->dev, "Failed to update CONFIG2 register\n"); ++ else ++ adc->auto_zeroing_ref = auto_zero; ++ ++ mutex_unlock(&adc->lock); ++ ++ return ret ? ret : len; ++} ++ ++#define MCP356X_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr) ++ ++static IIO_DEVICE_ATTR(enable_auto_zeroing_ref, 0644, ++ mcp356x_auto_zeroing_ref_show, ++ mcp356x_auto_zeroing_ref_store, 0); ++ ++static struct attribute *mcp356x_particular_attributes[] = { ++ MCP356X_DEV_ATTR(enable_auto_zeroing_ref), ++ NULL ++}; ++ ++static IIO_DEVICE_ATTR(enable_auto_zeroing_mux, 0644, ++ mcp356x_auto_zeroing_mux_show, ++ mcp356x_auto_zeroing_mux_store, 0); ++ ++static struct attribute *mcp356x_shared_attributes[] = { ++ MCP356X_DEV_ATTR(enable_auto_zeroing_mux), ++ NULL, ++}; ++ ++static int mcp356x_prep_custom_attributes(struct mcp356x_state *adc, ++ struct iio_dev *indio_dev) ++{ ++ int i; ++ struct attribute **mcp356x_custom_attr; ++ struct attribute_group *mcp356x_group; ++ ++ mcp356x_group = devm_kzalloc(&adc->spi->dev, sizeof(*mcp356x_group), GFP_KERNEL); ++ ++ if (!mcp356x_group) ++ return (-ENOMEM); ++ ++ mcp356x_custom_attr = devm_kzalloc(&adc->spi->dev, (MCP356X_SHARED_DEVATTRS_COUNT + ++ MCP356X_PARTICULAR_DEVATTRS_COUNT + 1) * sizeof(struct attribute *), ++ GFP_KERNEL); ++ ++ if (!mcp356x_custom_attr) ++ return (-ENOMEM); ++ ++ for (i = 0; i < MCP356X_SHARED_DEVATTRS_COUNT; i++) ++ mcp356x_custom_attr[i] = mcp356x_shared_attributes[i]; ++ ++ if (adc->chip_info->has_vref) { ++ dev_dbg(&indio_dev->dev, "Setup custom attr for R variant\n"); ++ for (i = 0; i < MCP356X_PARTICULAR_DEVATTRS_COUNT; i++) ++ mcp356x_custom_attr[MCP356X_SHARED_DEVATTRS_COUNT + i] = ++ mcp356x_particular_attributes[i]; ++ } ++ ++ mcp356x_group->attrs = mcp356x_custom_attr; ++ adc->mcp356x_info.attrs = mcp356x_group; ++ ++ return 0; ++} ++ ++#define MCP356X_V_CHANNEL(index, addr, depth) { \ ++ .type = IIO_VOLTAGE, \ ++ .indexed = 1, \ ++ .channel = (index), \ ++ .address = (((addr) << 4) | MCP356X_AGND), \ ++ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ ++ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ ++ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_HARDWAREGAIN) | \ ++ BIT(IIO_CHAN_INFO_CALIBBIAS) | \ ++ BIT(IIO_CHAN_INFO_CALIBSCALE) | \ ++ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ ++ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_CALIBSCALE) | \ ++ BIT(IIO_CHAN_INFO_HARDWAREGAIN) | \ ++ BIT(IIO_CHAN_INFO_CALIBBIAS) | \ ++ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ ++ .ext_info = mcp356x_ext_info, \ ++ .scan_index = 0, \ ++ .scan_type = { \ ++ .sign = 's', \ ++ .realbits = depth, \ ++ .storagebits = 32, \ ++ .endianness = IIO_BE, \ ++ }, \ ++} ++ ++#define MCP356X_T_CHAN(depth) { \ ++ .type = IIO_TEMP, \ ++ .channel = 0, \ ++ .address = ((MCP356X_TEMP_DIODE_P << 4) | MCP356X_TEMP_DIODE_M), \ ++ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ ++ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ ++ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_HARDWAREGAIN) | \ ++ BIT(IIO_CHAN_INFO_CALIBBIAS) | \ ++ BIT(IIO_CHAN_INFO_CALIBSCALE) | \ ++ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ ++ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_CALIBSCALE) | \ ++ BIT(IIO_CHAN_INFO_CALIBBIAS) | \ ++ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ ++ .ext_info = mcp356x_ext_info, \ ++ .scan_index = 0, \ ++ .scan_type = { \ ++ .sign = 'u', \ ++ .realbits = depth, \ ++ .storagebits = 32, \ ++ .endianness = IIO_BE, \ ++ }, \ ++} ++ ++#define MCP356X_V_CHANNEL_DIFF(chan1, chan2, addr, depth) { \ ++ .type = IIO_VOLTAGE, \ ++ .indexed = 1, \ ++ .channel = (chan1), \ ++ .channel2 = (chan2), \ ++ .address = (addr), \ ++ .differential = 1, \ ++ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ ++ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ ++ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_HARDWAREGAIN) | \ ++ BIT(IIO_CHAN_INFO_CALIBBIAS) | \ ++ BIT(IIO_CHAN_INFO_CALIBSCALE) | \ ++ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ ++ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_CALIBSCALE) | \ ++ BIT(IIO_CHAN_INFO_CALIBBIAS) | \ ++ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ ++ .ext_info = mcp356x_ext_info, \ ++ .scan_index = 0, \ ++ .scan_type = { \ ++ .sign = 's', \ ++ .realbits = depth, \ ++ .storagebits = 32, \ ++ .endianness = IIO_BE, \ ++ }, \ ++} ++ ++#define MCP3561_CHANNELS(depth) { \ ++ MCP356X_V_CHANNEL(0, 0, depth), \ ++ MCP356X_V_CHANNEL(1, 1, depth), \ ++ MCP356X_V_CHANNEL_DIFF(0, 1, 0x01, depth), \ ++ MCP356X_V_CHANNEL_DIFF(1, 0, 0x10, depth), \ ++ MCP356X_T_CHAN(depth) \ ++} ++ ++#define MCP3562_CHANNELS(depth) { \ ++ MCP356X_V_CHANNEL(0, 0, depth), \ ++ MCP356X_V_CHANNEL(1, 1, depth), \ ++ MCP356X_V_CHANNEL(2, 2, depth), \ ++ MCP356X_V_CHANNEL(3, 3, depth), \ ++ MCP356X_T_CHAN(depth), \ ++ MCP356X_V_CHANNEL_DIFF(0, 1, 0x01, depth), \ ++ MCP356X_V_CHANNEL_DIFF(1, 0, 0x10, depth), \ ++ MCP356X_V_CHANNEL_DIFF(0, 2, 0x02, depth), \ ++ MCP356X_V_CHANNEL_DIFF(0, 3, 0x03, depth), \ ++ MCP356X_V_CHANNEL_DIFF(1, 2, 0x12, depth), \ ++ MCP356X_V_CHANNEL_DIFF(1, 3, 0x13, depth), \ ++ MCP356X_V_CHANNEL_DIFF(2, 3, 0x23, depth), \ ++ MCP356X_V_CHANNEL_DIFF(2, 0, 0x20, depth), \ ++ MCP356X_V_CHANNEL_DIFF(3, 0, 0x30, depth), \ ++ MCP356X_V_CHANNEL_DIFF(2, 1, 0x21, depth), \ ++ MCP356X_V_CHANNEL_DIFF(3, 1, 0x31, depth), \ ++ MCP356X_V_CHANNEL_DIFF(3, 2, 0x32, depth), \ ++} ++ ++#define MCP3564_CHANNELS(depth) { \ ++ MCP356X_V_CHANNEL(0, 0, depth), \ ++ MCP356X_V_CHANNEL(1, 1, depth), \ ++ MCP356X_V_CHANNEL(2, 2, depth), \ ++ MCP356X_V_CHANNEL(3, 3, depth), \ ++ MCP356X_V_CHANNEL(4, 4, depth), \ ++ MCP356X_V_CHANNEL(5, 5, depth), \ ++ MCP356X_V_CHANNEL(6, 6, depth), \ ++ MCP356X_V_CHANNEL(7, 7, depth), \ ++ MCP356X_T_CHAN(depth), \ ++ MCP356X_V_CHANNEL_DIFF(0, 1, 0x01, depth), \ ++ MCP356X_V_CHANNEL_DIFF(1, 0, 0x10, depth), \ ++ MCP356X_V_CHANNEL_DIFF(0, 2, 0x02, depth), \ ++ MCP356X_V_CHANNEL_DIFF(0, 3, 0x03, depth), \ ++ MCP356X_V_CHANNEL_DIFF(1, 2, 0x12, depth), \ ++ MCP356X_V_CHANNEL_DIFF(1, 3, 0x13, depth), \ ++ MCP356X_V_CHANNEL_DIFF(2, 3, 0x23, depth), \ ++ MCP356X_V_CHANNEL_DIFF(2, 0, 0x20, depth), \ ++ MCP356X_V_CHANNEL_DIFF(3, 0, 0x30, depth), \ ++ MCP356X_V_CHANNEL_DIFF(2, 1, 0x21, depth), \ ++ MCP356X_V_CHANNEL_DIFF(3, 1, 0x31, depth), \ ++ MCP356X_V_CHANNEL_DIFF(3, 2, 0x32, depth), \ ++ MCP356X_V_CHANNEL_DIFF(0, 4, 0x04, depth), \ ++ MCP356X_V_CHANNEL_DIFF(0, 5, 0x05, depth), \ ++ MCP356X_V_CHANNEL_DIFF(0, 6, 0x06, depth), \ ++ MCP356X_V_CHANNEL_DIFF(0, 7, 0x07, depth), \ ++ MCP356X_V_CHANNEL_DIFF(1, 4, 0x14, depth), \ ++ MCP356X_V_CHANNEL_DIFF(1, 5, 0x15, depth), \ ++ MCP356X_V_CHANNEL_DIFF(1, 6, 0x16, depth), \ ++ MCP356X_V_CHANNEL_DIFF(1, 7, 0x17, depth), \ ++ MCP356X_V_CHANNEL_DIFF(2, 4, 0x24, depth), \ ++ MCP356X_V_CHANNEL_DIFF(2, 5, 0x25, depth), \ ++ MCP356X_V_CHANNEL_DIFF(2, 6, 0x26, depth), \ ++ MCP356X_V_CHANNEL_DIFF(2, 7, 0x27, depth), \ ++ MCP356X_V_CHANNEL_DIFF(3, 4, 0x34, depth), \ ++ MCP356X_V_CHANNEL_DIFF(3, 5, 0x35, depth), \ ++ MCP356X_V_CHANNEL_DIFF(3, 6, 0x36, depth), \ ++ MCP356X_V_CHANNEL_DIFF(3, 7, 0x37, depth), \ ++ MCP356X_V_CHANNEL_DIFF(4, 5, 0x45, depth), \ ++ MCP356X_V_CHANNEL_DIFF(4, 6, 0x46, depth), \ ++ MCP356X_V_CHANNEL_DIFF(4, 7, 0x47, depth), \ ++ MCP356X_V_CHANNEL_DIFF(5, 6, 0x56, depth), \ ++ MCP356X_V_CHANNEL_DIFF(5, 7, 0x57, depth), \ ++ MCP356X_V_CHANNEL_DIFF(6, 7, 0x67, depth), \ ++ MCP356X_V_CHANNEL_DIFF(4, 0, 0x40, depth), \ ++ MCP356X_V_CHANNEL_DIFF(5, 0, 0x50, depth), \ ++ MCP356X_V_CHANNEL_DIFF(6, 0, 0x60, depth), \ ++ MCP356X_V_CHANNEL_DIFF(7, 0, 0x70, depth), \ ++ MCP356X_V_CHANNEL_DIFF(4, 1, 0x41, depth), \ ++ MCP356X_V_CHANNEL_DIFF(5, 1, 0x51, depth), \ ++ MCP356X_V_CHANNEL_DIFF(6, 1, 0x61, depth), \ ++ MCP356X_V_CHANNEL_DIFF(7, 1, 0x71, depth), \ ++ MCP356X_V_CHANNEL_DIFF(4, 2, 0x42, depth), \ ++ MCP356X_V_CHANNEL_DIFF(5, 2, 0x52, depth), \ ++ MCP356X_V_CHANNEL_DIFF(6, 2, 0x62, depth), \ ++ MCP356X_V_CHANNEL_DIFF(7, 2, 0x72, depth), \ ++ MCP356X_V_CHANNEL_DIFF(4, 3, 0x43, depth), \ ++ MCP356X_V_CHANNEL_DIFF(5, 3, 0x53, depth), \ ++ MCP356X_V_CHANNEL_DIFF(6, 3, 0x63, depth), \ ++ MCP356X_V_CHANNEL_DIFF(7, 3, 0x73, depth), \ ++ MCP356X_V_CHANNEL_DIFF(5, 4, 0x54, depth), \ ++ MCP356X_V_CHANNEL_DIFF(6, 4, 0x64, depth), \ ++ MCP356X_V_CHANNEL_DIFF(7, 4, 0x74, depth), \ ++ MCP356X_V_CHANNEL_DIFF(6, 5, 0x65, depth), \ ++ MCP356X_V_CHANNEL_DIFF(7, 5, 0x75, depth), \ ++ MCP356X_V_CHANNEL_DIFF(7, 6, 0x76, depth) \ ++} ++ ++static const struct iio_chan_spec mcp3461_channels[] = MCP3561_CHANNELS(16); ++static const struct iio_chan_spec mcp3462_channels[] = MCP3562_CHANNELS(16); ++static const struct iio_chan_spec mcp3464_channels[] = MCP3564_CHANNELS(16); ++static const struct iio_chan_spec mcp3561_channels[] = MCP3561_CHANNELS(24); ++static const struct iio_chan_spec mcp3562_channels[] = MCP3562_CHANNELS(24); ++static const struct iio_chan_spec mcp3564_channels[] = MCP3564_CHANNELS(24); ++ ++static const struct mcp356x_chip_info mcp356x_chip_infos_tbl[] = { ++ [mcp3461] = { ++ .channels = mcp3461_channels, ++ .num_channels = ARRAY_SIZE(mcp3461_channels), ++ .int_vref_uv = 0, ++ .has_vref = false ++ }, ++ [mcp3462] = { ++ .channels = mcp3462_channels, ++ .num_channels = ARRAY_SIZE(mcp3462_channels), ++ .int_vref_uv = 0, ++ .has_vref = false ++ }, ++ [mcp3464] = { ++ .channels = mcp3464_channels, ++ .num_channels = ARRAY_SIZE(mcp3464_channels), ++ .int_vref_uv = 0, ++ .has_vref = false ++ }, ++ [mcp3461r] = { ++ .channels = mcp3461_channels, ++ .num_channels = ARRAY_SIZE(mcp3461_channels), ++ .int_vref_uv = MCP356XR_INT_VREF_MV, ++ .has_vref = true ++ }, ++ [mcp3462r] = { ++ .channels = mcp3462_channels, ++ .num_channels = ARRAY_SIZE(mcp3462_channels), ++ .int_vref_uv = MCP356XR_INT_VREF_MV, ++ .has_vref = true ++ }, ++ [mcp3464r] = { ++ .channels = mcp3464_channels, ++ .num_channels = ARRAY_SIZE(mcp3464_channels), ++ .int_vref_uv = MCP356XR_INT_VREF_MV, ++ .has_vref = true ++ }, ++ [mcp3561] = { ++ .channels = mcp3561_channels, ++ .num_channels = ARRAY_SIZE(mcp3561_channels), ++ .int_vref_uv = 0, ++ .has_vref = false ++ }, ++ [mcp3562] = { ++ .channels = mcp3562_channels, ++ .num_channels = ARRAY_SIZE(mcp3562_channels), ++ .int_vref_uv = 0, ++ .has_vref = false ++ }, ++ [mcp3564] = { ++ .channels = mcp3564_channels, ++ .num_channels = ARRAY_SIZE(mcp3564_channels), ++ .int_vref_uv = 0, ++ .has_vref = false ++ }, ++ [mcp3561r] = { ++ .channels = mcp3561_channels, ++ .num_channels = ARRAY_SIZE(mcp3561_channels), ++ .int_vref_uv = MCP356XR_INT_VREF_MV, ++ .has_vref = true ++ }, ++ [mcp3562r] = { ++ .channels = mcp3562_channels, ++ .num_channels = ARRAY_SIZE(mcp3562_channels), ++ .int_vref_uv = MCP356XR_INT_VREF_MV, ++ .has_vref = true ++ }, ++ [mcp3564r] = { ++ .channels = mcp3564_channels, ++ .num_channels = ARRAY_SIZE(mcp3564_channels), ++ .int_vref_uv = MCP356XR_INT_VREF_MV, ++ .has_vref = true ++ }, ++}; ++ ++static int mcp356x_read_single_value(struct iio_dev *indio_dev, ++ struct iio_chan_spec const *channel, ++ int *val) ++{ ++ struct mcp356x_state *adc = iio_priv(indio_dev); ++ int ret, tmp, ret_read = 0; ++ ++ /* Configure MUX register with the requested channel */ ++ ret = mcp356x_write(adc, MCP356X_MUX, channel->address, 1); ++ if (ret) { ++ dev_err(&indio_dev->dev, "Failed to configure MUX register\n"); ++ return ret; ++ } ++ ++ /* ADC Conversion starts by writing ADC_MODE[1:0] = 11 to CONFIG0[1:0] = */ ++ ret = mcp356x_update(adc, MCP356X_CONFIG0, MCP356X_ADC_MODE_MASK, ++ MCP356X_ADC_CONVERSION_MODE, 1); ++ if (ret) { ++ dev_err(&indio_dev->dev, ++ "Failed to configure CONFIG0 register\n"); ++ return ret; ++ } ++ ++ /* ++ * Check if the conversion is ready. If not, wait a little bit, and ++ * in case of timeout exit with an error. ++ */ ++ ++ ret = read_poll_timeout(mcp356x_read, ret_read, ++ ret_read || !(tmp & MCP356X_DATA_READY_MASK), ++ 1000, MCP356X_DATA_READY_TIMEOUT_MS * 1000, true, ++ adc, MCP356X_IRQ, &tmp, 1); ++ ++ /* failed to read status register */ ++ if (ret_read) ++ return ret; ++ ++ if (ret) ++ return -ETIMEDOUT; ++ ++ if (tmp & MCP356X_DATA_READY_MASK) ++ /* failing to finish conversion */ ++ return -EBUSY; ++ ++ ret = mcp356x_read(adc, MCP356X_ADCDATA, &tmp, 4); ++ if (ret) ++ return ret; ++ ++ *val = tmp; ++ ++ return ret; ++} ++ ++static int mcp356x_read_avail(struct iio_dev *indio_dev, ++ struct iio_chan_spec const *channel, ++ const int **vals, int *type, ++ int *length, long mask) ++{ ++ switch (mask) { ++ case IIO_CHAN_INFO_OVERSAMPLING_RATIO: ++ *type = IIO_VAL_INT; ++ *vals = mcp356x_oversampling_avail; ++ *length = ARRAY_SIZE(mcp356x_oversampling_avail); ++ return IIO_AVAIL_LIST; ++ case IIO_CHAN_INFO_HARDWAREGAIN: ++ *type = IIO_VAL_FRACTIONAL; ++ *length = ARRAY_SIZE(mcp356x_hwgain_frac); ++ *vals = mcp356x_hwgain_frac; ++ return IIO_AVAIL_LIST; ++ case IIO_CHAN_INFO_CALIBBIAS: ++ *vals = mcp356x_calib_bias; ++ *type = IIO_VAL_INT; ++ return IIO_AVAIL_RANGE; ++ case IIO_CHAN_INFO_CALIBSCALE: ++ *vals = mcp356x_calib_scale; ++ *type = IIO_VAL_INT; ++ return IIO_AVAIL_RANGE; ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int mcp356x_read_raw(struct iio_dev *indio_dev, ++ struct iio_chan_spec const *channel, ++ int *val, int *val2, long mask) ++{ ++ struct mcp356x_state *adc = iio_priv(indio_dev); ++ int ret; ++ ++ mutex_lock(&adc->lock); ++ ++ switch (mask) { ++ case IIO_CHAN_INFO_RAW: ++ ret = mcp356x_read_single_value(indio_dev, channel, val); ++ if (ret) ++ ret = -EINVAL; ++ else ++ ret = IIO_VAL_INT; ++ break; ++ case IIO_CHAN_INFO_SCALE: ++ *val = adc->vref_mv; ++ *val2 = channel->scan_type.realbits - 1; ++ ret = IIO_VAL_FRACTIONAL_LOG2; ++ break; ++ case IIO_CHAN_INFO_OVERSAMPLING_RATIO: ++ *val = mcp356x_oversampling_avail[adc->oversampling]; ++ ret = IIO_VAL_INT; ++ break; ++ case IIO_CHAN_INFO_HARDWAREGAIN: ++ *val = mcp356x_hwgain_frac[2 * adc->hwgain]; ++ *val2 = mcp356x_hwgain_frac[(2 * adc->hwgain) + 1]; ++ ret = IIO_VAL_FRACTIONAL; ++ break; ++ case IIO_CHAN_INFO_CALIBBIAS: ++ *val = adc->calib_bias; ++ ret = IIO_VAL_INT; ++ break; ++ case IIO_CHAN_INFO_CALIBSCALE: ++ *val = adc->calib_scale; ++ ret = IIO_VAL_INT; ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ ++ mutex_unlock(&adc->lock); ++ ++ return ret; ++} ++ ++static int mcp356x_write_raw(struct iio_dev *indio_dev, ++ struct iio_chan_spec const *channel, int val, ++ int val2, long mask) ++{ ++ struct mcp356x_state *adc = iio_priv(indio_dev); ++ int tmp; ++ int ret = -EINVAL; ++ ++ mutex_lock(&adc->lock); ++ switch (mask) { ++ case IIO_CHAN_INFO_CALIBBIAS: ++ if (val < mcp356x_calib_bias[0] && val > mcp356x_calib_bias[2]) ++ goto out; ++ ++ adc->calib_bias = val; ++ ret = mcp356x_write(adc, MCP356X_OFFSETCAL, val, 3); ++ break; ++ case IIO_CHAN_INFO_CALIBSCALE: ++ if (val < mcp356x_calib_bias[0] && val > mcp356x_calib_bias[2]) ++ goto out; ++ ++ adc->calib_scale = val; ++ ret = mcp356x_write(adc, MCP356X_GAINCAL, val, 3); ++ break; ++ case IIO_CHAN_INFO_OVERSAMPLING_RATIO: ++ if (val < 0) ++ goto out; ++ ++ adc->oversampling = find_closest(val, mcp356x_oversampling_avail, ++ ARRAY_SIZE(mcp356x_oversampling_avail)); ++ ++ dev_dbg(&adc->spi->dev, ++ "IIO_CHAN_INFO_OVERSAMPLING_RATIO index %d\n", ++ adc->oversampling); ++ ++ ret = mcp356x_update(adc, MCP356X_CONFIG1, MCP356X_OVERSAMPLING_RATIO_MASK, ++ (adc->oversampling << MCP356X_OVERSAMPLING_RATIO_SHIFT), ++ 1); ++ if (ret) ++ dev_err(&indio_dev->dev, ++ "Failed to configure CONFIG1 register\n"); ++ ++ break; ++ case IIO_CHAN_INFO_HARDWAREGAIN: ++ /* ++ * calculate gain from read values. ++ * avoid using fractional numbers so ++ * multiply the value with 1000. In case of x1/3 gain ++ * the tmp will be 300 ++ */ ++ tmp = ((val * 1000000) + val2) / 1000; ++ if (tmp < 1 || tmp > MAX_HWGAIN) ++ goto out; ++ ++ adc->hwgain = find_closest(tmp, mcp356x_hwgain, ++ ARRAY_SIZE(mcp356x_hwgain)); ++ ++ dev_dbg(&adc->spi->dev, ++ "IIO_CHAN_INFO_HARDWAREGAIN Gain:%d; index %d\n", ++ tmp, adc->hwgain); ++ ++ /* Update GAIN in CONFIG2[5:3] -> GAIN[2:0]*/ ++ ret = mcp356x_update(adc, MCP356X_CONFIG2, MCP356X_HARDWARE_GAIN_MASK, ++ (adc->hwgain << MCP356X_HARDWARE_GAIN_SHIFT), 1); ++ if (ret) ++ dev_err(&indio_dev->dev, ++ "Failed to configure CONFIG0 register\n"); ++ break; ++ } ++ ++out: ++ mutex_unlock(&adc->lock); ++ ++ return ret; ++} ++ ++static int mcp356x_config(struct mcp356x_state *adc) ++{ ++ int ret = 0; ++ unsigned int tmp; ++ ++ dev_dbg(&adc->spi->dev, "%s: Start config...\n", __func__); ++ ++ /* ++ * The address is set on a per-device basis by fuses in the factory, ++ * configured on request. If not requested, the fuses are set for 0x1. ++ * The device address is part of the device markings to avoid ++ * potential confusion. This address is coded on two bits, so four possible ++ * addresses are available when multiple devices are present on the same ++ * SPI bus with only one Chip Select line for all devices. ++ */ ++ device_property_read_u32(&adc->spi->dev, "microchip,hw-device-address", &tmp); ++ ++ if (tmp > 3) { ++ dev_err_probe(&adc->spi->dev, tmp, ++ "invalid device address. Must be in range 0-3.\n"); ++ return -EINVAL; ++ } ++ ++ adc->dev_addr = 0xff & tmp; ++ ++ dev_dbg(&adc->spi->dev, "use device address %i\n", adc->dev_addr); ++ ++ ret = mcp356x_read(adc, MCP356X_RESERVED_E, &tmp, 2); ++ ++ if (ret == 0) { ++ switch (tmp & MCP356X_HW_ID_MASK) { ++ case MCP3461_HW_ID: ++ dev_dbg(&adc->spi->dev, "Found MCP3461 chip\n"); ++ break; ++ case MCP3462_HW_ID: ++ dev_dbg(&adc->spi->dev, "Found MCP3462 chip\n"); ++ break; ++ case MCP3464_HW_ID: ++ dev_dbg(&adc->spi->dev, "Found MCP3464 chip\n"); ++ break; ++ case MCP3561_HW_ID: ++ dev_dbg(&adc->spi->dev, "Found MCP3561 chip\n"); ++ break; ++ case MCP3562_HW_ID: ++ dev_dbg(&adc->spi->dev, "Found MCP3562 chip\n"); ++ break; ++ case MCP3564_HW_ID: ++ dev_dbg(&adc->spi->dev, "Found MCP3564 chip\n"); ++ break; ++ default: ++ dev_err_probe(&adc->spi->dev, tmp, ++ "Unknown chip found\n"); ++ return -EINVAL; ++ } ++ } else { ++ return ret; ++ } ++ ++ /* Command sequence that ensures a recovery with ++ * the desired settings in any cases of loss-of-power scenario. ++ */ ++ ++ /* Write LOCK register to 0xA5 (Write Access Password) ++ * Write access is allowed on the full register map. ++ */ ++ ret = mcp356x_write(adc, MCP356X_LOCK, 0x000000A5, 1); ++ if (ret) ++ return ret; ++ ++ /* Write IRQ register to 0x03 */ ++ /* IRQ --> IRQ Mode = Hi-Z IRQ Output --> (0b00000011). ++ * IRQ = 0x00000003 ++ */ ++ ret = mcp356x_write(adc, MCP356X_IRQ, 0x00000003, 1); ++ if (ret) ++ return ret; ++ ++ /* Device Full Reset Fast Command */ ++ ret = mcp356x_fast_cmd(adc, MCP356X_FULL_RESET_CMD); ++ ++ /* wait 1ms for the chip to restart after a full reset */ ++ mdelay(1); ++ ++ /* Reconfigure the ADC chip */ ++ ++ /* GAINCAL --> Disabled. ++ * Default value is GAINCAL = 0x00800000; which provides a gain of 1x ++ */ ++ ret = mcp356x_write(adc, MCP356X_GAINCAL, 0x00800000, 3); ++ if (ret) ++ return ret; ++ ++ adc->calib_scale = 0x00800000; ++ ++ /* OFFSETCAL --> 0 Counts of Offset Cancellation ++ * (Measured offset is negative). ++ * OFFSETCAL = 0x0 ++ */ ++ ret = mcp356x_write(adc, MCP356X_OFFSETCAL, 0x00000000, 3); ++ if (ret) ++ return ret; ++ ++ /* TIMER --> Disabled. ++ * TIMER = 0x00000000 ++ */ ++ ret = mcp356x_write(adc, MCP356X_TIMER, 0x00000000, 3); ++ if (ret) ++ return ret; ++ ++ /* SCAN --> Disabled. ++ * SCAN = 0x00000000 ++ */ ++ ret = mcp356x_write(adc, MCP356X_SCAN, 0x00000000, 3); ++ if (ret) ++ return ret; ++ ++ /* MUX --> VIN+ = CH0, VIN- = CH1 --> (0b00000001). ++ * MUX = 0x00000001 ++ */ ++ ret = mcp356x_write(adc, MCP356X_MUX, 0x00000001, 1); ++ if (ret) ++ return ret; ++ ++ /* IRQ --> IRQ Mode = Hi-Z IRQ Output --> (0b00000011). ++ * IRQ = 0x00000003 ++ */ ++ ret = mcp356x_write(adc, MCP356X_IRQ, 0x00000003, 1); ++ if (ret) ++ return ret; ++ ++ /* CONFIG3 ++ * Conv. Mod = One-Shot/Standby, ++ * FORMAT = 32-bit (right justified data): SGN extension + ADC data, ++ * CRC_FORMAT = 16b, CRC-COM = Disabled, ++ * OFFSETCAL = Enabled, GAINCAL = Enabled --> (10100011). ++ * CONFIG3 = 0x000000A3 ++ * ++ */ ++ ret = mcp356x_write(adc, MCP356X_CONFIG3, 0x000000A3, 1); ++ if (ret) ++ return ret; ++ ++ /* CONFIG2 --> BOOST = 1x, GAIN = 1x, AZ_MUX = 1 --> (0b10001101). ++ * CONFIG2 = 0x0000008D ++ */ ++ ret = mcp356x_write(adc, MCP356X_CONFIG2, 0x0000008D, 1); ++ if (ret) ++ return ret; ++ ++ adc->hwgain = 0x01; ++ adc->auto_zeroing_mux = true; ++ adc->auto_zeroing_ref = false; ++ adc->current_boost_mode = MCP356X_BOOST_CURRENT_x1_00; ++ ++ /* CONFIG1 --> AMCLK = MCLK, OSR = 98304 --> (0b00111100). ++ * CONFIG1 = 0x0000003C ++ */ ++ ret = mcp356x_write(adc, MCP356X_CONFIG1, 0x0000003C, 1); ++ if (ret) ++ return ret; ++ ++ adc->oversampling = 0x0F; ++ ++ if (!adc->vref) { ++ /* CONFIG0 --> VREF_SEL = Internal Voltage Reference 2.4v ++ * CLK_SEL = INTOSC w/o CLKOUT, CS_SEL = No Bias, ++ * ADC_MODE = Standby Mode --> (0b11100010). ++ * CONFIG0 = 0x000000E2 ++ */ ++ ret = mcp356x_write(adc, MCP356X_CONFIG0, 0x000000E2, 1); ++ ++ dev_dbg(&adc->spi->dev, "%s: Using internal Vref\n", ++ __func__); ++ adc->vref_mv = MCP356XR_INT_VREF_MV; ++ ++ } else { ++ /* CONFIG0 --> CLK_SEL = INTOSC w/o CLKOUT, CS_SEL = No Bias, ++ * ADC_MODE = Standby Mode --> (0b01100010). ++ * CONFIG0 = 0x000000E2 ++ */ ++ ret = mcp356x_write(adc, MCP356X_CONFIG0, 0x00000062, 1); ++ } ++ adc->current_bias_mode = MCP356X_CS_SEL_0_0_uA; ++ ++ return ret; ++} ++ ++static int mcp356x_probe(struct spi_device *spi) ++{ ++ int ret, device_index; ++ struct iio_dev *indio_dev; ++ struct mcp356x_state *adc; ++ ++ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); ++ if (!indio_dev) { ++ dev_err_probe(&indio_dev->dev, PTR_ERR(indio_dev), ++ "Can't allocate iio device\n"); ++ return -ENOMEM; ++ } ++ ++ adc = iio_priv(indio_dev); ++ adc->spi = spi; ++ ++ dev_dbg(&adc->spi->dev, "%s: probe(spi = 0x%p)\n", __func__, spi); ++ ++ adc->vref = devm_regulator_get_optional(&adc->spi->dev, "vref"); ++ if (IS_ERR(adc->vref)) { ++ if (PTR_ERR(adc->vref) == -ENODEV) { ++ adc->vref = NULL; ++ dev_dbg(&adc->spi->dev, "%s: Using internal Vref\n", ++ __func__); ++ } else { ++ dev_err_probe(&adc->spi->dev, PTR_ERR(adc->vref), ++ "failed to get regulator\n"); ++ return PTR_ERR(adc->vref); ++ } ++ } else { ++ ret = regulator_enable(adc->vref); ++ if (ret) ++ return ret; ++ ++ dev_dbg(&adc->spi->dev, "%s: Using External Vref\n", ++ __func__); ++ ++ ret = regulator_get_voltage(adc->vref); ++ if (ret < 0) { ++ dev_err_probe(&adc->spi->dev, ret, ++ "Failed to read vref regulator\n"); ++ goto error_disable_reg; ++ } ++ ++ adc->vref_mv = ret / 1000; ++ } ++ ++ spi_set_drvdata(spi, indio_dev); ++ device_index = spi_get_device_id(spi)->driver_data; ++ adc->chip_info = &mcp356x_chip_infos_tbl[device_index]; ++ ++ adc->mcp356x_info.read_raw = mcp356x_read_raw; ++ adc->mcp356x_info.write_raw = mcp356x_write_raw; ++ adc->mcp356x_info.read_avail = mcp356x_read_avail; ++ ++ ret = mcp356x_prep_custom_attributes(adc, indio_dev); ++ if (ret) { ++ dev_err_probe(&adc->spi->dev, ret, ++ "Can't configure custom attributes for MCP356X device\n"); ++ goto error_disable_reg; ++ } ++ ++ indio_dev->name = spi_get_device_id(spi)->name; ++ indio_dev->modes = INDIO_DIRECT_MODE; ++ indio_dev->info = &adc->mcp356x_info; ++ ++ indio_dev->channels = adc->chip_info->channels; ++ indio_dev->num_channels = adc->chip_info->num_channels; ++ indio_dev->masklength = adc->chip_info->num_channels - 1; ++ ++ /* initialize the chip access mutex */ ++ mutex_init(&adc->lock); ++ ++ /* Do any chip specific initialization, e.g: ++ * read/write some registers ++ * enable/disable certain channels ++ * change the sampling rate to the requested value ++ */ ++ ret = mcp356x_config(adc); ++ if (ret) { ++ dev_err_probe(&adc->spi->dev, ret, ++ "Can't configure MCP356X device\n"); ++ goto error_disable_reg; ++ } ++ ++ dev_dbg(&adc->spi->dev, "%s: Vref (mV): %d\n", __func__, adc->vref_mv); ++ ++ ret = devm_iio_device_register(&spi->dev, indio_dev); ++ if (ret) { ++ dev_err_probe(&adc->spi->dev, ret, ++ "Can't register IIO device\n"); ++ goto error_disable_reg; ++ } ++ ++ return 0; ++ ++error_disable_reg: ++ if (adc->vref) ++ regulator_disable(adc->vref); ++ ++ return ret; ++} ++ ++static void mcp356x_remove(struct spi_device *spi) ++{ ++ struct iio_dev *indio_dev = spi_get_drvdata(spi); ++ struct mcp356x_state *adc = iio_priv(indio_dev); ++ ++ if (adc->vref) ++ regulator_disable(adc->vref); ++} ++ ++static const struct of_device_id mcp356x_dt_ids[] = { ++ { .compatible = "microchip,mcp3461" }, ++ { .compatible = "microchip,mcp3462" }, ++ { .compatible = "microchip,mcp3464" }, ++ { .compatible = "microchip,mcp3461r" }, ++ { .compatible = "microchip,mcp3462r" }, ++ { .compatible = "microchip,mcp3464r" }, ++ { .compatible = "microchip,mcp3561" }, ++ { .compatible = "microchip,mcp3562" }, ++ { .compatible = "microchip,mcp3564" }, ++ { .compatible = "microchip,mcp3561r" }, ++ { .compatible = "microchip,mcp3562r" }, ++ { .compatible = "microchip,mcp3564r" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, mcp356x_dt_ids); ++ ++static const struct spi_device_id mcp356x_id[] = { ++ { "mcp3461", mcp3461 }, ++ { "mcp3462", mcp3462 }, ++ { "mcp3464", mcp3464 }, ++ { "mcp3461r", mcp3461r }, ++ { "mcp3462r", mcp3462r }, ++ { "mcp3464r", mcp3464r }, ++ { "mcp3561", mcp3561 }, ++ { "mcp3562", mcp3562 }, ++ { "mcp3564", mcp3564 }, ++ { "mcp3561r", mcp3561r }, ++ { "mcp3562r", mcp3562r }, ++ { "mcp3564r", mcp3564r }, ++ { } ++}; ++MODULE_DEVICE_TABLE(spi, mcp356x_id); ++ ++static struct spi_driver mcp356x_driver = { ++ .driver = { ++ .name = "mcp3564", ++ .of_match_table = mcp356x_dt_ids, ++ }, ++ .probe = mcp356x_probe, ++ .remove = mcp356x_remove, ++ .id_table = mcp356x_id, ++}; ++ ++module_spi_driver(mcp356x_driver); ++ ++MODULE_AUTHOR("Marius Cristea "); ++MODULE_DESCRIPTION("Microchip MCP346x/MCP346xR and MCP356x/MCP346xR ADCs"); ++MODULE_LICENSE("GPL v2"); ++MODULE_VERSION("0.1.2"); +-- +2.39.2 + diff --git a/patches/linux/0005-Microchip-QSPI-Add-regular-transfers.patch b/patches/linux/0005-Microchip-QSPI-Add-regular-transfers.patch new file mode 100644 index 0000000..b691b57 --- /dev/null +++ b/patches/linux/0005-Microchip-QSPI-Add-regular-transfers.patch @@ -0,0 +1,275 @@ +From 0eeed461b7b86e3b822984b1c266316ac70ccf69 Mon Sep 17 00:00:00 2001 +From: vauban353 +Date: Sun, 6 Aug 2023 09:37:40 +0100 +Subject: [PATCH 5/8] Microchip QSPI: Add regular transfers. + +--- + drivers/spi/spi-microchip-core-qspi.c | 226 +++++++++++++++++++++++++- + 1 file changed, 223 insertions(+), 3 deletions(-) + +diff --git a/drivers/spi/spi-microchip-core-qspi.c b/drivers/spi/spi-microchip-core-qspi.c +index 33c19b98b..5f1623ac4 100644 +--- a/drivers/spi/spi-microchip-core-qspi.c ++++ b/drivers/spi/spi-microchip-core-qspi.c +@@ -1,3 +1,4 @@ ++ + // SPDX-License-Identifier: (GPL-2.0) + /* + * Microchip coreQSPI QSPI controller driver +@@ -117,10 +118,10 @@ struct mchp_coreqspi { + struct completion data_completion; + struct mutex op_lock; /* lock access to the device */ + u8 *txbuf; +- u8 *rxbuf; ++ volatile u8 *rxbuf; + int irq; +- int tx_len; +- int rx_len; ++ volatile int tx_len; ++ volatile int rx_len; + }; + + static int mchp_coreqspi_set_mode(struct mchp_coreqspi *qspi, const struct spi_mem_op *op) +@@ -222,6 +223,68 @@ static inline void mchp_coreqspi_write_op(struct mchp_coreqspi *qspi, bool word) + } + } + ++static inline void mchp_coreqspi_write_read_op(struct mchp_coreqspi *qspi, bool last) ++{ ++ u32 frames, control, data; ++ qspi->rx_len = qspi->tx_len; ++ ++ control = readl_relaxed(qspi->regs + REG_CONTROL); ++ control |= CONTROL_FLAGSX4; ++ writel_relaxed(control, qspi->regs + REG_CONTROL); ++ ++ while (qspi->tx_len >= 4) { ++ while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_TXFIFOFULL) ++ ; ++ ++ data = *(u32 *)qspi->txbuf; ++ qspi->txbuf += 4; ++ qspi->tx_len -= 4; ++ writel_relaxed(data, qspi->regs + REG_X4_TX_DATA); ++ ++ if (qspi->rx_len >= 8) { ++ if (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_RXAVAILABLE) { ++ data = readl_relaxed(qspi->regs + REG_X4_RX_DATA); ++ *(u32 *)qspi->rxbuf = data; ++ qspi->rxbuf += 4; ++ qspi->rx_len -= 4; ++ } ++ } ++ } ++ ++ if (!last ) { ++ while (qspi->rx_len >= 4) { ++ while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_RXFIFOEMPTY) ++ ; ++ data = readl_relaxed(qspi->regs + REG_X4_RX_DATA); ++ *(u32 *)qspi->rxbuf = data; ++ qspi->rxbuf += 4; ++ qspi->rx_len -= 4; ++ } ++ } ++ ++ if (qspi->tx_len) { ++ control &= ~CONTROL_FLAGSX4; ++ writel_relaxed(control, qspi->regs + REG_CONTROL); ++ ++ ++ while (qspi->tx_len--) { ++ while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_TXFIFOFULL) ++ ; ++ data = *qspi->txbuf++; ++ writel_relaxed(data, qspi->regs + REG_TX_DATA); ++ } ++ if (!last) { ++ while (qspi->rx_len--) { ++ while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_RXFIFOEMPTY) ++ ; ++ data = readl_relaxed(qspi->regs + REG_RX_DATA); ++ *qspi->rxbuf++ = (data & 0xFF); ++ } ++ } ++ } ++} ++ ++ + static void mchp_coreqspi_enable_ints(struct mchp_coreqspi *qspi) + { + u32 mask = IEN_TXDONE | +@@ -497,6 +560,160 @@ static const struct spi_controller_mem_ops mchp_coreqspi_mem_ops = { + .exec_op = mchp_coreqspi_exec_op, + }; + ++static int mchp_coreqspi_transfer_one_message(struct spi_controller *ctlr, ++ struct spi_message *m) ++{ ++ struct mchp_coreqspi *qspi = spi_controller_get_devdata(ctlr); ++ struct spi_transfer *t = NULL; ++ u32 control, frames, status; ++ u32 total_bytes, cmd_bytes = 0, idle_cycles = 0; ++ int ret; ++ bool quad = false, dual = false; ++ bool keep_cs = false; ++ ++ mutex_lock(&qspi->op_lock); ++ ret = readl_poll_timeout(qspi->regs + REG_STATUS, status, ++ (status & STATUS_READY), 0, ++ TIMEOUT_MS); ++ if (ret) { ++ dev_err(&ctlr->dev, ++ "Timeout waiting on QSPI ready.\n"); ++ return -ETIMEDOUT; ++ } ++ ++ ret = mchp_coreqspi_setup_clock(qspi, m->spi); ++ if (ret) ++ goto error; ++ ++ if (m->spi->cs_gpiod) { ++ if (m->spi->mode & SPI_CS_HIGH) { ++ gpiod_set_value(m->spi->cs_gpiod, 0); ++ } else { ++ gpiod_set_value(m->spi->cs_gpiod, 1); ++ } ++ } ++ ++ control = readl_relaxed(qspi->regs + REG_CONTROL); ++ control &= ~(CONTROL_MODE12_MASK | ++ CONTROL_MODE0); ++ writel_relaxed(control, qspi->regs + REG_CONTROL); ++ ++ reinit_completion(&qspi->data_completion); ++ ++ /* Check the total bytes and command bytes */ ++ list_for_each_entry(t, &m->transfers, transfer_list) { ++ total_bytes += t->len; ++ if ((!cmd_bytes) && !(t->tx_buf && t->rx_buf)) ++ cmd_bytes = t->len; ++ if (!t->rx_buf) ++ cmd_bytes = total_bytes; ++ if (t->tx_nbits == SPI_NBITS_QUAD || t->rx_nbits == SPI_NBITS_QUAD) ++ quad = true; ++ else if (t->tx_nbits == SPI_NBITS_DUAL || t->rx_nbits == SPI_NBITS_DUAL) ++ dual = true; ++ } ++ ++ control = readl_relaxed(qspi->regs + REG_CONTROL); ++ if (quad) { ++ control |= (CONTROL_MODE0 | CONTROL_MODE12_EX_RW); ++ } else if (dual) { ++ control &= ~CONTROL_MODE0; ++ control |= CONTROL_MODE12_FULL; ++ } else { ++ control &= ~(CONTROL_MODE12_MASK | ++ CONTROL_MODE0); ++ } ++ ++ writel_relaxed(control, qspi->regs + REG_CONTROL); ++ frames = total_bytes & BYTESUPPER_MASK; ++ writel_relaxed(frames, qspi->regs + REG_FRAMESUP); ++ frames = total_bytes & BYTESLOWER_MASK; ++ frames |= cmd_bytes << FRAMES_CMDBYTES_SHIFT; ++ frames |= idle_cycles << FRAMES_IDLE_SHIFT; ++ control = readl_relaxed(qspi->regs + REG_CONTROL); ++ if (control & CONTROL_MODE12_MASK) ++ frames |= (1 << FRAMES_SHIFT); ++ ++ frames |= FRAMES_FLAGWORD; ++ writel_relaxed(frames, qspi->regs + REG_FRAMES); ++ ++ list_for_each_entry(t, &m->transfers, transfer_list) { ++ ++ if ((t->tx_buf) && (t->rx_buf)){ ++ bool last = false; ++ qspi->txbuf = (u8 *)t->tx_buf; ++ qspi->rxbuf = (u8 *)t->rx_buf; ++ qspi->tx_len = t->len; ++ if (list_is_last(&t->transfer_list, &m->transfers)) ++ last = true; ++ mchp_coreqspi_write_read_op(qspi, last); ++ } else if (t->tx_buf) { ++ qspi->txbuf = (u8 *)t->tx_buf; ++ qspi->tx_len = t->len; ++ mchp_coreqspi_write_op(qspi, true); ++ } else { ++ qspi->rxbuf = (u8 *)t->rx_buf; ++ qspi->rx_len = t->len; ++ } ++ ++ if (t->cs_change) { ++ if (list_is_last(&t->transfer_list, ++ &m->transfers)) { ++ keep_cs = true; ++ } else { ++ if (!t->cs_off) { ++// gpiod_set_value(m->spi->cs_gpiod, 0); ++ if (m->spi->mode & SPI_CS_HIGH) { ++ gpiod_set_value(m->spi->cs_gpiod, 0); ++ } else { ++ gpiod_set_value(m->spi->cs_gpiod, 1); ++ } ++ } ++// _spi_transfer_cs_change_delay(m, t); ++ if (!list_next_entry(t, transfer_list)->cs_off) { ++// spi_set_cs(m->spi, true, false); ++ if (m->spi->mode & SPI_CS_HIGH) { ++// gpiod_set_value(m->spi->cs_gpiod, 0); ++ gpiod_set_value(m->spi->cs_gpiod, 1); ++ } else { ++// gpiod_set_value(m->spi->cs_gpiod, 1); ++ gpiod_set_value(m->spi->cs_gpiod, 0); ++ } ++// gpiod_set_value(m->spi->cs_gpiod, 1); ++ } ++ } ++ } else if (!list_is_last(&t->transfer_list, &m->transfers) && ++ t->cs_off != list_next_entry(t, transfer_list)->cs_off) { ++// spi_set_cs(m->spi, t->cs_off, false); ++ if (m->spi->mode & SPI_CS_HIGH) { ++ gpiod_set_value(m->spi->cs_gpiod, !t->cs_off); ++ } else { ++ gpiod_set_value(m->spi->cs_gpiod, !t->cs_off); ++ } ++ } ++ ++ ++ ++ } ++ ++ mchp_coreqspi_enable_ints(qspi); ++ ++ if (!wait_for_completion_timeout(&qspi->data_completion, msecs_to_jiffies(1000))) ++ ret = -ETIMEDOUT; ++ ++ m->actual_length = total_bytes; ++ ++error: ++ if (ret != 0 || !keep_cs) ++ gpiod_set_value(m->spi->cs_gpiod, 0); ++ ++ m->status = ret; ++ spi_finalize_current_message(ctlr); ++ mutex_unlock(&qspi->op_lock); ++ mchp_coreqspi_disable_ints(qspi); ++ return ret; ++} ++ + static int mchp_coreqspi_probe(struct platform_device *pdev) + { + struct spi_controller *ctlr; +@@ -550,6 +767,9 @@ static int mchp_coreqspi_probe(struct platform_device *pdev) + ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD | + SPI_TX_DUAL | SPI_TX_QUAD; + ctlr->dev.of_node = np; ++ ctlr->transfer_one_message = mchp_coreqspi_transfer_one_message; ++ ctlr->num_chipselect = 2; ++ ctlr->use_gpio_descriptors = true; + + ret = devm_spi_register_controller(&pdev->dev, ctlr); + if (ret) { +-- +2.39.2 + diff --git a/patches/linux/0006-BeagleV-Fire-Add-printk-to-IM219-driver-for-board-te.patch b/patches/linux/0006-BeagleV-Fire-Add-printk-to-IM219-driver-for-board-te.patch new file mode 100644 index 0000000..b2aad86 --- /dev/null +++ b/patches/linux/0006-BeagleV-Fire-Add-printk-to-IM219-driver-for-board-te.patch @@ -0,0 +1,45 @@ +From 747c6d5984cccf7c3e48c2cdb4dd1d626889096e Mon Sep 17 00:00:00 2001 +From: vauban353 +Date: Sat, 12 Aug 2023 18:14:01 +0100 +Subject: [PATCH 6/8] BeagleV-Fire: Add printk to IM219 driver for board tests. + +--- + drivers/media/i2c/imx219.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c +index 7a14688f8..effb399b1 100644 +--- a/drivers/media/i2c/imx219.c ++++ b/drivers/media/i2c/imx219.c +@@ -1181,6 +1181,9 @@ static int imx219_identify_module(struct imx219 *imx219) + int ret; + u32 val; + ++ printk(KERN_INFO "imx219_identify_module()\n"); ++ ++ + ret = imx219_read_reg(imx219, IMX219_REG_CHIP_ID, + IMX219_REG_VALUE_16BIT, &val); + if (ret) { +@@ -1195,6 +1198,9 @@ static int imx219_identify_module(struct imx219 *imx219) + return -EIO; + } + ++ printk(KERN_INFO "imx219_identify_module() - Success\n"); ++ ++ + return 0; + } + +@@ -1402,6 +1408,8 @@ static int imx219_probe(struct i2c_client *client) + struct imx219 *imx219; + int ret; + ++ printk(KERN_INFO "imx219_probe()\n"); ++ + imx219 = devm_kzalloc(&client->dev, sizeof(*imx219), GFP_KERNEL); + if (!imx219) + return -ENOMEM; +-- +2.39.2 + diff --git a/patches/linux/0007-MMC-SPI-Hack-to-support-non-DMA-capable-SPI-ctrl.patch b/patches/linux/0007-MMC-SPI-Hack-to-support-non-DMA-capable-SPI-ctrl.patch new file mode 100644 index 0000000..7953390 --- /dev/null +++ b/patches/linux/0007-MMC-SPI-Hack-to-support-non-DMA-capable-SPI-ctrl.patch @@ -0,0 +1,66 @@ +From e9aea028119afd0858e77989dd1a4ecc21d30dc1 Mon Sep 17 00:00:00 2001 +From: vauban353 +Date: Sun, 6 Aug 2023 11:13:27 +0100 +Subject: [PATCH 7/8] MMC SPI: Hack to support non DMA capable SPI ctrl. + +--- + drivers/mmc/host/mmc_spi.c | 6 +++++- + drivers/spi/internals.h | 3 +++ + drivers/spi/spi.c | 3 +++ + 3 files changed, 11 insertions(+), 1 deletion(-) + +diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c +index cc333ad67..fb45c7fa4 100644 +--- a/drivers/mmc/host/mmc_spi.c ++++ b/drivers/mmc/host/mmc_spi.c +@@ -29,6 +29,9 @@ + + #include + ++//: ++#undef CONFIG_HAS_DMA ++//#define DEBUG 1 + + /* NOTES: + * +@@ -1375,7 +1378,8 @@ static int mmc_spi_probe(struct spi_device *spi) + * that's the only reason not to use a few MHz for f_min (until + * the upper layer reads the target frequency from the CSD). + */ +- mmc->f_min = 400000; ++// mmc->f_min = 400000; ++ mmc->f_min = 5000000; + mmc->f_max = spi->max_speed_hz; + + host = mmc_priv(mmc); +diff --git a/drivers/spi/internals.h b/drivers/spi/internals.h +index 4a28a8395..c49350240 100644 +--- a/drivers/spi/internals.h ++++ b/drivers/spi/internals.h +@@ -12,6 +12,9 @@ + #ifndef __LINUX_SPI_INTERNALS_H + #define __LINUX_SPI_INTERNALS_H + ++//: ++#undef CONFIG_HAS_DMA ++ + #include + #include + #include +diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c +index a221478e5..e198049cc 100644 +--- a/drivers/spi/spi.c ++++ b/drivers/spi/spi.c +@@ -42,6 +42,9 @@ EXPORT_TRACEPOINT_SYMBOL(spi_transfer_stop); + + #include "internals.h" + ++//: ++#undef CONFIG_HAS_DMA ++ + static DEFINE_IDR(spi_master_idr); + + static void spidev_release(struct device *dev) +-- +2.39.2 + diff --git a/patches/linux/0008-Add-wireless-regdb-regulatory-database-file.patch b/patches/linux/0008-Add-wireless-regdb-regulatory-database-file.patch new file mode 100644 index 0000000..cd33500 --- /dev/null +++ b/patches/linux/0008-Add-wireless-regdb-regulatory-database-file.patch @@ -0,0 +1,100 @@ +From 17558b89548b694599af7d6144426f30fe83e2c7 Mon Sep 17 00:00:00 2001 +From: Robert Nelson +Date: Sun, 10 Sep 2023 19:07:40 -0500 +Subject: [PATCH 8/8] Add wireless-regdb regulatory database file + +https://git.kernel.org/pub/scm/linux/kernel/git/sforshee/wireless-regdb.git/commit/?id=991b1ef696b7a034a5bf001cf31ab7735888c6e1 +Signed-off-by: Robert Nelson +--- + firmware/regulatory.db | Bin 0 -> 4896 bytes + firmware/regulatory.db.p7s | Bin 0 -> 1182 bytes + 2 files changed, 0 insertions(+), 0 deletions(-) + create mode 100644 firmware/regulatory.db + create mode 100644 firmware/regulatory.db.p7s + +diff --git a/firmware/regulatory.db b/firmware/regulatory.db +new file mode 100644 +index 0000000000000000000000000000000000000000..ebdc7e354e9c2317f9b862300bdc9381def4f0e0 +GIT binary patch +literal 4896 +zcmZvfe~eUD702(r=j>0)_OTf^7As;4Bqd5#YLK8s-kY7BnVnyc`L#P^e~FO@Dt6lR +zM?_azsUoYjXbH8g6tS%|!Glth5V7H5c)=ZkOKt)i?kG;WX`FJiaMsP??QWj9Nc$yp-7+-Y +z3jE3)r@cDf?YVHt^Kg$hg3oygXnLdA@RIa5g`e`WIO*lk_42sGE8w$U5tn!+-0PKT +zzeb-rz7%)yL-A4E7*D~)cp4k=EbfTsp%pLCx`;Q&D>xLNz`gMrG~;z>j!=$~41IFA +zW~4x$IzEz!4rvQOZ49gbPUz-b#+(P00k_mmI~ZWC|O}EdBNA +zQ-o%+jBAn=T$QZhkyHk6NaeASD#9zNGPG}9w^O`7!6S@hF+Je)4#bLkRXO4BUH +za@ZIv;I1(rGnoWFlu6-aCXd@QJ|;3noSzxTt(h9`&D7yU*26|Nj@z?oxRlM{&Dj!8 +zW=SJUIkHt~=3LyGi^JL62z?Utp;~h(baNTFl*{AZTmcW|eB$-AU&P5A#pWusUxm{- +zI*;50HgYw5ET6<@@@aJQS!m`9a53-Wj(i=b3pt$hUHqU=XY7-DF2uXnMpn82xG0h&eXxmZD29LGk<#mN%Qq?E+L63x0qXHufMm8h;# +z6>Fs$rps~MUmn59auPR}Y3AiTZY>w#WVuZ1aa>%kK!lP=2`;NwK7iCUA(_eJ=5J4*DIkutOSmM=pO44-PcQE +zDR4Akr?+QT%zJRP7`TDFduAOmZ(7WM=?*dfckAYu)&qSrLag0p>{rO2`RaX|d9CB$ +zw@&ktCxuu#P4Q0QduDd79*(y; +zwEx)`BEQ(w%XVh2pX;+Zj1TQ$e9+&!iI;x)fQctP$voqTWdp-nzpsN?f9!9EM0N?; +zDW5}}-#sh}#}8?ab??{gzh_p+)!Sl&6wmg6g%HoTV?v)$!}b{rbVHvG4s=?!cFdu( +z=*%@3-HUaw)*0;orX8p6gr(y+bY?cUH8#D$JvytJ(!R2SbHK4b11sV-ixFZ%({Mw#wJ#7;RPOf|Nd=76@HIA-aG=N9^N +z-+Yc1bB(c~ckVgYM)#bURTt~pd37GE4xJ~;dKO5}sg+ybDb4%!otmr7u#Rch7Wj0q +z_KEcl8(rfwS8KpSVeNrNjP9zH*W`)r&Gi>*hW&(^^gLM_juZA4+Iwiu^z4Ms2hXc? +z@35%cCG_)y^bWO|`_lZFqwT$Yw`d;!M9TOFc4mH_x}zNHU^8!;3u}fkVGRMt!WshR +znAlKIm$m00^=j!*T!hUTMECUj#fou|&Oq~)SzUj-7Q=J37&i5XnqdshxOCJP<<_j3 +zNx+r!^q%Hti)T#s>MGyl;GE3c%{xffO+3EXyvJ>(nVP-=JF%{TzuQ=YV|ea-ha(*1 +z9H$!X8Z-ybr}ig1KPOl5xv^f@Q&3}@Ej?XRl+9ei8jMCvvBs%WVng2nu2K8&_e6#6 +z5}#xEyz*Q)k7hb&oB4gsT#G-f`{egK$4;C*ul@Nq12fw>p8aj+@60Hh__lwb7qXzf +zBL38q&HOysEUx-%lYA7y=y>pwjZ*1#IHM|q=rj&P2_r)So|?88DVzD%=)^O`TlwrN(~UWDnc +zMfvJ|5mp@>>W6yto0|Rcqqof%edlKf?@WB +zww>Z>4X=M(>q8%P{(|_2oG;%c_sEykAy^B~lIA=q#LvZx;y2>BT&@@G{| +z9`$$FXnb`2x2pfb)9^g}1iENGC*)@JN2tT@K;xubO7)CVKi|-rCuKqnDG&N#H}rxK +zr{#O)LF#C&dKEqk-_tWWEtk{IE$RyQOga7}H>x%20s#4+l^fK{q}@gPP5FM+r84RY +z=hDox+@j-pH0RZIYNPrOokiHwYw|`lUu}hjP}MQ7$*NkWHiLrufnzR{eUVzBeh*{t +zg&@ymxkVmPPr*uJuIIA+nmj{xu7TIDs%aQ&`~Gl_sosCfd*osDRai!Lau+T7%YC=x +z0(p&mNFGy%;Z}H1pIuAOW&#Gtr$_tL)pE5;{T2rG`Eu?S)iMn$b#7jVbpf4yOWrBz +z8x&B-QeIdQ_%Z|`#- +z-wm{R{Y20YulK?7c1^^vCh+@?oK<^*S|3+mf$Q}7w&dgBwf&!zXVjFQc}vndn0ZU` +zjK2<}QgROUqL~H#9PE-swNMpc +jkn<>J>#Vh%{SRw@IvcW^8c9=jktWSr_Q@6UU$E$Y57IP8L6oT +z3gWzm=7t6aW`>p~2F9jQ68uJnhUNyAhDJ~U?An?bm5_}Gx`?@nk)HwR0xqT|Mn;CM +z4v*((v{*8Kvb>(A<7gqerfz9-p=#ZPnvA>o_BXFbTFkG>3OdBBrCXQwMQ*cDfkO0^ +z=|;AN-PY0^;v(6JN5Z?OiOKv`xysG?GefZD_o+ahi>{Kl1YEjq{wrJgH$hi|TVv`b +z-B%qmc3cco{17bvktzSB+w95l8WZ@}nElF7E({O*cINW^TCJPX#jdY+skgqbs$Y7# +z;kbd>rQmmO*2q0K{AjjlPV%wT=z7)CjD@c**D=J*io2m8VN}kpDU>E>R{VTAi=+9* +zjYZmD7HTTgOsnM9zHsB-i5&aMzmLAF2oe;PuKM;UW9IYUZelHuk1u@o;F8AX=M0{IyM6Wwa{mIf7OHQT7tSL%AoG*M>*R=9_*YmGe6b-ztep`IAGVBw- +z!1vkYwQ2f*qmwdu%Ti1qxCzl8GMutX;+>(KaknSwU#Nh +zc=g}&Q_k>OCH%D!(HApbsVILsU}Hb?y9H@0#VSP%n;5qNQ^_V^qBc;#nzqnVFjAsl +zkZB+RPtu&shRAuqKn5YhYRE6nYh-L-X<%q*X=-L_9%Z19kYzDcVo~7o+FSLAmwk6Z +zil+IQO`29k%L3dD-YtpB>}ZOdACa7a9Cg6N&C