root/drivers/soc/fsl/qe/qmc.c
// SPDX-License-Identifier: GPL-2.0
/*
 * QMC driver
 *
 * Copyright 2022 CS GROUP France
 *
 * Author: Herve Codina <herve.codina@bootlin.com>
 */

#include <soc/fsl/qe/qmc.h>
#include <linux/bitfield.h>
#include <linux/dma-mapping.h>
#include <linux/firmware.h>
#include <linux/hdlc.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <soc/fsl/cpm.h>
#include <soc/fsl/qe/ucc_slow.h>
#include <soc/fsl/qe/qe.h>
#include <sysdev/fsl_soc.h>
#include "tsa.h"

/* SCC general mode register low (32 bits) (GUMR_L in QE) */
#define SCC_GSMRL       0x00
#define SCC_GSMRL_ENR           BIT(5)
#define SCC_GSMRL_ENT           BIT(4)
#define SCC_GSMRL_MODE_MASK     GENMASK(3, 0)
#define SCC_CPM1_GSMRL_MODE_QMC FIELD_PREP_CONST(SCC_GSMRL_MODE_MASK, 0x0A)
#define SCC_QE_GSMRL_MODE_QMC   FIELD_PREP_CONST(SCC_GSMRL_MODE_MASK, 0x02)

/* SCC general mode register high (32 bits) (identical to GUMR_H in QE) */
#define SCC_GSMRH       0x04
#define   SCC_GSMRH_CTSS        BIT(7)
#define   SCC_GSMRH_CDS         BIT(8)
#define   SCC_GSMRH_CTSP        BIT(9)
#define   SCC_GSMRH_CDP         BIT(10)
#define   SCC_GSMRH_TTX         BIT(11)
#define   SCC_GSMRH_TRX         BIT(12)

/* SCC event register (16 bits) (identical to UCCE in QE) */
#define SCC_SCCE        0x10
#define   SCC_SCCE_IQOV         BIT(3)
#define   SCC_SCCE_GINT         BIT(2)
#define   SCC_SCCE_GUN          BIT(1)
#define   SCC_SCCE_GOV          BIT(0)

/* SCC mask register (16 bits) */
#define SCC_SCCM        0x14

/* UCC Extended Mode Register (8 bits, QE only) */
#define SCC_QE_UCC_GUEMR        0x90

/* Multichannel base pointer (32 bits) */
#define QMC_GBL_MCBASE          0x00
/* Multichannel controller state (16 bits) */
#define QMC_GBL_QMCSTATE        0x04
/* Maximum receive buffer length (16 bits) */
#define QMC_GBL_MRBLR           0x06
/* Tx time-slot assignment table pointer (16 bits) */
#define QMC_GBL_TX_S_PTR        0x08
/* Rx pointer (16 bits) */
#define QMC_GBL_RXPTR           0x0A
/* Global receive frame threshold (16 bits) */
#define QMC_GBL_GRFTHR          0x0C
/* Global receive frame count (16 bits) */
#define QMC_GBL_GRFCNT          0x0E
/* Multichannel interrupt base address (32 bits) */
#define QMC_GBL_INTBASE         0x10
/* Multichannel interrupt pointer (32 bits) */
#define QMC_GBL_INTPTR          0x14
/* Rx time-slot assignment table pointer (16 bits) */
#define QMC_GBL_RX_S_PTR        0x18
/* Tx pointer (16 bits) */
#define QMC_GBL_TXPTR           0x1A
/* CRC constant (32 bits) */
#define QMC_GBL_C_MASK32        0x1C
/* Time slot assignment table Rx (32 x 16 bits) */
#define QMC_GBL_TSATRX          0x20
/* Time slot assignment table Tx (32 x 16 bits) */
#define QMC_GBL_TSATTX          0x60
/* CRC constant (16 bits) */
#define QMC_GBL_C_MASK16        0xA0
/* Rx framer base pointer (16 bits, QE only) */
#define QMC_QE_GBL_RX_FRM_BASE  0xAC
/* Tx framer base pointer (16 bits, QE only) */
#define QMC_QE_GBL_TX_FRM_BASE  0xAE
/* A reserved area (0xB0 -> 0xC3) that must be initialized to 0 (QE only) */
#define QMC_QE_GBL_RSV_B0_START 0xB0
#define QMC_QE_GBL_RSV_B0_SIZE  0x14
/* QMC Global Channel specific base (32 bits, QE only) */
#define QMC_QE_GBL_GCSBASE      0xC4

/* TSA entry (16bit entry in TSATRX and TSATTX) */
#define QMC_TSA_VALID           BIT(15)
#define QMC_TSA_WRAP            BIT(14)
#define QMC_TSA_MASK_MASKH      GENMASK(13, 12)
#define QMC_TSA_MASK_MASKL      GENMASK(5, 0)
#define QMC_TSA_MASK_8BIT       (FIELD_PREP_CONST(QMC_TSA_MASK_MASKH, 0x3) | \
                                 FIELD_PREP_CONST(QMC_TSA_MASK_MASKL, 0x3F))
#define QMC_TSA_CHANNEL_MASK    GENMASK(11, 6)
#define QMC_TSA_CHANNEL(x)      FIELD_PREP(QMC_TSA_CHANNEL_MASK, x)

/* Tx buffer descriptor base address (16 bits, offset from MCBASE) */
#define QMC_SPE_TBASE   0x00

/* Channel mode register (16 bits) */
#define QMC_SPE_CHAMR   0x02
#define   QMC_SPE_CHAMR_MODE_MASK       GENMASK(15, 15)
#define   QMC_SPE_CHAMR_MODE_HDLC       FIELD_PREP_CONST(QMC_SPE_CHAMR_MODE_MASK, 1)
#define   QMC_SPE_CHAMR_MODE_TRANSP     (FIELD_PREP_CONST(QMC_SPE_CHAMR_MODE_MASK, 0) | BIT(13))
#define   QMC_SPE_CHAMR_ENT             BIT(12)
#define   QMC_SPE_CHAMR_POL             BIT(8)
#define   QMC_SPE_CHAMR_HDLC_IDLM       BIT(13)
#define   QMC_SPE_CHAMR_HDLC_CRC        BIT(7)
#define   QMC_SPE_CHAMR_HDLC_NOF_MASK   GENMASK(3, 0)
#define   QMC_SPE_CHAMR_HDLC_NOF(x)     FIELD_PREP(QMC_SPE_CHAMR_HDLC_NOF_MASK, x)
#define   QMC_SPE_CHAMR_TRANSP_RD       BIT(14)
#define   QMC_SPE_CHAMR_TRANSP_SYNC     BIT(10)

/* Tx internal state (32 bits) */
#define QMC_SPE_TSTATE  0x04
/* Tx buffer descriptor pointer (16 bits) */
#define QMC_SPE_TBPTR   0x0C
/* Zero-insertion state (32 bits) */
#define QMC_SPE_ZISTATE 0x14
/* Channel’s interrupt mask flags (16 bits) */
#define QMC_SPE_INTMSK  0x1C
/* Rx buffer descriptor base address (16 bits, offset from MCBASE) */
#define QMC_SPE_RBASE   0x20
/* HDLC: Maximum frame length register (16 bits) */
#define QMC_SPE_MFLR    0x22
/* TRANSPARENT: Transparent maximum receive length (16 bits) */
#define QMC_SPE_TMRBLR  0x22
/* Rx internal state (32 bits) */
#define QMC_SPE_RSTATE  0x24
/* Rx buffer descriptor pointer (16 bits) */
#define QMC_SPE_RBPTR   0x2C
/* Packs 4 bytes to 1 long word before writing to buffer (32 bits) */
#define QMC_SPE_RPACK   0x30
/* Zero deletion state (32 bits) */
#define QMC_SPE_ZDSTATE 0x34

/* Transparent synchronization (16 bits) */
#define QMC_SPE_TRNSYNC 0x3C
#define   QMC_SPE_TRNSYNC_RX_MASK       GENMASK(15, 8)
#define   QMC_SPE_TRNSYNC_RX(x)         FIELD_PREP(QMC_SPE_TRNSYNC_RX_MASK, x)
#define   QMC_SPE_TRNSYNC_TX_MASK       GENMASK(7, 0)
#define   QMC_SPE_TRNSYNC_TX(x)         FIELD_PREP(QMC_SPE_TRNSYNC_TX_MASK, x)

/* Interrupt related registers bits */
#define QMC_INT_V               BIT(15)
#define QMC_INT_W               BIT(14)
#define QMC_INT_NID             BIT(13)
#define QMC_INT_IDL             BIT(12)
#define QMC_INT_CHANNEL_MASK    GENMASK(11, 6)
#define QMC_INT_GET_CHANNEL(x)  FIELD_GET(QMC_INT_CHANNEL_MASK, x)
#define QMC_INT_MRF             BIT(5)
#define QMC_INT_UN              BIT(4)
#define QMC_INT_RXF             BIT(3)
#define QMC_INT_BSY             BIT(2)
#define QMC_INT_TXB             BIT(1)
#define QMC_INT_RXB             BIT(0)

/* BD related registers bits */
#define QMC_BD_RX_E     BIT(15)
#define QMC_BD_RX_W     BIT(13)
#define QMC_BD_RX_I     BIT(12)
#define QMC_BD_RX_L     BIT(11)
#define QMC_BD_RX_F     BIT(10)
#define QMC_BD_RX_CM    BIT(9)
#define QMC_BD_RX_UB    BIT(7)
#define QMC_BD_RX_LG    BIT(5)
#define QMC_BD_RX_NO    BIT(4)
#define QMC_BD_RX_AB    BIT(3)
#define QMC_BD_RX_CR    BIT(2)

#define QMC_BD_TX_R             BIT(15)
#define QMC_BD_TX_W             BIT(13)
#define QMC_BD_TX_I             BIT(12)
#define QMC_BD_TX_L             BIT(11)
#define QMC_BD_TX_TC            BIT(10)
#define QMC_BD_TX_CM            BIT(9)
#define QMC_BD_TX_UB            BIT(7)
#define QMC_BD_TX_PAD_MASK      GENMASK(3, 0)
#define QMC_BD_TX_PAD(x)        FIELD_PREP(QMC_BD_TX_PAD_MASK, x)

/* Numbers of BDs and interrupt items */
#define QMC_NB_TXBDS    8
#define QMC_NB_RXBDS    8
#define QMC_NB_INTS     128

struct qmc_xfer_desc {
        union {
                void (*tx_complete)(void *context);
                void (*rx_complete)(void *context, size_t length, unsigned int flags);
        };
        void *context;
};

