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(&ltq_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(&ltq_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(&ltq_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(&ltq_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(&ltq_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(&ltq_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(&ltq_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(&ltq_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(&ltq_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(&ltq_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(&ltq_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(&ltq_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(&ltq_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(&ltq_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(&ltq_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(&ltq_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