ipq806x: add support for 4.0 kernel

Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>

SVN-Revision: 45210
This commit is contained in:
John Crispin 2015-04-01 08:32:03 +00:00
parent 2d74104b52
commit d3a9fc4586
10 changed files with 1771 additions and 0 deletions

View file

@ -0,0 +1,389 @@
CONFIG_ALIGNMENT_TRAP=y
# CONFIG_AMBA_PL08X is not set
# CONFIG_APM_EMULATION is not set
CONFIG_APQ_GCC_8084=y
CONFIG_APQ_MMCC_8084=y
CONFIG_AR8216_PHY=y
CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y
CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y
CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y
CONFIG_ARCH_HAS_SG_CHAIN=y
CONFIG_ARCH_HAS_TICK_BROADCAST=y
CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y
CONFIG_ARCH_HIBERNATION_POSSIBLE=y
CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
CONFIG_ARCH_MSM8960=y
CONFIG_ARCH_MSM8974=y
CONFIG_ARCH_MSM8X60=y
CONFIG_ARCH_MULTIPLATFORM=y
# CONFIG_ARCH_MULTI_CPU_AUTO is not set
CONFIG_ARCH_MULTI_V6_V7=y
CONFIG_ARCH_MULTI_V7=y
CONFIG_ARCH_NR_GPIO=0
CONFIG_ARCH_QCOM=y
# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y
CONFIG_ARCH_SUPPORTS_BIG_ENDIAN=y
CONFIG_ARCH_SUPPORTS_UPROBES=y
CONFIG_ARCH_SUSPEND_POSSIBLE=y
CONFIG_ARCH_USE_BUILTIN_BSWAP=y
CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y
CONFIG_ARCH_WANT_GENERAL_HUGETLB=y
CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
CONFIG_ARM=y
CONFIG_ARM_AMBA=y
CONFIG_ARM_ARCH_TIMER=y
CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y
CONFIG_ARM_CPU_SUSPEND=y
CONFIG_ARM_GIC=y
CONFIG_ARM_HAS_SG_CHAIN=y
CONFIG_ARM_L1_CACHE_SHIFT=6
CONFIG_ARM_L1_CACHE_SHIFT_6=y
# CONFIG_ARM_LPAE is not set
CONFIG_ARM_PATCH_PHYS_VIRT=y
# CONFIG_ARM_SMMU is not set
# CONFIG_ARM_SP805_WATCHDOG is not set
CONFIG_ARM_THUMB=y
# CONFIG_ARM_THUMBEE is not set
CONFIG_ARM_UNWIND=y
CONFIG_ARM_VIRT_EXT=y
CONFIG_AUTO_ZRELADDR=y
# CONFIG_BATTERY_GAUGE_LTC2941 is not set
CONFIG_BCMA_DRIVER_PCI=y
# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
CONFIG_BOUNCE=y
CONFIG_BUILD_BIN2C=y
# CONFIG_CACHE_L2X0 is not set
CONFIG_CLEANCACHE=y
CONFIG_CLKDEV_LOOKUP=y
CONFIG_CLKSRC_OF=y
CONFIG_CLKSRC_QCOM=y
CONFIG_CLONE_BACKWARDS=y
CONFIG_COMMON_CLK=y
CONFIG_COMMON_CLK_QCOM=y
CONFIG_COMPACTION=y
CONFIG_COREDUMP=y
CONFIG_CPU_32v6K=y
CONFIG_CPU_32v7=y
CONFIG_CPU_ABRT_EV7=y
# CONFIG_CPU_BIG_ENDIAN is not set
# CONFIG_CPU_BPREDICT_DISABLE is not set
CONFIG_CPU_CACHE_V7=y
CONFIG_CPU_CACHE_VIPT=y
CONFIG_CPU_COPY_V6=y
CONFIG_CPU_CP15=y
CONFIG_CPU_CP15_MMU=y
CONFIG_CPU_HAS_ASID=y
# CONFIG_CPU_ICACHE_DISABLE is not set
CONFIG_CPU_PABRT_V7=y
CONFIG_CPU_PM=y
CONFIG_CPU_RMAP=y
CONFIG_CPU_TLB_V7=y
CONFIG_CPU_V7=y
CONFIG_CRC16=y
# CONFIG_CRC32_SARWATE is not set
CONFIG_CRC32_SLICEBY8=y
CONFIG_CROSS_MEMORY_ATTACH=y
# CONFIG_CRYPTO_SHA1_ARM_NEON is not set
# CONFIG_CRYPTO_SHA512_ARM_NEON is not set
CONFIG_CRYPTO_XZ=y
CONFIG_DCACHE_WORD_ACCESS=y
CONFIG_DEBUG_BUGVERBOSE=y
CONFIG_DEBUG_GPIO=y
CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S"
CONFIG_DEBUG_PREEMPT=y
# CONFIG_DEBUG_UART_8250 is not set
# CONFIG_DEBUG_USER is not set
CONFIG_DECOMPRESS_GZIP=y
CONFIG_DEVMEM=y
CONFIG_DMADEVICES=y
CONFIG_DMA_ENGINE=y
CONFIG_DMA_OF=y
CONFIG_DMA_VIRTUAL_CHANNELS=y
CONFIG_DTC=y
# CONFIG_DW_DMAC_CORE is not set
# CONFIG_DW_DMAC_PCI is not set
CONFIG_DYNAMIC_DEBUG=y
CONFIG_ETHERNET_PACKET_MANGLE=y
CONFIG_FREEZER=y
CONFIG_GENERIC_ALLOCATOR=y
CONFIG_GENERIC_BUG=y
CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
CONFIG_GENERIC_IDLE_POLL_SETUP=y
CONFIG_GENERIC_IO=y
CONFIG_GENERIC_IRQ_SHOW=y
CONFIG_GENERIC_MSI_IRQ=y
CONFIG_GENERIC_PCI_IOMAP=y
CONFIG_GENERIC_PHY=y
CONFIG_GENERIC_PINCONF=y
CONFIG_GENERIC_SCHED_CLOCK=y
CONFIG_GENERIC_SMP_IDLE_THREAD=y
CONFIG_GENERIC_STRNCPY_FROM_USER=y
CONFIG_GENERIC_STRNLEN_USER=y
CONFIG_GPIOLIB=y
CONFIG_GPIOLIB_IRQCHIP=y
CONFIG_GPIO_DEVRES=y
# CONFIG_GPIO_MSM_V2 is not set
CONFIG_GPIO_SYSFS=y
CONFIG_HANDLE_DOMAIN_IRQ=y
CONFIG_HARDIRQS_SW_RESEND=y
CONFIG_HAS_DMA=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT_MAP=y
# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set
CONFIG_HAVE_ARCH_AUDITSYSCALL=y
CONFIG_HAVE_ARCH_BITREVERSE=y
CONFIG_HAVE_ARCH_JUMP_LABEL=y
CONFIG_HAVE_ARCH_KGDB=y
CONFIG_HAVE_ARCH_PFN_VALID=y
CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
CONFIG_HAVE_ARCH_TRACEHOOK=y
CONFIG_HAVE_ARM_ARCH_TIMER=y
# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set
CONFIG_HAVE_BPF_JIT=y
CONFIG_HAVE_CC_STACKPROTECTOR=y
CONFIG_HAVE_CLK=y
CONFIG_HAVE_CLK_PREPARE=y
CONFIG_HAVE_CONTEXT_TRACKING=y
CONFIG_HAVE_C_RECORDMCOUNT=y
CONFIG_HAVE_DEBUG_KMEMLEAK=y
CONFIG_HAVE_DMA_API_DEBUG=y
CONFIG_HAVE_DMA_ATTRS=y
CONFIG_HAVE_DMA_CONTIGUOUS=y
CONFIG_HAVE_DYNAMIC_FTRACE=y
CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y
CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
CONFIG_HAVE_FUNCTION_TRACER=y
CONFIG_HAVE_GENERIC_DMA_COHERENT=y
CONFIG_HAVE_HW_BREAKPOINT=y
CONFIG_HAVE_IDE=y
CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y
CONFIG_HAVE_KERNEL_GZIP=y
CONFIG_HAVE_KERNEL_LZ4=y
CONFIG_HAVE_KERNEL_LZMA=y
CONFIG_HAVE_KERNEL_LZO=y
CONFIG_HAVE_KERNEL_XZ=y
CONFIG_HAVE_MEMBLOCK=y
CONFIG_HAVE_MOD_ARCH_SPECIFIC=y
CONFIG_HAVE_NET_DSA=y
CONFIG_HAVE_OPROFILE=y
CONFIG_HAVE_OPTPROBES=y
CONFIG_HAVE_PERF_EVENTS=y
CONFIG_HAVE_PERF_REGS=y
CONFIG_HAVE_PERF_USER_STACK_DUMP=y
CONFIG_HAVE_PROC_CPU=y
CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
CONFIG_HAVE_SMP=y
CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
CONFIG_HAVE_UID16=y
CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y
CONFIG_HIGHMEM=y
CONFIG_HIGHPTE=y
CONFIG_HOTPLUG_CPU=y
CONFIG_HWMON=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_MSM=y
CONFIG_HZ_FIXED=0
CONFIG_I2C=y
CONFIG_I2C_BOARDINFO=y
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_COMPAT=y
CONFIG_I2C_HELPER_AUTO=y
CONFIG_I2C_QUP=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_INITRAMFS_SOURCE=""
CONFIG_IOMMU_HELPER=y
# CONFIG_IOMMU_IO_PGTABLE_LPAE is not set
CONFIG_IOMMU_SUPPORT=y
CONFIG_IPQ_GCC_806X=y
# CONFIG_IPQ_LCC_806X is not set
CONFIG_IRQCHIP=y
CONFIG_IRQ_DOMAIN=y
CONFIG_IRQ_DOMAIN_HIERARCHY=y
CONFIG_IRQ_FORCED_THREADING=y
CONFIG_IRQ_WORK=y
# CONFIG_LEDS_REGULATOR is not set
CONFIG_LIBFDT=y
CONFIG_LOCKUP_DETECTOR=y
CONFIG_LOCK_SPIN_ON_OWNER=y
# CONFIG_LZ4_COMPRESS is not set
# CONFIG_LZ4_DECOMPRESS is not set
CONFIG_LZO_COMPRESS=y
CONFIG_LZO_DECOMPRESS=y
CONFIG_MDIO_BITBANG=y
CONFIG_MDIO_BOARDINFO=y
CONFIG_MDIO_GPIO=y
# CONFIG_MFD_DA9150 is not set
# CONFIG_MFD_QCOM_RPM is not set
# CONFIG_MFD_RT5033 is not set
# CONFIG_MFD_SPMI_PMIC is not set
CONFIG_MIGHT_HAVE_CACHE_L2X0=y
CONFIG_MIGHT_HAVE_PCI=y
CONFIG_MIGRATION=y
CONFIG_MODULES_USE_ELF_REL=y
# CONFIG_MPLS is not set
CONFIG_MSM_GCC_8660=y
CONFIG_MSM_GCC_8960=y
CONFIG_MSM_GCC_8974=y
# CONFIG_MSM_LCC_8960 is not set
CONFIG_MSM_MMCC_8960=y
CONFIG_MSM_MMCC_8974=y
CONFIG_MTD_CMDLINE_PARTS=y
CONFIG_MTD_M25P80=y
CONFIG_MTD_SPI_NOR=y
CONFIG_MTD_SPLIT_FIRMWARE=y
CONFIG_MTD_SPLIT_FIT_FW=y
CONFIG_MULTI_IRQ_HANDLER=y
CONFIG_MUTEX_SPIN_ON_OWNER=y
CONFIG_NEED_DMA_MAP_STATE=y
CONFIG_NEON=y
CONFIG_NET_FLOW_LIMIT=y
CONFIG_NET_VENDOR_WIZNET=y
CONFIG_NO_BOOTMEM=y
CONFIG_NO_HZ=y
CONFIG_NO_HZ_COMMON=y
CONFIG_NO_HZ_IDLE=y
CONFIG_NR_CPUS=4
CONFIG_OF=y
CONFIG_OF_ADDRESS=y
CONFIG_OF_ADDRESS_PCI=y
CONFIG_OF_EARLY_FLATTREE=y
CONFIG_OF_FLATTREE=y
CONFIG_OF_GPIO=y
CONFIG_OF_IRQ=y
CONFIG_OF_MDIO=y
CONFIG_OF_MTD=y
CONFIG_OF_NET=y
CONFIG_OF_PCI=y
CONFIG_OF_PCI_IRQ=y
CONFIG_OF_RESERVED_MEM=y
CONFIG_OLD_SIGACTION=y
CONFIG_OLD_SIGSUSPEND3=y
CONFIG_PAGEFLAGS_EXTENDED=y
CONFIG_PAGE_OFFSET=0xC0000000
CONFIG_PCI=y
# CONFIG_PCI_DOMAINS_GENERIC is not set
CONFIG_PCI_MSI=y
CONFIG_PERF_EVENTS=y
CONFIG_PERF_USE_VMALLOC=y
CONFIG_PHYLIB=y
# CONFIG_PHY_QCOM_APQ8064_SATA is not set
CONFIG_PHY_QCOM_IPQ806X_SATA=y
CONFIG_PINCTRL=y
CONFIG_PINCTRL_APQ8064=y
# CONFIG_PINCTRL_APQ8084 is not set
CONFIG_PINCTRL_IPQ8064=y
CONFIG_PINCTRL_MSM=y
# CONFIG_PINCTRL_MSM8916 is not set
# CONFIG_PINCTRL_MSM8960 is not set
CONFIG_PINCTRL_MSM8X74=y
# CONFIG_PINCTRL_QCOM_SPMI_PMIC is not set
# CONFIG_PL330_DMA is not set
CONFIG_PM=y
CONFIG_PM_CLK=y
# CONFIG_PM_DEBUG is not set
CONFIG_PM_SLEEP=y
CONFIG_PM_SLEEP_SMP=y
CONFIG_POWER_RESET=y
# CONFIG_POWER_RESET_BRCMSTB is not set
# CONFIG_POWER_RESET_GPIO is not set
# CONFIG_POWER_RESET_GPIO_RESTART is not set
# CONFIG_POWER_RESET_LTC2952 is not set
CONFIG_POWER_RESET_MSM=y
# CONFIG_POWER_RESET_SYSCON is not set
CONFIG_POWER_SUPPLY=y
CONFIG_PREEMPT=y
CONFIG_PREEMPT_COUNT=y
# CONFIG_PREEMPT_NONE is not set
CONFIG_PREEMPT_RCU=y
CONFIG_PRINTK_TIME=y
CONFIG_PROC_PAGE_MONITOR=y
CONFIG_QCOM_BAM_DMA=y
CONFIG_QCOM_GSBI=y
CONFIG_QCOM_SCM=y
CONFIG_QCOM_WDT=y
# CONFIG_RCU_BOOST is not set
CONFIG_RCU_CPU_STALL_TIMEOUT=21
CONFIG_RCU_STALL_COMMON=y
CONFIG_RD_GZIP=y
CONFIG_REGMAP=y
CONFIG_REGMAP_MMIO=y
CONFIG_REGULATOR=y
# CONFIG_REGULATOR_DEBUG is not set
# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set
CONFIG_RESET_CONTROLLER=y
CONFIG_RFS_ACCEL=y
CONFIG_RPS=y
CONFIG_RTC_CLASS=y
# CONFIG_RTC_DRV_CMOS is not set
CONFIG_RWSEM_SPIN_ON_OWNER=y
CONFIG_RWSEM_XCHGADD_ALGORITHM=y
CONFIG_SCHED_HRTICK=y
# CONFIG_SCSI_DMA is not set
# CONFIG_SERIAL_AMBA_PL010 is not set
# CONFIG_SERIAL_AMBA_PL011 is not set
CONFIG_SERIAL_MSM=y
CONFIG_SERIAL_MSM_CONSOLE=y
# CONFIG_SLAB is not set
CONFIG_SLUB=y
CONFIG_SLUB_CPU_PARTIAL=y
CONFIG_SMP=y
CONFIG_SMP_ON_UP=y
CONFIG_SPARSE_IRQ=y
CONFIG_SPI=y
CONFIG_SPI_MASTER=y
CONFIG_SPI_QUP=y
CONFIG_SPMI=y
CONFIG_SPMI_MSM_PMIC_ARB=y
CONFIG_SRCU=y
CONFIG_STOP_MACHINE=y
# CONFIG_STRIP_ASM_SYMS is not set
CONFIG_SUSPEND=y
CONFIG_SUSPEND_FREEZER=y
CONFIG_SWCONFIG=y
CONFIG_SWIOTLB=y
CONFIG_SWP_EMULATE=y
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
# CONFIG_TEGRA_AHB is not set
CONFIG_THERMAL=y
# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set
CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y
# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set
# CONFIG_THERMAL_EMULATION is not set
# CONFIG_THERMAL_GOV_FAIR_SHARE is not set
CONFIG_THERMAL_GOV_STEP_WISE=y
# CONFIG_THERMAL_GOV_USER_SPACE is not set
CONFIG_THERMAL_HWMON=y
CONFIG_THERMAL_OF=y
# CONFIG_THUMB2_KERNEL is not set
CONFIG_TICK_CPU_ACCOUNTING=y
CONFIG_TIMER_STATS=y
CONFIG_UEVENT_HELPER_PATH=""
CONFIG_UID16=y
CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h"
CONFIG_UNINLINE_SPIN_UNLOCK=y
CONFIG_USE_OF=y
CONFIG_VECTORS_BASE=0xffff0000
CONFIG_VFP=y
CONFIG_VFPv3=y
CONFIG_VM_EVENT_COUNTERS=y
CONFIG_WATCHDOG_CORE=y
# CONFIG_WIZNET_W5100 is not set
# CONFIG_WIZNET_W5300 is not set
# CONFIG_WL_TI is not set
# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set
# CONFIG_XEN is not set
CONFIG_XPS=y
CONFIG_XZ_DEC_ARM=y
CONFIG_XZ_DEC_BCJ=y
CONFIG_ZBOOT_ROM_BSS=0
CONFIG_ZBOOT_ROM_TEXT=0
CONFIG_ZLIB_INFLATE=y
CONFIG_ZONE_DMA_FLAG=0