struct qmc_chan {
        struct list_head list;
        unsigned int id;
        struct qmc *qmc;
        void __iomem *s_param;
        enum qmc_mode mode;
        spinlock_t      ts_lock; /* Protect timeslots */
        u64     tx_ts_mask_avail;
        u64     tx_ts_mask;
        u64     rx_ts_mask_avail;
        u64     rx_ts_mask;
        bool is_reverse_data;

        spinlock_t      tx_lock; /* Protect Tx related data */
        cbd_t __iomem *txbds;
        cbd_t __iomem *txbd_free;
        cbd_t __iomem *txbd_done;
        struct qmc_xfer_desc tx_desc[QMC_NB_TXBDS];
        u64     nb_tx_underrun;
        bool    is_tx_stopped;

        spinlock_t      rx_lock; /* Protect Rx related data */
        cbd_t __iomem *rxbds;
        cbd_t __iomem *rxbd_free;
        cbd_t __iomem *rxbd_done;
        struct qmc_xfer_desc rx_desc[QMC_NB_RXBDS];
        u64     nb_rx_busy;
        int     rx_pending;
        bool    is_rx_halted;
        bool    is_rx_stopped;
};

enum qmc_version {
        QMC_CPM1,
        QMC_QE,
};

struct qmc_data {
        enum qmc_version version;
        u32 tstate; /* Initial TSTATE value */
        u32 rstate; /* Initial RSTATE value */
        u32 zistate; /* Initial ZISTATE value */
        u32 zdstate_hdlc; /* Initial ZDSTATE value (HDLC mode) */
        u32 zdstate_transp; /* Initial ZDSTATE value (Transparent mode) */
        u32 rpack; /* Initial RPACK value */
};

struct qmc {
        struct device *dev;
        const struct qmc_data *data;
        struct tsa_serial *tsa_serial;
        void __iomem *scc_regs;
        void __iomem *scc_pram;
        void __iomem *dpram;
        u16 scc_pram_offset;
        u32 dpram_offset;
        u32 qe_subblock;
        cbd_t __iomem *bd_table;
        dma_addr_t bd_dma_addr;
        size_t bd_size;
        u16 __iomem *int_table;
        u16 __iomem *int_curr;
        dma_addr_t int_dma_addr;
        size_t int_size;
        bool is_tsa_64rxtx;
        struct list_head chan_head;
        struct qmc_chan *chans[64];
};

static void qmc_write8(void __iomem *addr, u8 val)
{
        iowrite8(val, addr);
}

static void qmc_write16(void __iomem *addr, u16 val)
{
        iowrite16be(val, addr);
}

static u16 qmc_read16(void __iomem *addr)
{
        return ioread16be(addr);
}

static void qmc_setbits16(void __iomem *addr, u16 set)
{
        qmc_write16(addr, qmc_read16(addr) | set);
}

static void qmc_clrbits16(void __iomem *addr, u16 clr)
{
        qmc_write16(addr, qmc_read16(addr) & ~clr);
}

static void qmc_clrsetbits16(void __iomem *addr, u16 clr, u16 set)
{
        qmc_write16(addr, (qmc_read16(addr) & ~clr) | set);
}

static void qmc_write32(void __iomem *addr, u32 val)
{
        iowrite32be(val, addr);
}

static u32 qmc_read32(void __iomem *addr)
{
        return ioread32be(addr);
}

static void qmc_setbits32(void __iomem *addr, u32 set)
{
        qmc_write32(addr, qmc_read32(addr) | set);
}

static bool qmc_is_qe(const struct qmc *qmc)
{
        if (IS_ENABLED(CONFIG_QUICC_ENGINE) && IS_ENABLED(CONFIG_CPM))
                return qmc->data->version == QMC_QE;

        return IS_ENABLED(CONFIG_QUICC_ENGINE);
}

int qmc_chan_get_info(struct qmc_chan *chan, struct qmc_chan_info *info)
{
        struct tsa_serial_info tsa_info;
        unsigned long flags;
        int ret;

        /* Retrieve info from the TSA related serial */
        ret = tsa_serial_get_info(chan->qmc->tsa_serial, &tsa_info);
        if (ret)
                return ret;

        spin_lock_irqsave(&chan->ts_lock, flags);

        info->mode = chan->mode;
        info->rx_fs_rate = tsa_info.rx_fs_rate;
        info->rx_bit_rate = tsa_info.rx_bit_rate;
        info->nb_tx_ts = hweight64(chan->tx_ts_mask);
        info->tx_fs_rate = tsa_info.tx_fs_rate;
        info->tx_bit_rate = tsa_info.tx_bit_rate;
        info->nb_rx_ts = hweight64(chan->rx_ts_mask);

        spin_unlock_irqrestore(&chan->ts_lock, flags);

        return 0;
}
EXPORT_SYMBOL(qmc_chan_get_info);

int qmc_chan_get_ts_info(struct qmc_chan *chan, struct qmc_chan_ts_info *ts_info)
{
        unsigned long flags;

        spin_lock_irqsave(&chan->ts_lock, flags);

        ts_info->rx_ts_mask_avail = chan->rx_ts_mask_avail;
        ts_info->tx_ts_mask_avail = chan->tx_ts_mask_avail;
        ts_info->rx_ts_mask = chan->rx_ts_mask;
        ts_info->tx_ts_mask = chan->tx_ts_mask;

        spin_unlock_irqrestore(&chan->ts_lock, flags);

        return 0;
}
EXPORT_SYMBOL(qmc_chan_get_ts_info);

int qmc_chan_set_ts_info(struct qmc_chan *chan, const struct qmc_chan_ts_info *ts_info)
{
        unsigned long flags;
        int ret;

        /* Only a subset of available timeslots is allowed */
        if ((ts_info->rx_ts_mask & chan->rx_ts_mask_avail) != ts_info->rx_ts_mask)
                return -EINVAL;
        if ((ts_info->tx_ts_mask & chan->tx_ts_mask_avail) != ts_info->tx_ts_mask)
                return -EINVAL;

        /* In case of common rx/tx table, rx/tx masks must be identical */
        if (chan->qmc->is_tsa_64rxtx) {
                if (ts_info->rx_ts_mask != ts_info->tx_ts_mask)
                        return -EINVAL;
        }

        spin_lock_irqsave(&chan->ts_lock, flags);

        if ((chan->tx_ts_mask != ts_info->tx_ts_mask && !chan->is_tx_stopped) ||
            (chan->rx_ts_mask != ts_info->rx_ts_mask && !chan->is_rx_stopped)) {
                dev_err(chan->qmc->dev, "Channel rx and/or tx not stopped\n");
                ret = -EBUSY;
        } else {
                chan->tx_ts_mask = ts_info->tx_ts_mask;
                chan->rx_ts_mask = ts_info->rx_ts_mask;
                ret = 0;
        }
        spin_unlock_irqrestore(&chan->ts_lock, flags);

        return ret;
}
EXPORT_SYMBOL(qmc_chan_set_ts_info);

int qmc_chan_set_param(struct qmc_chan *chan, const struct qmc_chan_param *param)
{
        if (param->mode != chan->mode)
                return -EINVAL;

        switch (param->mode) {
        case QMC_HDLC:
                if (param->hdlc.max_rx_buf_size % 4 ||
                    param->hdlc.max_rx_buf_size < 8)
                        return -EINVAL;

                qmc_write16(chan->qmc->scc_pram + QMC_GBL_MRBLR,
                            param->hdlc.max_rx_buf_size - 8);
                qmc_write16(chan->s_param + QMC_SPE_MFLR,
                            param->hdlc.max_rx_frame_size);
                if (param->hdlc.is_crc32) {
                        qmc_setbits16(chan->s_param + QMC_SPE_CHAMR,
                                      QMC_SPE_CHAMR_HDLC_CRC);
                } else {
                        qmc_clrbits16(chan->s_param + QMC_SPE_CHAMR,
                                      QMC_SPE_CHAMR_HDLC_CRC);
                }
                break;

        case QMC_TRANSPARENT:
                qmc_write16(chan->s_param + QMC_SPE_TMRBLR,
                            param->transp.max_rx_buf_size);
                break;

        default:
                return -EINVAL;
        }

        return 0;
}
EXPORT_SYMBOL(qmc_chan_set_param);

int qmc_chan_write_submit(struct qmc_chan *chan, dma_addr_t addr, size_t length,
                          void (*complete)(void *context), void *context)
{
        struct qmc_xfer_desc *xfer_desc;
        unsigned long flags;
        cbd_t __iomem *bd;
        u16 ctrl;
        int ret;

        /*
         * R bit  UB bit
         *   0       0  : The BD is free
         *   1       1  : The BD is in used, waiting for transfer
         *   0       1  : The BD is in used, waiting for completion
         *   1       0  : Should not append
         */

        spin_lock_irqsave(&chan->tx_lock, flags);
        bd = chan->txbd_free;

        ctrl = qmc_read16(&bd->cbd_sc);
        if (ctrl & (QMC_BD_TX_R | QMC_BD_TX_UB)) {
                if (!(ctrl & (QMC_BD_TX_R | QMC_BD_TX_I)) && bd == chan->txbd_done) {
                        if (ctrl & QMC_BD_TX_W)
                                chan->txbd_done = chan->txbds;
                        else
                                chan->txbd_done++;
                } else {
                        /* We are full ... */
                        ret = -EBUSY;
                        goto end;
                }
        }

        qmc_write16(&bd->cbd_datlen, length);
        qmc_write32(&bd->cbd_bufaddr, addr);

        xfer_desc = &chan->tx_desc[bd - chan->txbds];
        xfer_desc->tx_complete = complete;
        xfer_desc->context = context;

        /* Activate the descriptor */
        ctrl |= (QMC_BD_TX_R | QMC_BD_TX_UB);
        if (complete)
                ctrl |= QMC_BD_TX_I;
        else
                ctrl &= ~QMC_BD_TX_I;
        wmb(); /* Be sure to flush the descriptor before control update */
        qmc_write16(&bd->cbd_sc, ctrl);

        if (!chan->is_tx_stopped)
                qmc_setbits16(chan->s_param + QMC_SPE_CHAMR, QMC_SPE_CHAMR_POL);

        if (ctrl & QMC_BD_TX_W)
                chan->txbd_free = chan->txbds;
        else
                chan->txbd_free++;

        ret = 0;

end:
        spin_unlock_irqrestore(&chan->tx_lock, flags);
        return ret;
}
EXPORT_SYMBOL(qmc_chan_write_submit);

