162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * QMC driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2022 CS GROUP France
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Herve Codina <herve.codina@bootlin.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <soc/fsl/qe/qmc.h>
1162306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1262306a36Sopenharmony_ci#include <linux/hdlc.h>
1362306a36Sopenharmony_ci#include <linux/interrupt.h>
1462306a36Sopenharmony_ci#include <linux/io.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/of.h>
1762306a36Sopenharmony_ci#include <linux/of_platform.h>
1862306a36Sopenharmony_ci#include <linux/platform_device.h>
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci#include <soc/fsl/cpm.h>
2162306a36Sopenharmony_ci#include <sysdev/fsl_soc.h>
2262306a36Sopenharmony_ci#include "tsa.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* SCC general mode register high (32 bits) */
2562306a36Sopenharmony_ci#define SCC_GSMRL	0x00
2662306a36Sopenharmony_ci#define SCC_GSMRL_ENR		(1 << 5)
2762306a36Sopenharmony_ci#define SCC_GSMRL_ENT		(1 << 4)
2862306a36Sopenharmony_ci#define SCC_GSMRL_MODE_QMC	(0x0A << 0)
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/* SCC general mode register low (32 bits) */
3162306a36Sopenharmony_ci#define SCC_GSMRH	0x04
3262306a36Sopenharmony_ci#define   SCC_GSMRH_CTSS	(1 << 7)
3362306a36Sopenharmony_ci#define   SCC_GSMRH_CDS		(1 << 8)
3462306a36Sopenharmony_ci#define   SCC_GSMRH_CTSP	(1 << 9)
3562306a36Sopenharmony_ci#define   SCC_GSMRH_CDP		(1 << 10)
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/* SCC event register (16 bits) */
3862306a36Sopenharmony_ci#define SCC_SCCE	0x10
3962306a36Sopenharmony_ci#define   SCC_SCCE_IQOV		(1 << 3)
4062306a36Sopenharmony_ci#define   SCC_SCCE_GINT		(1 << 2)
4162306a36Sopenharmony_ci#define   SCC_SCCE_GUN		(1 << 1)
4262306a36Sopenharmony_ci#define   SCC_SCCE_GOV		(1 << 0)
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/* SCC mask register (16 bits) */
4562306a36Sopenharmony_ci#define SCC_SCCM	0x14
4662306a36Sopenharmony_ci/* Multichannel base pointer (32 bits) */
4762306a36Sopenharmony_ci#define QMC_GBL_MCBASE		0x00
4862306a36Sopenharmony_ci/* Multichannel controller state (16 bits) */
4962306a36Sopenharmony_ci#define QMC_GBL_QMCSTATE	0x04
5062306a36Sopenharmony_ci/* Maximum receive buffer length (16 bits) */
5162306a36Sopenharmony_ci#define QMC_GBL_MRBLR		0x06
5262306a36Sopenharmony_ci/* Tx time-slot assignment table pointer (16 bits) */
5362306a36Sopenharmony_ci#define QMC_GBL_TX_S_PTR	0x08
5462306a36Sopenharmony_ci/* Rx pointer (16 bits) */
5562306a36Sopenharmony_ci#define QMC_GBL_RXPTR		0x0A
5662306a36Sopenharmony_ci/* Global receive frame threshold (16 bits) */
5762306a36Sopenharmony_ci#define QMC_GBL_GRFTHR		0x0C
5862306a36Sopenharmony_ci/* Global receive frame count (16 bits) */
5962306a36Sopenharmony_ci#define QMC_GBL_GRFCNT		0x0E
6062306a36Sopenharmony_ci/* Multichannel interrupt base address (32 bits) */
6162306a36Sopenharmony_ci#define QMC_GBL_INTBASE		0x10
6262306a36Sopenharmony_ci/* Multichannel interrupt pointer (32 bits) */
6362306a36Sopenharmony_ci#define QMC_GBL_INTPTR		0x14
6462306a36Sopenharmony_ci/* Rx time-slot assignment table pointer (16 bits) */
6562306a36Sopenharmony_ci#define QMC_GBL_RX_S_PTR	0x18
6662306a36Sopenharmony_ci/* Tx pointer (16 bits) */
6762306a36Sopenharmony_ci#define QMC_GBL_TXPTR		0x1A
6862306a36Sopenharmony_ci/* CRC constant (32 bits) */
6962306a36Sopenharmony_ci#define QMC_GBL_C_MASK32	0x1C
7062306a36Sopenharmony_ci/* Time slot assignment table Rx (32 x 16 bits) */
7162306a36Sopenharmony_ci#define QMC_GBL_TSATRX		0x20
7262306a36Sopenharmony_ci/* Time slot assignment table Tx (32 x 16 bits) */
7362306a36Sopenharmony_ci#define QMC_GBL_TSATTX		0x60
7462306a36Sopenharmony_ci/* CRC constant (16 bits) */
7562306a36Sopenharmony_ci#define QMC_GBL_C_MASK16	0xA0
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci/* TSA entry (16bit entry in TSATRX and TSATTX) */
7862306a36Sopenharmony_ci#define QMC_TSA_VALID		(1 << 15)
7962306a36Sopenharmony_ci#define QMC_TSA_WRAP		(1 << 14)
8062306a36Sopenharmony_ci#define QMC_TSA_MASK		(0x303F)
8162306a36Sopenharmony_ci#define QMC_TSA_CHANNEL(x)	((x) << 6)
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/* Tx buffer descriptor base address (16 bits, offset from MCBASE) */
8462306a36Sopenharmony_ci#define QMC_SPE_TBASE	0x00
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci/* Channel mode register (16 bits) */
8762306a36Sopenharmony_ci#define QMC_SPE_CHAMR	0x02
8862306a36Sopenharmony_ci#define   QMC_SPE_CHAMR_MODE_HDLC	(1 << 15)
8962306a36Sopenharmony_ci#define   QMC_SPE_CHAMR_MODE_TRANSP	((0 << 15) | (1 << 13))
9062306a36Sopenharmony_ci#define   QMC_SPE_CHAMR_ENT		(1 << 12)
9162306a36Sopenharmony_ci#define   QMC_SPE_CHAMR_POL		(1 << 8)
9262306a36Sopenharmony_ci#define   QMC_SPE_CHAMR_HDLC_IDLM	(1 << 13)
9362306a36Sopenharmony_ci#define   QMC_SPE_CHAMR_HDLC_CRC	(1 << 7)
9462306a36Sopenharmony_ci#define   QMC_SPE_CHAMR_HDLC_NOF	(0x0f << 0)
9562306a36Sopenharmony_ci#define   QMC_SPE_CHAMR_TRANSP_RD	(1 << 14)
9662306a36Sopenharmony_ci#define   QMC_SPE_CHAMR_TRANSP_SYNC	(1 << 10)
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci/* Tx internal state (32 bits) */
9962306a36Sopenharmony_ci#define QMC_SPE_TSTATE	0x04
10062306a36Sopenharmony_ci/* Tx buffer descriptor pointer (16 bits) */
10162306a36Sopenharmony_ci#define QMC_SPE_TBPTR	0x0C
10262306a36Sopenharmony_ci/* Zero-insertion state (32 bits) */
10362306a36Sopenharmony_ci#define QMC_SPE_ZISTATE	0x14
10462306a36Sopenharmony_ci/* Channel’s interrupt mask flags (16 bits) */
10562306a36Sopenharmony_ci#define QMC_SPE_INTMSK	0x1C
10662306a36Sopenharmony_ci/* Rx buffer descriptor base address (16 bits, offset from MCBASE) */
10762306a36Sopenharmony_ci#define QMC_SPE_RBASE	0x20
10862306a36Sopenharmony_ci/* HDLC: Maximum frame length register (16 bits) */
10962306a36Sopenharmony_ci#define QMC_SPE_MFLR	0x22
11062306a36Sopenharmony_ci/* TRANSPARENT: Transparent maximum receive length (16 bits) */
11162306a36Sopenharmony_ci#define QMC_SPE_TMRBLR	0x22
11262306a36Sopenharmony_ci/* Rx internal state (32 bits) */
11362306a36Sopenharmony_ci#define QMC_SPE_RSTATE	0x24
11462306a36Sopenharmony_ci/* Rx buffer descriptor pointer (16 bits) */
11562306a36Sopenharmony_ci#define QMC_SPE_RBPTR	0x2C
11662306a36Sopenharmony_ci/* Packs 4 bytes to 1 long word before writing to buffer (32 bits) */
11762306a36Sopenharmony_ci#define QMC_SPE_RPACK	0x30
11862306a36Sopenharmony_ci/* Zero deletion state (32 bits) */
11962306a36Sopenharmony_ci#define QMC_SPE_ZDSTATE	0x34
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci/* Transparent synchronization (16 bits) */
12262306a36Sopenharmony_ci#define QMC_SPE_TRNSYNC 0x3C
12362306a36Sopenharmony_ci#define   QMC_SPE_TRNSYNC_RX(x)	((x) << 8)
12462306a36Sopenharmony_ci#define   QMC_SPE_TRNSYNC_TX(x)	((x) << 0)
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/* Interrupt related registers bits */
12762306a36Sopenharmony_ci#define QMC_INT_V		(1 << 15)
12862306a36Sopenharmony_ci#define QMC_INT_W		(1 << 14)
12962306a36Sopenharmony_ci#define QMC_INT_NID		(1 << 13)
13062306a36Sopenharmony_ci#define QMC_INT_IDL		(1 << 12)
13162306a36Sopenharmony_ci#define QMC_INT_GET_CHANNEL(x)	(((x) & 0x0FC0) >> 6)
13262306a36Sopenharmony_ci#define QMC_INT_MRF		(1 << 5)
13362306a36Sopenharmony_ci#define QMC_INT_UN		(1 << 4)
13462306a36Sopenharmony_ci#define QMC_INT_RXF		(1 << 3)
13562306a36Sopenharmony_ci#define QMC_INT_BSY		(1 << 2)
13662306a36Sopenharmony_ci#define QMC_INT_TXB		(1 << 1)
13762306a36Sopenharmony_ci#define QMC_INT_RXB		(1 << 0)
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/* BD related registers bits */
14062306a36Sopenharmony_ci#define QMC_BD_RX_E	(1 << 15)
14162306a36Sopenharmony_ci#define QMC_BD_RX_W	(1 << 13)
14262306a36Sopenharmony_ci#define QMC_BD_RX_I	(1 << 12)
14362306a36Sopenharmony_ci#define QMC_BD_RX_L	(1 << 11)
14462306a36Sopenharmony_ci#define QMC_BD_RX_F	(1 << 10)
14562306a36Sopenharmony_ci#define QMC_BD_RX_CM	(1 << 9)
14662306a36Sopenharmony_ci#define QMC_BD_RX_UB	(1 << 7)
14762306a36Sopenharmony_ci#define QMC_BD_RX_LG	(1 << 5)
14862306a36Sopenharmony_ci#define QMC_BD_RX_NO	(1 << 4)
14962306a36Sopenharmony_ci#define QMC_BD_RX_AB	(1 << 3)
15062306a36Sopenharmony_ci#define QMC_BD_RX_CR	(1 << 2)
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci#define QMC_BD_TX_R	(1 << 15)
15362306a36Sopenharmony_ci#define QMC_BD_TX_W	(1 << 13)
15462306a36Sopenharmony_ci#define QMC_BD_TX_I	(1 << 12)
15562306a36Sopenharmony_ci#define QMC_BD_TX_L	(1 << 11)
15662306a36Sopenharmony_ci#define QMC_BD_TX_TC	(1 << 10)
15762306a36Sopenharmony_ci#define QMC_BD_TX_CM	(1 << 9)
15862306a36Sopenharmony_ci#define QMC_BD_TX_UB	(1 << 7)
15962306a36Sopenharmony_ci#define QMC_BD_TX_PAD	(0x0f << 0)
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci/* Numbers of BDs and interrupt items */
16262306a36Sopenharmony_ci#define QMC_NB_TXBDS	8
16362306a36Sopenharmony_ci#define QMC_NB_RXBDS	8
16462306a36Sopenharmony_ci#define QMC_NB_INTS	128
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistruct qmc_xfer_desc {
16762306a36Sopenharmony_ci	union {
16862306a36Sopenharmony_ci		void (*tx_complete)(void *context);
16962306a36Sopenharmony_ci		void (*rx_complete)(void *context, size_t length);
17062306a36Sopenharmony_ci	};
17162306a36Sopenharmony_ci	void *context;
17262306a36Sopenharmony_ci};
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistruct qmc_chan {
17562306a36Sopenharmony_ci	struct list_head list;
17662306a36Sopenharmony_ci	unsigned int id;
17762306a36Sopenharmony_ci	struct qmc *qmc;
17862306a36Sopenharmony_ci	void __iomem *s_param;
17962306a36Sopenharmony_ci	enum qmc_mode mode;
18062306a36Sopenharmony_ci	u64	tx_ts_mask;
18162306a36Sopenharmony_ci	u64	rx_ts_mask;
18262306a36Sopenharmony_ci	bool is_reverse_data;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	spinlock_t	tx_lock;
18562306a36Sopenharmony_ci	cbd_t __iomem *txbds;
18662306a36Sopenharmony_ci	cbd_t __iomem *txbd_free;
18762306a36Sopenharmony_ci	cbd_t __iomem *txbd_done;
18862306a36Sopenharmony_ci	struct qmc_xfer_desc tx_desc[QMC_NB_TXBDS];
18962306a36Sopenharmony_ci	u64	nb_tx_underrun;
19062306a36Sopenharmony_ci	bool	is_tx_stopped;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	spinlock_t	rx_lock;
19362306a36Sopenharmony_ci	cbd_t __iomem *rxbds;
19462306a36Sopenharmony_ci	cbd_t __iomem *rxbd_free;
19562306a36Sopenharmony_ci	cbd_t __iomem *rxbd_done;
19662306a36Sopenharmony_ci	struct qmc_xfer_desc rx_desc[QMC_NB_RXBDS];
19762306a36Sopenharmony_ci	u64	nb_rx_busy;
19862306a36Sopenharmony_ci	int	rx_pending;
19962306a36Sopenharmony_ci	bool	is_rx_halted;
20062306a36Sopenharmony_ci	bool	is_rx_stopped;
20162306a36Sopenharmony_ci};
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistruct qmc {
20462306a36Sopenharmony_ci	struct device *dev;
20562306a36Sopenharmony_ci	struct tsa_serial *tsa_serial;
20662306a36Sopenharmony_ci	void __iomem *scc_regs;
20762306a36Sopenharmony_ci	void __iomem *scc_pram;
20862306a36Sopenharmony_ci	void __iomem *dpram;
20962306a36Sopenharmony_ci	u16 scc_pram_offset;
21062306a36Sopenharmony_ci	cbd_t __iomem *bd_table;
21162306a36Sopenharmony_ci	dma_addr_t bd_dma_addr;
21262306a36Sopenharmony_ci	size_t bd_size;
21362306a36Sopenharmony_ci	u16 __iomem *int_table;
21462306a36Sopenharmony_ci	u16 __iomem *int_curr;
21562306a36Sopenharmony_ci	dma_addr_t int_dma_addr;
21662306a36Sopenharmony_ci	size_t int_size;
21762306a36Sopenharmony_ci	struct list_head chan_head;
21862306a36Sopenharmony_ci	struct qmc_chan *chans[64];
21962306a36Sopenharmony_ci};
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic inline void qmc_write16(void __iomem *addr, u16 val)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	iowrite16be(val, addr);
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic inline u16 qmc_read16(void __iomem *addr)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	return ioread16be(addr);
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic inline void qmc_setbits16(void __iomem *addr, u16 set)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	qmc_write16(addr, qmc_read16(addr) | set);
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic inline void qmc_clrbits16(void __iomem *addr, u16 clr)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	qmc_write16(addr, qmc_read16(addr) & ~clr);
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic inline void qmc_write32(void __iomem *addr, u32 val)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	iowrite32be(val, addr);
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic inline u32 qmc_read32(void __iomem *addr)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	return ioread32be(addr);
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic inline void qmc_setbits32(void __iomem *addr, u32 set)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	qmc_write32(addr, qmc_read32(addr) | set);
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ciint qmc_chan_get_info(struct qmc_chan *chan, struct qmc_chan_info *info)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	struct tsa_serial_info tsa_info;
26062306a36Sopenharmony_ci	int ret;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	/* Retrieve info from the TSA related serial */
26362306a36Sopenharmony_ci	ret = tsa_serial_get_info(chan->qmc->tsa_serial, &tsa_info);
26462306a36Sopenharmony_ci	if (ret)
26562306a36Sopenharmony_ci		return ret;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	info->mode = chan->mode;
26862306a36Sopenharmony_ci	info->rx_fs_rate = tsa_info.rx_fs_rate;
26962306a36Sopenharmony_ci	info->rx_bit_rate = tsa_info.rx_bit_rate;
27062306a36Sopenharmony_ci	info->nb_tx_ts = hweight64(chan->tx_ts_mask);
27162306a36Sopenharmony_ci	info->tx_fs_rate = tsa_info.tx_fs_rate;
27262306a36Sopenharmony_ci	info->tx_bit_rate = tsa_info.tx_bit_rate;
27362306a36Sopenharmony_ci	info->nb_rx_ts = hweight64(chan->rx_ts_mask);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	return 0;
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ciEXPORT_SYMBOL(qmc_chan_get_info);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ciint qmc_chan_set_param(struct qmc_chan *chan, const struct qmc_chan_param *param)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci	if (param->mode != chan->mode)
28262306a36Sopenharmony_ci		return -EINVAL;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	switch (param->mode) {
28562306a36Sopenharmony_ci	case QMC_HDLC:
28662306a36Sopenharmony_ci		if ((param->hdlc.max_rx_buf_size % 4) ||
28762306a36Sopenharmony_ci		    (param->hdlc.max_rx_buf_size < 8))
28862306a36Sopenharmony_ci			return -EINVAL;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci		qmc_write16(chan->qmc->scc_pram + QMC_GBL_MRBLR,
29162306a36Sopenharmony_ci			    param->hdlc.max_rx_buf_size - 8);
29262306a36Sopenharmony_ci		qmc_write16(chan->s_param + QMC_SPE_MFLR,
29362306a36Sopenharmony_ci			    param->hdlc.max_rx_frame_size);
29462306a36Sopenharmony_ci		if (param->hdlc.is_crc32) {
29562306a36Sopenharmony_ci			qmc_setbits16(chan->s_param + QMC_SPE_CHAMR,
29662306a36Sopenharmony_ci				      QMC_SPE_CHAMR_HDLC_CRC);
29762306a36Sopenharmony_ci		} else {
29862306a36Sopenharmony_ci			qmc_clrbits16(chan->s_param + QMC_SPE_CHAMR,
29962306a36Sopenharmony_ci				      QMC_SPE_CHAMR_HDLC_CRC);
30062306a36Sopenharmony_ci		}
30162306a36Sopenharmony_ci		break;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	case QMC_TRANSPARENT:
30462306a36Sopenharmony_ci		qmc_write16(chan->s_param + QMC_SPE_TMRBLR,
30562306a36Sopenharmony_ci			    param->transp.max_rx_buf_size);
30662306a36Sopenharmony_ci		break;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	default:
30962306a36Sopenharmony_ci		return -EINVAL;
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	return 0;
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ciEXPORT_SYMBOL(qmc_chan_set_param);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ciint qmc_chan_write_submit(struct qmc_chan *chan, dma_addr_t addr, size_t length,
31762306a36Sopenharmony_ci			  void (*complete)(void *context), void *context)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	struct qmc_xfer_desc *xfer_desc;
32062306a36Sopenharmony_ci	unsigned long flags;
32162306a36Sopenharmony_ci	cbd_t __iomem *bd;
32262306a36Sopenharmony_ci	u16 ctrl;
32362306a36Sopenharmony_ci	int ret;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	/*
32662306a36Sopenharmony_ci	 * R bit  UB bit
32762306a36Sopenharmony_ci	 *   0       0  : The BD is free
32862306a36Sopenharmony_ci	 *   1       1  : The BD is in used, waiting for transfer
32962306a36Sopenharmony_ci	 *   0       1  : The BD is in used, waiting for completion
33062306a36Sopenharmony_ci	 *   1       0  : Should not append
33162306a36Sopenharmony_ci	 */
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	spin_lock_irqsave(&chan->tx_lock, flags);
33462306a36Sopenharmony_ci	bd = chan->txbd_free;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	ctrl = qmc_read16(&bd->cbd_sc);
33762306a36Sopenharmony_ci	if (ctrl & (QMC_BD_TX_R | QMC_BD_TX_UB)) {
33862306a36Sopenharmony_ci		/* We are full ... */
33962306a36Sopenharmony_ci		ret = -EBUSY;
34062306a36Sopenharmony_ci		goto end;
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	qmc_write16(&bd->cbd_datlen, length);
34462306a36Sopenharmony_ci	qmc_write32(&bd->cbd_bufaddr, addr);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	xfer_desc = &chan->tx_desc[bd - chan->txbds];
34762306a36Sopenharmony_ci	xfer_desc->tx_complete = complete;
34862306a36Sopenharmony_ci	xfer_desc->context = context;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	/* Activate the descriptor */
35162306a36Sopenharmony_ci	ctrl |= (QMC_BD_TX_R | QMC_BD_TX_UB);
35262306a36Sopenharmony_ci	wmb(); /* Be sure to flush the descriptor before control update */
35362306a36Sopenharmony_ci	qmc_write16(&bd->cbd_sc, ctrl);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	if (!chan->is_tx_stopped)
35662306a36Sopenharmony_ci		qmc_setbits16(chan->s_param + QMC_SPE_CHAMR, QMC_SPE_CHAMR_POL);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	if (ctrl & QMC_BD_TX_W)
35962306a36Sopenharmony_ci		chan->txbd_free = chan->txbds;
36062306a36Sopenharmony_ci	else
36162306a36Sopenharmony_ci		chan->txbd_free++;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	ret = 0;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ciend:
36662306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->tx_lock, flags);
36762306a36Sopenharmony_ci	return ret;
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ciEXPORT_SYMBOL(qmc_chan_write_submit);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_cistatic void qmc_chan_write_done(struct qmc_chan *chan)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	struct qmc_xfer_desc *xfer_desc;
37462306a36Sopenharmony_ci	void (*complete)(void *context);
37562306a36Sopenharmony_ci	unsigned long flags;
37662306a36Sopenharmony_ci	void *context;
37762306a36Sopenharmony_ci	cbd_t __iomem *bd;
37862306a36Sopenharmony_ci	u16 ctrl;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	/*
38162306a36Sopenharmony_ci	 * R bit  UB bit
38262306a36Sopenharmony_ci	 *   0       0  : The BD is free
38362306a36Sopenharmony_ci	 *   1       1  : The BD is in used, waiting for transfer
38462306a36Sopenharmony_ci	 *   0       1  : The BD is in used, waiting for completion
38562306a36Sopenharmony_ci	 *   1       0  : Should not append
38662306a36Sopenharmony_ci	 */
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	spin_lock_irqsave(&chan->tx_lock, flags);
38962306a36Sopenharmony_ci	bd = chan->txbd_done;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	ctrl = qmc_read16(&bd->cbd_sc);
39262306a36Sopenharmony_ci	while (!(ctrl & QMC_BD_TX_R)) {
39362306a36Sopenharmony_ci		if (!(ctrl & QMC_BD_TX_UB))
39462306a36Sopenharmony_ci			goto end;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci		xfer_desc = &chan->tx_desc[bd - chan->txbds];
39762306a36Sopenharmony_ci		complete = xfer_desc->tx_complete;
39862306a36Sopenharmony_ci		context = xfer_desc->context;
39962306a36Sopenharmony_ci		xfer_desc->tx_complete = NULL;
40062306a36Sopenharmony_ci		xfer_desc->context = NULL;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci		qmc_write16(&bd->cbd_sc, ctrl & ~QMC_BD_TX_UB);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci		if (ctrl & QMC_BD_TX_W)
40562306a36Sopenharmony_ci			chan->txbd_done = chan->txbds;
40662306a36Sopenharmony_ci		else
40762306a36Sopenharmony_ci			chan->txbd_done++;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci		if (complete) {
41062306a36Sopenharmony_ci			spin_unlock_irqrestore(&chan->tx_lock, flags);
41162306a36Sopenharmony_ci			complete(context);
41262306a36Sopenharmony_ci			spin_lock_irqsave(&chan->tx_lock, flags);
41362306a36Sopenharmony_ci		}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci		bd = chan->txbd_done;
41662306a36Sopenharmony_ci		ctrl = qmc_read16(&bd->cbd_sc);
41762306a36Sopenharmony_ci	}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ciend:
42062306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->tx_lock, flags);
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ciint qmc_chan_read_submit(struct qmc_chan *chan, dma_addr_t addr, size_t length,
42462306a36Sopenharmony_ci			 void (*complete)(void *context, size_t length), void *context)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	struct qmc_xfer_desc *xfer_desc;
42762306a36Sopenharmony_ci	unsigned long flags;
42862306a36Sopenharmony_ci	cbd_t __iomem *bd;
42962306a36Sopenharmony_ci	u16 ctrl;
43062306a36Sopenharmony_ci	int ret;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	/*
43362306a36Sopenharmony_ci	 * E bit  UB bit
43462306a36Sopenharmony_ci	 *   0       0  : The BD is free
43562306a36Sopenharmony_ci	 *   1       1  : The BD is in used, waiting for transfer
43662306a36Sopenharmony_ci	 *   0       1  : The BD is in used, waiting for completion
43762306a36Sopenharmony_ci	 *   1       0  : Should not append
43862306a36Sopenharmony_ci	 */
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	spin_lock_irqsave(&chan->rx_lock, flags);
44162306a36Sopenharmony_ci	bd = chan->rxbd_free;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	ctrl = qmc_read16(&bd->cbd_sc);
44462306a36Sopenharmony_ci	if (ctrl & (QMC_BD_RX_E | QMC_BD_RX_UB)) {
44562306a36Sopenharmony_ci		/* We are full ... */
44662306a36Sopenharmony_ci		ret = -EBUSY;
44762306a36Sopenharmony_ci		goto end;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	qmc_write16(&bd->cbd_datlen, 0); /* data length is updated by the QMC */
45162306a36Sopenharmony_ci	qmc_write32(&bd->cbd_bufaddr, addr);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	xfer_desc = &chan->rx_desc[bd - chan->rxbds];
45462306a36Sopenharmony_ci	xfer_desc->rx_complete = complete;
45562306a36Sopenharmony_ci	xfer_desc->context = context;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	/* Activate the descriptor */
45862306a36Sopenharmony_ci	ctrl |= (QMC_BD_RX_E | QMC_BD_RX_UB);
45962306a36Sopenharmony_ci	wmb(); /* Be sure to flush data before descriptor activation */
46062306a36Sopenharmony_ci	qmc_write16(&bd->cbd_sc, ctrl);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	/* Restart receiver if needed */
46362306a36Sopenharmony_ci	if (chan->is_rx_halted && !chan->is_rx_stopped) {
46462306a36Sopenharmony_ci		/* Restart receiver */
46562306a36Sopenharmony_ci		if (chan->mode == QMC_TRANSPARENT)
46662306a36Sopenharmony_ci			qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x18000080);
46762306a36Sopenharmony_ci		else
46862306a36Sopenharmony_ci			qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x00000080);
46962306a36Sopenharmony_ci		qmc_write32(chan->s_param + QMC_SPE_RSTATE, 0x31000000);
47062306a36Sopenharmony_ci		chan->is_rx_halted = false;
47162306a36Sopenharmony_ci	}
47262306a36Sopenharmony_ci	chan->rx_pending++;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	if (ctrl & QMC_BD_RX_W)
47562306a36Sopenharmony_ci		chan->rxbd_free = chan->rxbds;
47662306a36Sopenharmony_ci	else
47762306a36Sopenharmony_ci		chan->rxbd_free++;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	ret = 0;
48062306a36Sopenharmony_ciend:
48162306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->rx_lock, flags);
48262306a36Sopenharmony_ci	return ret;
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ciEXPORT_SYMBOL(qmc_chan_read_submit);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_cistatic void qmc_chan_read_done(struct qmc_chan *chan)
48762306a36Sopenharmony_ci{
48862306a36Sopenharmony_ci	void (*complete)(void *context, size_t size);
48962306a36Sopenharmony_ci	struct qmc_xfer_desc *xfer_desc;
49062306a36Sopenharmony_ci	unsigned long flags;
49162306a36Sopenharmony_ci	cbd_t __iomem *bd;
49262306a36Sopenharmony_ci	void *context;
49362306a36Sopenharmony_ci	u16 datalen;
49462306a36Sopenharmony_ci	u16 ctrl;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	/*
49762306a36Sopenharmony_ci	 * E bit  UB bit
49862306a36Sopenharmony_ci	 *   0       0  : The BD is free
49962306a36Sopenharmony_ci	 *   1       1  : The BD is in used, waiting for transfer
50062306a36Sopenharmony_ci	 *   0       1  : The BD is in used, waiting for completion
50162306a36Sopenharmony_ci	 *   1       0  : Should not append
50262306a36Sopenharmony_ci	 */
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	spin_lock_irqsave(&chan->rx_lock, flags);
50562306a36Sopenharmony_ci	bd = chan->rxbd_done;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	ctrl = qmc_read16(&bd->cbd_sc);
50862306a36Sopenharmony_ci	while (!(ctrl & QMC_BD_RX_E)) {
50962306a36Sopenharmony_ci		if (!(ctrl & QMC_BD_RX_UB))
51062306a36Sopenharmony_ci			goto end;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci		xfer_desc = &chan->rx_desc[bd - chan->rxbds];
51362306a36Sopenharmony_ci		complete = xfer_desc->rx_complete;
51462306a36Sopenharmony_ci		context = xfer_desc->context;
51562306a36Sopenharmony_ci		xfer_desc->rx_complete = NULL;
51662306a36Sopenharmony_ci		xfer_desc->context = NULL;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci		datalen = qmc_read16(&bd->cbd_datlen);
51962306a36Sopenharmony_ci		qmc_write16(&bd->cbd_sc, ctrl & ~QMC_BD_RX_UB);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci		if (ctrl & QMC_BD_RX_W)
52262306a36Sopenharmony_ci			chan->rxbd_done = chan->rxbds;
52362306a36Sopenharmony_ci		else
52462306a36Sopenharmony_ci			chan->rxbd_done++;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci		chan->rx_pending--;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci		if (complete) {
52962306a36Sopenharmony_ci			spin_unlock_irqrestore(&chan->rx_lock, flags);
53062306a36Sopenharmony_ci			complete(context, datalen);
53162306a36Sopenharmony_ci			spin_lock_irqsave(&chan->rx_lock, flags);
53262306a36Sopenharmony_ci		}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci		bd = chan->rxbd_done;
53562306a36Sopenharmony_ci		ctrl = qmc_read16(&bd->cbd_sc);
53662306a36Sopenharmony_ci	}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ciend:
53962306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->rx_lock, flags);
54062306a36Sopenharmony_ci}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_cistatic int qmc_chan_command(struct qmc_chan *chan, u8 qmc_opcode)
54362306a36Sopenharmony_ci{
54462306a36Sopenharmony_ci	return cpm_command(chan->id << 2, (qmc_opcode << 4) | 0x0E);
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistatic int qmc_chan_stop_rx(struct qmc_chan *chan)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	unsigned long flags;
55062306a36Sopenharmony_ci	int ret;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	spin_lock_irqsave(&chan->rx_lock, flags);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	/* Send STOP RECEIVE command */
55562306a36Sopenharmony_ci	ret = qmc_chan_command(chan, 0x0);
55662306a36Sopenharmony_ci	if (ret) {
55762306a36Sopenharmony_ci		dev_err(chan->qmc->dev, "chan %u: Send STOP RECEIVE failed (%d)\n",
55862306a36Sopenharmony_ci			chan->id, ret);
55962306a36Sopenharmony_ci		goto end;
56062306a36Sopenharmony_ci	}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	chan->is_rx_stopped = true;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ciend:
56562306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->rx_lock, flags);
56662306a36Sopenharmony_ci	return ret;
56762306a36Sopenharmony_ci}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_cistatic int qmc_chan_stop_tx(struct qmc_chan *chan)
57062306a36Sopenharmony_ci{
57162306a36Sopenharmony_ci	unsigned long flags;
57262306a36Sopenharmony_ci	int ret;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	spin_lock_irqsave(&chan->tx_lock, flags);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	/* Send STOP TRANSMIT command */
57762306a36Sopenharmony_ci	ret = qmc_chan_command(chan, 0x1);
57862306a36Sopenharmony_ci	if (ret) {
57962306a36Sopenharmony_ci		dev_err(chan->qmc->dev, "chan %u: Send STOP TRANSMIT failed (%d)\n",
58062306a36Sopenharmony_ci			chan->id, ret);
58162306a36Sopenharmony_ci		goto end;
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	chan->is_tx_stopped = true;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ciend:
58762306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->tx_lock, flags);
58862306a36Sopenharmony_ci	return ret;
58962306a36Sopenharmony_ci}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ciint qmc_chan_stop(struct qmc_chan *chan, int direction)
59262306a36Sopenharmony_ci{
59362306a36Sopenharmony_ci	int ret;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	if (direction & QMC_CHAN_READ) {
59662306a36Sopenharmony_ci		ret = qmc_chan_stop_rx(chan);
59762306a36Sopenharmony_ci		if (ret)
59862306a36Sopenharmony_ci			return ret;
59962306a36Sopenharmony_ci	}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	if (direction & QMC_CHAN_WRITE) {
60262306a36Sopenharmony_ci		ret = qmc_chan_stop_tx(chan);
60362306a36Sopenharmony_ci		if (ret)
60462306a36Sopenharmony_ci			return ret;
60562306a36Sopenharmony_ci	}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	return 0;
60862306a36Sopenharmony_ci}
60962306a36Sopenharmony_ciEXPORT_SYMBOL(qmc_chan_stop);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_cistatic void qmc_chan_start_rx(struct qmc_chan *chan)
61262306a36Sopenharmony_ci{
61362306a36Sopenharmony_ci	unsigned long flags;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	spin_lock_irqsave(&chan->rx_lock, flags);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	/* Restart the receiver */
61862306a36Sopenharmony_ci	if (chan->mode == QMC_TRANSPARENT)
61962306a36Sopenharmony_ci		qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x18000080);
62062306a36Sopenharmony_ci	else
62162306a36Sopenharmony_ci		qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x00000080);
62262306a36Sopenharmony_ci	qmc_write32(chan->s_param + QMC_SPE_RSTATE, 0x31000000);
62362306a36Sopenharmony_ci	chan->is_rx_halted = false;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	chan->is_rx_stopped = false;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->rx_lock, flags);
62862306a36Sopenharmony_ci}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_cistatic void qmc_chan_start_tx(struct qmc_chan *chan)
63162306a36Sopenharmony_ci{
63262306a36Sopenharmony_ci	unsigned long flags;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	spin_lock_irqsave(&chan->tx_lock, flags);
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	/*
63762306a36Sopenharmony_ci	 * Enable channel transmitter as it could be disabled if
63862306a36Sopenharmony_ci	 * qmc_chan_reset() was called.
63962306a36Sopenharmony_ci	 */
64062306a36Sopenharmony_ci	qmc_setbits16(chan->s_param + QMC_SPE_CHAMR, QMC_SPE_CHAMR_ENT);
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	/* Set the POL bit in the channel mode register */
64362306a36Sopenharmony_ci	qmc_setbits16(chan->s_param + QMC_SPE_CHAMR, QMC_SPE_CHAMR_POL);
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	chan->is_tx_stopped = false;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->tx_lock, flags);
64862306a36Sopenharmony_ci}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ciint qmc_chan_start(struct qmc_chan *chan, int direction)
65162306a36Sopenharmony_ci{
65262306a36Sopenharmony_ci	if (direction & QMC_CHAN_READ)
65362306a36Sopenharmony_ci		qmc_chan_start_rx(chan);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	if (direction & QMC_CHAN_WRITE)
65662306a36Sopenharmony_ci		qmc_chan_start_tx(chan);
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	return 0;
65962306a36Sopenharmony_ci}
66062306a36Sopenharmony_ciEXPORT_SYMBOL(qmc_chan_start);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_cistatic void qmc_chan_reset_rx(struct qmc_chan *chan)
66362306a36Sopenharmony_ci{
66462306a36Sopenharmony_ci	struct qmc_xfer_desc *xfer_desc;
66562306a36Sopenharmony_ci	unsigned long flags;
66662306a36Sopenharmony_ci	cbd_t __iomem *bd;
66762306a36Sopenharmony_ci	u16 ctrl;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	spin_lock_irqsave(&chan->rx_lock, flags);
67062306a36Sopenharmony_ci	bd = chan->rxbds;
67162306a36Sopenharmony_ci	do {
67262306a36Sopenharmony_ci		ctrl = qmc_read16(&bd->cbd_sc);
67362306a36Sopenharmony_ci		qmc_write16(&bd->cbd_sc, ctrl & ~(QMC_BD_RX_UB | QMC_BD_RX_E));
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci		xfer_desc = &chan->rx_desc[bd - chan->rxbds];
67662306a36Sopenharmony_ci		xfer_desc->rx_complete = NULL;
67762306a36Sopenharmony_ci		xfer_desc->context = NULL;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci		bd++;
68062306a36Sopenharmony_ci	} while (!(ctrl & QMC_BD_RX_W));
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	chan->rxbd_free = chan->rxbds;
68362306a36Sopenharmony_ci	chan->rxbd_done = chan->rxbds;
68462306a36Sopenharmony_ci	qmc_write16(chan->s_param + QMC_SPE_RBPTR,
68562306a36Sopenharmony_ci		    qmc_read16(chan->s_param + QMC_SPE_RBASE));
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	chan->rx_pending = 0;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->rx_lock, flags);
69062306a36Sopenharmony_ci}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_cistatic void qmc_chan_reset_tx(struct qmc_chan *chan)
69362306a36Sopenharmony_ci{
69462306a36Sopenharmony_ci	struct qmc_xfer_desc *xfer_desc;
69562306a36Sopenharmony_ci	unsigned long flags;
69662306a36Sopenharmony_ci	cbd_t __iomem *bd;
69762306a36Sopenharmony_ci	u16 ctrl;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	spin_lock_irqsave(&chan->tx_lock, flags);
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	/* Disable transmitter. It will be re-enable on qmc_chan_start() */
70262306a36Sopenharmony_ci	qmc_clrbits16(chan->s_param + QMC_SPE_CHAMR, QMC_SPE_CHAMR_ENT);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	bd = chan->txbds;
70562306a36Sopenharmony_ci	do {
70662306a36Sopenharmony_ci		ctrl = qmc_read16(&bd->cbd_sc);
70762306a36Sopenharmony_ci		qmc_write16(&bd->cbd_sc, ctrl & ~(QMC_BD_TX_UB | QMC_BD_TX_R));
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci		xfer_desc = &chan->tx_desc[bd - chan->txbds];
71062306a36Sopenharmony_ci		xfer_desc->tx_complete = NULL;
71162306a36Sopenharmony_ci		xfer_desc->context = NULL;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci		bd++;
71462306a36Sopenharmony_ci	} while (!(ctrl & QMC_BD_TX_W));
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	chan->txbd_free = chan->txbds;
71762306a36Sopenharmony_ci	chan->txbd_done = chan->txbds;
71862306a36Sopenharmony_ci	qmc_write16(chan->s_param + QMC_SPE_TBPTR,
71962306a36Sopenharmony_ci		    qmc_read16(chan->s_param + QMC_SPE_TBASE));
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	/* Reset TSTATE and ZISTATE to their initial value */
72262306a36Sopenharmony_ci	qmc_write32(chan->s_param + QMC_SPE_TSTATE, 0x30000000);
72362306a36Sopenharmony_ci	qmc_write32(chan->s_param + QMC_SPE_ZISTATE, 0x00000100);
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->tx_lock, flags);
72662306a36Sopenharmony_ci}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ciint qmc_chan_reset(struct qmc_chan *chan, int direction)
72962306a36Sopenharmony_ci{
73062306a36Sopenharmony_ci	if (direction & QMC_CHAN_READ)
73162306a36Sopenharmony_ci		qmc_chan_reset_rx(chan);
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	if (direction & QMC_CHAN_WRITE)
73462306a36Sopenharmony_ci		qmc_chan_reset_tx(chan);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	return 0;
73762306a36Sopenharmony_ci}
73862306a36Sopenharmony_ciEXPORT_SYMBOL(qmc_chan_reset);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_cistatic int qmc_check_chans(struct qmc *qmc)
74162306a36Sopenharmony_ci{
74262306a36Sopenharmony_ci	struct tsa_serial_info info;
74362306a36Sopenharmony_ci	bool is_one_table = false;
74462306a36Sopenharmony_ci	struct qmc_chan *chan;
74562306a36Sopenharmony_ci	u64 tx_ts_mask = 0;
74662306a36Sopenharmony_ci	u64 rx_ts_mask = 0;
74762306a36Sopenharmony_ci	u64 tx_ts_assigned_mask;
74862306a36Sopenharmony_ci	u64 rx_ts_assigned_mask;
74962306a36Sopenharmony_ci	int ret;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	/* Retrieve info from the TSA related serial */
75262306a36Sopenharmony_ci	ret = tsa_serial_get_info(qmc->tsa_serial, &info);
75362306a36Sopenharmony_ci	if (ret)
75462306a36Sopenharmony_ci		return ret;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	if ((info.nb_tx_ts > 64) || (info.nb_rx_ts > 64)) {
75762306a36Sopenharmony_ci		dev_err(qmc->dev, "Number of TSA Tx/Rx TS assigned not supported\n");
75862306a36Sopenharmony_ci		return -EINVAL;
75962306a36Sopenharmony_ci	}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	/*
76262306a36Sopenharmony_ci	 * If more than 32 TS are assigned to this serial, one common table is
76362306a36Sopenharmony_ci	 * used for Tx and Rx and so masks must be equal for all channels.
76462306a36Sopenharmony_ci	 */
76562306a36Sopenharmony_ci	if ((info.nb_tx_ts > 32) || (info.nb_rx_ts > 32)) {
76662306a36Sopenharmony_ci		if (info.nb_tx_ts != info.nb_rx_ts) {
76762306a36Sopenharmony_ci			dev_err(qmc->dev, "Number of TSA Tx/Rx TS assigned are not equal\n");
76862306a36Sopenharmony_ci			return -EINVAL;
76962306a36Sopenharmony_ci		}
77062306a36Sopenharmony_ci		is_one_table = true;
77162306a36Sopenharmony_ci	}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	tx_ts_assigned_mask = info.nb_tx_ts == 64 ? U64_MAX : (((u64)1) << info.nb_tx_ts) - 1;
77462306a36Sopenharmony_ci	rx_ts_assigned_mask = info.nb_rx_ts == 64 ? U64_MAX : (((u64)1) << info.nb_rx_ts) - 1;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	list_for_each_entry(chan, &qmc->chan_head, list) {
77762306a36Sopenharmony_ci		if (chan->tx_ts_mask > tx_ts_assigned_mask) {
77862306a36Sopenharmony_ci			dev_err(qmc->dev, "chan %u uses TSA unassigned Tx TS\n", chan->id);
77962306a36Sopenharmony_ci			return -EINVAL;
78062306a36Sopenharmony_ci		}
78162306a36Sopenharmony_ci		if (tx_ts_mask & chan->tx_ts_mask) {
78262306a36Sopenharmony_ci			dev_err(qmc->dev, "chan %u uses an already used Tx TS\n", chan->id);
78362306a36Sopenharmony_ci			return -EINVAL;
78462306a36Sopenharmony_ci		}
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci		if (chan->rx_ts_mask > rx_ts_assigned_mask) {
78762306a36Sopenharmony_ci			dev_err(qmc->dev, "chan %u uses TSA unassigned Rx TS\n", chan->id);
78862306a36Sopenharmony_ci			return -EINVAL;
78962306a36Sopenharmony_ci		}
79062306a36Sopenharmony_ci		if (rx_ts_mask & chan->rx_ts_mask) {
79162306a36Sopenharmony_ci			dev_err(qmc->dev, "chan %u uses an already used Rx TS\n", chan->id);
79262306a36Sopenharmony_ci			return -EINVAL;
79362306a36Sopenharmony_ci		}
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci		if (is_one_table && (chan->tx_ts_mask != chan->rx_ts_mask)) {
79662306a36Sopenharmony_ci			dev_err(qmc->dev, "chan %u uses different Rx and Tx TS\n", chan->id);
79762306a36Sopenharmony_ci			return -EINVAL;
79862306a36Sopenharmony_ci		}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci		tx_ts_mask |= chan->tx_ts_mask;
80162306a36Sopenharmony_ci		rx_ts_mask |= chan->rx_ts_mask;
80262306a36Sopenharmony_ci	}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	return 0;
80562306a36Sopenharmony_ci}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_cistatic unsigned int qmc_nb_chans(struct qmc *qmc)
80862306a36Sopenharmony_ci{
80962306a36Sopenharmony_ci	unsigned int count = 0;
81062306a36Sopenharmony_ci	struct qmc_chan *chan;
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	list_for_each_entry(chan, &qmc->chan_head, list)
81362306a36Sopenharmony_ci		count++;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	return count;
81662306a36Sopenharmony_ci}
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_cistatic int qmc_of_parse_chans(struct qmc *qmc, struct device_node *np)
81962306a36Sopenharmony_ci{
82062306a36Sopenharmony_ci	struct device_node *chan_np;
82162306a36Sopenharmony_ci	struct qmc_chan *chan;
82262306a36Sopenharmony_ci	const char *mode;
82362306a36Sopenharmony_ci	u32 chan_id;
82462306a36Sopenharmony_ci	u64 ts_mask;
82562306a36Sopenharmony_ci	int ret;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	for_each_available_child_of_node(np, chan_np) {
82862306a36Sopenharmony_ci		ret = of_property_read_u32(chan_np, "reg", &chan_id);
82962306a36Sopenharmony_ci		if (ret) {
83062306a36Sopenharmony_ci			dev_err(qmc->dev, "%pOF: failed to read reg\n", chan_np);
83162306a36Sopenharmony_ci			of_node_put(chan_np);
83262306a36Sopenharmony_ci			return ret;
83362306a36Sopenharmony_ci		}
83462306a36Sopenharmony_ci		if (chan_id > 63) {
83562306a36Sopenharmony_ci			dev_err(qmc->dev, "%pOF: Invalid chan_id\n", chan_np);
83662306a36Sopenharmony_ci			of_node_put(chan_np);
83762306a36Sopenharmony_ci			return -EINVAL;
83862306a36Sopenharmony_ci		}
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci		chan = devm_kzalloc(qmc->dev, sizeof(*chan), GFP_KERNEL);
84162306a36Sopenharmony_ci		if (!chan) {
84262306a36Sopenharmony_ci			of_node_put(chan_np);
84362306a36Sopenharmony_ci			return -ENOMEM;
84462306a36Sopenharmony_ci		}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci		chan->id = chan_id;
84762306a36Sopenharmony_ci		spin_lock_init(&chan->rx_lock);
84862306a36Sopenharmony_ci		spin_lock_init(&chan->tx_lock);
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci		ret = of_property_read_u64(chan_np, "fsl,tx-ts-mask", &ts_mask);
85162306a36Sopenharmony_ci		if (ret) {
85262306a36Sopenharmony_ci			dev_err(qmc->dev, "%pOF: failed to read fsl,tx-ts-mask\n",
85362306a36Sopenharmony_ci				chan_np);
85462306a36Sopenharmony_ci			of_node_put(chan_np);
85562306a36Sopenharmony_ci			return ret;
85662306a36Sopenharmony_ci		}
85762306a36Sopenharmony_ci		chan->tx_ts_mask = ts_mask;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci		ret = of_property_read_u64(chan_np, "fsl,rx-ts-mask", &ts_mask);
86062306a36Sopenharmony_ci		if (ret) {
86162306a36Sopenharmony_ci			dev_err(qmc->dev, "%pOF: failed to read fsl,rx-ts-mask\n",
86262306a36Sopenharmony_ci				chan_np);
86362306a36Sopenharmony_ci			of_node_put(chan_np);
86462306a36Sopenharmony_ci			return ret;
86562306a36Sopenharmony_ci		}
86662306a36Sopenharmony_ci		chan->rx_ts_mask = ts_mask;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci		mode = "transparent";
86962306a36Sopenharmony_ci		ret = of_property_read_string(chan_np, "fsl,operational-mode", &mode);
87062306a36Sopenharmony_ci		if (ret && ret != -EINVAL) {
87162306a36Sopenharmony_ci			dev_err(qmc->dev, "%pOF: failed to read fsl,operational-mode\n",
87262306a36Sopenharmony_ci				chan_np);
87362306a36Sopenharmony_ci			of_node_put(chan_np);
87462306a36Sopenharmony_ci			return ret;
87562306a36Sopenharmony_ci		}
87662306a36Sopenharmony_ci		if (!strcmp(mode, "transparent")) {
87762306a36Sopenharmony_ci			chan->mode = QMC_TRANSPARENT;
87862306a36Sopenharmony_ci		} else if (!strcmp(mode, "hdlc")) {
87962306a36Sopenharmony_ci			chan->mode = QMC_HDLC;
88062306a36Sopenharmony_ci		} else {
88162306a36Sopenharmony_ci			dev_err(qmc->dev, "%pOF: Invalid fsl,operational-mode (%s)\n",
88262306a36Sopenharmony_ci				chan_np, mode);
88362306a36Sopenharmony_ci			of_node_put(chan_np);
88462306a36Sopenharmony_ci			return -EINVAL;
88562306a36Sopenharmony_ci		}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci		chan->is_reverse_data = of_property_read_bool(chan_np,
88862306a36Sopenharmony_ci							      "fsl,reverse-data");
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci		list_add_tail(&chan->list, &qmc->chan_head);
89162306a36Sopenharmony_ci		qmc->chans[chan->id] = chan;
89262306a36Sopenharmony_ci	}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	return qmc_check_chans(qmc);
89562306a36Sopenharmony_ci}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_cistatic int qmc_setup_tsa_64rxtx(struct qmc *qmc, const struct tsa_serial_info *info)
89862306a36Sopenharmony_ci{
89962306a36Sopenharmony_ci	struct qmc_chan *chan;
90062306a36Sopenharmony_ci	unsigned int i;
90162306a36Sopenharmony_ci	u16 val;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	/*
90462306a36Sopenharmony_ci	 * Use a common Tx/Rx 64 entries table.
90562306a36Sopenharmony_ci	 * Everything was previously checked, Tx and Rx related stuffs are
90662306a36Sopenharmony_ci	 * identical -> Used Rx related stuff to build the table
90762306a36Sopenharmony_ci	 */
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	/* Invalidate all entries */
91062306a36Sopenharmony_ci	for (i = 0; i < 64; i++)
91162306a36Sopenharmony_ci		qmc_write16(qmc->scc_pram + QMC_GBL_TSATRX + (i * 2), 0x0000);
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	/* Set entries based on Rx stuff*/
91462306a36Sopenharmony_ci	list_for_each_entry(chan, &qmc->chan_head, list) {
91562306a36Sopenharmony_ci		for (i = 0; i < info->nb_rx_ts; i++) {
91662306a36Sopenharmony_ci			if (!(chan->rx_ts_mask & (((u64)1) << i)))
91762306a36Sopenharmony_ci				continue;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci			val = QMC_TSA_VALID | QMC_TSA_MASK |
92062306a36Sopenharmony_ci			      QMC_TSA_CHANNEL(chan->id);
92162306a36Sopenharmony_ci			qmc_write16(qmc->scc_pram + QMC_GBL_TSATRX + (i * 2), val);
92262306a36Sopenharmony_ci		}
92362306a36Sopenharmony_ci	}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	/* Set Wrap bit on last entry */
92662306a36Sopenharmony_ci	qmc_setbits16(qmc->scc_pram + QMC_GBL_TSATRX + ((info->nb_rx_ts - 1) * 2),
92762306a36Sopenharmony_ci		      QMC_TSA_WRAP);
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	/* Init pointers to the table */
93062306a36Sopenharmony_ci	val = qmc->scc_pram_offset + QMC_GBL_TSATRX;
93162306a36Sopenharmony_ci	qmc_write16(qmc->scc_pram + QMC_GBL_RX_S_PTR, val);
93262306a36Sopenharmony_ci	qmc_write16(qmc->scc_pram + QMC_GBL_RXPTR, val);
93362306a36Sopenharmony_ci	qmc_write16(qmc->scc_pram + QMC_GBL_TX_S_PTR, val);
93462306a36Sopenharmony_ci	qmc_write16(qmc->scc_pram + QMC_GBL_TXPTR, val);
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	return 0;
93762306a36Sopenharmony_ci}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_cistatic int qmc_setup_tsa_32rx_32tx(struct qmc *qmc, const struct tsa_serial_info *info)
94062306a36Sopenharmony_ci{
94162306a36Sopenharmony_ci	struct qmc_chan *chan;
94262306a36Sopenharmony_ci	unsigned int i;
94362306a36Sopenharmony_ci	u16 val;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	/*
94662306a36Sopenharmony_ci	 * Use a Tx 32 entries table and a Rx 32 entries table.
94762306a36Sopenharmony_ci	 * Everything was previously checked.
94862306a36Sopenharmony_ci	 */
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	/* Invalidate all entries */
95162306a36Sopenharmony_ci	for (i = 0; i < 32; i++) {
95262306a36Sopenharmony_ci		qmc_write16(qmc->scc_pram + QMC_GBL_TSATRX + (i * 2), 0x0000);
95362306a36Sopenharmony_ci		qmc_write16(qmc->scc_pram + QMC_GBL_TSATTX + (i * 2), 0x0000);
95462306a36Sopenharmony_ci	}
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	/* Set entries based on Rx and Tx stuff*/
95762306a36Sopenharmony_ci	list_for_each_entry(chan, &qmc->chan_head, list) {
95862306a36Sopenharmony_ci		/* Rx part */
95962306a36Sopenharmony_ci		for (i = 0; i < info->nb_rx_ts; i++) {
96062306a36Sopenharmony_ci			if (!(chan->rx_ts_mask & (((u64)1) << i)))
96162306a36Sopenharmony_ci				continue;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci			val = QMC_TSA_VALID | QMC_TSA_MASK |
96462306a36Sopenharmony_ci			      QMC_TSA_CHANNEL(chan->id);
96562306a36Sopenharmony_ci			qmc_write16(qmc->scc_pram + QMC_GBL_TSATRX + (i * 2), val);
96662306a36Sopenharmony_ci		}
96762306a36Sopenharmony_ci		/* Tx part */
96862306a36Sopenharmony_ci		for (i = 0; i < info->nb_tx_ts; i++) {
96962306a36Sopenharmony_ci			if (!(chan->tx_ts_mask & (((u64)1) << i)))
97062306a36Sopenharmony_ci				continue;
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci			val = QMC_TSA_VALID | QMC_TSA_MASK |
97362306a36Sopenharmony_ci			      QMC_TSA_CHANNEL(chan->id);
97462306a36Sopenharmony_ci			qmc_write16(qmc->scc_pram + QMC_GBL_TSATTX + (i * 2), val);
97562306a36Sopenharmony_ci		}
97662306a36Sopenharmony_ci	}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	/* Set Wrap bit on last entries */
97962306a36Sopenharmony_ci	qmc_setbits16(qmc->scc_pram + QMC_GBL_TSATRX + ((info->nb_rx_ts - 1) * 2),
98062306a36Sopenharmony_ci		      QMC_TSA_WRAP);
98162306a36Sopenharmony_ci	qmc_setbits16(qmc->scc_pram + QMC_GBL_TSATTX + ((info->nb_tx_ts - 1) * 2),
98262306a36Sopenharmony_ci		      QMC_TSA_WRAP);
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	/* Init Rx pointers ...*/
98562306a36Sopenharmony_ci	val = qmc->scc_pram_offset + QMC_GBL_TSATRX;
98662306a36Sopenharmony_ci	qmc_write16(qmc->scc_pram + QMC_GBL_RX_S_PTR, val);
98762306a36Sopenharmony_ci	qmc_write16(qmc->scc_pram + QMC_GBL_RXPTR, val);
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	/* ... and Tx pointers */
99062306a36Sopenharmony_ci	val = qmc->scc_pram_offset + QMC_GBL_TSATTX;
99162306a36Sopenharmony_ci	qmc_write16(qmc->scc_pram + QMC_GBL_TX_S_PTR, val);
99262306a36Sopenharmony_ci	qmc_write16(qmc->scc_pram + QMC_GBL_TXPTR, val);
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	return 0;
99562306a36Sopenharmony_ci}
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_cistatic int qmc_setup_tsa(struct qmc *qmc)
99862306a36Sopenharmony_ci{
99962306a36Sopenharmony_ci	struct tsa_serial_info info;
100062306a36Sopenharmony_ci	int ret;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	/* Retrieve info from the TSA related serial */
100362306a36Sopenharmony_ci	ret = tsa_serial_get_info(qmc->tsa_serial, &info);
100462306a36Sopenharmony_ci	if (ret)
100562306a36Sopenharmony_ci		return ret;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	/*
100862306a36Sopenharmony_ci	 * Setup one common 64 entries table or two 32 entries (one for Tx and
100962306a36Sopenharmony_ci	 * one for Tx) according to assigned TS numbers.
101062306a36Sopenharmony_ci	 */
101162306a36Sopenharmony_ci	return ((info.nb_tx_ts > 32) || (info.nb_rx_ts > 32)) ?
101262306a36Sopenharmony_ci		qmc_setup_tsa_64rxtx(qmc, &info) :
101362306a36Sopenharmony_ci		qmc_setup_tsa_32rx_32tx(qmc, &info);
101462306a36Sopenharmony_ci}
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_cistatic int qmc_setup_chan_trnsync(struct qmc *qmc, struct qmc_chan *chan)
101762306a36Sopenharmony_ci{
101862306a36Sopenharmony_ci	struct tsa_serial_info info;
101962306a36Sopenharmony_ci	u16 first_rx, last_tx;
102062306a36Sopenharmony_ci	u16 trnsync;
102162306a36Sopenharmony_ci	int ret;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	/* Retrieve info from the TSA related serial */
102462306a36Sopenharmony_ci	ret = tsa_serial_get_info(chan->qmc->tsa_serial, &info);
102562306a36Sopenharmony_ci	if (ret)
102662306a36Sopenharmony_ci		return ret;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	/* Find the first Rx TS allocated to the channel */
102962306a36Sopenharmony_ci	first_rx = chan->rx_ts_mask ? __ffs64(chan->rx_ts_mask) + 1 : 0;
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	/* Find the last Tx TS allocated to the channel */
103262306a36Sopenharmony_ci	last_tx = fls64(chan->tx_ts_mask);
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	trnsync = 0;
103562306a36Sopenharmony_ci	if (info.nb_rx_ts)
103662306a36Sopenharmony_ci		trnsync |= QMC_SPE_TRNSYNC_RX((first_rx % info.nb_rx_ts) * 2);
103762306a36Sopenharmony_ci	if (info.nb_tx_ts)
103862306a36Sopenharmony_ci		trnsync |= QMC_SPE_TRNSYNC_TX((last_tx % info.nb_tx_ts) * 2);
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	qmc_write16(chan->s_param + QMC_SPE_TRNSYNC, trnsync);
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	dev_dbg(qmc->dev, "chan %u: trnsync=0x%04x, rx %u/%u 0x%llx, tx %u/%u 0x%llx\n",
104362306a36Sopenharmony_ci		chan->id, trnsync,
104462306a36Sopenharmony_ci		first_rx, info.nb_rx_ts, chan->rx_ts_mask,
104562306a36Sopenharmony_ci		last_tx, info.nb_tx_ts, chan->tx_ts_mask);
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	return 0;
104862306a36Sopenharmony_ci}
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_cistatic int qmc_setup_chan(struct qmc *qmc, struct qmc_chan *chan)
105162306a36Sopenharmony_ci{
105262306a36Sopenharmony_ci	unsigned int i;
105362306a36Sopenharmony_ci	cbd_t __iomem *bd;
105462306a36Sopenharmony_ci	int ret;
105562306a36Sopenharmony_ci	u16 val;
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	chan->qmc = qmc;
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	/* Set channel specific parameter base address */
106062306a36Sopenharmony_ci	chan->s_param = qmc->dpram + (chan->id * 64);
106162306a36Sopenharmony_ci	/* 16 bd per channel (8 rx and 8 tx) */
106262306a36Sopenharmony_ci	chan->txbds = qmc->bd_table + (chan->id * (QMC_NB_TXBDS + QMC_NB_RXBDS));
106362306a36Sopenharmony_ci	chan->rxbds = qmc->bd_table + (chan->id * (QMC_NB_TXBDS + QMC_NB_RXBDS)) + QMC_NB_TXBDS;
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	chan->txbd_free = chan->txbds;
106662306a36Sopenharmony_ci	chan->txbd_done = chan->txbds;
106762306a36Sopenharmony_ci	chan->rxbd_free = chan->rxbds;
106862306a36Sopenharmony_ci	chan->rxbd_done = chan->rxbds;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	/* TBASE and TBPTR*/
107162306a36Sopenharmony_ci	val = chan->id * (QMC_NB_TXBDS + QMC_NB_RXBDS) * sizeof(cbd_t);
107262306a36Sopenharmony_ci	qmc_write16(chan->s_param + QMC_SPE_TBASE, val);
107362306a36Sopenharmony_ci	qmc_write16(chan->s_param + QMC_SPE_TBPTR, val);
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	/* RBASE and RBPTR*/
107662306a36Sopenharmony_ci	val = ((chan->id * (QMC_NB_TXBDS + QMC_NB_RXBDS)) + QMC_NB_TXBDS) * sizeof(cbd_t);
107762306a36Sopenharmony_ci	qmc_write16(chan->s_param + QMC_SPE_RBASE, val);
107862306a36Sopenharmony_ci	qmc_write16(chan->s_param + QMC_SPE_RBPTR, val);
107962306a36Sopenharmony_ci	qmc_write32(chan->s_param + QMC_SPE_TSTATE, 0x30000000);
108062306a36Sopenharmony_ci	qmc_write32(chan->s_param + QMC_SPE_RSTATE, 0x31000000);
108162306a36Sopenharmony_ci	qmc_write32(chan->s_param + QMC_SPE_ZISTATE, 0x00000100);
108262306a36Sopenharmony_ci	if (chan->mode == QMC_TRANSPARENT) {
108362306a36Sopenharmony_ci		qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x18000080);
108462306a36Sopenharmony_ci		qmc_write16(chan->s_param + QMC_SPE_TMRBLR, 60);
108562306a36Sopenharmony_ci		val = QMC_SPE_CHAMR_MODE_TRANSP | QMC_SPE_CHAMR_TRANSP_SYNC;
108662306a36Sopenharmony_ci		if (chan->is_reverse_data)
108762306a36Sopenharmony_ci			val |= QMC_SPE_CHAMR_TRANSP_RD;
108862306a36Sopenharmony_ci		qmc_write16(chan->s_param + QMC_SPE_CHAMR, val);
108962306a36Sopenharmony_ci		ret = qmc_setup_chan_trnsync(qmc, chan);
109062306a36Sopenharmony_ci		if (ret)
109162306a36Sopenharmony_ci			return ret;
109262306a36Sopenharmony_ci	} else {
109362306a36Sopenharmony_ci		qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x00000080);
109462306a36Sopenharmony_ci		qmc_write16(chan->s_param + QMC_SPE_MFLR, 60);
109562306a36Sopenharmony_ci		qmc_write16(chan->s_param + QMC_SPE_CHAMR,
109662306a36Sopenharmony_ci			QMC_SPE_CHAMR_MODE_HDLC | QMC_SPE_CHAMR_HDLC_IDLM);
109762306a36Sopenharmony_ci	}
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	/* Do not enable interrupts now. They will be enabled later */
110062306a36Sopenharmony_ci	qmc_write16(chan->s_param + QMC_SPE_INTMSK, 0x0000);
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	/* Init Rx BDs and set Wrap bit on last descriptor */
110362306a36Sopenharmony_ci	BUILD_BUG_ON(QMC_NB_RXBDS == 0);
110462306a36Sopenharmony_ci	val = QMC_BD_RX_I;
110562306a36Sopenharmony_ci	for (i = 0; i < QMC_NB_RXBDS; i++) {
110662306a36Sopenharmony_ci		bd = chan->rxbds + i;
110762306a36Sopenharmony_ci		qmc_write16(&bd->cbd_sc, val);
110862306a36Sopenharmony_ci	}
110962306a36Sopenharmony_ci	bd = chan->rxbds + QMC_NB_RXBDS - 1;
111062306a36Sopenharmony_ci	qmc_write16(&bd->cbd_sc, val | QMC_BD_RX_W);
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	/* Init Tx BDs and set Wrap bit on last descriptor */
111362306a36Sopenharmony_ci	BUILD_BUG_ON(QMC_NB_TXBDS == 0);
111462306a36Sopenharmony_ci	val = QMC_BD_TX_I;
111562306a36Sopenharmony_ci	if (chan->mode == QMC_HDLC)
111662306a36Sopenharmony_ci		val |= QMC_BD_TX_L | QMC_BD_TX_TC;
111762306a36Sopenharmony_ci	for (i = 0; i < QMC_NB_TXBDS; i++) {
111862306a36Sopenharmony_ci		bd = chan->txbds + i;
111962306a36Sopenharmony_ci		qmc_write16(&bd->cbd_sc, val);
112062306a36Sopenharmony_ci	}
112162306a36Sopenharmony_ci	bd = chan->txbds + QMC_NB_TXBDS - 1;
112262306a36Sopenharmony_ci	qmc_write16(&bd->cbd_sc, val | QMC_BD_TX_W);
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	return 0;
112562306a36Sopenharmony_ci}
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_cistatic int qmc_setup_chans(struct qmc *qmc)
112862306a36Sopenharmony_ci{
112962306a36Sopenharmony_ci	struct qmc_chan *chan;
113062306a36Sopenharmony_ci	int ret;
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	list_for_each_entry(chan, &qmc->chan_head, list) {
113362306a36Sopenharmony_ci		ret = qmc_setup_chan(qmc, chan);
113462306a36Sopenharmony_ci		if (ret)
113562306a36Sopenharmony_ci			return ret;
113662306a36Sopenharmony_ci	}
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	return 0;
113962306a36Sopenharmony_ci}
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_cistatic int qmc_finalize_chans(struct qmc *qmc)
114262306a36Sopenharmony_ci{
114362306a36Sopenharmony_ci	struct qmc_chan *chan;
114462306a36Sopenharmony_ci	int ret;
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	list_for_each_entry(chan, &qmc->chan_head, list) {
114762306a36Sopenharmony_ci		/* Unmask channel interrupts */
114862306a36Sopenharmony_ci		if (chan->mode == QMC_HDLC) {
114962306a36Sopenharmony_ci			qmc_write16(chan->s_param + QMC_SPE_INTMSK,
115062306a36Sopenharmony_ci				    QMC_INT_NID | QMC_INT_IDL | QMC_INT_MRF |
115162306a36Sopenharmony_ci				    QMC_INT_UN | QMC_INT_RXF | QMC_INT_BSY |
115262306a36Sopenharmony_ci				    QMC_INT_TXB | QMC_INT_RXB);
115362306a36Sopenharmony_ci		} else {
115462306a36Sopenharmony_ci			qmc_write16(chan->s_param + QMC_SPE_INTMSK,
115562306a36Sopenharmony_ci				    QMC_INT_UN | QMC_INT_BSY |
115662306a36Sopenharmony_ci				    QMC_INT_TXB | QMC_INT_RXB);
115762306a36Sopenharmony_ci		}
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci		/* Forced stop the channel */
116062306a36Sopenharmony_ci		ret = qmc_chan_stop(chan, QMC_CHAN_ALL);
116162306a36Sopenharmony_ci		if (ret)
116262306a36Sopenharmony_ci			return ret;
116362306a36Sopenharmony_ci	}
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	return 0;
116662306a36Sopenharmony_ci}
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_cistatic int qmc_setup_ints(struct qmc *qmc)
116962306a36Sopenharmony_ci{
117062306a36Sopenharmony_ci	unsigned int i;
117162306a36Sopenharmony_ci	u16 __iomem *last;
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	/* Raz all entries */
117462306a36Sopenharmony_ci	for (i = 0; i < (qmc->int_size / sizeof(u16)); i++)
117562306a36Sopenharmony_ci		qmc_write16(qmc->int_table + i, 0x0000);
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	/* Set Wrap bit on last entry */
117862306a36Sopenharmony_ci	if (qmc->int_size >= sizeof(u16)) {
117962306a36Sopenharmony_ci		last = qmc->int_table + (qmc->int_size / sizeof(u16)) - 1;
118062306a36Sopenharmony_ci		qmc_write16(last, QMC_INT_W);
118162306a36Sopenharmony_ci	}
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	return 0;
118462306a36Sopenharmony_ci}
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_cistatic void qmc_irq_gint(struct qmc *qmc)
118762306a36Sopenharmony_ci{
118862306a36Sopenharmony_ci	struct qmc_chan *chan;
118962306a36Sopenharmony_ci	unsigned int chan_id;
119062306a36Sopenharmony_ci	unsigned long flags;
119162306a36Sopenharmony_ci	u16 int_entry;
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	int_entry = qmc_read16(qmc->int_curr);
119462306a36Sopenharmony_ci	while (int_entry & QMC_INT_V) {
119562306a36Sopenharmony_ci		/* Clear all but the Wrap bit */
119662306a36Sopenharmony_ci		qmc_write16(qmc->int_curr, int_entry & QMC_INT_W);
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci		chan_id = QMC_INT_GET_CHANNEL(int_entry);
119962306a36Sopenharmony_ci		chan = qmc->chans[chan_id];
120062306a36Sopenharmony_ci		if (!chan) {
120162306a36Sopenharmony_ci			dev_err(qmc->dev, "interrupt on invalid chan %u\n", chan_id);
120262306a36Sopenharmony_ci			goto int_next;
120362306a36Sopenharmony_ci		}
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci		if (int_entry & QMC_INT_TXB)
120662306a36Sopenharmony_ci			qmc_chan_write_done(chan);
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci		if (int_entry & QMC_INT_UN) {
120962306a36Sopenharmony_ci			dev_info(qmc->dev, "intr chan %u, 0x%04x (UN)\n", chan_id,
121062306a36Sopenharmony_ci				 int_entry);
121162306a36Sopenharmony_ci			chan->nb_tx_underrun++;
121262306a36Sopenharmony_ci		}
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci		if (int_entry & QMC_INT_BSY) {
121562306a36Sopenharmony_ci			dev_info(qmc->dev, "intr chan %u, 0x%04x (BSY)\n", chan_id,
121662306a36Sopenharmony_ci				 int_entry);
121762306a36Sopenharmony_ci			chan->nb_rx_busy++;
121862306a36Sopenharmony_ci			/* Restart the receiver if needed */
121962306a36Sopenharmony_ci			spin_lock_irqsave(&chan->rx_lock, flags);
122062306a36Sopenharmony_ci			if (chan->rx_pending && !chan->is_rx_stopped) {
122162306a36Sopenharmony_ci				if (chan->mode == QMC_TRANSPARENT)
122262306a36Sopenharmony_ci					qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x18000080);
122362306a36Sopenharmony_ci				else
122462306a36Sopenharmony_ci					qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x00000080);
122562306a36Sopenharmony_ci				qmc_write32(chan->s_param + QMC_SPE_RSTATE, 0x31000000);
122662306a36Sopenharmony_ci				chan->is_rx_halted = false;
122762306a36Sopenharmony_ci			} else {
122862306a36Sopenharmony_ci				chan->is_rx_halted = true;
122962306a36Sopenharmony_ci			}
123062306a36Sopenharmony_ci			spin_unlock_irqrestore(&chan->rx_lock, flags);
123162306a36Sopenharmony_ci		}
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci		if (int_entry & QMC_INT_RXB)
123462306a36Sopenharmony_ci			qmc_chan_read_done(chan);
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ciint_next:
123762306a36Sopenharmony_ci		if (int_entry & QMC_INT_W)
123862306a36Sopenharmony_ci			qmc->int_curr = qmc->int_table;
123962306a36Sopenharmony_ci		else
124062306a36Sopenharmony_ci			qmc->int_curr++;
124162306a36Sopenharmony_ci		int_entry = qmc_read16(qmc->int_curr);
124262306a36Sopenharmony_ci	}
124362306a36Sopenharmony_ci}
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_cistatic irqreturn_t qmc_irq_handler(int irq, void *priv)
124662306a36Sopenharmony_ci{
124762306a36Sopenharmony_ci	struct qmc *qmc = (struct qmc *)priv;
124862306a36Sopenharmony_ci	u16 scce;
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	scce = qmc_read16(qmc->scc_regs + SCC_SCCE);
125162306a36Sopenharmony_ci	qmc_write16(qmc->scc_regs + SCC_SCCE, scce);
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci	if (unlikely(scce & SCC_SCCE_IQOV))
125462306a36Sopenharmony_ci		dev_info(qmc->dev, "IRQ queue overflow\n");
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	if (unlikely(scce & SCC_SCCE_GUN))
125762306a36Sopenharmony_ci		dev_err(qmc->dev, "Global transmitter underrun\n");
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	if (unlikely(scce & SCC_SCCE_GOV))
126062306a36Sopenharmony_ci		dev_err(qmc->dev, "Global receiver overrun\n");
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	/* normal interrupt */
126362306a36Sopenharmony_ci	if (likely(scce & SCC_SCCE_GINT))
126462306a36Sopenharmony_ci		qmc_irq_gint(qmc);
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	return IRQ_HANDLED;
126762306a36Sopenharmony_ci}
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_cistatic int qmc_probe(struct platform_device *pdev)
127062306a36Sopenharmony_ci{
127162306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
127262306a36Sopenharmony_ci	unsigned int nb_chans;
127362306a36Sopenharmony_ci	struct resource *res;
127462306a36Sopenharmony_ci	struct qmc *qmc;
127562306a36Sopenharmony_ci	int irq;
127662306a36Sopenharmony_ci	int ret;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	qmc = devm_kzalloc(&pdev->dev, sizeof(*qmc), GFP_KERNEL);
127962306a36Sopenharmony_ci	if (!qmc)
128062306a36Sopenharmony_ci		return -ENOMEM;
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	qmc->dev = &pdev->dev;
128362306a36Sopenharmony_ci	INIT_LIST_HEAD(&qmc->chan_head);
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	qmc->scc_regs = devm_platform_ioremap_resource_byname(pdev, "scc_regs");
128662306a36Sopenharmony_ci	if (IS_ERR(qmc->scc_regs))
128762306a36Sopenharmony_ci		return PTR_ERR(qmc->scc_regs);
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "scc_pram");
129162306a36Sopenharmony_ci	if (!res)
129262306a36Sopenharmony_ci		return -EINVAL;
129362306a36Sopenharmony_ci	qmc->scc_pram_offset = res->start - get_immrbase();
129462306a36Sopenharmony_ci	qmc->scc_pram = devm_ioremap_resource(qmc->dev, res);
129562306a36Sopenharmony_ci	if (IS_ERR(qmc->scc_pram))
129662306a36Sopenharmony_ci		return PTR_ERR(qmc->scc_pram);
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	qmc->dpram  = devm_platform_ioremap_resource_byname(pdev, "dpram");
129962306a36Sopenharmony_ci	if (IS_ERR(qmc->dpram))
130062306a36Sopenharmony_ci		return PTR_ERR(qmc->dpram);
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	qmc->tsa_serial = devm_tsa_serial_get_byphandle(qmc->dev, np, "fsl,tsa-serial");
130362306a36Sopenharmony_ci	if (IS_ERR(qmc->tsa_serial)) {
130462306a36Sopenharmony_ci		return dev_err_probe(qmc->dev, PTR_ERR(qmc->tsa_serial),
130562306a36Sopenharmony_ci				     "Failed to get TSA serial\n");
130662306a36Sopenharmony_ci	}
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	/* Connect the serial (SCC) to TSA */
130962306a36Sopenharmony_ci	ret = tsa_serial_connect(qmc->tsa_serial);
131062306a36Sopenharmony_ci	if (ret) {
131162306a36Sopenharmony_ci		dev_err(qmc->dev, "Failed to connect TSA serial\n");
131262306a36Sopenharmony_ci		return ret;
131362306a36Sopenharmony_ci	}
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	/* Parse channels informationss */
131662306a36Sopenharmony_ci	ret = qmc_of_parse_chans(qmc, np);
131762306a36Sopenharmony_ci	if (ret)
131862306a36Sopenharmony_ci		goto err_tsa_serial_disconnect;
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	nb_chans = qmc_nb_chans(qmc);
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	/* Init GMSR_H and GMSR_L registers */
132362306a36Sopenharmony_ci	qmc_write32(qmc->scc_regs + SCC_GSMRH,
132462306a36Sopenharmony_ci		    SCC_GSMRH_CDS | SCC_GSMRH_CTSS | SCC_GSMRH_CDP | SCC_GSMRH_CTSP);
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	/* enable QMC mode */
132762306a36Sopenharmony_ci	qmc_write32(qmc->scc_regs + SCC_GSMRL, SCC_GSMRL_MODE_QMC);
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	/*
133062306a36Sopenharmony_ci	 * Allocate the buffer descriptor table
133162306a36Sopenharmony_ci	 * 8 rx and 8 tx descriptors per channel
133262306a36Sopenharmony_ci	 */
133362306a36Sopenharmony_ci	qmc->bd_size = (nb_chans * (QMC_NB_TXBDS + QMC_NB_RXBDS)) * sizeof(cbd_t);
133462306a36Sopenharmony_ci	qmc->bd_table = dmam_alloc_coherent(qmc->dev, qmc->bd_size,
133562306a36Sopenharmony_ci		&qmc->bd_dma_addr, GFP_KERNEL);
133662306a36Sopenharmony_ci	if (!qmc->bd_table) {
133762306a36Sopenharmony_ci		dev_err(qmc->dev, "Failed to allocate bd table\n");
133862306a36Sopenharmony_ci		ret = -ENOMEM;
133962306a36Sopenharmony_ci		goto err_tsa_serial_disconnect;
134062306a36Sopenharmony_ci	}
134162306a36Sopenharmony_ci	memset(qmc->bd_table, 0, qmc->bd_size);
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci	qmc_write32(qmc->scc_pram + QMC_GBL_MCBASE, qmc->bd_dma_addr);
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	/* Allocate the interrupt table */
134662306a36Sopenharmony_ci	qmc->int_size = QMC_NB_INTS * sizeof(u16);
134762306a36Sopenharmony_ci	qmc->int_table = dmam_alloc_coherent(qmc->dev, qmc->int_size,
134862306a36Sopenharmony_ci		&qmc->int_dma_addr, GFP_KERNEL);
134962306a36Sopenharmony_ci	if (!qmc->int_table) {
135062306a36Sopenharmony_ci		dev_err(qmc->dev, "Failed to allocate interrupt table\n");
135162306a36Sopenharmony_ci		ret = -ENOMEM;
135262306a36Sopenharmony_ci		goto err_tsa_serial_disconnect;
135362306a36Sopenharmony_ci	}
135462306a36Sopenharmony_ci	memset(qmc->int_table, 0, qmc->int_size);
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	qmc->int_curr = qmc->int_table;
135762306a36Sopenharmony_ci	qmc_write32(qmc->scc_pram + QMC_GBL_INTBASE, qmc->int_dma_addr);
135862306a36Sopenharmony_ci	qmc_write32(qmc->scc_pram + QMC_GBL_INTPTR, qmc->int_dma_addr);
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	/* Set MRBLR (valid for HDLC only) max MRU + max CRC */
136162306a36Sopenharmony_ci	qmc_write16(qmc->scc_pram + QMC_GBL_MRBLR, HDLC_MAX_MRU + 4);
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci	qmc_write16(qmc->scc_pram + QMC_GBL_GRFTHR, 1);
136462306a36Sopenharmony_ci	qmc_write16(qmc->scc_pram + QMC_GBL_GRFCNT, 1);
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	qmc_write32(qmc->scc_pram + QMC_GBL_C_MASK32, 0xDEBB20E3);
136762306a36Sopenharmony_ci	qmc_write16(qmc->scc_pram + QMC_GBL_C_MASK16, 0xF0B8);
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	ret = qmc_setup_tsa(qmc);
137062306a36Sopenharmony_ci	if (ret)
137162306a36Sopenharmony_ci		goto err_tsa_serial_disconnect;
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	qmc_write16(qmc->scc_pram + QMC_GBL_QMCSTATE, 0x8000);
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	ret = qmc_setup_chans(qmc);
137662306a36Sopenharmony_ci	if (ret)
137762306a36Sopenharmony_ci		goto err_tsa_serial_disconnect;
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	/* Init interrupts table */
138062306a36Sopenharmony_ci	ret = qmc_setup_ints(qmc);
138162306a36Sopenharmony_ci	if (ret)
138262306a36Sopenharmony_ci		goto err_tsa_serial_disconnect;
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	/* Disable and clear interrupts,  set the irq handler */
138562306a36Sopenharmony_ci	qmc_write16(qmc->scc_regs + SCC_SCCM, 0x0000);
138662306a36Sopenharmony_ci	qmc_write16(qmc->scc_regs + SCC_SCCE, 0x000F);
138762306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
138862306a36Sopenharmony_ci	if (irq < 0)
138962306a36Sopenharmony_ci		goto err_tsa_serial_disconnect;
139062306a36Sopenharmony_ci	ret = devm_request_irq(qmc->dev, irq, qmc_irq_handler, 0, "qmc", qmc);
139162306a36Sopenharmony_ci	if (ret < 0)
139262306a36Sopenharmony_ci		goto err_tsa_serial_disconnect;
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	/* Enable interrupts */
139562306a36Sopenharmony_ci	qmc_write16(qmc->scc_regs + SCC_SCCM,
139662306a36Sopenharmony_ci		SCC_SCCE_IQOV | SCC_SCCE_GINT | SCC_SCCE_GUN | SCC_SCCE_GOV);
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	ret = qmc_finalize_chans(qmc);
139962306a36Sopenharmony_ci	if (ret < 0)
140062306a36Sopenharmony_ci		goto err_disable_intr;
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	/* Enable transmiter and receiver */
140362306a36Sopenharmony_ci	qmc_setbits32(qmc->scc_regs + SCC_GSMRL, SCC_GSMRL_ENR | SCC_GSMRL_ENT);
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci	platform_set_drvdata(pdev, qmc);
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	return 0;
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_cierr_disable_intr:
141062306a36Sopenharmony_ci	qmc_write16(qmc->scc_regs + SCC_SCCM, 0);
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_cierr_tsa_serial_disconnect:
141362306a36Sopenharmony_ci	tsa_serial_disconnect(qmc->tsa_serial);
141462306a36Sopenharmony_ci	return ret;
141562306a36Sopenharmony_ci}
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_cistatic int qmc_remove(struct platform_device *pdev)
141862306a36Sopenharmony_ci{
141962306a36Sopenharmony_ci	struct qmc *qmc = platform_get_drvdata(pdev);
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	/* Disable transmiter and receiver */
142262306a36Sopenharmony_ci	qmc_setbits32(qmc->scc_regs + SCC_GSMRL, 0);
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci	/* Disable interrupts */
142562306a36Sopenharmony_ci	qmc_write16(qmc->scc_regs + SCC_SCCM, 0);
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	/* Disconnect the serial from TSA */
142862306a36Sopenharmony_ci	tsa_serial_disconnect(qmc->tsa_serial);
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	return 0;
143162306a36Sopenharmony_ci}
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_cistatic const struct of_device_id qmc_id_table[] = {
143462306a36Sopenharmony_ci	{ .compatible = "fsl,cpm1-scc-qmc" },
143562306a36Sopenharmony_ci	{} /* sentinel */
143662306a36Sopenharmony_ci};
143762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, qmc_id_table);
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_cistatic struct platform_driver qmc_driver = {
144062306a36Sopenharmony_ci	.driver = {
144162306a36Sopenharmony_ci		.name = "fsl-qmc",
144262306a36Sopenharmony_ci		.of_match_table = of_match_ptr(qmc_id_table),
144362306a36Sopenharmony_ci	},
144462306a36Sopenharmony_ci	.probe = qmc_probe,
144562306a36Sopenharmony_ci	.remove = qmc_remove,
144662306a36Sopenharmony_ci};
144762306a36Sopenharmony_cimodule_platform_driver(qmc_driver);
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_cistruct qmc_chan *qmc_chan_get_byphandle(struct device_node *np, const char *phandle_name)
145062306a36Sopenharmony_ci{
145162306a36Sopenharmony_ci	struct of_phandle_args out_args;
145262306a36Sopenharmony_ci	struct platform_device *pdev;
145362306a36Sopenharmony_ci	struct qmc_chan *qmc_chan;
145462306a36Sopenharmony_ci	struct qmc *qmc;
145562306a36Sopenharmony_ci	int ret;
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	ret = of_parse_phandle_with_fixed_args(np, phandle_name, 1, 0,
145862306a36Sopenharmony_ci					       &out_args);
145962306a36Sopenharmony_ci	if (ret < 0)
146062306a36Sopenharmony_ci		return ERR_PTR(ret);
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	if (!of_match_node(qmc_driver.driver.of_match_table, out_args.np)) {
146362306a36Sopenharmony_ci		of_node_put(out_args.np);
146462306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
146562306a36Sopenharmony_ci	}
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	pdev = of_find_device_by_node(out_args.np);
146862306a36Sopenharmony_ci	of_node_put(out_args.np);
146962306a36Sopenharmony_ci	if (!pdev)
147062306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	qmc = platform_get_drvdata(pdev);
147362306a36Sopenharmony_ci	if (!qmc) {
147462306a36Sopenharmony_ci		platform_device_put(pdev);
147562306a36Sopenharmony_ci		return ERR_PTR(-EPROBE_DEFER);
147662306a36Sopenharmony_ci	}
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	if (out_args.args_count != 1) {
147962306a36Sopenharmony_ci		platform_device_put(pdev);
148062306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
148162306a36Sopenharmony_ci	}
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	if (out_args.args[0] >= ARRAY_SIZE(qmc->chans)) {
148462306a36Sopenharmony_ci		platform_device_put(pdev);
148562306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
148662306a36Sopenharmony_ci	}
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	qmc_chan = qmc->chans[out_args.args[0]];
148962306a36Sopenharmony_ci	if (!qmc_chan) {
149062306a36Sopenharmony_ci		platform_device_put(pdev);
149162306a36Sopenharmony_ci		return ERR_PTR(-ENOENT);
149262306a36Sopenharmony_ci	}
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	return qmc_chan;
149562306a36Sopenharmony_ci}
149662306a36Sopenharmony_ciEXPORT_SYMBOL(qmc_chan_get_byphandle);
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_civoid qmc_chan_put(struct qmc_chan *chan)
149962306a36Sopenharmony_ci{
150062306a36Sopenharmony_ci	put_device(chan->qmc->dev);
150162306a36Sopenharmony_ci}
150262306a36Sopenharmony_ciEXPORT_SYMBOL(qmc_chan_put);
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_cistatic void devm_qmc_chan_release(struct device *dev, void *res)
150562306a36Sopenharmony_ci{
150662306a36Sopenharmony_ci	struct qmc_chan **qmc_chan = res;
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	qmc_chan_put(*qmc_chan);
150962306a36Sopenharmony_ci}
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_cistruct qmc_chan *devm_qmc_chan_get_byphandle(struct device *dev,
151262306a36Sopenharmony_ci					     struct device_node *np,
151362306a36Sopenharmony_ci					     const char *phandle_name)
151462306a36Sopenharmony_ci{
151562306a36Sopenharmony_ci	struct qmc_chan *qmc_chan;
151662306a36Sopenharmony_ci	struct qmc_chan **dr;
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci	dr = devres_alloc(devm_qmc_chan_release, sizeof(*dr), GFP_KERNEL);
151962306a36Sopenharmony_ci	if (!dr)
152062306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci	qmc_chan = qmc_chan_get_byphandle(np, phandle_name);
152362306a36Sopenharmony_ci	if (!IS_ERR(qmc_chan)) {
152462306a36Sopenharmony_ci		*dr = qmc_chan;
152562306a36Sopenharmony_ci		devres_add(dev, dr);
152662306a36Sopenharmony_ci	} else {
152762306a36Sopenharmony_ci		devres_free(dr);
152862306a36Sopenharmony_ci	}
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	return qmc_chan;
153162306a36Sopenharmony_ci}
153262306a36Sopenharmony_ciEXPORT_SYMBOL(devm_qmc_chan_get_byphandle);
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ciMODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
153562306a36Sopenharmony_ciMODULE_DESCRIPTION("CPM QMC driver");
153662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1537