View file

@ -0,0 +1,522 @@
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: spi: qup: Add DMA capabilities
From: Andy Gross <agross@codeaurora.org>
X-Patchwork-Id: 4432401
Message-Id: <1403816781-31008-1-git-send-email-agross@codeaurora.org>
To: Mark Brown <broonie@kernel.org>
Cc: linux-spi@vger.kernel.org, Sagar Dharia <sdharia@codeaurora.org>,
Daniel Sneddon <dsneddon@codeaurora.org>,
Bjorn Andersson <bjorn.andersson@sonymobile.com>,
"Ivan T. Ivanov" <iivanov@mm-sol.com>,
linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
linux-arm-msm@vger.kernel.org, Andy Gross <agross@codeaurora.org>
Date: Thu, 26 Jun 2014 16:06:21 -0500
This patch adds DMA capabilities to the spi-qup driver. If DMA channels are
present, the QUP will use DMA instead of block mode for transfers to/from SPI
peripherals for transactions larger than the length of a block.
Signed-off-by: Andy Gross <agross@codeaurora.org>
---
.../devicetree/bindings/spi/qcom,spi-qup.txt | 10 +
drivers/spi/spi-qup.c | 361 ++++++++++++++++++--
2 files changed, 350 insertions(+), 21 deletions(-)
--- a/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt
+++ b/Documentation/devicetree/bindings/spi/qcom,spi-qup.txt
@@ -27,6 +27,11 @@ Optional properties:
- spi-max-frequency: Specifies maximum SPI clock frequency,
Units - Hz. Definition as per
Documentation/devicetree/bindings/spi/spi-bus.txt
+- dmas : Two DMA channel specifiers following the convention outlined
+ in bindings/dma/dma.txt
+- dma-names: Names for the dma channels, if present. There must be at
+ least one channel named "tx" for transmit and named "rx" for
+ receive.
- num-cs: total number of chipselects
- cs-gpios: should specify GPIOs used for chipselects.
The gpios will be referred to as reg = <index> in the SPI child
@@ -51,6 +56,10 @@ Example:
clocks = <&gcc GCC_BLSP2_QUP2_SPI_APPS_CLK>, <&gcc GCC_BLSP2_AHB_CLK>;
clock-names = "core", "iface";
+ dmas = <&blsp2_bam 2>,
+ <&blsp2_bam 3>;
+ dma-names = "rx", "tx";
+
pinctrl-names = "default";
pinctrl-0 = <&spi8_default>;
--- a/drivers/spi/spi-qup.c
+++ b/drivers/spi/spi-qup.c
@@ -22,6 +22,8 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/spi/spi.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
#define QUP_CONFIG 0x0000
#define QUP_STATE 0x0004
@@ -116,6 +118,8 @@
#define SPI_NUM_CHIPSELECTS 4
+#define SPI_MAX_XFER (SZ_64K - 64)
+
/* high speed mode is when bus rate is greater then 26MHz */
#define SPI_HS_MIN_RATE 26000000
#define SPI_MAX_RATE 50000000
@@ -143,6 +147,17 @@ struct spi_qup {
int tx_bytes;
int rx_bytes;
int qup_v1;
+
+ int use_dma;
+
+ struct dma_chan *rx_chan;
+ struct dma_slave_config rx_conf;
+ struct dma_chan *tx_chan;
+ struct dma_slave_config tx_conf;
+ dma_addr_t rx_dma;
+ dma_addr_t tx_dma;
+ void *dummy;
+ atomic_t dma_outstanding;
};
@@ -266,6 +281,221 @@ static void spi_qup_fifo_write(struct sp
}
}
+static void qup_dma_callback(void *data)
+{
+ struct spi_qup *controller = data;
+
+ if (atomic_dec_and_test(&controller->dma_outstanding))
+ complete(&controller->done);
+}
+
+static int spi_qup_do_dma(struct spi_qup *controller, struct spi_transfer *xfer)
+{
+ struct dma_async_tx_descriptor *rxd, *txd;
+ dma_cookie_t rx_cookie, tx_cookie;
+ u32 xfer_len, rx_align = 0, tx_align = 0, n_words;
+ struct scatterlist tx_sg[2], rx_sg[2];
+ int ret = 0;
+ u32 bytes_to_xfer = xfer->len;
+ u32 offset = 0;
+ u32 rx_nents = 0, tx_nents = 0;
+ dma_addr_t rx_dma = 0, tx_dma = 0, rx_dummy_dma = 0, tx_dummy_dma = 0;
+
+
+ if (xfer->rx_buf) {
+ rx_dma = dma_map_single(controller->dev, xfer->rx_buf,
+ xfer->len, DMA_FROM_DEVICE);
+
+ if (dma_mapping_error(controller->dev, rx_dma)) {
+ ret = -ENOMEM;
+ return ret;
+ }
+
+ /* check to see if we need dummy buffer for leftover bytes */
+ rx_align = xfer->len % controller->in_blk_sz;
+ if (rx_align) {
+ rx_dummy_dma = dma_map_single(controller->dev,
+ controller->dummy, controller->in_fifo_sz,
+ DMA_FROM_DEVICE);
+
+ if (dma_mapping_error(controller->dev, rx_dummy_dma)) {
+ ret = -ENOMEM;
+ goto err_map_rx_dummy;
+ }
+ }
+ }
+
+ if (xfer->tx_buf) {
+ tx_dma = dma_map_single(controller->dev,
+ (void *)xfer->tx_buf, xfer->len, DMA_TO_DEVICE);
+
+ if (dma_mapping_error(controller->dev, tx_dma)) {
+ ret = -ENOMEM;
+ goto err_map_tx;
+ }
+
+ /* check to see if we need dummy buffer for leftover bytes */
+ tx_align = xfer->len % controller->out_blk_sz;
+ if (tx_align) {
+ memcpy(controller->dummy + SZ_1K,
+ xfer->tx_buf + xfer->len - tx_align,
+ tx_align);
+ memset(controller->dummy + SZ_1K + tx_align, 0,
+ controller->out_blk_sz - tx_align);
+
+ tx_dummy_dma = dma_map_single(controller->dev,
+ controller->dummy + SZ_1K,
+ controller->out_blk_sz, DMA_TO_DEVICE);
+
+ if (dma_mapping_error(controller->dev, tx_dummy_dma)) {
+ ret = -ENOMEM;
+ goto err_map_tx_dummy;
+ }
+ }
+ }
+
+ atomic_set(&controller->dma_outstanding, 0);
+
+ while (bytes_to_xfer > 0) {
+ xfer_len = min_t(u32, bytes_to_xfer, SPI_MAX_XFER);
+ n_words = DIV_ROUND_UP(xfer_len, controller->w_size);
+
+ /* write out current word count to controller */
+ writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
+ writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
+
+ reinit_completion(&controller->done);
+
+ if (xfer->tx_buf) {
+ /* recalc align for each transaction */
+ tx_align = xfer_len % controller->out_blk_sz;
+
+ if (tx_align)
+ tx_nents = 2;
+ else
+ tx_nents = 1;
+
+ /* initialize scatterlists */
+ sg_init_table(tx_sg, tx_nents);
+ sg_dma_len(&tx_sg[0]) = xfer_len - tx_align;
+ sg_dma_address(&tx_sg[0]) = tx_dma + offset;
+
+ /* account for non block size transfer */
+ if (tx_align) {
+ sg_dma_len(&tx_sg[1]) = controller->out_blk_sz;
+ sg_dma_address(&tx_sg[1]) = tx_dummy_dma;
+ }
+
+ txd = dmaengine_prep_slave_sg(controller->tx_chan,
+ tx_sg, tx_nents, DMA_MEM_TO_DEV, 0);
+ if (!txd) {
+ ret = -ENOMEM;
+ goto err_unmap;
+ }
+
+ atomic_inc(&controller->dma_outstanding);
+
+ txd->callback = qup_dma_callback;
+ txd->callback_param = controller;
+
+ tx_cookie = dmaengine_submit(txd);
+
+ dma_async_issue_pending(controller->tx_chan);
+ }
+
+ if (xfer->rx_buf) {
+ /* recalc align for each transaction */
+ rx_align = xfer_len % controller->in_blk_sz;
+
+ if (rx_align)
+ rx_nents = 2;
+ else
+ rx_nents = 1;
+
+ /* initialize scatterlists */
+ sg_init_table(rx_sg, rx_nents);
+ sg_dma_address(&rx_sg[0]) = rx_dma + offset;
+ sg_dma_len(&rx_sg[0]) = xfer_len - rx_align;
+
+ /* account for non block size transfer */
+ if (rx_align) {
+ sg_dma_len(&rx_sg[1]) = controller->in_blk_sz;
+ sg_dma_address(&rx_sg[1]) = rx_dummy_dma;
+ }
+
+ rxd = dmaengine_prep_slave_sg(controller->rx_chan,
+ rx_sg, rx_nents, DMA_DEV_TO_MEM, 0);
+ if (!rxd) {
+ ret = -ENOMEM;
+ goto err_unmap;
+ }
+
+ atomic_inc(&controller->dma_outstanding);
+
+ rxd->callback = qup_dma_callback;
+ rxd->callback_param = controller;
+
+ rx_cookie = dmaengine_submit(rxd);
+
+ dma_async_issue_pending(controller->rx_chan);
+ }
+
+ if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
+ dev_warn(controller->dev, "cannot set EXECUTE state\n");
+ goto err_unmap;
+ }
+
+ if (!wait_for_completion_timeout(&controller->done,
+ msecs_to_jiffies(1000))) {
+ ret = -ETIMEDOUT;
+
+ /* clear out all the DMA transactions */
+ if (xfer->tx_buf)
+ dmaengine_terminate_all(controller->tx_chan);
+ if (xfer->rx_buf)
+ dmaengine_terminate_all(controller->rx_chan);
+
+ goto err_unmap;
+ }
+
+ if (rx_align)
+ memcpy(xfer->rx_buf + offset + xfer->len - rx_align,
+ controller->dummy, rx_align);
+
+ /* adjust remaining bytes to transfer */
+ bytes_to_xfer -= xfer_len;
+ offset += xfer_len;
+
+
+ /* reset mini-core state so we can program next transaction */
+ if (spi_qup_set_state(controller, QUP_STATE_RESET)) {
+ dev_err(controller->dev, "cannot set RESET state\n");
+ goto err_unmap;
+ }
+ }
+
+ ret = 0;
+
+err_unmap:
+ if (tx_align)
+ dma_unmap_single(controller->dev, tx_dummy_dma,
+ controller->out_fifo_sz, DMA_TO_DEVICE);
+err_map_tx_dummy:
+ if (xfer->tx_buf)
+ dma_unmap_single(controller->dev, tx_dma, xfer->len,
+ DMA_TO_DEVICE);
+err_map_tx:
+ if (rx_align)
+ dma_unmap_single(controller->dev, rx_dummy_dma,
+ controller->in_fifo_sz, DMA_FROM_DEVICE);
+err_map_rx_dummy:
+ if (xfer->rx_buf)
+ dma_unmap_single(controller->dev, rx_dma, xfer->len,
+ DMA_FROM_DEVICE);
+
+ return ret;
+}
+
static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
{
struct spi_qup *controller = dev_id;
@@ -315,11 +545,13 @@ static irqreturn_t spi_qup_qup_irq(int i
error = -EIO;
}
- if (opflags & QUP_OP_IN_SERVICE_FLAG)
- spi_qup_fifo_read(controller, xfer);
+ if (!controller->use_dma) {
+ if (opflags & QUP_OP_IN_SERVICE_FLAG)
+ spi_qup_fifo_read(controller, xfer);
- if (opflags & QUP_OP_OUT_SERVICE_FLAG)
- spi_qup_fifo_write(controller, xfer);
+ if (opflags & QUP_OP_OUT_SERVICE_FLAG)
+ spi_qup_fifo_write(controller, xfer);
+ }
spin_lock_irqsave(&controller->lock, flags);
controller->error = error;
@@ -339,6 +571,8 @@ static int spi_qup_io_config(struct spi_
struct spi_qup *controller = spi_master_get_devdata(spi->master);
u32 config, iomode, mode;
int ret, n_words, w_size;
+ size_t dma_align = dma_get_cache_alignment();
+ u32 dma_available = 0;
if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) {
dev_err(controller->dev, "too big size for loopback %d > %d\n",
@@ -367,6 +601,11 @@ static int spi_qup_io_config(struct spi_
n_words = xfer->len / w_size;
controller->w_size = w_size;
+ if (controller->rx_chan &&
+ IS_ALIGNED((size_t)xfer->tx_buf, dma_align) &&
+ IS_ALIGNED((size_t)xfer->rx_buf, dma_align))
+ dma_available = 1;
+
if (n_words <= (controller->in_fifo_sz / sizeof(u32))) {
mode = QUP_IO_M_MODE_FIFO;
writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT);
@@ -374,19 +613,31 @@ static int spi_qup_io_config(struct spi_
/* must be zero for FIFO */
writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
- } else {
+ controller->use_dma = 0;
+ } else if (!dma_available) {
mode = QUP_IO_M_MODE_BLOCK;
writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
/* must be zero for BLOCK and BAM */
writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
+ controller->use_dma = 0;
+ } else {
+ mode = QUP_IO_M_MODE_DMOV;
+ writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
+ writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
+ controller->use_dma = 1;
}
iomode = readl_relaxed(controller->base + QUP_IO_M_MODES);
/* Set input and output transfer mode */
iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK);
- iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN);
+
+ if (!controller->use_dma)
+ iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN);
+ else
+ iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN;
+
iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
@@ -419,6 +670,14 @@ static int spi_qup_io_config(struct spi_
config &= ~(QUP_CONFIG_NO_INPUT | QUP_CONFIG_NO_OUTPUT | QUP_CONFIG_N);
config |= xfer->bits_per_word - 1;
config |= QUP_CONFIG_SPI_MODE;
+
+ if (controller->use_dma) {
+ if (!xfer->tx_buf)
+ config |= QUP_CONFIG_NO_OUTPUT;
+ if (!xfer->rx_buf)
+ config |= QUP_CONFIG_NO_INPUT;
+ }
+
writel_relaxed(config, controller->base + QUP_CONFIG);
/* only write to OPERATIONAL_MASK when register is present */
@@ -452,25 +711,29 @@ static int spi_qup_transfer_one(struct s
controller->tx_bytes = 0;
spin_unlock_irqrestore(&controller->lock, flags);
- if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
- dev_warn(controller->dev, "cannot set RUN state\n");
- goto exit;
- }
+ if (controller->use_dma) {
+ ret = spi_qup_do_dma(controller, xfer);
+ } else {
+ if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
+ dev_warn(controller->dev, "cannot set RUN state\n");
+ goto exit;
+ }
- if (spi_qup_set_state(controller, QUP_STATE_PAUSE)) {
- dev_warn(controller->dev, "cannot set PAUSE state\n");
- goto exit;
- }
+ if (spi_qup_set_state(controller, QUP_STATE_PAUSE)) {
+ dev_warn(controller->dev, "cannot set PAUSE state\n");
+ goto exit;
+ }
- spi_qup_fifo_write(controller, xfer);
+ spi_qup_fifo_write(controller, xfer);
- if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
- dev_warn(controller->dev, "cannot set EXECUTE state\n");
- goto exit;
- }
+ if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
+ dev_warn(controller->dev, "cannot set EXECUTE state\n");
+ goto exit;
+ }
- if (!wait_for_completion_timeout(&controller->done, timeout))
- ret = -ETIMEDOUT;
+ if (!wait_for_completion_timeout(&controller->done, timeout))
+ ret = -ETIMEDOUT;
+ }
exit:
spi_qup_set_state(controller, QUP_STATE_RESET);
spin_lock_irqsave(&controller->lock, flags);
@@ -553,6 +816,7 @@ static int spi_qup_probe(struct platform
master->transfer_one = spi_qup_transfer_one;
master->dev.of_node = pdev->dev.of_node;
master->auto_runtime_pm = true;
+ master->dma_alignment = dma_get_cache_alignment();
platform_set_drvdata(pdev, master);
@@ -618,6 +882,56 @@ static int spi_qup_probe(struct platform
QUP_ERROR_INPUT_UNDER_RUN | QUP_ERROR_OUTPUT_UNDER_RUN,
base + QUP_ERROR_FLAGS_EN);
+ /* allocate dma resources, if available */
+ controller->rx_chan = dma_request_slave_channel(&pdev->dev, "rx");
+ if (controller->rx_chan) {
+ controller->tx_chan =
+ dma_request_slave_channel(&pdev->dev, "tx");
+
+ if (!controller->tx_chan) {
+ dev_err(&pdev->dev, "Failed to allocate dma tx chan");
+ dma_release_channel(controller->rx_chan);
+ }
+
+ /* set DMA parameters */
+ controller->rx_conf.device_fc = 1;
+ controller->rx_conf.src_addr = res->start + QUP_INPUT_FIFO;
+ controller->rx_conf.src_maxburst = controller->in_blk_sz;
+
+ controller->tx_conf.device_fc = 1;
+ controller->tx_conf.dst_addr = res->start + QUP_OUTPUT_FIFO;
+ controller->tx_conf.dst_maxburst = controller->out_blk_sz;
+
+ if (dmaengine_slave_config(controller->rx_chan,
+ &controller->rx_conf)) {
+ dev_err(&pdev->dev, "failed to configure RX channel\n");
+
+ dma_release_channel(controller->rx_chan);
+ dma_release_channel(controller->tx_chan);
+ controller->tx_chan = NULL;
+ controller->rx_chan = NULL;
+ } else if (dmaengine_slave_config(controller->tx_chan,
+ &controller->tx_conf)) {
+ dev_err(&pdev->dev, "failed to configure TX channel\n");
+
+ dma_release_channel(controller->rx_chan);
+ dma_release_channel(controller->tx_chan);
+ controller->tx_chan = NULL;
+ controller->rx_chan = NULL;
+ }
+
+ controller->dummy = devm_kmalloc(controller->dev, PAGE_SIZE,
+ GFP_KERNEL);
+
+ if (!controller->dummy) {
+ dma_release_channel(controller->rx_chan);
+ dma_release_channel(controller->tx_chan);
+ controller->tx_chan = NULL;
+ controller->rx_chan = NULL;
+ }
+ }
+
+
writel_relaxed(0, base + SPI_CONFIG);
writel_relaxed(SPI_IO_C_NO_TRI_STATE, base + SPI_IO_CONTROL);
@@ -730,6 +1044,11 @@ static int spi_qup_remove(struct platfor
if (ret)
return ret;
+ if (controller->rx_chan)
+ dma_release_channel(controller->rx_chan);
+ if (controller->tx_chan)
+ dma_release_channel(controller->tx_chan);
+
clk_disable_unprepare(controller->cclk);
clk_disable_unprepare(controller->iclk);

View file

@ -0,0 +1,376 @@
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: [v3] spi: qup: Fix incorrect block transfers
From: Andy Gross <agross@codeaurora.org>
X-Patchwork-Id: 5007321
Message-Id: <1412112088-25928-1-git-send-email-agross@codeaurora.org>
To: Mark Brown <broonie@kernel.org>
Cc: linux-spi@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org, linux-arm-msm@vger.kernel.org,
"Ivan T. Ivanov" <iivanov@mm-sol.com>,
Bjorn Andersson <bjorn.andersson@sonymobile.com>,
Kumar Gala <galak@codeaurora.org>, Andy Gross <agross@codeaurora.org>
Date: Tue, 30 Sep 2014 16:21:28 -0500
This patch fixes a number of errors with the QUP block transfer mode. Errors
manifested themselves as input underruns, output overruns, and timed out
transactions.
The block mode does not require the priming that occurs in FIFO mode. At the
moment that the QUP is placed into the RUN state, the QUP will immediately raise
an interrupt if the request is a write. Therefore, there is no need to prime
the pump.
In addition, the block transfers require that whole blocks of data are
read/written at a time. The last block of data that completes a transaction may
contain less than a full blocks worth of data.
Each block of data results in an input/output service interrupt accompanied with
a input/output block flag set. Additional block reads/writes require clearing
of the service flag. It is ok to check for additional blocks of data in the
ISR, but you have to ack every block you transfer. Imbalanced acks result in
early return from complete transactions with pending interrupts that still have
to be ack'd. The next transaction can be affected by these interrupts.
Transactions are deemed complete when the MAX_INPUT or MAX_OUTPUT flag are set.
Changes from v2:
- Added in additional completion check so that transaction done is not
prematurely signaled.
- Fixed various review comments.
Changes from v1:
- Split out read/write block function.
- Removed extraneous checks for transfer length
Signed-off-by: Andy Gross <agross@codeaurora.org>
---
drivers/spi/spi-qup.c | 201 ++++++++++++++++++++++++++++++++++++-------------
1 file changed, 148 insertions(+), 53 deletions(-)
--- a/drivers/spi/spi-qup.c
+++ b/drivers/spi/spi-qup.c
@@ -82,6 +82,8 @@
#define QUP_IO_M_MODE_BAM 3
/* QUP_OPERATIONAL fields */
+#define QUP_OP_IN_BLOCK_READ_REQ BIT(13)
+#define QUP_OP_OUT_BLOCK_WRITE_REQ BIT(12)
#define QUP_OP_MAX_INPUT_DONE_FLAG BIT(11)
#define QUP_OP_MAX_OUTPUT_DONE_FLAG BIT(10)
#define QUP_OP_IN_SERVICE_FLAG BIT(9)
@@ -147,6 +149,7 @@ struct spi_qup {
int tx_bytes;
int rx_bytes;
int qup_v1;
+ int mode;
int use_dma;
@@ -213,30 +216,14 @@ static int spi_qup_set_state(struct spi_
return 0;
}
-
-static void spi_qup_fifo_read(struct spi_qup *controller,
- struct spi_transfer *xfer)
+static void spi_qup_fill_read_buffer(struct spi_qup *controller,
+ struct spi_transfer *xfer, u32 data)
{
u8 *rx_buf = xfer->rx_buf;
- u32 word, state;
- int idx, shift, w_size;
-
- w_size = controller->w_size;
-
- while (controller->rx_bytes < xfer->len) {
-
- state = readl_relaxed(controller->base + QUP_OPERATIONAL);
- if (0 == (state & QUP_OP_IN_FIFO_NOT_EMPTY))
- break;
-
- word = readl_relaxed(controller->base + QUP_INPUT_FIFO);
-
- if (!rx_buf) {
- controller->rx_bytes += w_size;
- continue;
- }
+ int idx, shift;
- for (idx = 0; idx < w_size; idx++, controller->rx_bytes++) {
+ if (rx_buf)
+ for (idx = 0; idx < controller->w_size; idx++) {
/*
* The data format depends on bytes per SPI word:
* 4 bytes: 0x12345678
@@ -244,41 +231,139 @@ static void spi_qup_fifo_read(struct spi
* 1 byte : 0x00000012
*/
shift = BITS_PER_BYTE;
- shift *= (w_size - idx - 1);
- rx_buf[controller->rx_bytes] = word >> shift;
+ shift *= (controller->w_size - idx - 1);
+ rx_buf[controller->rx_bytes + idx] = data >> shift;
+ }
+
+ controller->rx_bytes += controller->w_size;
+}
+
+static void spi_qup_prepare_write_data(struct spi_qup *controller,
+ struct spi_transfer *xfer, u32 *data)
+{
+ const u8 *tx_buf = xfer->tx_buf;
+ u32 val;
+ int idx;
+
+ *data = 0;
+
+ if (tx_buf)
+ for (idx = 0; idx < controller->w_size; idx++) {
+ val = tx_buf[controller->tx_bytes + idx];
+ *data |= val << (BITS_PER_BYTE * (3 - idx));
}
+
+ controller->tx_bytes += controller->w_size;
+}
+
+static void spi_qup_fifo_read(struct spi_qup *controller,
+ struct spi_transfer *xfer)
+{
+ u32 data;
+
+ /* clear service request */
+ writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
+ controller->base + QUP_OPERATIONAL);
+
+ while (controller->rx_bytes < xfer->len) {
+ if (!(readl_relaxed(controller->base + QUP_OPERATIONAL) &
+ QUP_OP_IN_FIFO_NOT_EMPTY))
+ break;
+
+ data = readl_relaxed(controller->base + QUP_INPUT_FIFO);
+
+ spi_qup_fill_read_buffer(controller, xfer, data);
}
}
static void spi_qup_fifo_write(struct spi_qup *controller,
- struct spi_transfer *xfer)
+ struct spi_transfer *xfer)
{
- const u8 *tx_buf = xfer->tx_buf;
- u32 word, state, data;
- int idx, w_size;
+ u32 data;
- w_size = controller->w_size;
+ /* clear service request */
+ writel_relaxed(QUP_OP_OUT_SERVICE_FLAG,
+ controller->base + QUP_OPERATIONAL);
while (controller->tx_bytes < xfer->len) {
- state = readl_relaxed(controller->base + QUP_OPERATIONAL);
- if (state & QUP_OP_OUT_FIFO_FULL)
+ if (readl_relaxed(controller->base + QUP_OPERATIONAL) &
+ QUP_OP_OUT_FIFO_FULL)
break;
- word = 0;
- for (idx = 0; idx < w_size; idx++, controller->tx_bytes++) {
+ spi_qup_prepare_write_data(controller, xfer, &data);
+ writel_relaxed(data, controller->base + QUP_OUTPUT_FIFO);
- if (!tx_buf) {
- controller->tx_bytes += w_size;
- break;
- }
+ }
+}
- data = tx_buf[controller->tx_bytes];
- word |= data << (BITS_PER_BYTE * (3 - idx));
- }
+static void spi_qup_block_read(struct spi_qup *controller,
+ struct spi_transfer *xfer)
+{
+ u32 data;
+ u32 reads_per_blk = controller->in_blk_sz >> 2;
+ u32 num_words = (xfer->len - controller->rx_bytes) / controller->w_size;
+ int i;
+
+ do {
+ /* ACK by clearing service flag */
+ writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
+ controller->base + QUP_OPERATIONAL);
+
+ /* transfer up to a block size of data in a single pass */
+ for (i = 0; num_words && i < reads_per_blk; i++, num_words--) {
+
+ /* read data and fill up rx buffer */
+ data = readl_relaxed(controller->base + QUP_INPUT_FIFO);
+ spi_qup_fill_read_buffer(controller, xfer, data);
+ }
+
+ /* check to see if next block is ready */
+ if (!(readl_relaxed(controller->base + QUP_OPERATIONAL) &
+ QUP_OP_IN_BLOCK_READ_REQ))
+ break;
- writel_relaxed(word, controller->base + QUP_OUTPUT_FIFO);
- }
+ } while (num_words);
+
+ /*
+ * Due to extra stickiness of the QUP_OP_IN_SERVICE_FLAG during block
+ * reads, it has to be cleared again at the very end
+ */
+ if (readl_relaxed(controller->base + QUP_OPERATIONAL) &
+ QUP_OP_MAX_INPUT_DONE_FLAG)
+ writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
+ controller->base + QUP_OPERATIONAL);
+
+}
+
+static void spi_qup_block_write(struct spi_qup *controller,
+ struct spi_transfer *xfer)
+{
+ u32 data;
+ u32 writes_per_blk = controller->out_blk_sz >> 2;
+ u32 num_words = (xfer->len - controller->tx_bytes) / controller->w_size;
+ int i;
+
+ do {
+ /* ACK by clearing service flag */
+ writel_relaxed(QUP_OP_OUT_SERVICE_FLAG,
+ controller->base + QUP_OPERATIONAL);
+
+ /* transfer up to a block size of data in a single pass */
+ for (i = 0; num_words && i < writes_per_blk; i++, num_words--) {
+
+ /* swizzle the bytes for output and write out */
+ spi_qup_prepare_write_data(controller, xfer, &data);
+ writel_relaxed(data,
+ controller->base + QUP_OUTPUT_FIFO);
+ }
+
+ /* check to see if next block is ready */
+ if (!(readl_relaxed(controller->base + QUP_OPERATIONAL) &
+ QUP_OP_OUT_BLOCK_WRITE_REQ))
+ break;
+
+ } while (num_words);
}
static void qup_dma_callback(void *data)
@@ -515,9 +600,9 @@ static irqreturn_t spi_qup_qup_irq(int i
writel_relaxed(qup_err, controller->base + QUP_ERROR_FLAGS);
writel_relaxed(spi_err, controller->base + SPI_ERROR_FLAGS);
- writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
if (!xfer) {
+ writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
dev_err_ratelimited(controller->dev, "unexpected irq %08x %08x %08x\n",
qup_err, spi_err, opflags);
return IRQ_HANDLED;
@@ -546,11 +631,19 @@ static irqreturn_t spi_qup_qup_irq(int i
}
if (!controller->use_dma) {
- if (opflags & QUP_OP_IN_SERVICE_FLAG)
- spi_qup_fifo_read(controller, xfer);
+ if (opflags & QUP_OP_IN_SERVICE_FLAG) {
+ if (opflags & QUP_OP_IN_BLOCK_READ_REQ)
+ spi_qup_block_read(controller, xfer);
+ else
+ spi_qup_fifo_read(controller, xfer);
+ }
- if (opflags & QUP_OP_OUT_SERVICE_FLAG)
- spi_qup_fifo_write(controller, xfer);
+ if (opflags & QUP_OP_OUT_SERVICE_FLAG) {
+ if (opflags & QUP_OP_OUT_BLOCK_WRITE_REQ)
+ spi_qup_block_write(controller, xfer);
+ else
+ spi_qup_fifo_write(controller, xfer);
+ }
}
spin_lock_irqsave(&controller->lock, flags);
@@ -558,7 +651,8 @@ static irqreturn_t spi_qup_qup_irq(int i
controller->xfer = xfer;
spin_unlock_irqrestore(&controller->lock, flags);
- if (controller->rx_bytes == xfer->len || error)
+ if ((controller->rx_bytes == xfer->len &&
+ (opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) || error)
complete(&controller->done);
return IRQ_HANDLED;
@@ -569,7 +663,7 @@ static irqreturn_t spi_qup_qup_irq(int i
static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
{
struct spi_qup *controller = spi_master_get_devdata(spi->master);
- u32 config, iomode, mode, control;
+ u32 config, iomode, control;
int ret, n_words, w_size;
size_t dma_align = dma_get_cache_alignment();
u32 dma_available = 0;
@@ -607,7 +701,7 @@ static int spi_qup_io_config(struct spi_
dma_available = 1;
if (n_words <= (controller->in_fifo_sz / sizeof(u32))) {
- mode = QUP_IO_M_MODE_FIFO;
+ controller->mode = QUP_IO_M_MODE_FIFO;
writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT);
writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT);
/* must be zero for FIFO */
@@ -615,7 +709,7 @@ static int spi_qup_io_config(struct spi_
writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
controller->use_dma = 0;
} else if (!dma_available) {
- mode = QUP_IO_M_MODE_BLOCK;
+ controller->mode = QUP_IO_M_MODE_BLOCK;
writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
/* must be zero for BLOCK and BAM */
@@ -623,7 +717,7 @@ static int spi_qup_io_config(struct spi_
writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
controller->use_dma = 0;
} else {
- mode = QUP_IO_M_MODE_DMOV;
+ controller->mode = QUP_IO_M_MODE_DMOV;
writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
controller->use_dma = 1;
@@ -638,8 +732,8 @@ static int spi_qup_io_config(struct spi_
else
iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN;
- iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
- iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
+ iomode |= (controller->mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
+ iomode |= (controller->mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
writel_relaxed(iomode, controller->base + QUP_IO_M_MODES);
@@ -733,7 +827,8 @@ static int spi_qup_transfer_one(struct s
goto exit;
}
- spi_qup_fifo_write(controller, xfer);
+ if (controller->mode == QUP_IO_M_MODE_FIFO)
+ spi_qup_fifo_write(controller, xfer);
if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
dev_warn(controller->dev, "cannot set EXECUTE state\n");
@@ -750,6 +845,7 @@ exit:
if (!ret)
ret = controller->error;
spin_unlock_irqrestore(&controller->lock, flags);
+
return ret;
}

View file

@ -0,0 +1,56 @@
From 4faba89e3ffbb1c5f6232651375b9b3212b50f02 Mon Sep 17 00:00:00 2001
From: Andy Gross <agross@codeaurora.org>
Date: Thu, 15 Jan 2015 17:56:02 -0800
Subject: [PATCH] spi: qup: Ensure done detection
This patch fixes an issue where a SPI transaction has completed, but the done
condition is missed. This occurs because at the time of interrupt the
MAX_INPUT_DONE_FLAG is not asserted. However, in the process of reading blocks
of data from the FIFO, the last portion of data comes in.
The opflags read at the beginning of the irq handler no longer matches the
current opflag state. To get around this condition, the block read function
should update the opflags so that done detection is correct after the return.
Change-Id: If109e0eeb432f96000d765c4b34dbb2269f8093f
Signed-off-by: Andy Gross <agross@codeaurora.org>
---
drivers/spi/spi-qup.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
--- a/drivers/spi/spi-qup.c
+++ b/drivers/spi/spi-qup.c
@@ -298,7 +298,7 @@ static void spi_qup_fifo_write(struct sp
}
static void spi_qup_block_read(struct spi_qup *controller,
- struct spi_transfer *xfer)
+ struct spi_transfer *xfer, u32 *opflags)
{
u32 data;
u32 reads_per_blk = controller->in_blk_sz >> 2;
@@ -327,10 +327,12 @@ static void spi_qup_block_read(struct sp
/*
* Due to extra stickiness of the QUP_OP_IN_SERVICE_FLAG during block
- * reads, it has to be cleared again at the very end
+ * reads, it has to be cleared again at the very end. However, be sure
+ * to refresh opflags value because MAX_INPUT_DONE_FLAG may now be
+ * present and this is used to determine if transaction is complete
*/
- if (readl_relaxed(controller->base + QUP_OPERATIONAL) &
- QUP_OP_MAX_INPUT_DONE_FLAG)
+ *opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
+ if (*opflags & QUP_OP_MAX_INPUT_DONE_FLAG)
writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
controller->base + QUP_OPERATIONAL);
@@ -633,7 +635,7 @@ static irqreturn_t spi_qup_qup_irq(int i
if (!controller->use_dma) {
if (opflags & QUP_OP_IN_SERVICE_FLAG) {
if (opflags & QUP_OP_IN_BLOCK_READ_REQ)
- spi_qup_block_read(controller, xfer);
+ spi_qup_block_read(controller, xfer, &opflags);
else
spi_qup_fifo_read(controller, xfer);
}

View file

@ -0,0 +1,72 @@
From fded70251b1b58f68de1d3757ece9965f0b75452 Mon Sep 17 00:00:00 2001
From: Mathieu Olivari <mathieu@codeaurora.org>
Date: Thu, 19 Feb 2015 20:19:30 -0800
Subject: [PATCH 1/3] watchdog: qcom: use timer devicetree binding
MSM watchdog configuration happens in the same register block as the
timer, so we'll use the same binding as the existing timer.
The qcom-wdt will now be probed when devicetree has an entry compatible
with "qcom,kpss-timer" or "qcom-scss-timer".
Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
---
drivers/watchdog/qcom-wdt.c | 21 +++++++++++++++------
1 file changed, 15 insertions(+), 6 deletions(-)
diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c
index aa85618..aa03ca8 100644
--- a/drivers/watchdog/qcom-wdt.c
+++ b/drivers/watchdog/qcom-wdt.c
@@ -20,9 +20,9 @@
#include <linux/reboot.h>
#include <linux/watchdog.h>
-#define WDT_RST 0x0
-#define WDT_EN 0x8
-#define WDT_BITE_TIME 0x24
+#define WDT_RST 0x38
+#define WDT_EN 0x40
+#define WDT_BITE_TIME 0x5C
struct qcom_wdt {
struct watchdog_device wdd;
@@ -117,6 +117,8 @@ static int qcom_wdt_probe(struct platform_device *pdev)
{
struct qcom_wdt *wdt;
struct resource *res;
+ struct device_node *np = pdev->dev.of_node;
+ u32 percpu_offset;
int ret;
wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
@@ -124,6 +126,14 @@ static int qcom_wdt_probe(struct platform_device *pdev)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ /* We use CPU0's DGT for the watchdog */
+ if (of_property_read_u32(np, "cpu-offset", &percpu_offset))
+ percpu_offset = 0;
+
+ res->start += percpu_offset;
+ res->end += percpu_offset;
+
wdt->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(wdt->base))
return PTR_ERR(wdt->base);
@@ -203,9 +213,8 @@ static int qcom_wdt_remove(struct platform_device *pdev)
}
static const struct of_device_id qcom_wdt_of_table[] = {
- { .compatible = "qcom,kpss-wdt-msm8960", },
- { .compatible = "qcom,kpss-wdt-apq8064", },
- { .compatible = "qcom,kpss-wdt-ipq8064", },
+ { .compatible = "qcom,kpss-timer" },
+ { .compatible = "qcom,scss-timer" },
{ },
};
MODULE_DEVICE_TABLE(of, qcom_wdt_of_table);
--
1.9.1

View file

@ -0,0 +1,53 @@
From 297cf8136ecd6a56520888fd28948393766b8ee7 Mon Sep 17 00:00:00 2001
From: Mathieu Olivari <mathieu@codeaurora.org>
Date: Thu, 19 Feb 2015 20:27:39 -0800
Subject: [PATCH 2/3] ARM: qcom: add description of KPSS WDT for IPQ8064
Add the watchdog related entries to the Krait Processor Sub-system
(KPSS) timer IPQ8064 devicetree section. Also, add a fixed-clock
description of SLEEP_CLK, which will do for now.
Signed-off-by: Josh Cartwright <joshc@codeaurora.org>
Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
---
arch/arm/boot/dts/qcom-ipq8064.dtsi | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/qcom-ipq8064.dtsi b/arch/arm/boot/dts/qcom-ipq8064.dtsi
index cb225da..d01f618 100644
--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
@@ -60,6 +60,14 @@
};
};
+ clocks {
+ sleep_clk: sleep_clk {
+ compatible = "fixed-clock";
+ clock-frequency = <32768>;
+ #clock-cells = <0>;
+ };
+ };
+
soc: soc {
#address-cells = <1>;
#size-cells = <1>;
@@ -89,10 +97,14 @@
compatible = "qcom,kpss-timer", "qcom,msm-timer";
interrupts = <1 1 0x301>,
<1 2 0x301>,
- <1 3 0x301>;
+ <1 3 0x301>,
+ <1 4 0x301>,
+ <1 5 0x301>;
reg = <0x0200a000 0x100>;
clock-frequency = <25000000>,
<32768>;
+ clocks = <&sleep_clk>;
+ clock-names = "sleep";
cpu-offset = <0x80000>;
};
--
1.9.1

View file

@ -0,0 +1,50 @@
From e535f01dffb6dd9e09934fa219be52af3437a8f6 Mon Sep 17 00:00:00 2001
From: Mathieu Olivari <mathieu@codeaurora.org>
Date: Thu, 19 Feb 2015 20:36:27 -0800
Subject: [PATCH 3/3] ARM: msm: add watchdog entries to DT timer binding doc
The watchdog has been reworked to use the same DT node as the timer.
This change is updating the device tree doc accordingly.
Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>
---
Documentation/devicetree/bindings/arm/msm/timer.txt | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
--- a/Documentation/devicetree/bindings/arm/msm/timer.txt
+++ b/Documentation/devicetree/bindings/arm/msm/timer.txt
@@ -9,11 +9,17 @@ Properties:
"qcom,scss-timer" - scorpion subsystem
- interrupts : Interrupts for the debug timer, the first general purpose
- timer, and optionally a second general purpose timer in that
- order.
+ timer, and optionally a second general purpose timer, and
+ optionally as well, 2 watchdog interrupts, in that order.
- reg : Specifies the base address of the timer registers.
+- clocks: Reference to the parent clocks, one per output clock. The parents
+ must appear in the same order as the clock names.
+
+- clock-names: The name of the clocks as free-form strings. They should be in
+ the same order as the clocks.
+
- clock-frequency : The frequency of the debug timer and the general purpose
timer(s) in Hz in that order.
@@ -29,9 +35,13 @@ Example:
compatible = "qcom,scss-timer", "qcom,msm-timer";
interrupts = <1 1 0x301>,
<1 2 0x301>,
- <1 3 0x301>;
+ <1 3 0x301>,
+ <1 4 0x301>,
+ <1 5 0x301>;
reg = <0x0200a000 0x100>;
clock-frequency = <19200000>,
<32768>;
+ clocks = <&sleep_clk>;
+ clock-names = "sleep";
cpu-offset = <0x40000>;
};

View file

@ -0,0 +1,46 @@
--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
@@ -14,6 +14,14 @@
};
};
+ alias {
+ serial0 = &uart4;
+ };
+
+ chosen {
+ linux,stdout-path = "serial0:115200n8";
+ };
+
soc {
pinmux@800000 {
i2c4_pins: i2c4_pinmux {
--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
@@ -140,7 +140,7 @@
ranges;
status = "disabled";
- serial@12490000 {
+ uart2: serial@12490000 {
compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
reg = <0x12490000 0x1000>,
<0x12480000 0x1000>;
@@ -175,7 +175,7 @@
ranges;
status = "disabled";
- serial@16340000 {
+ uart4: serial@16340000 {
compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
reg = <0x16340000 0x1000>,
<0x16300000 0x1000>;
@@ -209,7 +209,7 @@
ranges;
status = "disabled";
- serial@1a240000 {
+ uart5: serial@1a240000 {
compatible = "qcom,msm-uartdm-v1.3", "qcom,msm-uartdm";
reg = <0x1a240000 0x1000>,
<0x1a200000 0x1000>;

View file

@ -0,0 +1,35 @@
--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
@@ -78,13 +78,28 @@
reg = <0>;
partition@0 {
- label = "rootfs";
- reg = <0x0 0x1000000>;
+ label = "lowlevel_init";
+ reg = <0x0 0x1b0000>;
};
partition@1 {
- label = "scratch";
- reg = <0x1000000 0x1000000>;
+ label = "u-boot";
+ reg = <0x1b0000 0x80000>;
+ };
+
+ partition@2 {
+ label = "u-boot-env";
+ reg = <0x230000 0x40000>;
+ };
+
+ partition@3 {
+ label = "caldata";
+ reg = <0x270000 0x40000>;
+ };
+
+ partition@4 {
+ label = "firmware";
+ reg = <0x2b0000 0x1d50000>;
};
};
};

View file

@ -0,0 +1,172 @@
--- a/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
+++ b/arch/arm/boot/dts/qcom-ipq8064-ap148.dts
@@ -18,8 +18,15 @@
bootargs = "console=ttyMSM0,115200 root=/dev/mtdblock12 rootfstype=squashfs,jffs2";
};
+ aliases {
+ mdio-gpio0 = &mdio0;
+ };
+
soc {
pinmux@800000 {
+ pinctrl-0 = <&mdio0_pins &rgmii2_pins>;
+ pinctrl-names = "default";
+
i2c4_pins: i2c4_pinmux {
pins = "gpio12", "gpio13";
function = "gsbi4";
@@ -34,6 +41,25 @@
bias-none;
};
};
+
+ mdio0_pins: mdio0_pins {
+ mux {
+ pins = "gpio0", "gpio1";
+ function = "gpio";
+ drive-strength = <8>;
+ bias-disable;
+ };
+ };
+
+ rgmii2_pins: rgmii2_pins {
+ mux {
+ pins = "gpio27", "gpio28", "gpio29", "gpio30", "gpio31", "gpio32",
+ "gpio51", "gpio52", "gpio59", "gpio60", "gpio61", "gpio62" ;
+ function = "rgmii2";
+ drive-strength = <8>;
+ bias-disable;
+ };
+ };
};
gsbi@16300000 {
@@ -72,6 +98,7 @@
#size-cells = <1>;
spi-max-frequency = <50000000>;
reg = <0>;
+ m25p,fast-read;
partition@0 {
label = "0:SBL1";
@@ -148,5 +175,66 @@
sata@29000000 {
status = "ok";
};
+
+ mdio0: mdio {
+ compatible = "virtual,mdio-gpio";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ gpios = <&qcom_pinmux 1 0 &qcom_pinmux 0 0>;
+
+ phy0: ethernet-phy@0 {
+ device_type = "ethernet-phy";
+ reg = <0>;
+ qca,ar8327-initvals = <
+ 0x00004 0x7600000 /* PAD0_MODE */
+ 0x00008 0x1000000 /* PAD5_MODE */
+ 0x0000c 0x80 /* PAD6_MODE */
+ 0x000e4 0xaa545 /* MAC_POWER_SEL */
+ 0x000e0 0xc74164de /* SGMII_CTRL */
+ 0x0007c 0x4e /* PORT0_STATUS */
+ 0x00094 0x4e /* PORT6_STATUS */
+ >;
+ };
+
+ phy4: ethernet-phy@4 {
+ device_type = "ethernet-phy";
+ reg = <4>;
+ };
+ };
+
+ nss-gmac-common {
+ reg = <0x03000000 0x0000FFFF 0x1bb00000 0x0000FFFF 0x00900000 0x00004000>;
+ reg-names = "nss_reg_base" , "qsgmii_reg_base", "clk_ctl_base";
+ };
+
+ gmac1: ethernet@37200000 {
+ status = "ok";
+ phy-mode = "rgmii";
+ qcom,id = <1>;
+ qcom,phy_mdio_addr = <4>;
+ qcom,poll_required = <1>;
+ qcom,rgmii_delay = <0>;
+ qcom,emulation = <0>;
+ qcom,forced_speed = <1000>;
+ qcom,forced_duplex = <1>;
+ qcom,socver = <0>;
+ local-mac-address = [000000000000];
+ mdiobus = <&mdio0>;
+ };
+
+ gmac2: ethernet@37400000 {
+ status = "ok";
+ phy-mode = "sgmii";
+ qcom,id = <2>;
+ qcom,phy_mdio_addr = <0>;
+ qcom,poll_required = <0>;
+ qcom,rgmii_delay = <0>;
+ qcom,emulation = <0>;
+ qcom,forced_speed = <1000>;
+ qcom,forced_duplex = <1>;
+ qcom,socver = <0>;
+ local-mac-address = [000000000000];
+ mdiobus = <&mdio0>;
+ };
};
};
--- a/arch/arm/boot/dts/qcom-ipq8064.dtsi
+++ b/arch/arm/boot/dts/qcom-ipq8064.dtsi
@@ -3,6 +3,7 @@
#include "skeleton.dtsi"
#include <dt-bindings/clock/qcom,gcc-ipq806x.h>
#include <dt-bindings/soc/qcom,gsbi.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
/ {
model = "Qualcomm IPQ8064";
@@ -279,5 +280,42 @@
#clock-cells = <1>;
#reset-cells = <1>;
};
+
+ nss-gmac-common {
+ reg = <0x03000000 0x0000FFFF 0x1bb00000 0x0000FFFF 0x00900000 0x00004000>;
+ reg-names = "nss_reg_base" , "qsgmii_reg_base", "clk_ctl_base";
+ };
+
+ gmac0: ethernet@37000000 {
+ device_type = "network";
+ compatible = "qcom,nss-gmac";
+ reg = <0x37000000 0x200000>;
+ interrupts = <GIC_SPI 220 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ };
+
+ gmac1: ethernet@37200000 {
+ device_type = "network";
+ compatible = "qcom,nss-gmac";
+ reg = <0x37200000 0x200000>;
+ interrupts = <GIC_SPI 223 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ };
+
+ gmac2: ethernet@37400000 {
+ device_type = "network";
+ compatible = "qcom,nss-gmac";
+ reg = <0x37400000 0x200000>;
+ interrupts = <GIC_SPI 226 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ };
+
+ gmac3: ethernet@37600000 {
+ device_type = "network";
+ compatible = "qcom,nss-gmac";
+ reg = <0x37600000 0x200000>;
+ interrupts = <GIC_SPI 229 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ };
};
};