static void qmc_chan_write_done(struct qmc_chan *chan)
{
        struct qmc_xfer_desc *xfer_desc;
        void (*complete)(void *context);
        unsigned long flags;
        void *context;
        cbd_t __iomem *bd;
        u16 ctrl;

        /*
         * R bit  UB bit
         *   0       0  : The BD is free
         *   1       1  : The BD is in used, waiting for transfer
         *   0       1  : The BD is in used, waiting for completion
         *   1       0  : Should not append
         */

        spin_lock_irqsave(&chan->tx_lock, flags);
        bd = chan->txbd_done;

        ctrl = qmc_read16(&bd->cbd_sc);
        while (!(ctrl & QMC_BD_TX_R)) {
                if (!(ctrl & QMC_BD_TX_UB))
                        goto end;

                xfer_desc = &chan->tx_desc[bd - chan->txbds];
                complete = xfer_desc->tx_complete;
                context = xfer_desc->context;
                xfer_desc->tx_complete = NULL;
                xfer_desc->context = NULL;

                qmc_write16(&bd->cbd_sc, ctrl & ~QMC_BD_TX_UB);

                if (ctrl & QMC_BD_TX_W)
                        chan->txbd_done = chan->txbds;
                else
                        chan->txbd_done++;

                if (complete) {
                        spin_unlock_irqrestore(&chan->tx_lock, flags);
                        complete(context);
                        spin_lock_irqsave(&chan->tx_lock, flags);
                }

                bd = chan->txbd_done;
                ctrl = qmc_read16(&bd->cbd_sc);
        }

end:
        spin_unlock_irqrestore(&chan->tx_lock, flags);
}

int qmc_chan_read_submit(struct qmc_chan *chan, dma_addr_t addr, size_t length,
                         void (*complete)(void *context, size_t length, unsigned int flags),
                         void *context)
{
        struct qmc_xfer_desc *xfer_desc;
        unsigned long flags;
        cbd_t __iomem *bd;
        u16 ctrl;
        int ret;

        /*
         * E bit  UB bit
         *   0       0  : The BD is free
         *   1       1  : The BD is in used, waiting for transfer
         *   0       1  : The BD is in used, waiting for completion
         *   1       0  : Should not append
         */

        spin_lock_irqsave(&chan->rx_lock, flags);
        bd = chan->rxbd_free;

        ctrl = qmc_read16(&bd->cbd_sc);
        if (ctrl & (QMC_BD_RX_E | QMC_BD_RX_UB)) {
                if (!(ctrl & (QMC_BD_RX_E | QMC_BD_RX_I)) && bd == chan->rxbd_done) {
                        if (ctrl & QMC_BD_RX_W)
                                chan->rxbd_done = chan->rxbds;
                        else
                                chan->rxbd_done++;
                } else {
                        /* We are full ... */
                        ret = -EBUSY;
                        goto end;
                }
        }

        qmc_write16(&bd->cbd_datlen, 0); /* data length is updated by the QMC */
        qmc_write32(&bd->cbd_bufaddr, addr);

        xfer_desc = &chan->rx_desc[bd - chan->rxbds];
        xfer_desc->rx_complete = complete;
        xfer_desc->context = context;

        /* Clear previous status flags */
        ctrl &= ~(QMC_BD_RX_L | QMC_BD_RX_F | QMC_BD_RX_LG | QMC_BD_RX_NO |
                  QMC_BD_RX_AB | QMC_BD_RX_CR);

        /* Activate the descriptor */
        ctrl |= (QMC_BD_RX_E | QMC_BD_RX_UB);
        if (complete)
                ctrl |= QMC_BD_RX_I;
        else
                ctrl &= ~QMC_BD_RX_I;
        wmb(); /* Be sure to flush data before descriptor activation */
        qmc_write16(&bd->cbd_sc, ctrl);

        /* Restart receiver if needed */
        if (chan->is_rx_halted && !chan->is_rx_stopped) {
                /* Restart receiver */
                qmc_write32(chan->s_param + QMC_SPE_RPACK, chan->qmc->data->rpack);
                qmc_write32(chan->s_param + QMC_SPE_ZDSTATE,
                            chan->mode == QMC_TRANSPARENT ?
                                chan->qmc->data->zdstate_transp :
                                chan->qmc->data->zdstate_hdlc);
                qmc_write32(chan->s_param + QMC_SPE_RSTATE, chan->qmc->data->rstate);
                chan->is_rx_halted = false;
        }
        chan->rx_pending++;

        if (ctrl & QMC_BD_RX_W)
                chan->rxbd_free = chan->rxbds;
        else
                chan->rxbd_free++;

        ret = 0;
end:
        spin_unlock_irqrestore(&chan->rx_lock, flags);
        return ret;
}
EXPORT_SYMBOL(qmc_chan_read_submit);

static void qmc_chan_read_done(struct qmc_chan *chan)
{
        void (*complete)(void *context, size_t size, unsigned int flags);
        struct qmc_xfer_desc *xfer_desc;
        unsigned long flags;
        cbd_t __iomem *bd;
        void *context;
        u16 datalen;
        u16 ctrl;

        /*
         * E bit  UB bit
         *   0       0  : The BD is free
         *   1       1  : The BD is in used, waiting for transfer
         *   0       1  : The BD is in used, waiting for completion
         *   1       0  : Should not append
         */

        spin_lock_irqsave(&chan->rx_lock, flags);
        bd = chan->rxbd_done;

        ctrl = qmc_read16(&bd->cbd_sc);
        while (!(ctrl & QMC_BD_RX_E)) {
                if (!(ctrl & QMC_BD_RX_UB))
                        goto end;

                xfer_desc = &chan->rx_desc[bd - chan->rxbds];
                complete = xfer_desc->rx_complete;
                context = xfer_desc->context;
                xfer_desc->rx_complete = NULL;
                xfer_desc->context = NULL;

                datalen = qmc_read16(&bd->cbd_datlen);
                qmc_write16(&bd->cbd_sc, ctrl & ~QMC_BD_RX_UB);

                if (ctrl & QMC_BD_RX_W)
                        chan->rxbd_done = chan->rxbds;
                else
                        chan->rxbd_done++;

                chan->rx_pending--;

                if (complete) {
                        spin_unlock_irqrestore(&chan->rx_lock, flags);

                        /*
                         * Avoid conversion between internal hardware flags and
                         * the software API flags.
                         * -> Be sure that the software API flags are consistent
                         *    with the hardware flags
                         */
                        BUILD_BUG_ON(QMC_RX_FLAG_HDLC_LAST  != QMC_BD_RX_L);
                        BUILD_BUG_ON(QMC_RX_FLAG_HDLC_FIRST != QMC_BD_RX_F);
                        BUILD_BUG_ON(QMC_RX_FLAG_HDLC_OVF   != QMC_BD_RX_LG);
                        BUILD_BUG_ON(QMC_RX_FLAG_HDLC_UNA   != QMC_BD_RX_NO);
                        BUILD_BUG_ON(QMC_RX_FLAG_HDLC_ABORT != QMC_BD_RX_AB);
                        BUILD_BUG_ON(QMC_RX_FLAG_HDLC_CRC   != QMC_BD_RX_CR);

                        complete(context, datalen,
                                 ctrl & (QMC_BD_RX_L | QMC_BD_RX_F | QMC_BD_RX_LG |
                                         QMC_BD_RX_NO | QMC_BD_RX_AB | QMC_BD_RX_CR));
                        spin_lock_irqsave(&chan->rx_lock, flags);
                }

                bd = chan->rxbd_done;
                ctrl = qmc_read16(&bd->cbd_sc);
        }

end:
        spin_unlock_irqrestore(&chan->rx_lock, flags);
}

static int qmc_chan_setup_tsa_64rxtx(struct qmc_chan *chan, const struct tsa_serial_info *info,
                                     bool enable)
{
        unsigned int i;
        u16 curr;
        u16 val;

        /*
         * Use a common Tx/Rx 64 entries table.
         * Tx and Rx related stuffs must be identical
         */
        if (chan->tx_ts_mask != chan->rx_ts_mask) {
                dev_err(chan->qmc->dev, "chan %u uses different Rx and Tx TS\n", chan->id);
                return -EINVAL;
        }

        val = QMC_TSA_VALID | QMC_TSA_MASK_8BIT | QMC_TSA_CHANNEL(chan->id);

        /* Check entries based on Rx stuff*/
        for (i = 0; i < info->nb_rx_ts; i++) {
                if (!(chan->rx_ts_mask & (((u64)1) << i)))
                        continue;

                curr = qmc_read16(chan->qmc->scc_pram + QMC_GBL_TSATRX + (i * 2));
                if (curr & QMC_TSA_VALID && (curr & ~QMC_TSA_WRAP) != val) {
                        dev_err(chan->qmc->dev, "chan %u TxRx entry %d already used\n",
                                chan->id, i);
                        return -EBUSY;
                }
        }

        /* Set entries based on Rx stuff*/
        for (i = 0; i < info->nb_rx_ts; i++) {
                if (!(chan->rx_ts_mask & (((u64)1) << i)))
                        continue;

                qmc_clrsetbits16(chan->qmc->scc_pram + QMC_GBL_TSATRX + (i * 2),
                                 (u16)~QMC_TSA_WRAP, enable ? val : 0x0000);
        }

        return 0;
}

static int qmc_chan_setup_tsa_32rx(struct qmc_chan *chan, const struct tsa_serial_info *info,
                                   bool enable)
{
        unsigned int i;
        u16 curr;
        u16 val;

        /* Use a Rx 32 entries table */

        val = QMC_TSA_VALID | QMC_TSA_MASK_8BIT | QMC_TSA_CHANNEL(chan->id);

        /* Check entries based on Rx stuff */
        for (i = 0; i < info->nb_rx_ts; i++) {
                if (!(chan->rx_ts_mask & (((u64)1) << i)))
                        continue;

                curr = qmc_read16(chan->qmc->scc_pram + QMC_GBL_TSATRX + (i * 2));
                if (curr & QMC_TSA_VALID && (curr & ~QMC_TSA_WRAP) != val) {
                        dev_err(chan->qmc->dev, "chan %u Rx entry %d already used\n",
                                chan->id, i);
                        return -EBUSY;
                }
        }

        /* Set entries based on Rx stuff */
        for (i = 0; i < info->nb_rx_ts; i++) {
                if (!(chan->rx_ts_mask & (((u64)1) << i)))
                        continue;

                qmc_clrsetbits16(chan->qmc->scc_pram + QMC_GBL_TSATRX + (i * 2),
                                 (u16)~QMC_TSA_WRAP, enable ? val : 0x0000);
        }

        return 0;
}

static int qmc_chan_setup_tsa_32tx(struct qmc_chan *chan, const struct tsa_serial_info *info,
                                   bool enable)
{
        unsigned int i;
        u16 curr;
        u16 val;

        /* Use a Tx 32 entries table */

        val = QMC_TSA_VALID | QMC_TSA_MASK_8BIT | QMC_TSA_CHANNEL(chan->id);

        /* Check entries based on Tx stuff */
        for (i = 0; i < info->nb_tx_ts; i++) {
                if (!(chan->tx_ts_mask & (((u64)1) << i)))
                        continue;

                curr = qmc_read16(chan->qmc->scc_pram + QMC_GBL_TSATTX + (i * 2));
                if (curr & QMC_TSA_VALID && (curr & ~QMC_TSA_WRAP) != val) {
                        dev_err(chan->qmc->dev, "chan %u Tx entry %d already used\n",
                                chan->id, i);
                        return -EBUSY;
                }
        }

        /* Set entries based on Tx stuff */
        for (i = 0; i < info->nb_tx_ts; i++) {
                if (!(chan->tx_ts_mask & (((u64)1) << i)))
                        continue;

                qmc_clrsetbits16(chan->qmc->scc_pram + QMC_GBL_TSATTX + (i * 2),
                                 (u16)~QMC_TSA_WRAP, enable ? val : 0x0000);
        }

        return 0;
}

static int qmc_chan_setup_tsa_tx(struct qmc_chan *chan, bool enable)
{
        struct tsa_serial_info info;
        int ret;

        /* Retrieve info from the TSA related serial */
        ret = tsa_serial_get_info(chan->qmc->tsa_serial, &info);
        if (ret)
                return ret;

        /* Setup entries */
        if (chan->qmc->is_tsa_64rxtx)
                return qmc_chan_setup_tsa_64rxtx(chan, &info, enable);

        return qmc_chan_setup_tsa_32tx(chan, &info, enable);
}

static int qmc_chan_setup_tsa_rx(struct qmc_chan *chan, bool enable)
{
        struct tsa_serial_info info;
        int ret;

        /* Retrieve info from the TSA related serial */
        ret = tsa_serial_get_info(chan->qmc->tsa_serial, &info);
        if (ret)
                return ret;

        /* Setup entries */
        if (chan->qmc->is_tsa_64rxtx)
                return qmc_chan_setup_tsa_64rxtx(chan, &info, enable);

        return qmc_chan_setup_tsa_32rx(chan, &info, enable);
}

static int qmc_chan_cpm1_command(struct qmc_chan *chan, u8 qmc_opcode)
{
        return cpm_command(chan->id << 2, (qmc_opcode << 4) | 0x0E);
}

static int qmc_chan_qe_command(struct qmc_chan *chan, u32 cmd)
{
        if (!qe_issue_cmd(cmd, chan->qmc->qe_subblock, chan->id, 0))
                return -EIO;
        return 0;
}

static int qmc_chan_stop_rx(struct qmc_chan *chan)
{
        unsigned long flags;
        int ret;

        spin_lock_irqsave(&chan->rx_lock, flags);

        if (chan->is_rx_stopped) {
                /* The channel is already stopped -> simply return ok */
                ret = 0;
                goto end;
        }

        /* Send STOP RECEIVE command */
        ret = qmc_is_qe(chan->qmc) ?
                qmc_chan_qe_command(chan, QE_QMC_STOP_RX) :
                qmc_chan_cpm1_command(chan, 0x0);
        if (ret) {
                dev_err(chan->qmc->dev, "chan %u: Send STOP RECEIVE failed (%d)\n",
                        chan->id, ret);
                goto end;
        }

        chan->is_rx_stopped = true;

        if (!chan->qmc->is_tsa_64rxtx || chan->is_tx_stopped) {
                ret = qmc_chan_setup_tsa_rx(chan, false);
                if (ret) {
                        dev_err(chan->qmc->dev, "chan %u: Disable tsa entries failed (%d)\n",
                                chan->id, ret);
                        goto end;
                }
        }

end:
        spin_unlock_irqrestore(&chan->rx_lock, flags);
        return ret;
}

static int qmc_chan_stop_tx(struct qmc_chan *chan)
{
        unsigned long flags;
        int ret;

        spin_lock_irqsave(&chan->tx_lock, flags);

        if (chan->is_tx_stopped) {
                /* The channel is already stopped -> simply return ok */
                ret = 0;
                goto end;
        }

        /* Send STOP TRANSMIT command */
        ret = qmc_is_qe(chan->qmc) ?
                qmc_chan_qe_command(chan, QE_QMC_STOP_TX) :
                qmc_chan_cpm1_command(chan, 0x1);
        if (ret) {
                dev_err(chan->qmc->dev, "chan %u: Send STOP TRANSMIT failed (%d)\n",
                        chan->id, ret);
                goto end;
        }

        chan->is_tx_stopped = true;

        if (!chan->qmc->is_tsa_64rxtx || chan->is_rx_stopped) {
                ret = qmc_chan_setup_tsa_tx(chan, false);
                if (ret) {
                        dev_err(chan->qmc->dev, "chan %u: Disable tsa entries failed (%d)\n",
                                chan->id, ret);
                        goto end;
                }
        }

end:
        spin_unlock_irqrestore(&chan->tx_lock, flags);
        return ret;
}

static int qmc_chan_start_rx(struct qmc_chan *chan);

int qmc_chan_stop(struct qmc_chan *chan, int direction)
{
        bool is_rx_rollback_needed = false;
        unsigned long flags;
        int ret = 0;

        spin_lock_irqsave(&chan->ts_lock, flags);

        if (direction & QMC_CHAN_READ) {
                is_rx_rollback_needed = !chan->is_rx_stopped;
                ret = qmc_chan_stop_rx(chan);
                if (ret)
                        goto end;
        }

        if (direction & QMC_CHAN_WRITE) {
                ret = qmc_chan_stop_tx(chan);
                if (ret) {
                        /* Restart rx if needed */
                        if (is_rx_rollback_needed)
                                qmc_chan_start_rx(chan);
                        goto end;
                }
        }

end:
        spin_unlock_irqrestore(&chan->ts_lock, flags);
        return ret;
}
EXPORT_SYMBOL(qmc_chan_stop);

static int qmc_setup_chan_trnsync(struct qmc *qmc, struct qmc_chan *chan)
{
        struct tsa_serial_info info;
        unsigned int w_rx, w_tx;
        u16 first_rx, last_tx;
        u16 trnsync;
        int ret;

        /* Retrieve info from the TSA related serial */
        ret = tsa_serial_get_info(chan->qmc->tsa_serial, &info);
        if (ret)
                return ret;

        w_rx = hweight64(chan->rx_ts_mask);
        w_tx = hweight64(chan->tx_ts_mask);
        if (w_rx <= 1 && w_tx <= 1) {
                dev_dbg(qmc->dev, "only one or zero ts -> disable trnsync\n");
                qmc_clrbits16(chan->s_param + QMC_SPE_CHAMR, QMC_SPE_CHAMR_TRANSP_SYNC);
                return 0;
        }

        /* Find the first Rx TS allocated to the channel */
        first_rx = chan->rx_ts_mask ? __ffs64(chan->rx_ts_mask) + 1 : 0;

        /* Find the last Tx TS allocated to the channel */
        last_tx = fls64(chan->tx_ts_mask);

        trnsync = 0;
        if (info.nb_rx_ts)
                trnsync |= QMC_SPE_TRNSYNC_RX((first_rx % info.nb_rx_ts) * 2);
        if (info.nb_tx_ts)
                trnsync |= QMC_SPE_TRNSYNC_TX((last_tx % info.nb_tx_ts) * 2);

        qmc_write16(chan->s_param + QMC_SPE_TRNSYNC, trnsync);
        qmc_setbits16(chan->s_param + QMC_SPE_CHAMR, QMC_SPE_CHAMR_TRANSP_SYNC);

        dev_dbg(qmc->dev, "chan %u: trnsync=0x%04x, rx %u/%u 0x%llx, tx %u/%u 0x%llx\n",
                chan->id, trnsync,
                first_rx, info.nb_rx_ts, chan->rx_ts_mask,
                last_tx, info.nb_tx_ts, chan->tx_ts_mask);

        return 0;
}

static int qmc_chan_start_rx(struct qmc_chan *chan)
{
        unsigned long flags;
        int ret;

        spin_lock_irqsave(&chan->rx_lock, flags);

        if (!chan->is_rx_stopped) {
                /* The channel is already started -> simply return ok */
                ret = 0;
                goto end;
        }

        ret = qmc_chan_setup_tsa_rx(chan, true);
        if (ret) {
                dev_err(chan->qmc->dev, "chan %u: Enable tsa entries failed (%d)\n",
                        chan->id, ret);
                goto end;
        }

        if (chan->mode == QMC_TRANSPARENT) {
                ret = qmc_setup_chan_trnsync(chan->qmc, chan);
                if (ret) {
                        dev_err(chan->qmc->dev, "chan %u: setup TRNSYNC failed (%d)\n",
                                chan->id, ret);
                        goto end;
                }
        }

        /* Restart the receiver */
        qmc_write32(chan->s_param + QMC_SPE_RPACK, chan->qmc->data->rpack);
        qmc_write32(chan->s_param + QMC_SPE_ZDSTATE,
                    chan->mode == QMC_TRANSPARENT ?
                        chan->qmc->data->zdstate_transp :
                        chan->qmc->data->zdstate_hdlc);
        qmc_write32(chan->s_param + QMC_SPE_RSTATE, chan->qmc->data->rstate);
        chan->is_rx_halted = false;

        chan->is_rx_stopped = false;

end:
        spin_unlock_irqrestore(&chan->rx_lock, flags);
        return ret;
}

static int qmc_chan_start_tx(struct qmc_chan *chan)
{
        unsigned long flags;
        int ret;

        spin_lock_irqsave(&chan->tx_lock, flags);

        if (!chan->is_tx_stopped) {
                /* The channel is already started -> simply return ok */
                ret = 0;
                goto end;
        }

        ret = qmc_chan_setup_tsa_tx(chan, true);
        if (ret) {
                dev_err(chan->qmc->dev, "chan %u: Enable tsa entries failed (%d)\n",
                        chan->id, ret);
                goto end;
        }

        if (chan->mode == QMC_TRANSPARENT) {
                ret = qmc_setup_chan_trnsync(chan->qmc, chan);
                if (ret) {
                        dev_err(chan->qmc->dev, "chan %u: setup TRNSYNC failed (%d)\n",
                                chan->id, ret);
                        goto end;
                }
        }

        /*
         * Enable channel transmitter as it could be disabled if
         * qmc_chan_reset() was called.
         */
        qmc_setbits16(chan->s_param + QMC_SPE_CHAMR, QMC_SPE_CHAMR_ENT);

        /* Set the POL bit in the channel mode register */
        qmc_setbits16(chan->s_param + QMC_SPE_CHAMR, QMC_SPE_CHAMR_POL);

        chan->is_tx_stopped = false;

end:
        spin_unlock_irqrestore(&chan->tx_lock, flags);
        return ret;
}

