From 4d2a5a56b922ac37d2326d7b139697567aac37b8 Mon Sep 17 00:00:00 2001 From: David Hendricks Date: Tue, 8 Oct 2013 20:48:34 -0700 Subject: [PATCH] tegra124: add DMA support This adds basic DMA support. The actual code in this patch does very little since DMA transfers are pretty well intertwined with the module's usage. Perhaps in the future we'll add some helper functions so module code doesn't modify the DMA registers directly... BUG=none BRANCH=none TEST=Successfully used DMA to transmit data over SPI Change-Id: I3a76ec8350a71e24aac920beaf52b499fef271ec Signed-off-by: David Hendricks Reviewed-on: https://chromium-review.googlesource.com/172951 Reviewed-by: Ronald Minnich Commit-Queue: Ronald Minnich Tested-by: Ronald Minnich --- src/soc/nvidia/tegra124/Makefile.inc | 1 + src/soc/nvidia/tegra124/dma.c | 278 ++++++++++++++++++ src/soc/nvidia/tegra124/dma.h | 55 ++++ .../nvidia/tegra124/include/soc/addressmap.h | 1 + 4 files changed, 335 insertions(+) create mode 100644 src/soc/nvidia/tegra124/dma.c create mode 100644 src/soc/nvidia/tegra124/dma.h diff --git a/src/soc/nvidia/tegra124/Makefile.inc b/src/soc/nvidia/tegra124/Makefile.inc index 285ad27614..3d7d25d6c6 100644 --- a/src/soc/nvidia/tegra124/Makefile.inc +++ b/src/soc/nvidia/tegra124/Makefile.inc @@ -5,6 +5,7 @@ bootblock-y += bootblock_asm.S bootblock-y += cbfs.c bootblock-y += clock.c bootblock-y += i2c.c +bootblock-y += dma.c bootblock-y += monotonic_timer.c bootblock-y += power.c bootblock-y += ../tegra/gpio.c diff --git a/src/soc/nvidia/tegra124/dma.c b/src/soc/nvidia/tegra124/dma.c new file mode 100644 index 0000000000..8d71d76ac2 --- /dev/null +++ b/src/soc/nvidia/tegra124/dma.c @@ -0,0 +1,278 @@ +/* + * (C) Copyright 2010,2011 + * NVIDIA Corporation + * Copyright 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include +#include + +#include "dma.h" + +enum { + APB_DMA_CH0 = TEGRA_APB_DMA_BASE + 0x1000, + APB_DMA_CH1 = TEGRA_APB_DMA_BASE + 0x1040, + APB_DMA_CH2 = TEGRA_APB_DMA_BASE + 0x1080, + APB_DMA_CH3 = TEGRA_APB_DMA_BASE + 0x10c0, + APB_DMA_CH4 = TEGRA_APB_DMA_BASE + 0x1100, + APB_DMA_CH5 = TEGRA_APB_DMA_BASE + 0x1140, + APB_DMA_CH6 = TEGRA_APB_DMA_BASE + 0x1180, + APB_DMA_CH7 = TEGRA_APB_DMA_BASE + 0x11c0, + APB_DMA_CH8 = TEGRA_APB_DMA_BASE + 0x1200, + APB_DMA_CH9 = TEGRA_APB_DMA_BASE + 0x1240, + APB_DMA_CH10 = TEGRA_APB_DMA_BASE + 0x1280, + APB_DMA_CH11 = TEGRA_APB_DMA_BASE + 0x12c0, + APB_DMA_CH12 = TEGRA_APB_DMA_BASE + 0x1300, + APB_DMA_CH13 = TEGRA_APB_DMA_BASE + 0x1340, + APB_DMA_CH14 = TEGRA_APB_DMA_BASE + 0x1380, + APB_DMA_CH15 = TEGRA_APB_DMA_BASE + 0x13c0, + APB_DMA_CH16 = TEGRA_APB_DMA_BASE + 0x1400, + APB_DMA_CH17 = TEGRA_APB_DMA_BASE + 0x1440, + APB_DMA_CH18 = TEGRA_APB_DMA_BASE + 0x1480, + APB_DMA_CH19 = TEGRA_APB_DMA_BASE + 0x14c0, + APB_DMA_CH20 = TEGRA_APB_DMA_BASE + 0x1500, + APB_DMA_CH21 = TEGRA_APB_DMA_BASE + 0x1540, + APB_DMA_CH22 = TEGRA_APB_DMA_BASE + 0x1580, + APB_DMA_CH23 = TEGRA_APB_DMA_BASE + 0x15c0, + APB_DMA_CH24 = TEGRA_APB_DMA_BASE + 0x1600, + APB_DMA_CH25 = TEGRA_APB_DMA_BASE + 0x1640, + APB_DMA_CH26 = TEGRA_APB_DMA_BASE + 0x1680, + APB_DMA_CH27 = TEGRA_APB_DMA_BASE + 0x16c0, + APB_DMA_CH28 = TEGRA_APB_DMA_BASE + 0x1700, + APB_DMA_CH29 = TEGRA_APB_DMA_BASE + 0x1740, + APB_DMA_CH30 = TEGRA_APB_DMA_BASE + 0x1780, + APB_DMA_CH31 = TEGRA_APB_DMA_BASE + 0x17c0, +}; + +/* + * Note: Many APB DMA controller registers are laid out such that each + * bit controls or represents the status for the corresponding channel. + * So we will not bother to list each individual bit in this case. + */ +#define APBDMA_COMMAND_GEN (1 << 31) + +#define APBDMA_CNTRL_REG_COUNT_VALUE_MASK 0xffff +#define APBDMA_CNTRL_REG_COUNT_VALUE_SHIFT 0 + +struct apb_dma { + u32 command; /* 0x00 */ + u32 status; /* 0x04 */ + u8 rsvd1[0x8]; + u32 cntrl_reg; /* 0x10 */ + u32 irq_sta_cpu; /* 0x14 */ + u32 irq_sta_cop; /* 0x18 */ + u32 irq_mask; /* 0x1c */ + u32 irq_mask_set; /* 0x20 */ + u32 irq_mask_clr; /* 0x24 */ + u32 trig_reg; /* 0x28 */ + u32 channel_trig_reg; /* 0x2c */ + u32 dma_status; /* 0x30 */ + u32 channel_en_reg; /* 0x34 */ + u32 security_reg; /* 0x38 */ + u32 channel_swid; /* 0x3c */ + u8 rsvd[4]; + u32 chan_wt_reg0; /* 0x44 */ + u32 chan_wt_reg1; /* 0x48 */ + u32 chan_wt_reg2; /* 0x4c */ + u32 chan_wr_reg3; /* 0x50 */ + u32 channel_swid1; /* 0x54 */ +} __attribute__((packed)); +struct apb_dma * const apb_dma = (struct apb_dma *)TEGRA_APB_DMA_BASE; + +/* + * FIXME: Naming in the doc included a superfluous _CHANNEL_n_ for + * each entry which I left out for the sake of conciseness. + */ +#define APBDMACHAN_CSR_ENB (1 << 31) +#define APBDMACHAN_CSR_IE_EOC (1 << 30) +#define APBDMACHAN_CSR_HOLD (1 << 29) +#define APBDMACHAN_CSR_DIR (1 << 28) +#define APBDMACHAN_CSR_ONCE (1 << 27) +#define APBDMACHAN_CSR_FLOW (1 << 21) +#define APBDMACHAN_CSR_REQ_SEL_MASK 0x1f +#define APBDMACHAN_CSR_REQ_SEL_SHIFT 16 + +#define APBDMACHAN_STA_BSY (1 << 31) +#define APBDMACHAN_STA_ISE_EOC (1 << 30) +#define APBDMACHAN_STA_HALT (1 << 29) +#define APBDMACHAN_STA_PING_PONG_STA (1 << 28) +#define APBDMACHAN_STA_DMA_ACTIVITY (1 << 27) +#define APBDMACHAN_STA_CHANNEL_PAUSE (1 << 26) + +#define APBDMACHAN_CSRE_CHANNEL_PAUSE (1 << 31) +#define APBDMACHAN_CSRE_TRIG_SEL_MASK 0x3f +#define APBDMACHAN_CSRE_TRIG_SEL_SHIFT 14 + +#define APBDMACHAN_AHB_PTR_MASK (0x3fffffff) +#define APBDMACHAN_AHB_PTR_SHIFT 2 + +#define APBDMACHAN_AHB_SEQ_INTR_ENB (1 << 31) +#define APBDMACHAN_AHB_SEQ_AHB_BUS_WIDTH_MASK 0x7 +#define APBDMACHAN_AHB_SEQ_AHB_BUS_WIDTH_SHIFT 28 +#define APBDMACHAN_AHB_SEQ_AHB_DATA_SWAP (1 << 27) +#define APBDMACHAN_AHB_SEQ_AHB_BURST_MASK 0x7 +#define APBDMACHAN_AHB_SEQ_AHB_BURST_SHIFT 24 +#define APBDMACHAN_AHB_SEQ_DBL_BUF (1 << 19) +#define APBDMACHAN_AHB_SEQ_WRAP_MASK 0x7 +#define APBDMACHAN_AHB_SEQ_WRAP_SHIFT 16 + +#define APBDMACHAN_AHB_SEQ_AHB_BUS_WIDTH_MASK 0x7 +#define APBDMACHAN_AHB_SEQ_AHB_BUS_WIDTH_SHIFT 28 + +#define APBDMACHAN_APB_PTR_MASK 0x3fffffff +#define APBDMACHAN_APB_PTR_SHIFT 2 + +#define APBDMACHAN_APB_SEQ_APB_BUS_WIDTH_MASK 0x7 +#define APBDMACHAN_APB_SEQ_APB_BUS_WIDTH_SHIFT 28 +#define APBDMACHAN_APB_SEQ_APB_DATA_SWAP (1 << 27) +#define APBDMACHAN_APB_SEQ_APB_ADDR_WRAP_MASK 0x7 +#define APBDMACHAN_APB_SEQ_APB_ADDR_WRAP_SHIFT 16 + +#define APBDMACHAN_WORD_TRANSFER_ + +#define APBDMACHAN_WORD_TRANSFER_MASK 0x0fffffff +#define APBDMACHAN_WORD_TRANSFER_SHIFT 2 + +#define NUM_APB_DMA_CHANNELS 32 +#define APB_DMA_CHANNEL(n) \ + { \ + .num = n, \ + .regs = (struct apb_dma_channel_regs *)APB_DMA_CH##n, \ + } +struct apb_dma_channel apb_dma_channels[NUM_APB_DMA_CHANNELS] = { + APB_DMA_CHANNEL(0), + APB_DMA_CHANNEL(1), + APB_DMA_CHANNEL(2), + APB_DMA_CHANNEL(3), + APB_DMA_CHANNEL(4), + APB_DMA_CHANNEL(5), + APB_DMA_CHANNEL(6), + APB_DMA_CHANNEL(7), + APB_DMA_CHANNEL(8), + APB_DMA_CHANNEL(9), + APB_DMA_CHANNEL(10), + APB_DMA_CHANNEL(11), + APB_DMA_CHANNEL(12), + APB_DMA_CHANNEL(13), + APB_DMA_CHANNEL(14), + APB_DMA_CHANNEL(15), + APB_DMA_CHANNEL(16), + APB_DMA_CHANNEL(17), + APB_DMA_CHANNEL(18), + APB_DMA_CHANNEL(19), + APB_DMA_CHANNEL(20), + APB_DMA_CHANNEL(21), + APB_DMA_CHANNEL(22), + APB_DMA_CHANNEL(23), + APB_DMA_CHANNEL(24), + APB_DMA_CHANNEL(25), + APB_DMA_CHANNEL(26), + APB_DMA_CHANNEL(27), + APB_DMA_CHANNEL(28), + APB_DMA_CHANNEL(29), + APB_DMA_CHANNEL(30), + APB_DMA_CHANNEL(31), +}; + +int dma_busy(struct apb_dma_channel * const channel) +{ + /* + * In continuous mode, the BSY_n bit in APB_DMA_STATUS and + * BSY in APBDMACHAN_CHANNEL_n_STA_0 will remain set as '1' so long + * as the channel is enabled. So for this function we'll use the + * DMA_ACTIVITY bit. + */ + return read32(&channel->regs->sta) & APBDMACHAN_STA_DMA_ACTIVITY ? 1 : 0; +} +/* claim a DMA channel */ +struct apb_dma_channel * const dma_claim(void) +{ + int i; + struct apb_dma_channel_regs *regs = NULL; + + /* + * Set global enable bit, otherwise register access to channel + * DMA registers will not be possible. + */ + setbits_le32(&apb_dma->command, APBDMA_COMMAND_GEN); + + for (i = 0; i < ARRAY_SIZE(apb_dma_channels); i++) { + regs = apb_dma_channels[i].regs; + + if (!apb_dma_channels[i].in_use) { + u32 status = read32(®s->sta); + if (status & (1 << i)) { + /* FIXME: should this be fatal? */ + printk(BIOS_DEBUG, "%s: DMA channel %d busy?\n", + __func__, i); + } + break; + } + } + + if (i == NUM_APB_DMA_CHANNELS) + return NULL; + + apb_dma_channels[i].in_use = 1; + return &apb_dma_channels[i]; +} + +/* release a DMA channel */ +void dma_release(struct apb_dma_channel * const channel) +{ + int i; + int dma_in_use = 0; + + while (dma_busy(channel)) + ; + + channel->in_use = 0; + + /* clear the global enable bit if no channels are in use */ + for (i = 0; i < ARRAY_SIZE(apb_dma_channels); i++) { + if (apb_dma_channels[i].in_use) { + dma_in_use = 1; + break; + } + } + + if (!dma_in_use) + clrbits_le32(&apb_dma->command, APBDMA_COMMAND_GEN); +} + +int dma_start(struct apb_dma_channel * const channel) +{ + struct apb_dma_channel_regs *regs = channel->regs; + + /* Set ENB bit for this channel */ + setbits_le32(®s->csr, APBDMACHAN_CSR_ENB); + + return 0; +} + +int dma_stop(struct apb_dma_channel * const channel) +{ + struct apb_dma_channel_regs *regs = channel->regs; + + /* Clear ENB bit for this channel */ + clrbits_le32(®s->csr, APBDMACHAN_CSR_ENB); + + return 0; +} diff --git a/src/soc/nvidia/tegra124/dma.h b/src/soc/nvidia/tegra124/dma.h new file mode 100644 index 0000000000..bfbf7b867d --- /dev/null +++ b/src/soc/nvidia/tegra124/dma.h @@ -0,0 +1,55 @@ +/* + * (C) Copyright 2010,2011 + * NVIDIA Corporation + * Copyright (C) 2013 Google Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __NVIDIA_TEGRA124_DMA_H__ +#define __NVIDIA_TEGRA124_DMA_H__ + +#include +#include + +struct apb_dma_channel_regs { + u32 csr; /* 0x00 */ + u32 sta; /* 0x04 */ + u32 dma_byte_sta; /* 0x08 */ + u32 csre; /* 0x0c */ + u32 ahb_ptr; /* 0x10 */ + u32 ahb_seq; /* 0x14 */ + u32 apb_ptr; /* 0x18 */ + u32 apb_seq; /* 0x1c */ + u32 wcount; /* 0x20 */ + u32 word_transfer; /* 0x24 */ +} __attribute__((packed)); + +struct apb_dma_channel { + const int num; + struct apb_dma_channel_regs *regs; + + /* + * Basic high-level semaphore that can be used to "claim" + * a DMA channel e.g. by SPI, I2C, or other peripheral driver. + */ + int in_use; +}; + +struct apb_dma_channel * const dma_claim(void); +void dma_release(struct apb_dma_channel * const channel); +int dma_start(struct apb_dma_channel * const channel); +int dma_stop(struct apb_dma_channel * const channel); +int dma_busy(struct apb_dma_channel * const channel); + +#endif /* __NVIDIA_TEGRA124_DMA_H__ */ diff --git a/src/soc/nvidia/tegra124/include/soc/addressmap.h b/src/soc/nvidia/tegra124/include/soc/addressmap.h index 5148f5ecb9..ec73f46ffa 100644 --- a/src/soc/nvidia/tegra124/include/soc/addressmap.h +++ b/src/soc/nvidia/tegra124/include/soc/addressmap.h @@ -34,6 +34,7 @@ enum { TEGRA_FLOW_BASE = 0x60007000, TEGRA_GPIO_BASE = 0x6000D000, TEGRA_EVP_BASE = 0x6000F000, + TEGRA_APB_DMA_BASE = 0x60020000, TEGRA_APB_MISC_BASE = 0x70000000, TEGRA_APB_MISC_GP_BASE = TEGRA_APB_MISC_BASE + 0x0800, TEGRA_APB_PINGROUP_BASE = TEGRA_APB_MISC_BASE + 0x0868,