From 11aed1f766ae75d0695a70786e3e1c80ca155f7b Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Mon, 14 Aug 2023 21:05:36 -0500 Subject: [PATCH] linux: build: mpfs-beaglev-fire.dtb Signed-off-by: Robert Nelson --- .gitignore | 1 + 04_build_linux.sh | 21 +- ...001-ADC-Add-Microchip-MCP356X-driver.patch | 1450 +++++++++++++++++ .../0001-Add-BeagleV-Fire-device-tree.patch | 563 +++++++ ...-printk-to-IM219-driver-for-board-te.patch | 45 + ...1-GPIO-Add-Microchip-CoreGPIO-driver.patch | 371 +++++ ...-to-support-non-DMA-capable-SPI-ctrl.patch | 66 + ...Microchip-QSPI-Add-regular-transfers.patch | 275 ++++ ...e-controller-and-bridge-base-address.patch | 27 + patches/linux/Makefile | 9 + patches/linux/mpfs_defconfig | 2 + 11 files changed, 2827 insertions(+), 3 deletions(-) create mode 100644 patches/linux/0001-ADC-Add-Microchip-MCP356X-driver.patch create mode 100644 patches/linux/0001-Add-BeagleV-Fire-device-tree.patch create mode 100644 patches/linux/0001-BeagleV-Fire-Add-printk-to-IM219-driver-for-board-te.patch create mode 100644 patches/linux/0001-GPIO-Add-Microchip-CoreGPIO-driver.patch create mode 100644 patches/linux/0001-MMC-SPI-Hack-to-support-non-DMA-capable-SPI-ctrl.patch create mode 100644 patches/linux/0001-Microchip-QSPI-Add-regular-transfers.patch create mode 100644 patches/linux/0001-PCIe-Change-controller-and-bridge-base-address.patch create mode 100644 patches/linux/Makefile diff --git a/.gitignore b/.gitignore index 3457b19..26cd0b5 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ deploy/u-boot.bin deploy/.modules deploy/*-modules.tar.gz deploy/Image +deploy/*.dtb mirror/ riscv-toolchain/ hart-software-services/ diff --git a/04_build_linux.sh b/04_build_linux.sh index c28dcb1..25cc702 100755 --- a/04_build_linux.sh +++ b/04_build_linux.sh @@ -6,9 +6,25 @@ CC=${CC:-"${wdir}/riscv-toolchain/bin/riscv64-linux-"} cd ./linux/ +if [ ! -f ./.patched ] ; 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 + touch .patched +fi + +cp -v ../patches/linux/Makefile arch/riscv/boot/dts/microchip/ + make ARCH=riscv CROSS_COMPILE=${CC} clean make ARCH=riscv CROSS_COMPILE=${CC} mpfs_defconfig -#./scripts/config --enable CONFIG_OF_OVERLAY + +./scripts/config --enable CONFIG_OF_OVERLAY +./scripts/config --enable CONFIG_GPIO_MICROCHIP_CORE +./scripts/config --enable CONFIG_MCP356X echo "make -j${CORES} ARCH=riscv CROSS_COMPILE=${CC} Image modules dtbs" make -j${CORES} ARCH=riscv CROSS_COMPILE=${CC} Image modules dtbs @@ -27,10 +43,9 @@ tar --create --gzip --file "../${KERNEL_UTS}-modules.tar.gz" ./* cd "${wdir}/linux/" || exit rm -rf "${wdir}/deploy/tmp" || true -#cp -v ./arch/riscv/boot/dts/thead/th1520-beaglev-ahead.dts ../BeagleBoard-DeviceTrees/src/thead/ cp -v ./.config ../patches/linux/mpfs_defconfig cp -v ./arch/riscv/boot/Image ../deploy/ -#cp -v ./arch/riscv/boot/dts/thead/*.dtb ../deploy/ +cp -v ./arch/riscv/boot/dts/microchip/mpfs-beaglev-fire.dtb ../deploy/ cd ../ diff --git a/patches/linux/0001-ADC-Add-Microchip-MCP356X-driver.patch b/patches/linux/0001-ADC-Add-Microchip-MCP356X-driver.patch new file mode 100644 index 0000000..65e59bb --- /dev/null +++ b/patches/linux/0001-ADC-Add-Microchip-MCP356X-driver.patch @@ -0,0 +1,1450 @@ +From 51e3b82d70fd95e72035cfc393b50a9b5410cd35 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. + +--- + 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 48763d6a89a4..6c6b62a7f3e6 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 d5ca372050cb..5b3f027ac74c 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 000000000000..22d59413d342 +--- /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.25.1 + diff --git a/patches/linux/0001-Add-BeagleV-Fire-device-tree.patch b/patches/linux/0001-Add-BeagleV-Fire-device-tree.patch new file mode 100644 index 0000000..17b26a7 --- /dev/null +++ b/patches/linux/0001-Add-BeagleV-Fire-device-tree.patch @@ -0,0 +1,563 @@ +From 97e16ad53f7ac47279fbc559df6dd5ebb19c002b Mon Sep 17 00:00:00 2001 +From: vauban353 +Date: Sun, 6 Aug 2023 09:42:33 +0100 +Subject: [PATCH] Add: BeagleV-Fire device tree. + +--- + .../microchip/mpfs-beaglev-fire-fabric.dtsi | 156 +++++++ + .../boot/dts/microchip/mpfs-beaglev-fire.dts | 380 ++++++++++++++++++ + 2 files changed, 536 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 +--- /dev/null ++++ b/arch/riscv/boot/dts/microchip/mpfs-beaglev-fire-fabric.dtsi +@@ -0,0 +1,156 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* Copyright (c) 2020-2021 Microchip Technology Inc */ ++ ++#include "dt-bindings/mailbox/miv-ihc.h" ++ ++/ { ++ compatible = "microchip,mpfs-icicle-reference-rtlv2210"; ++ ++ fabric-bus@40000000 { ++ compatible = "simple-bus"; ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges = <0x0 0x40000000 0x0 0x40000000 0x0 0x20000000>, /* FIC3-FAB */ ++ <0x0 0x60000000 0x0 0x60000000 0x0 0x20000000>, /* FIC0, LO */ ++ <0x0 0xe0000000 0x0 0xe0000000 0x0 0x20000000>, /* FIC1, LO */ ++ <0x20 0x0 0x20 0x0 0x10 0x0>, /* FIC0,HI */ ++ <0x30 0x0 0x30 0x0 0x10 0x0>; /* FIC1,HI */ ++ ++// core_pwm0: pwm@40000000 { ++// compatible = "microchip,corepwm-rtl-v4"; ++// reg = <0x0 0x40000000 0x0 0xF0>; ++// microchip,sync-update-mask = /bits/ 32 <0>; ++// #pwm-cells = <2>; ++// clocks = <&fabric_clk3>; ++// status = "disabled"; ++// }; ++ ++ fabric_clk3: fabric-clk3 { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <50000000>; ++ }; ++ ++ fabric_clk1: fabric-clk1 { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <125000000>; ++ }; ++ ++ cape_gpios_p8: gpio@41100000 { ++ compatible = "microchip,core-gpio"; ++ reg = <0x0 0x41100000 0x0 0x1000>; ++ clocks = <&fabric_clk3>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ ngpios=<16>; ++ gpio-line-names = "P8_PIN31", "P8_PIN32", "P8_PIN33", "P8_PIN34", ++ "P8_PIN35", "P8_PIN36", "P8_PIN37", "P8_PIN38", ++ "P8_PIN39", "P8_PIN40", "P8_PIN41", "P8_PIN42", ++ "P8_PIN43", "P8_PIN44", "P8_PIN45", "P8_PIN46"; ++ }; ++ ++ cape_gpios_p9: gpio@41200000 { ++ compatible = "microchip,core-gpio"; ++ reg = <0x0 0x41200000 0x0 0x1000>; ++ clocks = <&fabric_clk3>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ ngpios=<21>; ++ gpio-line-names = "P9_PIN11", "P9_PIN12", "P9_PIN13", "P9_PIN14", ++ "P9_PIN15", "P9_PIN16", "P9_PIN17", "P9_PIN18", ++ "P9_PIN21", "P9_PIN22", "P9_PIN23", "P9_PIN24", ++ "P9_PIN25", "P9_PIN26", "P9_PIN27", "P9_PIN28", ++ "P9_PIN29", "P9_PIN31", "P9_PIN41", "P9_PIN42"; ++ }; ++ ++ hsi_gpios: gpio@44000000 { ++ compatible = "microchip,core-gpio"; ++ reg = <0x0 0x44000000 0x0 0x1000>; ++ clocks = <&fabric_clk3>; ++ gpio-controller; ++ #gpio-cells = <2>; ++ ngpios=<20>; ++ gpio-line-names = "B0_HSIO70N", "B0_HSIO71N", "B0_HSIO83N", "B0_HSIO73N_C2P_CLKN", ++ "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", ++ "XCVR_0B_REF_CLK_PLL_LOCK", "XCVR_0C_REF_CLK_PLL_LOCK", "B0_HSIO81N"; ++ }; ++ }; ++ ++ ihc: mailbox { ++ compatible = "microchip,miv-ihc"; ++ interrupt-parent = <&plic>; ++ interrupts = ; ++ microchip,miv-ihc-remote-context-id = ; ++ #mbox-cells = <1>; ++ status = "disabled"; ++ }; ++ ++ fabric-pcie-bus@3000000000 { ++ compatible = "simple-bus"; ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges = <0x0 0x40000000 0x0 0x40000000 0x0 0x20000000>, ++ <0x30 0x0 0x30 0x0 0x10 0x0>; ++ dma-ranges = <0x0 0x0 0x0 0x80000000 0x0 0x4000000>, ++ <0x0 0x4000000 0x0 0xc4000000 0x0 0x6000000>, ++ <0x0 0xa000000 0x0 0x8a000000 0x0 0x8000000>, ++ <0x0 0x12000000 0x14 0x12000000 0x0 0x10000000>, ++ <0x0 0x22000000 0x10 0x22000000 0x0 0x5e000000>; ++ ++ pcie: pcie@3000000000 { ++ compatible = "microchip,pcie-host-1.0"; ++ #address-cells = <0x3>; ++ #interrupt-cells = <0x1>; ++ #size-cells = <0x2>; ++ device_type = "pci"; ++ dma-noncoherent; ++ reg = <0x30 0x0 0x0 0x8000000>, <0x0 0x43000000 0x0 0x10000>; ++ reg-names = "cfg", "apb"; ++ bus-range = <0x0 0x7f>; ++ interrupt-parent = <&plic>; ++ interrupts = <119>; ++ interrupt-map = <0 0 0 1 &pcie_intc 0>, ++ <0 0 0 2 &pcie_intc 1>, ++ <0 0 0 3 &pcie_intc 2>, ++ <0 0 0 4 &pcie_intc 3>; ++ interrupt-map-mask = <0 0 0 7>; ++ clocks = <&ccc_nw CLK_CCC_PLL0_OUT1>, <&ccc_nw CLK_CCC_PLL0_OUT3>; ++ clock-names = "fic1", "fic3"; ++ ranges = <0x43000000 0x0 0x9000000 0x30 0x9000000 0x0 0xf000000>, ++ <0x1000000 0x0 0x8000000 0x30 0x8000000 0x0 0x1000000>, ++ <0x3000000 0x0 0x18000000 0x30 0x18000000 0x0 0x70000000>; ++ dma-ranges = <0x3000000 0x0 0x80000000 0x0 0x0 0x0 0x4000000>, ++ <0x3000000 0x0 0x84000000 0x0 0x4000000 0x0 0x6000000>, ++ <0x3000000 0x0 0x8a000000 0x0 0xa000000 0x0 0x8000000>, ++ <0x3000000 0x0 0x92000000 0x0 0x12000000 0x0 0x10000000>, ++ <0x3000000 0x0 0xa2000000 0x0 0x22000000 0x0 0x5e000000>; ++ msi-parent = <&pcie>; ++ msi-controller; ++ status = "disabled"; ++ ++ pcie_intc: interrupt-controller { ++ #address-cells = <0>; ++ #interrupt-cells = <1>; ++ interrupt-controller; ++ }; ++ }; ++ }; ++ ++ refclk_ccc: cccrefclk { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ }; ++}; ++ ++&ccc_nw { ++ clocks = <&refclk_ccc>, <&refclk_ccc>, <&refclk_ccc>, <&refclk_ccc>, ++ <&refclk_ccc>, <&refclk_ccc>; ++ clock-names = "pll0_ref0", "pll0_ref1", "pll1_ref0", "pll1_ref1", ++ "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 +--- /dev/null ++++ b/arch/riscv/boot/dts/microchip/mpfs-beaglev-fire.dts +@@ -0,0 +1,380 @@ ++// SPDX-License-Identifier: (GPL-2.0 OR MIT) ++/* Copyright (c) 2020-2021 Microchip Technology Inc */ ++ ++/dts-v1/; ++ ++#include ++#include "mpfs.dtsi" ++#include "mpfs-beaglev-fire-fabric.dtsi" ++ ++/* Clock frequency (in Hz) of the rtcclk */ ++#define RTCCLK_FREQ 1000000 ++ ++/ { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ model = "BeagleBoard BeagleV-Fire"; ++ compatible = "microchip,mpfs-icicle-reference-rtlv2210", "microchip,mpfs"; ++ ++ soc { ++ dma-ranges = <0x14 0x0 0x0 0x80000000 0x0 0x4000000>, ++ <0x14 0x4000000 0x0 0xc4000000 0x0 0x6000000>, ++ <0x14 0xa000000 0x0 0x8a000000 0x0 0x8000000>, ++ <0x14 0x12000000 0x14 0x12000000 0x0 0x10000000>, ++ <0x14 0x22000000 0x10 0x22000000 0x0 0x5e000000>; ++ }; ++ ++ aliases { ++ ethernet0 = &mac1; ++ serial0 = &mmuart0; ++ serial1 = &mmuart1; ++ serial2 = &mmuart2; ++ serial3 = &mmuart3; ++ serial4 = &mmuart4; ++ }; ++ ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ ++ cpus { ++ timebase-frequency = ; ++ }; ++ ++ kernel: memory@80000000 { ++ device_type = "memory"; ++ reg = <0x0 0x80000000 0x0 0x4000000>; ++ }; ++ ++ ddr_cached_low: memory@8a000000 { ++ device_type = "memory"; ++ reg = <0x0 0x8a000000 0x0 0x8000000>; ++ }; ++ ++ ddr_non_cached_low: memory@c4000000 { ++ device_type = "memory"; ++ reg = <0x0 0xc4000000 0x0 0x6000000>; ++ }; ++ ++ ddr_cached_high: memory@1022000000 { ++ device_type = "memory"; ++ reg = <0x10 0x22000000 0x0 0x5e000000>; ++ }; ++ ++ ddr_non_cached_high: memory@1412000000 { ++ device_type = "memory"; ++ reg = <0x14 0x12000000 0x0 0x10000000>; ++ }; ++ ++ reserved-memory { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ranges; ++ ++ hss: hss-buffer@103fc00000 { ++ compatible = "shared-dma-pool"; ++ reg = <0x10 0x3fc00000 0x0 0x400000>; ++ no-map; ++ }; ++ ++ dma_non_cached_low: non-cached-low-buffer { ++ compatible = "shared-dma-pool"; ++ size = <0x0 0x4000000>; ++ no-map; ++ linux,dma-default; ++ alloc-ranges = <0x0 0xc4000000 0x0 0x4000000>; ++ }; ++ ++ dma_non_cached_high: non-cached-high-buffer { ++ compatible = "shared-dma-pool"; ++ size = <0x0 0x10000000>; ++ no-map; ++ linux,dma-default; ++ alloc-ranges = <0x14 0x12000000 0x0 0x10000000>; ++ }; ++ }; ++ ++ imx219_vana: fixedregulator@0 { ++ compatible = "regulator-fixed"; ++ regulator-name = "imx219_vana"; ++ regulator-min-microvolt = <2800000>; ++ regulator-max-microvolt = <2800000>; ++ }; ++ imx219_vdig: fixedregulator@1 { ++ compatible = "regulator-fixed"; ++ regulator-name = "imx219_vdig"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ }; ++ imx219_vddl: fixedregulator@2 { ++ compatible = "regulator-fixed"; ++ regulator-name = "imx219_vddl"; ++ regulator-min-microvolt = <1200000>; ++ regulator-max-microvolt = <1200000>; ++ }; ++ ++ ++ imx219_clk: camera-clk { ++ compatible = "fixed-clock"; ++ #clock-cells = <0>; ++ clock-frequency = <24000000>; ++ }; ++ ++ ++}; ++ ++&gpio0 { ++ ngpios=<14>; ++ gpio-line-names = "", "", "", "", "", "", "", "", "", "", "", "", "SD_CARD_CS", "USER_BUTTON"; ++ status = "okay"; ++ ++ sd_card_cs { ++ gpio-hog; ++ gpios = <12 12>; ++ output_high; ++ line-name = "SD_CARD_CS"; ++ }; ++ ++ user_button { ++ gpio-hog; ++ gpios = <13 13>; ++ input; ++ line-name = "USER_BUTTON"; ++ }; ++}; ++ ++&gpio1 { ++ ngpios=<24>; ++ gpio-line-names = "", "", "", "", "", "", "", "", "", "", ++ "", "", "", "", "", "", "", "", "", "", ++ "ADC_IRQn", "", "", "USB_OCn"; ++ status = "okay"; ++ ++ adc_irqn { ++ gpio-hog; ++ gpios = <20 20>; ++ input; ++ line-name = "ADC_IRQn"; ++ }; ++ ++ user_button { ++ gpio-hog; ++ gpios = <23 23>; ++ input; ++ line-name = "USB_OCn"; ++ }; ++ ++}; ++ ++&gpio2 { ++ interrupts = <53>, <53>, <53>, <53>, ++ <53>, <53>, <53>, <53>, ++ <53>, <53>, <53>, <53>, ++ <53>, <53>, <53>, <53>, ++ <53>, <53>, <53>, <53>, ++ <53>, <53>, <53>, <53>, ++ <53>, <53>, <53>, <53>, ++ <53>, <53>, <53>, <53>; ++ gpio-line-names = "P8_PIN3_USER_LED_0", "P8_PIN4_USER_LED_1", "P8_PIN5_USER_LED_2", ++ "P8_PIN6_USER_LED_3", "P8_PIN7_USER_LED_4", "P8_PIN8_USER_LED_5", ++ "P8_PIN9_USER_LED_6", "P8_PIN10_USER_LED_7", "P8_PIN11_USER_LED_8", ++ "P8_PIN12_USER_LED_9", "P8_PIN13_USER_LED_10", "P8_PIN14_USER_LED_11", ++ "P8_PIN15", "P8_PIN16", "P8_PIN17", "P8_PIN18", "P8_PIN19", ++ "P8_PIN20", "P8_PIN21", "P8_PIN22", "P8_PIN23", "P8_PIN24", ++ "P8_PIN25", "P8_PIN26", "P8_PIN27", "P8_PIN28", "P8_PIN29", ++ "P8_PIN30", ++ "M2_W_DISABLE1", "M2_W_DISABLE2", ++ "VIO_ENABLE", "SD_DET"; ++ status = "okay"; ++ ++ vio_enable { ++ gpio-hog; ++ gpios = <30 30>; ++ output_high; ++ line-name = "VIO_ENABLE"; ++ }; ++ ++ sd_det { ++ gpio-hog; ++ gpios = <31 31>; ++ input; ++ line-name = "SD_DET"; ++ }; ++}; ++ ++&i2c0 { ++ status = "okay"; ++}; ++ ++&i2c1 { ++ status = "okay"; ++ eeprom: eeprom@50 { ++ compatible = "at,24c32"; ++ reg = <0x50>; ++ }; ++ ++ imx219: sensor@10 { ++ compatible = "sony,imx219"; ++ reg = <0x10>; ++ clocks = <&imx219_clk>; ++ VANA-supply = <&imx219_vana>; /* 2.8v */ ++ VDIG-supply = <&imx219_vdig>; /* 1.8v */ ++ VDDL-supply = <&imx219_vddl>; /* 1.2v */ ++ ++ port { ++ imx219_0: endpoint { ++// remote-endpoint = <&csi1_ep>; ++ data-lanes = <1 2>; ++ clock-noncontinuous; ++ link-frequencies = /bits/ 64 <456000000>; ++ }; ++ }; ++ }; ++ ++ ++ ++}; ++ ++&mac0 { ++ dma-noncoherent; ++ status = "okay"; ++ phy-mode = "sgmii"; ++ phy-handle = <&phy0>; ++ phy0: ethernet-phy@0 { ++ reg = <0>; ++ }; ++}; ++ ++&mac1 { ++ dma-noncoherent; ++ status = "okay"; ++ phy-mode = "sgmii"; ++ phy-handle = <&phy1>; ++ phy1: ethernet-phy@0 { ++ reg = <0>; ++ }; ++}; ++ ++&mbox { ++ status = "okay"; ++}; ++ ++//&mmc { ++// status = "okay"; ++// bus-width = <8>; ++// disable-wp; ++// cap-mmc-highspeed; ++// mmc-ddr-1_8v; ++// mmc-hs200-1_8v; ++//}; ++ ++&mmc { ++ dma-noncoherent; ++ bus-width = <4>; ++ disable-wp; ++ cap-sd-highspeed; ++ cap-mmc-highspeed; ++ mmc-ddr-1_8v; ++ mmc-hs200-1_8v; ++ sd-uhs-sdr12; ++ sd-uhs-sdr25; ++ sd-uhs-sdr50; ++ sd-uhs-sdr104; ++ status = "okay"; ++}; ++ ++ ++&mmuart0 { ++ status = "okay"; ++}; ++ ++&mmuart1 { ++ status = "okay"; ++}; ++ ++//&mmuart2 { ++// status = "okay"; ++//}; ++ ++//&mmuart3 //{ ++// statu//s = "okay"; ++//};// ++// ++//&mmuart4 { ++// status = "okay"; ++//}; ++ ++//&pcie { ++// status = "okay"; ++//}; ++ ++&qspi { ++ status = "okay"; ++ cs-gpios = <&gpio1 17 GPIO_ACTIVE_LOW>, <&gpio0 12 GPIO_ACTIVE_LOW>; ++ num-cs = <2>; ++ ++ ++ mcp3464: mcp3464@0 { ++ compatible = "microchip,mcp3464r"; ++ reg = <0>; /* CE0 */ ++ spi-cpol; ++ spi-cpha; ++ spi-max-frequency = <15000000>; ++ status = "okay"; ++ microchip,hw-device-address = <1>; ++ }; ++ ++ mmc-slot@1 { ++ compatible = "mmc-spi-slot"; ++ reg = <1>; ++ gpios = <&gpio2 31 1>; ++ voltage-ranges = <3300 3300>; ++ spi-max-frequency = <15000000>; ++ disable-wp; ++ }; ++}; ++ ++&refclk { ++ clock-frequency = <125000000>; ++}; ++ ++&refclk_ccc { ++ clock-frequency = <50000000>; ++}; ++ ++&rtc { ++ status = "okay"; ++}; ++ ++&spi0 { ++ status = "okay"; ++}; ++ ++&spi1 { ++ status = "okay"; ++}; ++ ++&syscontroller { ++ microchip,bitstream-flash = <&sys_ctrl_flash>; ++ status = "okay"; ++}; ++ ++&syscontroller_qspi { ++ status = "okay"; ++ ++ sys_ctrl_flash: flash@0 { // MT25QL01GBBB8ESF-0SIT ++ compatible = "jedec,spi-nor"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ spi-max-frequency = <20000000>; ++ spi-rx-bus-width = <1>; ++ reg = <0>; ++ }; ++}; ++ ++ ++&usb { ++ dma-noncoherent; ++ status = "okay"; ++ dr_mode = "otg"; ++}; +-- +2.25.1 + diff --git a/patches/linux/0001-BeagleV-Fire-Add-printk-to-IM219-driver-for-board-te.patch b/patches/linux/0001-BeagleV-Fire-Add-printk-to-IM219-driver-for-board-te.patch new file mode 100644 index 0000000..a3dd8d7 --- /dev/null +++ b/patches/linux/0001-BeagleV-Fire-Add-printk-to-IM219-driver-for-board-te.patch @@ -0,0 +1,45 @@ +From fa73238dfe5e29d9d77ecb2166fbae46215d59b6 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. + +--- + 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 +--- 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.25.1 + diff --git a/patches/linux/0001-GPIO-Add-Microchip-CoreGPIO-driver.patch b/patches/linux/0001-GPIO-Add-Microchip-CoreGPIO-driver.patch new file mode 100644 index 0000000..41c5b26 --- /dev/null +++ b/patches/linux/0001-GPIO-Add-Microchip-CoreGPIO-driver.patch @@ -0,0 +1,371 @@ +From 49e37dfd8c7070958960ba23d0e3e005b6604940 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. + +--- + 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 d71f5bd6f532..8e3858e34cc7 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 9be0691d9660..05a65ff04b23 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 000000000000..fd630cac4fe5 +--- /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.25.1 + diff --git a/patches/linux/0001-MMC-SPI-Hack-to-support-non-DMA-capable-SPI-ctrl.patch b/patches/linux/0001-MMC-SPI-Hack-to-support-non-DMA-capable-SPI-ctrl.patch new file mode 100644 index 0000000..96e219c --- /dev/null +++ b/patches/linux/0001-MMC-SPI-Hack-to-support-non-DMA-capable-SPI-ctrl.patch @@ -0,0 +1,66 @@ +From 5252a0a75c390e01fc9b402660c1a28f7f4d172f 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. + +--- + 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 cc333ad67cac..fb45c7fa431d 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 4a28a8395552..c49350240978 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 a221478e528e..e198049cc80c 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.25.1 + diff --git a/patches/linux/0001-Microchip-QSPI-Add-regular-transfers.patch b/patches/linux/0001-Microchip-QSPI-Add-regular-transfers.patch new file mode 100644 index 0000000..92a17be --- /dev/null +++ b/patches/linux/0001-Microchip-QSPI-Add-regular-transfers.patch @@ -0,0 +1,275 @@ +From 0777b8ed4d5994b906dcfd06fa9c626b6905d655 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. + +--- + 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 +--- 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.25.1 + diff --git a/patches/linux/0001-PCIe-Change-controller-and-bridge-base-address.patch b/patches/linux/0001-PCIe-Change-controller-and-bridge-base-address.patch new file mode 100644 index 0000000..d890e2b --- /dev/null +++ b/patches/linux/0001-PCIe-Change-controller-and-bridge-base-address.patch @@ -0,0 +1,27 @@ +From a9ba71fb483fdacbc4f0d1d3ab2ec3013cc69f17 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. + +--- + 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 +--- 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 + + /* 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.25.1 + diff --git a/patches/linux/Makefile b/patches/linux/Makefile new file mode 100644 index 0000000..4e35292 --- /dev/null +++ b/patches/linux/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 +dtb-$(CONFIG_SOC_MICROCHIP_POLARFIRE) += mpfs-icicle-kit.dtb +dtb-$(CONFIG_SOC_MICROCHIP_POLARFIRE) += mpfs-icicle-kit-context-a.dtb +dtb-$(CONFIG_SOC_MICROCHIP_POLARFIRE) += mpfs-m100pfsevp.dtb +dtb-$(CONFIG_SOC_MICROCHIP_POLARFIRE) += mpfs-polarberry.dtb +dtb-$(CONFIG_SOC_MICROCHIP_POLARFIRE) += mpfs-video-kit.dtb +dtb-$(CONFIG_SOC_MICROCHIP_POLARFIRE) += mpfs-tysom-m.dtb +dtb-$(CONFIG_SOC_MICROCHIP_POLARFIRE) += mpfs-beaglev-fire.dtb +obj-$(CONFIG_BUILTIN_DTB) += $(addsuffix .o, $(dtb-y)) diff --git a/patches/linux/mpfs_defconfig b/patches/linux/mpfs_defconfig index b596f01..83c0ccc 100644 --- a/patches/linux/mpfs_defconfig +++ b/patches/linux/mpfs_defconfig @@ -1977,6 +1977,7 @@ CONFIG_GPIO_CDEV_V1=y # CONFIG_GPIO_HLWD is not set # CONFIG_GPIO_MB86S7X is not set CONFIG_GPIO_POLARFIRE_SOC=y +CONFIG_GPIO_MICROCHIP_CORE=y # CONFIG_GPIO_SIFIVE is not set # CONFIG_GPIO_XILINX is not set # CONFIG_GPIO_AMD_FCH is not set @@ -3468,6 +3469,7 @@ CONFIG_IIO_SW_TRIGGER=m # CONFIG_MCP320X is not set # CONFIG_MCP3422 is not set # CONFIG_MCP3911 is not set +CONFIG_MCP356X=m # CONFIG_NAU7802 is not set # CONFIG_RICHTEK_RTQ6056 is not set # CONFIG_SD_ADC_MODULATOR is not set