int qmc_chan_start(struct qmc_chan *chan, int direction)
{
        bool is_rx_rollback_needed = false;
        unsigned long flags;
        int ret = 0;

        spin_lock_irqsave(&chan->ts_lock, flags);

        if (direction & QMC_CHAN_READ) {
                is_rx_rollback_needed = chan->is_rx_stopped;
                ret = qmc_chan_start_rx(chan);
                if (ret)
                        goto end;
        }

        if (direction & QMC_CHAN_WRITE) {
                ret = qmc_chan_start_tx(chan);
                if (ret) {
                        /* Restop rx if needed */
                        if (is_rx_rollback_needed)
                                qmc_chan_stop_rx(chan);
                        goto end;
                }
        }

end:
        spin_unlock_irqrestore(&chan->ts_lock, flags);
        return ret;
}
EXPORT_SYMBOL(qmc_chan_start);

static void qmc_chan_reset_rx(struct qmc_chan *chan)
{
        struct qmc_xfer_desc *xfer_desc;
        unsigned long flags;
        cbd_t __iomem *bd;
        u16 ctrl;

        spin_lock_irqsave(&chan->rx_lock, flags);
        bd = chan->rxbds;
        do {
                ctrl = qmc_read16(&bd->cbd_sc);
                qmc_write16(&bd->cbd_sc, ctrl & ~(QMC_BD_RX_UB | QMC_BD_RX_E));

                xfer_desc = &chan->rx_desc[bd - chan->rxbds];
                xfer_desc->rx_complete = NULL;
                xfer_desc->context = NULL;

                bd++;
        } while (!(ctrl & QMC_BD_RX_W));

        chan->rxbd_free = chan->rxbds;
        chan->rxbd_done = chan->rxbds;
        qmc_write16(chan->s_param + QMC_SPE_RBPTR,
                    qmc_read16(chan->s_param + QMC_SPE_RBASE));

        chan->rx_pending = 0;

        spin_unlock_irqrestore(&chan->rx_lock, flags);
}

static void qmc_chan_reset_tx(struct qmc_chan *chan)
{
        struct qmc_xfer_desc *xfer_desc;
        unsigned long flags;
        cbd_t __iomem *bd;
        u16 ctrl;

        spin_lock_irqsave(&chan->tx_lock, flags);

        /* Disable transmitter. It will be re-enable on qmc_chan_start() */
        qmc_clrbits16(chan->s_param + QMC_SPE_CHAMR, QMC_SPE_CHAMR_ENT);

        bd = chan->txbds;
        do {
                ctrl = qmc_read16(&bd->cbd_sc);
                qmc_write16(&bd->cbd_sc, ctrl & ~(QMC_BD_TX_UB | QMC_BD_TX_R));

                xfer_desc = &chan->tx_desc[bd - chan->txbds];
                xfer_desc->tx_complete = NULL;
                xfer_desc->context = NULL;

                bd++;
        } while (!(ctrl & QMC_BD_TX_W));

        chan->txbd_free = chan->txbds;
        chan->txbd_done = chan->txbds;
        qmc_write16(chan->s_param + QMC_SPE_TBPTR,
                    qmc_read16(chan->s_param + QMC_SPE_TBASE));

        /* Reset TSTATE and ZISTATE to their initial value */
        qmc_write32(chan->s_param + QMC_SPE_TSTATE, chan->qmc->data->tstate);
        qmc_write32(chan->s_param + QMC_SPE_ZISTATE, chan->qmc->data->zistate);

        spin_unlock_irqrestore(&chan->tx_lock, flags);
}

int qmc_chan_reset(struct qmc_chan *chan, int direction)
{
        if (direction & QMC_CHAN_READ)
                qmc_chan_reset_rx(chan);

        if (direction & QMC_CHAN_WRITE)
                qmc_chan_reset_tx(chan);

        return 0;
}
EXPORT_SYMBOL(qmc_chan_reset);

static int qmc_check_chans(struct qmc *qmc)
{
        struct tsa_serial_info info;
        struct qmc_chan *chan;
        u64 tx_ts_assigned_mask;
        u64 rx_ts_assigned_mask;
        int ret;

        /* Retrieve info from the TSA related serial */
        ret = tsa_serial_get_info(qmc->tsa_serial, &info);
        if (ret)
                return ret;

        if (info.nb_tx_ts > 64 || info.nb_rx_ts > 64) {
                dev_err(qmc->dev, "Number of TSA Tx/Rx TS assigned not supported\n");
                return -EINVAL;
        }

        /*
         * If more than 32 TS are assigned to this serial, one common table is
         * used for Tx and Rx and so masks must be equal for all channels.
         */
        if (info.nb_tx_ts > 32 || info.nb_rx_ts > 32) {
                if (info.nb_tx_ts != info.nb_rx_ts) {
                        dev_err(qmc->dev, "Number of TSA Tx/Rx TS assigned are not equal\n");
                        return -EINVAL;
                }
        }

        tx_ts_assigned_mask = info.nb_tx_ts == 64 ? U64_MAX : (((u64)1) << info.nb_tx_ts) - 1;
        rx_ts_assigned_mask = info.nb_rx_ts == 64 ? U64_MAX : (((u64)1) << info.nb_rx_ts) - 1;

        list_for_each_entry(chan, &qmc->chan_head, list) {
                if (chan->tx_ts_mask_avail > tx_ts_assigned_mask) {
                        dev_err(qmc->dev, "chan %u can use TSA unassigned Tx TS\n", chan->id);
                        return -EINVAL;
                }

                if (chan->rx_ts_mask_avail > rx_ts_assigned_mask) {
                        dev_err(qmc->dev, "chan %u can use TSA unassigned Rx TS\n", chan->id);
                        return -EINVAL;
                }
        }

        return 0;
}

static unsigned int qmc_nb_chans(struct qmc *qmc)
{
        unsigned int count = 0;
        struct qmc_chan *chan;

        list_for_each_entry(chan, &qmc->chan_head, list)
                count++;

        return count;
}

static int qmc_of_parse_chans(struct qmc *qmc, struct device_node *np)
{
        struct qmc_chan *chan;
        const char *mode;
        u32 chan_id;
        u64 ts_mask;
        int ret;

        for_each_available_child_of_node_scoped(np, chan_np) {
                ret = of_property_read_u32(chan_np, "reg", &chan_id);
                if (ret) {
                        dev_err(qmc->dev, "%pOF: failed to read reg\n", chan_np);
                        return ret;
                }
                if (chan_id > 63) {
                        dev_err(qmc->dev, "%pOF: Invalid chan_id\n", chan_np);
                        return -EINVAL;
                }

                chan = devm_kzalloc(qmc->dev, sizeof(*chan), GFP_KERNEL);
                if (!chan)
                        return -ENOMEM;

                chan->id = chan_id;
                spin_lock_init(&chan->ts_lock);
                spin_lock_init(&chan->rx_lock);
                spin_lock_init(&chan->tx_lock);

                ret = of_property_read_u64(chan_np, "fsl,tx-ts-mask", &ts_mask);
                if (ret) {
                        dev_err(qmc->dev, "%pOF: failed to read fsl,tx-ts-mask\n",
                                chan_np);
                        return ret;
                }
                chan->tx_ts_mask_avail = ts_mask;
                chan->tx_ts_mask = chan->tx_ts_mask_avail;

                ret = of_property_read_u64(chan_np, "fsl,rx-ts-mask", &ts_mask);
                if (ret) {
                        dev_err(qmc->dev, "%pOF: failed to read fsl,rx-ts-mask\n",
                                chan_np);
                        return ret;
                }
                chan->rx_ts_mask_avail = ts_mask;
                chan->rx_ts_mask = chan->rx_ts_mask_avail;

                mode = "transparent";
                ret = of_property_read_string(chan_np, "fsl,operational-mode", &mode);
                if (ret && ret != -EINVAL) {
                        dev_err(qmc->dev, "%pOF: failed to read fsl,operational-mode\n",
                                chan_np);
                        return ret;
                }
                if (!strcmp(mode, "transparent")) {
                        chan->mode = QMC_TRANSPARENT;
                } else if (!strcmp(mode, "hdlc")) {
                        chan->mode = QMC_HDLC;
                } else {
                        dev_err(qmc->dev, "%pOF: Invalid fsl,operational-mode (%s)\n",
                                chan_np, mode);
                        return -EINVAL;
                }

                chan->is_reverse_data = of_property_read_bool(chan_np,
                                                              "fsl,reverse-data");

                list_add_tail(&chan->list, &qmc->chan_head);
                qmc->chans[chan->id] = chan;
        }

        return qmc_check_chans(qmc);
}

static int qmc_init_tsa_64rxtx(struct qmc *qmc, const struct tsa_serial_info *info)
{
        unsigned int i;
        u16 val;

        /*
         * Use a common Tx/Rx 64 entries table.
         * Everything was previously checked, Tx and Rx related stuffs are
         * identical -> Used Rx related stuff to build the table
         */
        qmc->is_tsa_64rxtx = true;

        /* Invalidate all entries */
        for (i = 0; i < 64; i++)
                qmc_write16(qmc->scc_pram + QMC_GBL_TSATRX + (i * 2), 0x0000);

        /* Set Wrap bit on last entry */
        qmc_setbits16(qmc->scc_pram + QMC_GBL_TSATRX + ((info->nb_rx_ts - 1) * 2),
                      QMC_TSA_WRAP);

        /* Init pointers to the table */
        val = qmc->scc_pram_offset + QMC_GBL_TSATRX;
        qmc_write16(qmc->scc_pram + QMC_GBL_RX_S_PTR, val);
        qmc_write16(qmc->scc_pram + QMC_GBL_RXPTR, val);
        qmc_write16(qmc->scc_pram + QMC_GBL_TX_S_PTR, val);
        qmc_write16(qmc->scc_pram + QMC_GBL_TXPTR, val);

        return 0;
}

static int qmc_init_tsa_32rx_32tx(struct qmc *qmc, const struct tsa_serial_info *info)
{
        unsigned int i;
        u16 val;

        /*
         * Use a Tx 32 entries table and a Rx 32 entries table.
         * Everything was previously checked.
         */
        qmc->is_tsa_64rxtx = false;

        /* Invalidate all entries */
        for (i = 0; i < 32; i++) {
                qmc_write16(qmc->scc_pram + QMC_GBL_TSATRX + (i * 2), 0x0000);
                qmc_write16(qmc->scc_pram + QMC_GBL_TSATTX + (i * 2), 0x0000);
        }

        /* Set Wrap bit on last entries */
        qmc_setbits16(qmc->scc_pram + QMC_GBL_TSATRX + ((info->nb_rx_ts - 1) * 2),
                      QMC_TSA_WRAP);
        qmc_setbits16(qmc->scc_pram + QMC_GBL_TSATTX + ((info->nb_tx_ts - 1) * 2),
                      QMC_TSA_WRAP);

        /* Init Rx pointers ...*/
        val = qmc->scc_pram_offset + QMC_GBL_TSATRX;
        qmc_write16(qmc->scc_pram + QMC_GBL_RX_S_PTR, val);
        qmc_write16(qmc->scc_pram + QMC_GBL_RXPTR, val);

        /* ... and Tx pointers */
        val = qmc->scc_pram_offset + QMC_GBL_TSATTX;
        qmc_write16(qmc->scc_pram + QMC_GBL_TX_S_PTR, val);
        qmc_write16(qmc->scc_pram + QMC_GBL_TXPTR, val);

        return 0;
}

