~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/arm/mach-tegra/apbio.c

Version: ~ [ linux-5.12-rc7 ] ~ [ linux-5.11.13 ] ~ [ linux-5.10.29 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.111 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.186 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.230 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.266 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.266 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.18.140 ] ~ [ linux-3.16.85 ] ~ [ linux-3.14.79 ] ~ [ linux-3.12.74 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  * Copyright (C) 2010 NVIDIA Corporation.
  3  * Copyright (C) 2010 Google, Inc.
  4  *
  5  * This software is licensed under the terms of the GNU General Public
  6  * License version 2, as published by the Free Software Foundation, and
  7  * may be copied, distributed, and modified under those terms.
  8  *
  9  * This program is distributed in the hope that it will be useful,
 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 12  * GNU General Public License for more details.
 13  *
 14  */
 15 
 16 #include <linux/kernel.h>
 17 #include <linux/io.h>
 18 #include <linux/of.h>
 19 #include <linux/dmaengine.h>
 20 #include <linux/dma-mapping.h>
 21 #include <linux/spinlock.h>
 22 #include <linux/completion.h>
 23 #include <linux/sched.h>
 24 #include <linux/mutex.h>
 25 
 26 #include "apbio.h"
 27 #include "iomap.h"
 28 
 29 #if defined(CONFIG_TEGRA20_APB_DMA)
 30 static DEFINE_MUTEX(tegra_apb_dma_lock);
 31 static u32 *tegra_apb_bb;
 32 static dma_addr_t tegra_apb_bb_phys;
 33 static DECLARE_COMPLETION(tegra_apb_wait);
 34 
 35 static u32 tegra_apb_readl_direct(unsigned long offset);
 36 static void tegra_apb_writel_direct(u32 value, unsigned long offset);
 37 
 38 static struct dma_chan *tegra_apb_dma_chan;
 39 static struct dma_slave_config dma_sconfig;
 40 
 41 static bool tegra_apb_dma_init(void)
 42 {
 43         dma_cap_mask_t mask;
 44 
 45         mutex_lock(&tegra_apb_dma_lock);
 46 
 47         /* Check to see if we raced to setup */
 48         if (tegra_apb_dma_chan)
 49                 goto skip_init;
 50 
 51         dma_cap_zero(mask);
 52         dma_cap_set(DMA_SLAVE, mask);
 53         tegra_apb_dma_chan = dma_request_channel(mask, NULL, NULL);
 54         if (!tegra_apb_dma_chan) {
 55                 /*
 56                  * This is common until the device is probed, so don't
 57                  * shout about it.
 58                  */
 59                 pr_debug("%s: can not allocate dma channel\n", __func__);
 60                 goto err_dma_alloc;
 61         }
 62 
 63         tegra_apb_bb = dma_alloc_coherent(NULL, sizeof(u32),
 64                 &tegra_apb_bb_phys, GFP_KERNEL);
 65         if (!tegra_apb_bb) {
 66                 pr_err("%s: can not allocate bounce buffer\n", __func__);
 67                 goto err_buff_alloc;
 68         }
 69 
 70         dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
 71         dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
 72         dma_sconfig.src_maxburst = 1;
 73         dma_sconfig.dst_maxburst = 1;
 74 
 75 skip_init:
 76         mutex_unlock(&tegra_apb_dma_lock);
 77         return true;
 78 
 79 err_buff_alloc:
 80         dma_release_channel(tegra_apb_dma_chan);
 81         tegra_apb_dma_chan = NULL;
 82 
 83 err_dma_alloc:
 84         mutex_unlock(&tegra_apb_dma_lock);
 85         return false;
 86 }
 87 
 88 static void apb_dma_complete(void *args)
 89 {
 90         complete(&tegra_apb_wait);
 91 }
 92 
 93 static int do_dma_transfer(unsigned long apb_add,
 94                 enum dma_transfer_direction dir)
 95 {
 96         struct dma_async_tx_descriptor *dma_desc;
 97         int ret;
 98 
 99         if (dir == DMA_DEV_TO_MEM)
100                 dma_sconfig.src_addr = apb_add;
101         else
102                 dma_sconfig.dst_addr = apb_add;
103 
104         ret = dmaengine_slave_config(tegra_apb_dma_chan, &dma_sconfig);
105         if (ret)
106                 return ret;
107 
108         dma_desc = dmaengine_prep_slave_single(tegra_apb_dma_chan,
109                         tegra_apb_bb_phys, sizeof(u32), dir,
110                         DMA_PREP_INTERRUPT |  DMA_CTRL_ACK);
111         if (!dma_desc)
112                 return -EINVAL;
113 
114         dma_desc->callback = apb_dma_complete;
115         dma_desc->callback_param = NULL;
116 
117         reinit_completion(&tegra_apb_wait);
118 
119         dmaengine_submit(dma_desc);
120         dma_async_issue_pending(tegra_apb_dma_chan);
121         ret = wait_for_completion_timeout(&tegra_apb_wait,
122                 msecs_to_jiffies(50));
123 
124         if (WARN(ret == 0, "apb read dma timed out")) {
125                 dmaengine_terminate_all(tegra_apb_dma_chan);
126                 return -EFAULT;
127         }
128         return 0;
129 }
130 
131 static u32 tegra_apb_readl_using_dma(unsigned long offset)
132 {
133         int ret;
134 
135         if (!tegra_apb_dma_chan && !tegra_apb_dma_init())
136                 return tegra_apb_readl_direct(offset);
137 
138         mutex_lock(&tegra_apb_dma_lock);
139         ret = do_dma_transfer(offset, DMA_DEV_TO_MEM);
140         if (ret < 0) {
141                 pr_err("error in reading offset 0x%08lx using dma\n", offset);
142                 *(u32 *)tegra_apb_bb = 0;
143         }
144         mutex_unlock(&tegra_apb_dma_lock);
145         return *((u32 *)tegra_apb_bb);
146 }
147 
148 static void tegra_apb_writel_using_dma(u32 value, unsigned long offset)
149 {
150         int ret;
151 
152         if (!tegra_apb_dma_chan && !tegra_apb_dma_init()) {
153                 tegra_apb_writel_direct(value, offset);
154                 return;
155         }
156 
157         mutex_lock(&tegra_apb_dma_lock);
158         *((u32 *)tegra_apb_bb) = value;
159         ret = do_dma_transfer(offset, DMA_MEM_TO_DEV);
160         if (ret < 0)
161                 pr_err("error in writing offset 0x%08lx using dma\n", offset);
162         mutex_unlock(&tegra_apb_dma_lock);
163 }
164 #else
165 #define tegra_apb_readl_using_dma tegra_apb_readl_direct
166 #define tegra_apb_writel_using_dma tegra_apb_writel_direct
167 #endif
168 
169 typedef u32 (*apbio_read_fptr)(unsigned long offset);
170 typedef void (*apbio_write_fptr)(u32 value, unsigned long offset);
171 
172 static apbio_read_fptr apbio_read;
173 static apbio_write_fptr apbio_write;
174 
175 static u32 tegra_apb_readl_direct(unsigned long offset)
176 {
177         return readl(IO_ADDRESS(offset));
178 }
179 
180 static void tegra_apb_writel_direct(u32 value, unsigned long offset)
181 {
182         writel(value, IO_ADDRESS(offset));
183 }
184 
185 void tegra_apb_io_init(void)
186 {
187         /* Need to use dma only when it is Tegra20 based platform */
188         if (of_machine_is_compatible("nvidia,tegra20") ||
189                         !of_have_populated_dt()) {
190                 apbio_read = tegra_apb_readl_using_dma;
191                 apbio_write = tegra_apb_writel_using_dma;
192         } else {
193                 apbio_read = tegra_apb_readl_direct;
194                 apbio_write = tegra_apb_writel_direct;
195         }
196 }
197 
198 u32 tegra_apb_readl(unsigned long offset)
199 {
200         return apbio_read(offset);
201 }
202 
203 void tegra_apb_writel(u32 value, unsigned long offset)
204 {
205         apbio_write(value, offset);
206 }
207 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp