162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2011 John Crispin <john@phrozen.org> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/init.h> 862306a36Sopenharmony_ci#include <linux/platform_device.h> 962306a36Sopenharmony_ci#include <linux/io.h> 1062306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1162306a36Sopenharmony_ci#include <linux/export.h> 1262306a36Sopenharmony_ci#include <linux/spinlock.h> 1362306a36Sopenharmony_ci#include <linux/clk.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/err.h> 1662306a36Sopenharmony_ci#include <linux/of.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <lantiq_soc.h> 1962306a36Sopenharmony_ci#include <xway_dma.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define LTQ_DMA_ID 0x08 2262306a36Sopenharmony_ci#define LTQ_DMA_CTRL 0x10 2362306a36Sopenharmony_ci#define LTQ_DMA_CPOLL 0x14 2462306a36Sopenharmony_ci#define LTQ_DMA_CS 0x18 2562306a36Sopenharmony_ci#define LTQ_DMA_CCTRL 0x1C 2662306a36Sopenharmony_ci#define LTQ_DMA_CDBA 0x20 2762306a36Sopenharmony_ci#define LTQ_DMA_CDLEN 0x24 2862306a36Sopenharmony_ci#define LTQ_DMA_CIS 0x28 2962306a36Sopenharmony_ci#define LTQ_DMA_CIE 0x2C 3062306a36Sopenharmony_ci#define LTQ_DMA_PS 0x40 3162306a36Sopenharmony_ci#define LTQ_DMA_PCTRL 0x44 3262306a36Sopenharmony_ci#define LTQ_DMA_IRNEN 0xf4 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define DMA_ID_CHNR GENMASK(26, 20) /* channel number */ 3562306a36Sopenharmony_ci#define DMA_DESCPT BIT(3) /* descriptor complete irq */ 3662306a36Sopenharmony_ci#define DMA_TX BIT(8) /* TX channel direction */ 3762306a36Sopenharmony_ci#define DMA_CHAN_ON BIT(0) /* channel on / off bit */ 3862306a36Sopenharmony_ci#define DMA_PDEN BIT(6) /* enable packet drop */ 3962306a36Sopenharmony_ci#define DMA_CHAN_RST BIT(1) /* channel on / off bit */ 4062306a36Sopenharmony_ci#define DMA_RESET BIT(0) /* channel on / off bit */ 4162306a36Sopenharmony_ci#define DMA_IRQ_ACK 0x7e /* IRQ status register */ 4262306a36Sopenharmony_ci#define DMA_POLL BIT(31) /* turn on channel polling */ 4362306a36Sopenharmony_ci#define DMA_CLK_DIV4 BIT(6) /* polling clock divider */ 4462306a36Sopenharmony_ci#define DMA_PCTRL_2W_BURST 0x1 /* 2 word burst length */ 4562306a36Sopenharmony_ci#define DMA_PCTRL_4W_BURST 0x2 /* 4 word burst length */ 4662306a36Sopenharmony_ci#define DMA_PCTRL_8W_BURST 0x3 /* 8 word burst length */ 4762306a36Sopenharmony_ci#define DMA_TX_BURST_SHIFT 4 /* tx burst shift */ 4862306a36Sopenharmony_ci#define DMA_RX_BURST_SHIFT 2 /* rx burst shift */ 4962306a36Sopenharmony_ci#define DMA_ETOP_ENDIANNESS (0xf << 8) /* endianness swap etop channels */ 5062306a36Sopenharmony_ci#define DMA_WEIGHT (BIT(17) | BIT(16)) /* default channel wheight */ 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define ltq_dma_r32(x) ltq_r32(ltq_dma_membase + (x)) 5362306a36Sopenharmony_ci#define ltq_dma_w32(x, y) ltq_w32(x, ltq_dma_membase + (y)) 5462306a36Sopenharmony_ci#define ltq_dma_w32_mask(x, y, z) ltq_w32_mask(x, y, \ 5562306a36Sopenharmony_ci ltq_dma_membase + (z)) 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic void __iomem *ltq_dma_membase; 5862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(ltq_dma_lock); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_civoid 6162306a36Sopenharmony_ciltq_dma_enable_irq(struct ltq_dma_channel *ch) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci unsigned long flags; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci spin_lock_irqsave(<q_dma_lock, flags); 6662306a36Sopenharmony_ci ltq_dma_w32(ch->nr, LTQ_DMA_CS); 6762306a36Sopenharmony_ci ltq_dma_w32_mask(0, 1 << ch->nr, LTQ_DMA_IRNEN); 6862306a36Sopenharmony_ci spin_unlock_irqrestore(<q_dma_lock, flags); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ltq_dma_enable_irq); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_civoid 7362306a36Sopenharmony_ciltq_dma_disable_irq(struct ltq_dma_channel *ch) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci unsigned long flags; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci spin_lock_irqsave(<q_dma_lock, flags); 7862306a36Sopenharmony_ci ltq_dma_w32(ch->nr, LTQ_DMA_CS); 7962306a36Sopenharmony_ci ltq_dma_w32_mask(1 << ch->nr, 0, LTQ_DMA_IRNEN); 8062306a36Sopenharmony_ci spin_unlock_irqrestore(<q_dma_lock, flags); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ltq_dma_disable_irq); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_civoid 8562306a36Sopenharmony_ciltq_dma_ack_irq(struct ltq_dma_channel *ch) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci unsigned long flags; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci spin_lock_irqsave(<q_dma_lock, flags); 9062306a36Sopenharmony_ci ltq_dma_w32(ch->nr, LTQ_DMA_CS); 9162306a36Sopenharmony_ci ltq_dma_w32(DMA_IRQ_ACK, LTQ_DMA_CIS); 9262306a36Sopenharmony_ci spin_unlock_irqrestore(<q_dma_lock, flags); 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ltq_dma_ack_irq); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_civoid 9762306a36Sopenharmony_ciltq_dma_open(struct ltq_dma_channel *ch) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci unsigned long flag; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci spin_lock_irqsave(<q_dma_lock, flag); 10262306a36Sopenharmony_ci ltq_dma_w32(ch->nr, LTQ_DMA_CS); 10362306a36Sopenharmony_ci ltq_dma_w32_mask(0, DMA_CHAN_ON, LTQ_DMA_CCTRL); 10462306a36Sopenharmony_ci spin_unlock_irqrestore(<q_dma_lock, flag); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ltq_dma_open); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_civoid 10962306a36Sopenharmony_ciltq_dma_close(struct ltq_dma_channel *ch) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci unsigned long flag; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci spin_lock_irqsave(<q_dma_lock, flag); 11462306a36Sopenharmony_ci ltq_dma_w32(ch->nr, LTQ_DMA_CS); 11562306a36Sopenharmony_ci ltq_dma_w32_mask(DMA_CHAN_ON, 0, LTQ_DMA_CCTRL); 11662306a36Sopenharmony_ci ltq_dma_w32_mask(1 << ch->nr, 0, LTQ_DMA_IRNEN); 11762306a36Sopenharmony_ci spin_unlock_irqrestore(<q_dma_lock, flag); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ltq_dma_close); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic void 12262306a36Sopenharmony_ciltq_dma_alloc(struct ltq_dma_channel *ch) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci unsigned long flags; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci ch->desc = 0; 12762306a36Sopenharmony_ci ch->desc_base = dma_alloc_coherent(ch->dev, 12862306a36Sopenharmony_ci LTQ_DESC_NUM * LTQ_DESC_SIZE, 12962306a36Sopenharmony_ci &ch->phys, GFP_ATOMIC); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci spin_lock_irqsave(<q_dma_lock, flags); 13262306a36Sopenharmony_ci ltq_dma_w32(ch->nr, LTQ_DMA_CS); 13362306a36Sopenharmony_ci ltq_dma_w32(ch->phys, LTQ_DMA_CDBA); 13462306a36Sopenharmony_ci ltq_dma_w32(LTQ_DESC_NUM, LTQ_DMA_CDLEN); 13562306a36Sopenharmony_ci ltq_dma_w32_mask(DMA_CHAN_ON, 0, LTQ_DMA_CCTRL); 13662306a36Sopenharmony_ci wmb(); 13762306a36Sopenharmony_ci ltq_dma_w32_mask(0, DMA_CHAN_RST, LTQ_DMA_CCTRL); 13862306a36Sopenharmony_ci while (ltq_dma_r32(LTQ_DMA_CCTRL) & DMA_CHAN_RST) 13962306a36Sopenharmony_ci ; 14062306a36Sopenharmony_ci spin_unlock_irqrestore(<q_dma_lock, flags); 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_civoid 14462306a36Sopenharmony_ciltq_dma_alloc_tx(struct ltq_dma_channel *ch) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci unsigned long flags; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci ltq_dma_alloc(ch); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci spin_lock_irqsave(<q_dma_lock, flags); 15162306a36Sopenharmony_ci ltq_dma_w32(DMA_DESCPT, LTQ_DMA_CIE); 15262306a36Sopenharmony_ci ltq_dma_w32_mask(0, 1 << ch->nr, LTQ_DMA_IRNEN); 15362306a36Sopenharmony_ci ltq_dma_w32(DMA_WEIGHT | DMA_TX, LTQ_DMA_CCTRL); 15462306a36Sopenharmony_ci spin_unlock_irqrestore(<q_dma_lock, flags); 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ltq_dma_alloc_tx); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_civoid 15962306a36Sopenharmony_ciltq_dma_alloc_rx(struct ltq_dma_channel *ch) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci unsigned long flags; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci ltq_dma_alloc(ch); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci spin_lock_irqsave(<q_dma_lock, flags); 16662306a36Sopenharmony_ci ltq_dma_w32(DMA_DESCPT, LTQ_DMA_CIE); 16762306a36Sopenharmony_ci ltq_dma_w32_mask(0, 1 << ch->nr, LTQ_DMA_IRNEN); 16862306a36Sopenharmony_ci ltq_dma_w32(DMA_WEIGHT, LTQ_DMA_CCTRL); 16962306a36Sopenharmony_ci spin_unlock_irqrestore(<q_dma_lock, flags); 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ltq_dma_alloc_rx); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_civoid 17462306a36Sopenharmony_ciltq_dma_free(struct ltq_dma_channel *ch) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci if (!ch->desc_base) 17762306a36Sopenharmony_ci return; 17862306a36Sopenharmony_ci ltq_dma_close(ch); 17962306a36Sopenharmony_ci dma_free_coherent(ch->dev, LTQ_DESC_NUM * LTQ_DESC_SIZE, 18062306a36Sopenharmony_ci ch->desc_base, ch->phys); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ltq_dma_free); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_civoid 18562306a36Sopenharmony_ciltq_dma_init_port(int p, int tx_burst, int rx_burst) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci ltq_dma_w32(p, LTQ_DMA_PS); 18862306a36Sopenharmony_ci switch (p) { 18962306a36Sopenharmony_ci case DMA_PORT_ETOP: 19062306a36Sopenharmony_ci /* 19162306a36Sopenharmony_ci * Tell the DMA engine to swap the endianness of data frames and 19262306a36Sopenharmony_ci * drop packets if the channel arbitration fails. 19362306a36Sopenharmony_ci */ 19462306a36Sopenharmony_ci ltq_dma_w32_mask(0, (DMA_ETOP_ENDIANNESS | DMA_PDEN), 19562306a36Sopenharmony_ci LTQ_DMA_PCTRL); 19662306a36Sopenharmony_ci break; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci default: 19962306a36Sopenharmony_ci break; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci switch (rx_burst) { 20362306a36Sopenharmony_ci case 8: 20462306a36Sopenharmony_ci ltq_dma_w32_mask(0x0c, (DMA_PCTRL_8W_BURST << DMA_RX_BURST_SHIFT), 20562306a36Sopenharmony_ci LTQ_DMA_PCTRL); 20662306a36Sopenharmony_ci break; 20762306a36Sopenharmony_ci case 4: 20862306a36Sopenharmony_ci ltq_dma_w32_mask(0x0c, (DMA_PCTRL_4W_BURST << DMA_RX_BURST_SHIFT), 20962306a36Sopenharmony_ci LTQ_DMA_PCTRL); 21062306a36Sopenharmony_ci break; 21162306a36Sopenharmony_ci case 2: 21262306a36Sopenharmony_ci ltq_dma_w32_mask(0x0c, (DMA_PCTRL_2W_BURST << DMA_RX_BURST_SHIFT), 21362306a36Sopenharmony_ci LTQ_DMA_PCTRL); 21462306a36Sopenharmony_ci break; 21562306a36Sopenharmony_ci default: 21662306a36Sopenharmony_ci break; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci switch (tx_burst) { 22062306a36Sopenharmony_ci case 8: 22162306a36Sopenharmony_ci ltq_dma_w32_mask(0x30, (DMA_PCTRL_8W_BURST << DMA_TX_BURST_SHIFT), 22262306a36Sopenharmony_ci LTQ_DMA_PCTRL); 22362306a36Sopenharmony_ci break; 22462306a36Sopenharmony_ci case 4: 22562306a36Sopenharmony_ci ltq_dma_w32_mask(0x30, (DMA_PCTRL_4W_BURST << DMA_TX_BURST_SHIFT), 22662306a36Sopenharmony_ci LTQ_DMA_PCTRL); 22762306a36Sopenharmony_ci break; 22862306a36Sopenharmony_ci case 2: 22962306a36Sopenharmony_ci ltq_dma_w32_mask(0x30, (DMA_PCTRL_2W_BURST << DMA_TX_BURST_SHIFT), 23062306a36Sopenharmony_ci LTQ_DMA_PCTRL); 23162306a36Sopenharmony_ci break; 23262306a36Sopenharmony_ci default: 23362306a36Sopenharmony_ci break; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ltq_dma_init_port); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic int 23962306a36Sopenharmony_ciltq_dma_init(struct platform_device *pdev) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci struct clk *clk; 24262306a36Sopenharmony_ci unsigned int id, nchannels; 24362306a36Sopenharmony_ci int i; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci ltq_dma_membase = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); 24662306a36Sopenharmony_ci if (IS_ERR(ltq_dma_membase)) 24762306a36Sopenharmony_ci panic("Failed to remap dma resource"); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci /* power up and reset the dma engine */ 25062306a36Sopenharmony_ci clk = clk_get(&pdev->dev, NULL); 25162306a36Sopenharmony_ci if (IS_ERR(clk)) 25262306a36Sopenharmony_ci panic("Failed to get dma clock"); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci clk_enable(clk); 25562306a36Sopenharmony_ci ltq_dma_w32_mask(0, DMA_RESET, LTQ_DMA_CTRL); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci usleep_range(1, 10); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci /* disable all interrupts */ 26062306a36Sopenharmony_ci ltq_dma_w32(0, LTQ_DMA_IRNEN); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* reset/configure each channel */ 26362306a36Sopenharmony_ci id = ltq_dma_r32(LTQ_DMA_ID); 26462306a36Sopenharmony_ci nchannels = ((id & DMA_ID_CHNR) >> 20); 26562306a36Sopenharmony_ci for (i = 0; i < nchannels; i++) { 26662306a36Sopenharmony_ci ltq_dma_w32(i, LTQ_DMA_CS); 26762306a36Sopenharmony_ci ltq_dma_w32(DMA_CHAN_RST, LTQ_DMA_CCTRL); 26862306a36Sopenharmony_ci ltq_dma_w32(DMA_POLL | DMA_CLK_DIV4, LTQ_DMA_CPOLL); 26962306a36Sopenharmony_ci ltq_dma_w32_mask(DMA_CHAN_ON, 0, LTQ_DMA_CCTRL); 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci dev_info(&pdev->dev, 27362306a36Sopenharmony_ci "Init done - hw rev: %X, ports: %d, channels: %d\n", 27462306a36Sopenharmony_ci id & 0x1f, (id >> 16) & 0xf, nchannels); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci return 0; 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic const struct of_device_id dma_match[] = { 28062306a36Sopenharmony_ci { .compatible = "lantiq,dma-xway" }, 28162306a36Sopenharmony_ci {}, 28262306a36Sopenharmony_ci}; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic struct platform_driver dma_driver = { 28562306a36Sopenharmony_ci .probe = ltq_dma_init, 28662306a36Sopenharmony_ci .driver = { 28762306a36Sopenharmony_ci .name = "dma-xway", 28862306a36Sopenharmony_ci .of_match_table = dma_match, 28962306a36Sopenharmony_ci }, 29062306a36Sopenharmony_ci}; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ciint __init 29362306a36Sopenharmony_cidma_init(void) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci return platform_driver_register(&dma_driver); 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cipostcore_initcall(dma_init); 299