static int qmc_init_tsa(struct qmc *qmc)
{
        struct tsa_serial_info info;
        int ret;

        /* Retrieve info from the TSA related serial */
        ret = tsa_serial_get_info(qmc->tsa_serial, &info);
        if (ret)
                return ret;

        /*
         * Initialize one common 64 entries table or two 32 entries (one for Tx
         * and one for Tx) according to assigned TS numbers.
         */
        return ((info.nb_tx_ts > 32) || (info.nb_rx_ts > 32)) ?
                qmc_init_tsa_64rxtx(qmc, &info) :
                qmc_init_tsa_32rx_32tx(qmc, &info);
}

static int qmc_setup_chan(struct qmc *qmc, struct qmc_chan *chan)
{
        unsigned int i;
        cbd_t __iomem *bd;
        int ret;
        u16 val;

        chan->qmc = qmc;

        /* Set channel specific parameter base address */
        chan->s_param = qmc->dpram + (chan->id * 64);
        /* 16 bd per channel (8 rx and 8 tx) */
        chan->txbds = qmc->bd_table + (chan->id * (QMC_NB_TXBDS + QMC_NB_RXBDS));
        chan->rxbds = qmc->bd_table + (chan->id * (QMC_NB_TXBDS + QMC_NB_RXBDS)) + QMC_NB_TXBDS;

        chan->txbd_free = chan->txbds;
        chan->txbd_done = chan->txbds;
        chan->rxbd_free = chan->rxbds;
        chan->rxbd_done = chan->rxbds;

        /* TBASE and TBPTR*/
        val = chan->id * (QMC_NB_TXBDS + QMC_NB_RXBDS) * sizeof(cbd_t);
        qmc_write16(chan->s_param + QMC_SPE_TBASE, val);
        qmc_write16(chan->s_param + QMC_SPE_TBPTR, val);

        /* RBASE and RBPTR*/
        val = ((chan->id * (QMC_NB_TXBDS + QMC_NB_RXBDS)) + QMC_NB_TXBDS) * sizeof(cbd_t);
        qmc_write16(chan->s_param + QMC_SPE_RBASE, val);
        qmc_write16(chan->s_param + QMC_SPE_RBPTR, val);
        qmc_write32(chan->s_param + QMC_SPE_TSTATE, chan->qmc->data->tstate);
        qmc_write32(chan->s_param + QMC_SPE_RSTATE, chan->qmc->data->rstate);
        qmc_write32(chan->s_param + QMC_SPE_ZISTATE, chan->qmc->data->zistate);
        qmc_write32(chan->s_param + QMC_SPE_RPACK, chan->qmc->data->rpack);
        if (chan->mode == QMC_TRANSPARENT) {
                qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, chan->qmc->data->zdstate_transp);
                qmc_write16(chan->s_param + QMC_SPE_TMRBLR, 60);
                val = QMC_SPE_CHAMR_MODE_TRANSP;
                if (chan->is_reverse_data)
                        val |= QMC_SPE_CHAMR_TRANSP_RD;
                qmc_write16(chan->s_param + QMC_SPE_CHAMR, val);
                ret = qmc_setup_chan_trnsync(qmc, chan);
                if (ret)
                        return ret;
        } else {
                qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, chan->qmc->data->zdstate_hdlc);
                qmc_write16(chan->s_param + QMC_SPE_MFLR, 60);
                qmc_write16(chan->s_param + QMC_SPE_CHAMR,
                            QMC_SPE_CHAMR_MODE_HDLC | QMC_SPE_CHAMR_HDLC_IDLM);
        }

        /* Do not enable interrupts now. They will be enabled later */
        qmc_write16(chan->s_param + QMC_SPE_INTMSK, 0x0000);

        /* Init Rx BDs and set Wrap bit on last descriptor */
        BUILD_BUG_ON(QMC_NB_RXBDS == 0);
        for (i = 0; i < QMC_NB_RXBDS; i++) {
                bd = chan->rxbds + i;
                qmc_write16(&bd->cbd_sc, 0);
        }
        bd = chan->rxbds + QMC_NB_RXBDS - 1;
        qmc_write16(&bd->cbd_sc, QMC_BD_RX_W);

        /* Init Tx BDs and set Wrap bit on last descriptor */
        BUILD_BUG_ON(QMC_NB_TXBDS == 0);
        if (chan->mode == QMC_HDLC)
                val = QMC_BD_TX_L | QMC_BD_TX_TC;
        else
                val = 0;
        for (i = 0; i < QMC_NB_TXBDS; i++) {
                bd = chan->txbds + i;
                qmc_write16(&bd->cbd_sc, val);
        }
        bd = chan->txbds + QMC_NB_TXBDS - 1;
        qmc_write16(&bd->cbd_sc, val | QMC_BD_TX_W);

        return 0;
}

static int qmc_setup_chans(struct qmc *qmc)
{
        struct qmc_chan *chan;
        int ret;

        list_for_each_entry(chan, &qmc->chan_head, list) {
                ret = qmc_setup_chan(qmc, chan);
                if (ret)
                        return ret;
        }

        return 0;
}

static int qmc_finalize_chans(struct qmc *qmc)
{
        struct qmc_chan *chan;
        int ret;

        list_for_each_entry(chan, &qmc->chan_head, list) {
                /* Unmask channel interrupts */
                if (chan->mode == QMC_HDLC) {
                        qmc_write16(chan->s_param + QMC_SPE_INTMSK,
                                    QMC_INT_NID | QMC_INT_IDL | QMC_INT_MRF |
                                    QMC_INT_UN | QMC_INT_RXF | QMC_INT_BSY |
                                    QMC_INT_TXB | QMC_INT_RXB);
                } else {
                        qmc_write16(chan->s_param + QMC_SPE_INTMSK,
                                    QMC_INT_UN | QMC_INT_BSY |
                                    QMC_INT_TXB | QMC_INT_RXB);
                }

                /* Forced stop the channel */
                ret = qmc_chan_stop(chan, QMC_CHAN_ALL);
                if (ret)
                        return ret;
        }

        return 0;
}

static int qmc_setup_ints(struct qmc *qmc)
{
        unsigned int i;
        u16 __iomem *last;

        /* Raz all entries */
        for (i = 0; i < (qmc->int_size / sizeof(u16)); i++)
                qmc_write16(qmc->int_table + i, 0x0000);

        /* Set Wrap bit on last entry */
        if (qmc->int_size >= sizeof(u16)) {
                last = qmc->int_table + (qmc->int_size / sizeof(u16)) - 1;
                qmc_write16(last, QMC_INT_W);
        }

        return 0;
}

static void qmc_irq_gint(struct qmc *qmc)
{
        struct qmc_chan *chan;
        unsigned int chan_id;
        unsigned long flags;
        u16 int_entry;

        int_entry = qmc_read16(qmc->int_curr);
        while (int_entry & QMC_INT_V) {
                /* Clear all but the Wrap bit */
                qmc_write16(qmc->int_curr, int_entry & QMC_INT_W);

                chan_id = QMC_INT_GET_CHANNEL(int_entry);
                chan = qmc->chans[chan_id];
                if (!chan) {
                        dev_err(qmc->dev, "interrupt on invalid chan %u\n", chan_id);
                        goto int_next;
                }

                if (int_entry & QMC_INT_TXB)
                        qmc_chan_write_done(chan);

                if (int_entry & QMC_INT_UN) {
                        dev_info(qmc->dev, "intr chan %u, 0x%04x (UN)\n", chan_id,
                                 int_entry);
                        chan->nb_tx_underrun++;
                }

                if (int_entry & QMC_INT_BSY) {
                        dev_info(qmc->dev, "intr chan %u, 0x%04x (BSY)\n", chan_id,
                                 int_entry);
                        chan->nb_rx_busy++;
                        /* Restart the receiver if needed */
                        spin_lock_irqsave(&chan->rx_lock, flags);
                        if (chan->rx_pending && !chan->is_rx_stopped) {
                                qmc_write32(chan->s_param + QMC_SPE_RPACK,
                                            chan->qmc->data->rpack);
                                qmc_write32(chan->s_param + QMC_SPE_ZDSTATE,
                                            chan->mode == QMC_TRANSPARENT ?
                                                chan->qmc->data->zdstate_transp :
                                                chan->qmc->data->zdstate_hdlc);
                                qmc_write32(chan->s_param + QMC_SPE_RSTATE,
                                            chan->qmc->data->rstate);
                                chan->is_rx_halted = false;
                        } else {
                                chan->is_rx_halted = true;
                        }
                        spin_unlock_irqrestore(&chan->rx_lock, flags);
                }

                if (int_entry & QMC_INT_RXB)
                        qmc_chan_read_done(chan);

int_next:
                if (int_entry & QMC_INT_W)
                        qmc->int_curr = qmc->int_table;
                else
                        qmc->int_curr++;
                int_entry = qmc_read16(qmc->int_curr);
        }
}

static irqreturn_t qmc_irq_handler(int irq, void *priv)
{
        struct qmc *qmc = (struct qmc *)priv;
        u16 scce;

        scce = qmc_read16(qmc->scc_regs + SCC_SCCE);
        qmc_write16(qmc->scc_regs + SCC_SCCE, scce);

        if (unlikely(scce & SCC_SCCE_IQOV))
                dev_info(qmc->dev, "IRQ queue overflow\n");

        if (unlikely(scce & SCC_SCCE_GUN))
                dev_err(qmc->dev, "Global transmitter underrun\n");

        if (unlikely(scce & SCC_SCCE_GOV))
                dev_err(qmc->dev, "Global receiver overrun\n");

        /* normal interrupt */
        if (likely(scce & SCC_SCCE_GINT))
                qmc_irq_gint(qmc);

        return IRQ_HANDLED;
}

