| 1 | From bd620ec1ca053bab8ce2562968700e6f80e4ff83 Mon Sep 17 00:00:00 2001 |
| 2 | From: John Crispin <blogic@openwrt.org> |
| 3 | Date: Fri, 6 May 2011 00:10:00 +0200 |
| 4 | Subject: [PATCH 10/13] MIPS: Lantiq: Add DMA support |
| 5 | |
| 6 | This patch adds support for the DMA engine found inside the XWAY family of |
| 7 | SoCs. The engine has 5 ports and 20 channels. |
| 8 | |
| 9 | Signed-off-by: John Crispin <blogic@openwrt.org> |
| 10 | Signed-off-by: Ralph Hempel <ralph.hempel@lantiq.com> |
| 11 | Cc: linux-mips@linux-mips.org |
| 12 | Patchwork: https://patchwork.linux-mips.org/patch/2355/ |
| 13 | Signed-off-by: Ralf Baechle <ralf@linux-mips.org> |
| 14 | --- |
| 15 | .../mips/include/asm/mach-lantiq/xway/lantiq_soc.h | 3 +- |
| 16 | arch/mips/include/asm/mach-lantiq/xway/xway_dma.h | 60 +++++ |
| 17 | arch/mips/lantiq/xway/Makefile | 2 +- |
| 18 | arch/mips/lantiq/xway/devices.h | 1 + |
| 19 | arch/mips/lantiq/xway/dma.c | 253 ++++++++++++++++++++ |
| 20 | 5 files changed, 317 insertions(+), 2 deletions(-) |
| 21 | create mode 100644 arch/mips/include/asm/mach-lantiq/xway/xway_dma.h |
| 22 | create mode 100644 arch/mips/lantiq/xway/dma.c |
| 23 | |
| 24 | diff --git a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h |
| 25 | index 343e82c..4827afb 100644 |
| 26 | --- a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h |
| 27 | +++ b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h |
| 28 | @@ -86,7 +86,8 @@ |
| 29 | #define LTQ_PPE32_SIZE 0x40000 |
| 30 | |
| 31 | /* DMA */ |
| 32 | -#define LTQ_DMA_BASE_ADDR 0xBE104100 |
| 33 | +#define LTQ_DMA_BASE_ADDR 0x1E104100 |
| 34 | +#define LTQ_DMA_SIZE 0x800 |
| 35 | |
| 36 | /* PCI */ |
| 37 | #define PCI_CR_BASE_ADDR 0x1E105400 |
| 38 | diff --git a/arch/mips/include/asm/mach-lantiq/xway/xway_dma.h b/arch/mips/include/asm/mach-lantiq/xway/xway_dma.h |
| 39 | new file mode 100644 |
| 40 | index 0000000..872943a |
| 41 | --- /dev/null |
| 42 | +++ b/arch/mips/include/asm/mach-lantiq/xway/xway_dma.h |
| 43 | @@ -0,0 +1,60 @@ |
| 44 | +/* |
| 45 | + * This program is free software; you can redistribute it and/or modify it |
| 46 | + * under the terms of the GNU General Public License version 2 as published |
| 47 | + * by the Free Software Foundation. |
| 48 | + * |
| 49 | + * This program is distributed in the hope that it will be useful, |
| 50 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 51 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 52 | + * GNU General Public License for more details. |
| 53 | + * |
| 54 | + * You should have received a copy of the GNU General Public License |
| 55 | + * along with this program; if not, write to the Free Software |
| 56 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. |
| 57 | + * |
| 58 | + * Copyright (C) 2011 John Crispin <blogic@openwrt.org> |
| 59 | + */ |
| 60 | + |
| 61 | +#ifndef LTQ_DMA_H__ |
| 62 | +#define LTQ_DMA_H__ |
| 63 | + |
| 64 | +#define LTQ_DESC_SIZE 0x08 /* each descriptor is 64bit */ |
| 65 | +#define LTQ_DESC_NUM 0x40 /* 64 descriptors / channel */ |
| 66 | + |
| 67 | +#define LTQ_DMA_OWN BIT(31) /* owner bit */ |
| 68 | +#define LTQ_DMA_C BIT(30) /* complete bit */ |
| 69 | +#define LTQ_DMA_SOP BIT(29) /* start of packet */ |
| 70 | +#define LTQ_DMA_EOP BIT(28) /* end of packet */ |
| 71 | +#define LTQ_DMA_TX_OFFSET(x) ((x & 0x1f) << 23) /* data bytes offset */ |
| 72 | +#define LTQ_DMA_RX_OFFSET(x) ((x & 0x7) << 23) /* data bytes offset */ |
| 73 | +#define LTQ_DMA_SIZE_MASK (0xffff) /* the size field is 16 bit */ |
| 74 | + |
| 75 | +struct ltq_dma_desc { |
| 76 | + u32 ctl; |
| 77 | + u32 addr; |
| 78 | +}; |
| 79 | + |
| 80 | +struct ltq_dma_channel { |
| 81 | + int nr; /* the channel number */ |
| 82 | + int irq; /* the mapped irq */ |
| 83 | + int desc; /* the current descriptor */ |
| 84 | + struct ltq_dma_desc *desc_base; /* the descriptor base */ |
| 85 | + int phys; /* physical addr */ |
| 86 | +}; |
| 87 | + |
| 88 | +enum { |
| 89 | + DMA_PORT_ETOP = 0, |
| 90 | + DMA_PORT_DEU, |
| 91 | +}; |
| 92 | + |
| 93 | +extern void ltq_dma_enable_irq(struct ltq_dma_channel *ch); |
| 94 | +extern void ltq_dma_disable_irq(struct ltq_dma_channel *ch); |
| 95 | +extern void ltq_dma_ack_irq(struct ltq_dma_channel *ch); |
| 96 | +extern void ltq_dma_open(struct ltq_dma_channel *ch); |
| 97 | +extern void ltq_dma_close(struct ltq_dma_channel *ch); |
| 98 | +extern void ltq_dma_alloc_tx(struct ltq_dma_channel *ch); |
| 99 | +extern void ltq_dma_alloc_rx(struct ltq_dma_channel *ch); |
| 100 | +extern void ltq_dma_free(struct ltq_dma_channel *ch); |
| 101 | +extern void ltq_dma_init_port(int p); |
| 102 | + |
| 103 | +#endif |
| 104 | diff --git a/arch/mips/lantiq/xway/Makefile b/arch/mips/lantiq/xway/Makefile |
| 105 | index 6b5e07e..c517f2e 100644 |
| 106 | --- a/arch/mips/lantiq/xway/Makefile |
| 107 | +++ b/arch/mips/lantiq/xway/Makefile |
| 108 | @@ -1,4 +1,4 @@ |
| 109 | -obj-y := pmu.o ebu.o reset.o gpio.o gpio_stp.o gpio_ebu.o devices.o |
| 110 | +obj-y := pmu.o ebu.o reset.o gpio.o gpio_stp.o gpio_ebu.o devices.o dma.o |
| 111 | |
| 112 | obj-$(CONFIG_SOC_XWAY) += clk-xway.o prom-xway.o setup-xway.o |
| 113 | obj-$(CONFIG_SOC_AMAZON_SE) += clk-ase.o prom-ase.o setup-ase.o |
| 114 | diff --git a/arch/mips/lantiq/xway/devices.h b/arch/mips/lantiq/xway/devices.h |
| 115 | index 51f56b5..d573084 100644 |
| 116 | --- a/arch/mips/lantiq/xway/devices.h |
| 117 | +++ b/arch/mips/lantiq/xway/devices.h |
| 118 | @@ -10,6 +10,7 @@ |
| 119 | #define _LTQ_DEVICES_XWAY_H__ |
| 120 | |
| 121 | #include "../devices.h" |
| 122 | +#include <linux/phy.h> |
| 123 | |
| 124 | extern void ltq_register_gpio(void); |
| 125 | extern void ltq_register_gpio_stp(void); |
| 126 | diff --git a/arch/mips/lantiq/xway/dma.c b/arch/mips/lantiq/xway/dma.c |
| 127 | new file mode 100644 |
| 128 | index 0000000..4278a45 |
| 129 | --- /dev/null |
| 130 | +++ b/arch/mips/lantiq/xway/dma.c |
| 131 | @@ -0,0 +1,253 @@ |
| 132 | +/* |
| 133 | + * This program is free software; you can redistribute it and/or modify it |
| 134 | + * under the terms of the GNU General Public License version 2 as published |
| 135 | + * by the Free Software Foundation. |
| 136 | + * |
| 137 | + * This program is distributed in the hope that it will be useful, |
| 138 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 139 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 140 | + * GNU General Public License for more details. |
| 141 | + * |
| 142 | + * You should have received a copy of the GNU General Public License |
| 143 | + * along with this program; if not, write to the Free Software |
| 144 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. |
| 145 | + * |
| 146 | + * Copyright (C) 2011 John Crispin <blogic@openwrt.org> |
| 147 | + */ |
| 148 | + |
| 149 | +#include <linux/init.h> |
| 150 | +#include <linux/platform_device.h> |
| 151 | +#include <linux/io.h> |
| 152 | +#include <linux/dma-mapping.h> |
| 153 | + |
| 154 | +#include <lantiq_soc.h> |
| 155 | +#include <xway_dma.h> |
| 156 | + |
| 157 | +#define LTQ_DMA_CTRL 0x10 |
| 158 | +#define LTQ_DMA_CPOLL 0x14 |
| 159 | +#define LTQ_DMA_CS 0x18 |
| 160 | +#define LTQ_DMA_CCTRL 0x1C |
| 161 | +#define LTQ_DMA_CDBA 0x20 |
| 162 | +#define LTQ_DMA_CDLEN 0x24 |
| 163 | +#define LTQ_DMA_CIS 0x28 |
| 164 | +#define LTQ_DMA_CIE 0x2C |
| 165 | +#define LTQ_DMA_PS 0x40 |
| 166 | +#define LTQ_DMA_PCTRL 0x44 |
| 167 | +#define LTQ_DMA_IRNEN 0xf4 |
| 168 | + |
| 169 | +#define DMA_DESCPT BIT(3) /* descriptor complete irq */ |
| 170 | +#define DMA_TX BIT(8) /* TX channel direction */ |
| 171 | +#define DMA_CHAN_ON BIT(0) /* channel on / off bit */ |
| 172 | +#define DMA_PDEN BIT(6) /* enable packet drop */ |
| 173 | +#define DMA_CHAN_RST BIT(1) /* channel on / off bit */ |
| 174 | +#define DMA_RESET BIT(0) /* channel on / off bit */ |
| 175 | +#define DMA_IRQ_ACK 0x7e /* IRQ status register */ |
| 176 | +#define DMA_POLL BIT(31) /* turn on channel polling */ |
| 177 | +#define DMA_CLK_DIV4 BIT(6) /* polling clock divider */ |
| 178 | +#define DMA_2W_BURST BIT(1) /* 2 word burst length */ |
| 179 | +#define DMA_MAX_CHANNEL 20 /* the soc has 20 channels */ |
| 180 | +#define DMA_ETOP_ENDIANESS (0xf << 8) /* endianess swap etop channels */ |
| 181 | +#define DMA_WEIGHT (BIT(17) | BIT(16)) /* default channel wheight */ |
| 182 | + |
| 183 | +#define ltq_dma_r32(x) ltq_r32(ltq_dma_membase + (x)) |
| 184 | +#define ltq_dma_w32(x, y) ltq_w32(x, ltq_dma_membase + (y)) |
| 185 | +#define ltq_dma_w32_mask(x, y, z) ltq_w32_mask(x, y, \ |
| 186 | + ltq_dma_membase + (z)) |
| 187 | + |
| 188 | +static struct resource ltq_dma_resource = { |
| 189 | + .name = "dma", |
| 190 | + .start = LTQ_DMA_BASE_ADDR, |
| 191 | + .end = LTQ_DMA_BASE_ADDR + LTQ_DMA_SIZE - 1, |
| 192 | + .flags = IORESOURCE_MEM, |
| 193 | +}; |
| 194 | + |
| 195 | +static void __iomem *ltq_dma_membase; |
| 196 | + |
| 197 | +void |
| 198 | +ltq_dma_enable_irq(struct ltq_dma_channel *ch) |
| 199 | +{ |
| 200 | + unsigned long flags; |
| 201 | + |
| 202 | + local_irq_save(flags); |
| 203 | + ltq_dma_w32(ch->nr, LTQ_DMA_CS); |
| 204 | + ltq_dma_w32_mask(0, 1 << ch->nr, LTQ_DMA_IRNEN); |
| 205 | + local_irq_restore(flags); |
| 206 | +} |
| 207 | +EXPORT_SYMBOL_GPL(ltq_dma_enable_irq); |
| 208 | + |
| 209 | +void |
| 210 | +ltq_dma_disable_irq(struct ltq_dma_channel *ch) |
| 211 | +{ |
| 212 | + unsigned long flags; |
| 213 | + |
| 214 | + local_irq_save(flags); |
| 215 | + ltq_dma_w32(ch->nr, LTQ_DMA_CS); |
| 216 | + ltq_dma_w32_mask(1 << ch->nr, 0, LTQ_DMA_IRNEN); |
| 217 | + local_irq_restore(flags); |
| 218 | +} |
| 219 | +EXPORT_SYMBOL_GPL(ltq_dma_disable_irq); |
| 220 | + |
| 221 | +void |
| 222 | +ltq_dma_ack_irq(struct ltq_dma_channel *ch) |
| 223 | +{ |
| 224 | + unsigned long flags; |
| 225 | + |
| 226 | + local_irq_save(flags); |
| 227 | + ltq_dma_w32(ch->nr, LTQ_DMA_CS); |
| 228 | + ltq_dma_w32(DMA_IRQ_ACK, LTQ_DMA_CIS); |
| 229 | + local_irq_restore(flags); |
| 230 | +} |
| 231 | +EXPORT_SYMBOL_GPL(ltq_dma_ack_irq); |
| 232 | + |
| 233 | +void |
| 234 | +ltq_dma_open(struct ltq_dma_channel *ch) |
| 235 | +{ |
| 236 | + unsigned long flag; |
| 237 | + |
| 238 | + local_irq_save(flag); |
| 239 | + ltq_dma_w32(ch->nr, LTQ_DMA_CS); |
| 240 | + ltq_dma_w32_mask(0, DMA_CHAN_ON, LTQ_DMA_CCTRL); |
| 241 | + ltq_dma_enable_irq(ch); |
| 242 | + local_irq_restore(flag); |
| 243 | +} |
| 244 | +EXPORT_SYMBOL_GPL(ltq_dma_open); |
| 245 | + |
| 246 | +void |
| 247 | +ltq_dma_close(struct ltq_dma_channel *ch) |
| 248 | +{ |
| 249 | + unsigned long flag; |
| 250 | + |
| 251 | + local_irq_save(flag); |
| 252 | + ltq_dma_w32(ch->nr, LTQ_DMA_CS); |
| 253 | + ltq_dma_w32_mask(DMA_CHAN_ON, 0, LTQ_DMA_CCTRL); |
| 254 | + ltq_dma_disable_irq(ch); |
| 255 | + local_irq_restore(flag); |
| 256 | +} |
| 257 | +EXPORT_SYMBOL_GPL(ltq_dma_close); |
| 258 | + |
| 259 | +static void |
| 260 | +ltq_dma_alloc(struct ltq_dma_channel *ch) |
| 261 | +{ |
| 262 | + unsigned long flags; |
| 263 | + |
| 264 | + ch->desc = 0; |
| 265 | + ch->desc_base = dma_alloc_coherent(NULL, |
| 266 | + LTQ_DESC_NUM * LTQ_DESC_SIZE, |
| 267 | + &ch->phys, GFP_ATOMIC); |
| 268 | + memset(ch->desc_base, 0, LTQ_DESC_NUM * LTQ_DESC_SIZE); |
| 269 | + |
| 270 | + local_irq_save(flags); |
| 271 | + ltq_dma_w32(ch->nr, LTQ_DMA_CS); |
| 272 | + ltq_dma_w32(ch->phys, LTQ_DMA_CDBA); |
| 273 | + ltq_dma_w32(LTQ_DESC_NUM, LTQ_DMA_CDLEN); |
| 274 | + ltq_dma_w32_mask(DMA_CHAN_ON, 0, LTQ_DMA_CCTRL); |
| 275 | + wmb(); |
| 276 | + ltq_dma_w32_mask(0, DMA_CHAN_RST, LTQ_DMA_CCTRL); |
| 277 | + while (ltq_dma_r32(LTQ_DMA_CCTRL) & DMA_CHAN_RST) |
| 278 | + ; |
| 279 | + local_irq_restore(flags); |
| 280 | +} |
| 281 | + |
| 282 | +void |
| 283 | +ltq_dma_alloc_tx(struct ltq_dma_channel *ch) |
| 284 | +{ |
| 285 | + unsigned long flags; |
| 286 | + |
| 287 | + ltq_dma_alloc(ch); |
| 288 | + |
| 289 | + local_irq_save(flags); |
| 290 | + ltq_dma_w32(DMA_DESCPT, LTQ_DMA_CIE); |
| 291 | + ltq_dma_w32_mask(0, 1 << ch->nr, LTQ_DMA_IRNEN); |
| 292 | + ltq_dma_w32(DMA_WEIGHT | DMA_TX, LTQ_DMA_CCTRL); |
| 293 | + local_irq_restore(flags); |
| 294 | +} |
| 295 | +EXPORT_SYMBOL_GPL(ltq_dma_alloc_tx); |
| 296 | + |
| 297 | +void |
| 298 | +ltq_dma_alloc_rx(struct ltq_dma_channel *ch) |
| 299 | +{ |
| 300 | + unsigned long flags; |
| 301 | + |
| 302 | + ltq_dma_alloc(ch); |
| 303 | + |
| 304 | + local_irq_save(flags); |
| 305 | + ltq_dma_w32(DMA_DESCPT, LTQ_DMA_CIE); |
| 306 | + ltq_dma_w32_mask(0, 1 << ch->nr, LTQ_DMA_IRNEN); |
| 307 | + ltq_dma_w32(DMA_WEIGHT, LTQ_DMA_CCTRL); |
| 308 | + local_irq_restore(flags); |
| 309 | +} |
| 310 | +EXPORT_SYMBOL_GPL(ltq_dma_alloc_rx); |
| 311 | + |
| 312 | +void |
| 313 | +ltq_dma_free(struct ltq_dma_channel *ch) |
| 314 | +{ |
| 315 | + if (!ch->desc_base) |
| 316 | + return; |
| 317 | + ltq_dma_close(ch); |
| 318 | + dma_free_coherent(NULL, LTQ_DESC_NUM * LTQ_DESC_SIZE, |
| 319 | + ch->desc_base, ch->phys); |
| 320 | +} |
| 321 | +EXPORT_SYMBOL_GPL(ltq_dma_free); |
| 322 | + |
| 323 | +void |
| 324 | +ltq_dma_init_port(int p) |
| 325 | +{ |
| 326 | + ltq_dma_w32(p, LTQ_DMA_PS); |
| 327 | + switch (p) { |
| 328 | + case DMA_PORT_ETOP: |
| 329 | + /* |
| 330 | + * Tell the DMA engine to swap the endianess of data frames and |
| 331 | + * drop packets if the channel arbitration fails. |
| 332 | + */ |
| 333 | + ltq_dma_w32_mask(0, DMA_ETOP_ENDIANESS | DMA_PDEN, |
| 334 | + LTQ_DMA_PCTRL); |
| 335 | + break; |
| 336 | + |
| 337 | + case DMA_PORT_DEU: |
| 338 | + ltq_dma_w32((DMA_2W_BURST << 4) | (DMA_2W_BURST << 2), |
| 339 | + LTQ_DMA_PCTRL); |
| 340 | + break; |
| 341 | + |
| 342 | + default: |
| 343 | + break; |
| 344 | + } |
| 345 | +} |
| 346 | +EXPORT_SYMBOL_GPL(ltq_dma_init_port); |
| 347 | + |
| 348 | +int __init |
| 349 | +ltq_dma_init(void) |
| 350 | +{ |
| 351 | + int i; |
| 352 | + |
| 353 | + /* insert and request the memory region */ |
| 354 | + if (insert_resource(&iomem_resource, <q_dma_resource) < 0) |
| 355 | + panic("Failed to insert dma memory\n"); |
| 356 | + |
| 357 | + if (request_mem_region(ltq_dma_resource.start, |
| 358 | + resource_size(<q_dma_resource), "dma") < 0) |
| 359 | + panic("Failed to request dma memory\n"); |
| 360 | + |
| 361 | + /* remap dma register range */ |
| 362 | + ltq_dma_membase = ioremap_nocache(ltq_dma_resource.start, |
| 363 | + resource_size(<q_dma_resource)); |
| 364 | + if (!ltq_dma_membase) |
| 365 | + panic("Failed to remap dma memory\n"); |
| 366 | + |
| 367 | + /* power up and reset the dma engine */ |
| 368 | + ltq_pmu_enable(PMU_DMA); |
| 369 | + ltq_dma_w32_mask(0, DMA_RESET, LTQ_DMA_CTRL); |
| 370 | + |
| 371 | + /* disable all interrupts */ |
| 372 | + ltq_dma_w32(0, LTQ_DMA_IRNEN); |
| 373 | + |
| 374 | + /* reset/configure each channel */ |
| 375 | + for (i = 0; i < DMA_MAX_CHANNEL; i++) { |
| 376 | + ltq_dma_w32(i, LTQ_DMA_CS); |
| 377 | + ltq_dma_w32(DMA_CHAN_RST, LTQ_DMA_CCTRL); |
| 378 | + ltq_dma_w32(DMA_POLL | DMA_CLK_DIV4, LTQ_DMA_CPOLL); |
| 379 | + ltq_dma_w32_mask(DMA_CHAN_ON, 0, LTQ_DMA_CCTRL); |
| 380 | + } |
| 381 | + return 0; |
| 382 | +} |
| 383 | + |
| 384 | +postcore_initcall(ltq_dma_init); |
| 385 | -- |
| 386 | 1.7.2.3 |
| 387 | |
| 388 | |