static int qmc_qe_soft_qmc_init(struct qmc *qmc, struct device_node *np)
{
        struct qe_firmware_info *qe_fw_info;
        const struct qe_firmware *qe_fw;
        const struct firmware *fw;
        const char *filename;
        int ret;

        ret = of_property_read_string(np, "fsl,soft-qmc", &filename);
        switch (ret) {
        case 0:
                break;
        case -EINVAL:
                /* fsl,soft-qmc property not set -> Simply do nothing */
                return 0;
        default:
                dev_err(qmc->dev, "%pOF: failed to read fsl,soft-qmc\n",
                        np);
                return ret;
        }

        qe_fw_info = qe_get_firmware_info();
        if (qe_fw_info) {
                if (!strstr(qe_fw_info->id, "Soft-QMC")) {
                        dev_err(qmc->dev, "Another Firmware is already loaded\n");
                        return -EALREADY;
                }
                dev_info(qmc->dev, "Firmware already loaded\n");
                return 0;
        }

        dev_info(qmc->dev, "Using firmware %s\n", filename);

        ret = request_firmware(&fw, filename, qmc->dev);
        if (ret) {
                dev_err(qmc->dev, "Failed to request firmware %s\n", filename);
                return ret;
        }

        qe_fw = (const struct qe_firmware *)fw->data;

        if (fw->size < sizeof(qe_fw->header) ||
            be32_to_cpu(qe_fw->header.length) != fw->size) {
                dev_err(qmc->dev, "Invalid firmware %s\n", filename);
                ret = -EINVAL;
                goto end;
        }

        ret = qe_upload_firmware(qe_fw);
        if (ret) {
                dev_err(qmc->dev, "Failed to load firmware %s\n", filename);
                goto end;
        }

        ret = 0;
end:
        release_firmware(fw);
        return ret;
}

static int qmc_cpm1_init_resources(struct qmc *qmc, struct platform_device *pdev)
{
        struct resource *res;

        qmc->scc_regs = devm_platform_ioremap_resource_byname(pdev, "scc_regs");
        if (IS_ERR(qmc->scc_regs))
                return PTR_ERR(qmc->scc_regs);

        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "scc_pram");
        if (!res)
                return -EINVAL;
        qmc->scc_pram_offset = res->start - get_immrbase();
        qmc->scc_pram = devm_ioremap_resource(qmc->dev, res);
        if (IS_ERR(qmc->scc_pram))
                return PTR_ERR(qmc->scc_pram);

        qmc->dpram  = devm_platform_ioremap_resource_byname(pdev, "dpram");
        if (IS_ERR(qmc->dpram))
                return PTR_ERR(qmc->dpram);

        return 0;
}

static int qmc_qe_init_resources(struct qmc *qmc, struct platform_device *pdev)
{
        struct resource *res;
        int ucc_num;
        s32 info;

        qmc->scc_regs = devm_platform_ioremap_resource_byname(pdev, "ucc_regs");
        if (IS_ERR(qmc->scc_regs))
                return PTR_ERR(qmc->scc_regs);

        ucc_num = tsa_serial_get_num(qmc->tsa_serial);
        if (ucc_num < 0)
                return dev_err_probe(qmc->dev, ucc_num, "Failed to get UCC num\n");

        qmc->qe_subblock = ucc_slow_get_qe_cr_subblock(ucc_num);
        if (qmc->qe_subblock == QE_CR_SUBBLOCK_INVALID) {
                dev_err(qmc->dev, "Unsupported ucc num %u\n", ucc_num);
                return -EINVAL;
        }
        /* Allocate the 'Global Multichannel Parameters' and the
         * 'Framer parameters' areas. The 'Framer parameters' area
         * is located right after the 'Global Multichannel Parameters'.
         * The 'Framer parameters' need 1 byte per receive and transmit
         * channel. The maximum number of receive or transmit channel
         * is 64. So reserve 2 * 64 bytes for the 'Framer parameters'.
         */
        info = devm_qe_muram_alloc(qmc->dev, UCC_SLOW_PRAM_SIZE + 2 * 64,
                                   ALIGNMENT_OF_UCC_SLOW_PRAM);
        if (info < 0)
                return info;

        if (!qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, qmc->qe_subblock,
                          QE_CR_PROTOCOL_UNSPECIFIED, info)) {
                dev_err(qmc->dev, "QE_ASSIGN_PAGE_TO_DEVICE cmd failed");
                return -EIO;
        }
        qmc->scc_pram = qe_muram_addr(info);
        qmc->scc_pram_offset = info;

        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dpram");
        if (!res)
                return -EINVAL;
        qmc->dpram_offset = res->start - qe_muram_dma(qe_muram_addr(0));
        qmc->dpram = devm_ioremap_resource(qmc->dev, res);
        if (IS_ERR(qmc->dpram))
                return PTR_ERR(qmc->dpram);

        return 0;
}

static int qmc_init_resources(struct qmc *qmc, struct platform_device *pdev)
{
        return qmc_is_qe(qmc) ?
                qmc_qe_init_resources(qmc, pdev) :
                qmc_cpm1_init_resources(qmc, pdev);
}

static int qmc_cpm1_init_scc(struct qmc *qmc)
{
        u32 val;
        int ret;

        /* Connect the serial (SCC) to TSA */
        ret = tsa_serial_connect(qmc->tsa_serial);
        if (ret)
                return dev_err_probe(qmc->dev, ret, "Failed to connect TSA serial\n");

        /* Init GMSR_H and GMSR_L registers */
        val = SCC_GSMRH_CDS | SCC_GSMRH_CTSS | SCC_GSMRH_CDP | SCC_GSMRH_CTSP;
        qmc_write32(qmc->scc_regs + SCC_GSMRH, val);

        /* enable QMC mode */
        qmc_write32(qmc->scc_regs + SCC_GSMRL, SCC_CPM1_GSMRL_MODE_QMC);

        /* Disable and clear interrupts */
        qmc_write16(qmc->scc_regs + SCC_SCCM, 0x0000);
        qmc_write16(qmc->scc_regs + SCC_SCCE, 0x000F);

        return 0;
}

static int qmc_qe_init_ucc(struct qmc *qmc)
{
        u32 val;
        int ret;

        /* Set the UCC in slow mode */
        qmc_write8(qmc->scc_regs + SCC_QE_UCC_GUEMR,
                   UCC_GUEMR_SET_RESERVED3 | UCC_GUEMR_MODE_SLOW_RX | UCC_GUEMR_MODE_SLOW_TX);

        /* Connect the serial (UCC) to TSA */
        ret = tsa_serial_connect(qmc->tsa_serial);
        if (ret)
                return dev_err_probe(qmc->dev, ret, "Failed to connect TSA serial\n");

        /* Initialize the QMC tx startup addresses */
        if (!qe_issue_cmd(QE_PUSHSCHED, qmc->qe_subblock,
                          QE_CR_PROTOCOL_UNSPECIFIED, 0x80)) {
                dev_err(qmc->dev, "QE_CMD_PUSH_SCHED tx cmd failed");
                ret = -EIO;
                goto err_tsa_serial_disconnect;
        }

        /* Initialize the QMC rx startup addresses */
        if (!qe_issue_cmd(QE_PUSHSCHED, qmc->qe_subblock | 0x00020000,
                          QE_CR_PROTOCOL_UNSPECIFIED, 0x82)) {
                dev_err(qmc->dev, "QE_CMD_PUSH_SCHED rx cmd failed");
                ret = -EIO;
                goto err_tsa_serial_disconnect;
        }

        /* Re-init RXPTR and TXPTR with the content of RX_S_PTR and
         * TX_S_PTR (RX_S_PTR and TX_S_PTR are initialized during
         * qmc_setup_tsa() call
         */
        val = qmc_read16(qmc->scc_pram + QMC_GBL_RX_S_PTR);
        qmc_write16(qmc->scc_pram + QMC_GBL_RXPTR, val);
        val = qmc_read16(qmc->scc_pram + QMC_GBL_TX_S_PTR);
        qmc_write16(qmc->scc_pram + QMC_GBL_TXPTR, val);

        /* Init GUMR_H and GUMR_L registers (SCC GSMR_H and GSMR_L) */
        val = SCC_GSMRH_CDS | SCC_GSMRH_CTSS | SCC_GSMRH_CDP | SCC_GSMRH_CTSP |
              SCC_GSMRH_TRX | SCC_GSMRH_TTX;
        qmc_write32(qmc->scc_regs + SCC_GSMRH, val);

        /* enable QMC mode */
        qmc_write32(qmc->scc_regs + SCC_GSMRL, SCC_QE_GSMRL_MODE_QMC);

        /* Disable and clear interrupts */
        qmc_write16(qmc->scc_regs + SCC_SCCM, 0x0000);
        qmc_write16(qmc->scc_regs + SCC_SCCE, 0x000F);

        return 0;

err_tsa_serial_disconnect:
        tsa_serial_disconnect(qmc->tsa_serial);
        return ret;
}

static int qmc_init_xcc(struct qmc *qmc)
{
        return qmc_is_qe(qmc) ?
                qmc_qe_init_ucc(qmc) :
                qmc_cpm1_init_scc(qmc);
}

static void qmc_exit_xcc(struct qmc *qmc)
{
        /* Disconnect the serial from TSA */
        tsa_serial_disconnect(qmc->tsa_serial);
}

static int qmc_probe(struct platform_device *pdev)
{
        struct device_node *np = pdev->dev.of_node;
        unsigned int nb_chans;
        struct qmc *qmc;
        int irq;
        int ret;

        qmc = devm_kzalloc(&pdev->dev, sizeof(*qmc), GFP_KERNEL);
        if (!qmc)
                return -ENOMEM;

        qmc->dev = &pdev->dev;
        qmc->data = of_device_get_match_data(&pdev->dev);
        if (!qmc->data) {
                dev_err(qmc->dev, "Missing match data\n");
                return -EINVAL;
        }
        INIT_LIST_HEAD(&qmc->chan_head);

        qmc->tsa_serial = devm_tsa_serial_get_byphandle(qmc->dev, np, "fsl,tsa-serial");
        if (IS_ERR(qmc->tsa_serial)) {
                return dev_err_probe(qmc->dev, PTR_ERR(qmc->tsa_serial),
                                     "Failed to get TSA serial\n");
        }

        ret = qmc_init_resources(qmc, pdev);
        if (ret)
                return ret;

        if (qmc_is_qe(qmc)) {
                ret = qmc_qe_soft_qmc_init(qmc, np);
                if (ret)
                        return ret;
        }

        /* Parse channels informationss */
        ret = qmc_of_parse_chans(qmc, np);
        if (ret)
                return ret;

        nb_chans = qmc_nb_chans(qmc);

        /*
         * Allocate the buffer descriptor table
         * 8 rx and 8 tx descriptors per channel
         */
        qmc->bd_size = (nb_chans * (QMC_NB_TXBDS + QMC_NB_RXBDS)) * sizeof(cbd_t);
        qmc->bd_table = dmam_alloc_coherent(qmc->dev, qmc->bd_size,
                                            &qmc->bd_dma_addr, GFP_KERNEL);
        if (!qmc->bd_table) {
                dev_err(qmc->dev, "Failed to allocate bd table\n");
                return -ENOMEM;
        }
        memset(qmc->bd_table, 0, qmc->bd_size);

        qmc_write32(qmc->scc_pram + QMC_GBL_MCBASE, qmc->bd_dma_addr);

        /* Allocate the interrupt table */
        qmc->int_size = QMC_NB_INTS * sizeof(u16);
        qmc->int_table = dmam_alloc_coherent(qmc->dev, qmc->int_size,
                                             &qmc->int_dma_addr, GFP_KERNEL);
        if (!qmc->int_table) {
                dev_err(qmc->dev, "Failed to allocate interrupt table\n");
                return -ENOMEM;
        }
        memset(qmc->int_table, 0, qmc->int_size);

        qmc->int_curr = qmc->int_table;
        qmc_write32(qmc->scc_pram + QMC_GBL_INTBASE, qmc->int_dma_addr);
        qmc_write32(qmc->scc_pram + QMC_GBL_INTPTR, qmc->int_dma_addr);

        /* Set MRBLR (valid for HDLC only) max MRU + max CRC */
        qmc_write16(qmc->scc_pram + QMC_GBL_MRBLR, HDLC_MAX_MRU + 4);

        qmc_write16(qmc->scc_pram + QMC_GBL_GRFTHR, 1);
        qmc_write16(qmc->scc_pram + QMC_GBL_GRFCNT, 1);

        qmc_write32(qmc->scc_pram + QMC_GBL_C_MASK32, 0xDEBB20E3);
        qmc_write16(qmc->scc_pram + QMC_GBL_C_MASK16, 0xF0B8);

        if (qmc_is_qe(qmc)) {
                /* Zeroed the reserved area */
                memset_io(qmc->scc_pram + QMC_QE_GBL_RSV_B0_START, 0,
                          QMC_QE_GBL_RSV_B0_SIZE);

                qmc_write32(qmc->scc_pram + QMC_QE_GBL_GCSBASE, qmc->dpram_offset);

                /* Init 'framer parameters' area and set the base addresses */
                memset_io(qmc->scc_pram + UCC_SLOW_PRAM_SIZE, 0x01, 64);
                memset_io(qmc->scc_pram + UCC_SLOW_PRAM_SIZE + 64, 0x01, 64);
                qmc_write16(qmc->scc_pram + QMC_QE_GBL_RX_FRM_BASE,
                            qmc->scc_pram_offset + UCC_SLOW_PRAM_SIZE);
                qmc_write16(qmc->scc_pram + QMC_QE_GBL_TX_FRM_BASE,
                            qmc->scc_pram_offset + UCC_SLOW_PRAM_SIZE + 64);
        }

        ret = qmc_init_tsa(qmc);
        if (ret)
                return ret;

        qmc_write16(qmc->scc_pram + QMC_GBL_QMCSTATE, 0x8000);

        ret = qmc_setup_chans(qmc);
        if (ret)
                return ret;

        /* Init interrupts table */
        ret = qmc_setup_ints(qmc);
        if (ret)
                return ret;

        /* Init SCC (CPM1) or UCC (QE) */
        ret = qmc_init_xcc(qmc);
        if (ret)
                return ret;

        /* Set the irq handler */
        irq = platform_get_irq(pdev, 0);
        if (irq < 0) {
                ret = irq;
                goto err_exit_xcc;
        }
        ret = devm_request_irq(qmc->dev, irq, qmc_irq_handler, 0, "qmc", qmc);
        if (ret < 0)
                goto err_exit_xcc;

        /* Enable interrupts */
        qmc_write16(qmc->scc_regs + SCC_SCCM,
                    SCC_SCCE_IQOV | SCC_SCCE_GINT | SCC_SCCE_GUN | SCC_SCCE_GOV);

        ret = qmc_finalize_chans(qmc);
        if (ret < 0)
                goto err_disable_intr;

        /* Enable transmitter and receiver */
        qmc_setbits32(qmc->scc_regs + SCC_GSMRL, SCC_GSMRL_ENR | SCC_GSMRL_ENT);

        platform_set_drvdata(pdev, qmc);

        /* Populate channel related devices */
        ret = devm_of_platform_populate(qmc->dev);
        if (ret)
                goto err_disable_txrx;

        return 0;

err_disable_txrx:
        qmc_setbits32(qmc->scc_regs + SCC_GSMRL, 0);

err_disable_intr:
        qmc_write16(qmc->scc_regs + SCC_SCCM, 0);

err_exit_xcc:
        qmc_exit_xcc(qmc);
        return ret;
}

static void qmc_remove(struct platform_device *pdev)
{
        struct qmc *qmc = platform_get_drvdata(pdev);

        /* Disable transmitter and receiver */
        qmc_setbits32(qmc->scc_regs + SCC_GSMRL, 0);

        /* Disable interrupts */
        qmc_write16(qmc->scc_regs + SCC_SCCM, 0);

        /* Exit SCC (CPM1) or UCC (QE) */
        qmc_exit_xcc(qmc);
}

static const struct qmc_data qmc_data_cpm1 __maybe_unused = {
        .version = QMC_CPM1,
        .tstate = 0x30000000,
        .rstate = 0x31000000,
        .zistate = 0x00000100,
        .zdstate_hdlc = 0x00000080,
        .zdstate_transp = 0x18000080,
        .rpack = 0x00000000,
};

static const struct qmc_data qmc_data_qe __maybe_unused = {
        .version = QMC_QE,
        .tstate = 0x30000000,
        .rstate = 0x30000000,
        .zistate = 0x00000200,
        .zdstate_hdlc = 0x80FFFFE0,
        .zdstate_transp = 0x003FFFE2,
        .rpack = 0x80000000,
};

static const struct of_device_id qmc_id_table[] = {
#if IS_ENABLED(CONFIG_CPM1)
        { .compatible = "fsl,cpm1-scc-qmc", .data = &qmc_data_cpm1 },
#endif
#if IS_ENABLED(CONFIG_QUICC_ENGINE)
        { .compatible = "fsl,qe-ucc-qmc", .data = &qmc_data_qe },
#endif
        {} /* sentinel */
};
MODULE_DEVICE_TABLE(of, qmc_id_table);

static struct platform_driver qmc_driver = {
        .driver = {
                .name = "fsl-qmc",
                .of_match_table = of_match_ptr(qmc_id_table),
        },
        .probe = qmc_probe,
        .remove = qmc_remove,
};
module_platform_driver(qmc_driver);

static struct qmc_chan *qmc_chan_get_from_qmc(struct device_node *qmc_np, unsigned int chan_index)
{
        struct platform_device *pdev;
        struct qmc_chan *qmc_chan;
        struct qmc *qmc;

        if (!of_match_node(qmc_driver.driver.of_match_table, qmc_np))
                return ERR_PTR(-EINVAL);

        pdev = of_find_device_by_node(qmc_np);
        if (!pdev)
                return ERR_PTR(-ENODEV);

        qmc = platform_get_drvdata(pdev);
        if (!qmc) {
                platform_device_put(pdev);
                return ERR_PTR(-EPROBE_DEFER);
        }

        if (chan_index >= ARRAY_SIZE(qmc->chans)) {
                platform_device_put(pdev);
                return ERR_PTR(-EINVAL);
        }

        qmc_chan = qmc->chans[chan_index];
        if (!qmc_chan) {
                platform_device_put(pdev);
                return ERR_PTR(-ENOENT);
        }

        return qmc_chan;
}

int qmc_chan_count_phandles(struct device_node *np, const char *phandles_name)
{
        int count;

        /* phandles are fixed args phandles with one arg */
        count = of_count_phandle_with_args(np, phandles_name, NULL);
        if (count < 0)
                return count;

        return count / 2;
}
EXPORT_SYMBOL(qmc_chan_count_phandles);

struct qmc_chan *qmc_chan_get_byphandles_index(struct device_node *np,
                                               const char *phandles_name,
                                               int index)
{
        struct of_phandle_args out_args;
        struct qmc_chan *qmc_chan;
        int ret;

        ret = of_parse_phandle_with_fixed_args(np, phandles_name, 1, index,
                                               &out_args);
        if (ret < 0)
                return ERR_PTR(ret);

        if (out_args.args_count != 1) {
                of_node_put(out_args.np);
                return ERR_PTR(-EINVAL);
        }

        qmc_chan = qmc_chan_get_from_qmc(out_args.np, out_args.args[0]);
        of_node_put(out_args.np);
        return qmc_chan;
}
EXPORT_SYMBOL(qmc_chan_get_byphandles_index);

struct qmc_chan *qmc_chan_get_bychild(struct device_node *np)
{
        struct device_node *qmc_np;
        u32 chan_index;
        int ret;

        qmc_np = np->parent;
        ret = of_property_read_u32(np, "reg", &chan_index);
        if (ret)
                return ERR_PTR(-EINVAL);

        return qmc_chan_get_from_qmc(qmc_np, chan_index);
}
EXPORT_SYMBOL(qmc_chan_get_bychild);

void qmc_chan_put(struct qmc_chan *chan)
{
        put_device(chan->qmc->dev);
}
EXPORT_SYMBOL(qmc_chan_put);

static void devm_qmc_chan_release(struct device *dev, void *res)
{
        struct qmc_chan **qmc_chan = res;

        qmc_chan_put(*qmc_chan);
}

struct qmc_chan *devm_qmc_chan_get_byphandles_index(struct device *dev,
                                                    struct device_node *np,
                                                    const char *phandles_name,
                                                    int index)
{
        struct qmc_chan *qmc_chan;
        struct qmc_chan **dr;

        dr = devres_alloc(devm_qmc_chan_release, sizeof(*dr), GFP_KERNEL);
        if (!dr)
                return ERR_PTR(-ENOMEM);

        qmc_chan = qmc_chan_get_byphandles_index(np, phandles_name, index);
        if (!IS_ERR(qmc_chan)) {
                *dr = qmc_chan;
                devres_add(dev, dr);
        } else {
                devres_free(dr);
        }

        return qmc_chan;
}
EXPORT_SYMBOL(devm_qmc_chan_get_byphandles_index);

struct qmc_chan *devm_qmc_chan_get_bychild(struct device *dev,
                                           struct device_node *np)
{
        struct qmc_chan *qmc_chan;
        struct qmc_chan **dr;

        dr = devres_alloc(devm_qmc_chan_release, sizeof(*dr), GFP_KERNEL);
        if (!dr)
                return ERR_PTR(-ENOMEM);

        qmc_chan = qmc_chan_get_bychild(np);
        if (!IS_ERR(qmc_chan)) {
                *dr = qmc_chan;
                devres_add(dev, dr);
        } else {
                devres_free(dr);
        }

        return qmc_chan;
}
EXPORT_SYMBOL(devm_qmc_chan_get_bychild);

MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
MODULE_DESCRIPTION("CPM/QE QMC driver");
MODULE_LICENSE("GPL");