18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* Freescale QUICC Engine HDLC Device Driver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright 2016 Freescale Semiconductor Inc. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/delay.h> 88c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 98c2ecf20Sopenharmony_ci#include <linux/hdlc.h> 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 128c2ecf20Sopenharmony_ci#include <linux/io.h> 138c2ecf20Sopenharmony_ci#include <linux/irq.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 178c2ecf20Sopenharmony_ci#include <linux/of_address.h> 188c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 198c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 208c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 218c2ecf20Sopenharmony_ci#include <linux/sched.h> 228c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 258c2ecf20Sopenharmony_ci#include <linux/stddef.h> 268c2ecf20Sopenharmony_ci#include <soc/fsl/qe/qe_tdm.h> 278c2ecf20Sopenharmony_ci#include <uapi/linux/if_arp.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include "fsl_ucc_hdlc.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define DRV_DESC "Freescale QE UCC HDLC Driver" 328c2ecf20Sopenharmony_ci#define DRV_NAME "ucc_hdlc" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define TDM_PPPOHT_SLIC_MAXIN 358c2ecf20Sopenharmony_ci#define RX_BD_ERRORS (R_CD_S | R_OV_S | R_CR_S | R_AB_S | R_NO_S | R_LG_S) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic int uhdlc_close(struct net_device *dev); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic struct ucc_tdm_info utdm_primary_info = { 408c2ecf20Sopenharmony_ci .uf_info = { 418c2ecf20Sopenharmony_ci .tsa = 0, 428c2ecf20Sopenharmony_ci .cdp = 0, 438c2ecf20Sopenharmony_ci .cds = 1, 448c2ecf20Sopenharmony_ci .ctsp = 1, 458c2ecf20Sopenharmony_ci .ctss = 1, 468c2ecf20Sopenharmony_ci .revd = 0, 478c2ecf20Sopenharmony_ci .urfs = 256, 488c2ecf20Sopenharmony_ci .utfs = 256, 498c2ecf20Sopenharmony_ci .urfet = 128, 508c2ecf20Sopenharmony_ci .urfset = 192, 518c2ecf20Sopenharmony_ci .utfet = 128, 528c2ecf20Sopenharmony_ci .utftt = 0x40, 538c2ecf20Sopenharmony_ci .ufpt = 256, 548c2ecf20Sopenharmony_ci .mode = UCC_FAST_PROTOCOL_MODE_HDLC, 558c2ecf20Sopenharmony_ci .ttx_trx = UCC_FAST_GUMR_TRANSPARENT_TTX_TRX_NORMAL, 568c2ecf20Sopenharmony_ci .tenc = UCC_FAST_TX_ENCODING_NRZ, 578c2ecf20Sopenharmony_ci .renc = UCC_FAST_RX_ENCODING_NRZ, 588c2ecf20Sopenharmony_ci .tcrc = UCC_FAST_16_BIT_CRC, 598c2ecf20Sopenharmony_ci .synl = UCC_FAST_SYNC_LEN_NOT_USED, 608c2ecf20Sopenharmony_ci }, 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci .si_info = { 638c2ecf20Sopenharmony_ci#ifdef TDM_PPPOHT_SLIC_MAXIN 648c2ecf20Sopenharmony_ci .simr_rfsd = 1, 658c2ecf20Sopenharmony_ci .simr_tfsd = 2, 668c2ecf20Sopenharmony_ci#else 678c2ecf20Sopenharmony_ci .simr_rfsd = 0, 688c2ecf20Sopenharmony_ci .simr_tfsd = 0, 698c2ecf20Sopenharmony_ci#endif 708c2ecf20Sopenharmony_ci .simr_crt = 0, 718c2ecf20Sopenharmony_ci .simr_sl = 0, 728c2ecf20Sopenharmony_ci .simr_ce = 1, 738c2ecf20Sopenharmony_ci .simr_fe = 1, 748c2ecf20Sopenharmony_ci .simr_gm = 0, 758c2ecf20Sopenharmony_ci }, 768c2ecf20Sopenharmony_ci}; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic struct ucc_tdm_info utdm_info[UCC_MAX_NUM]; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic int uhdlc_init(struct ucc_hdlc_private *priv) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct ucc_tdm_info *ut_info; 838c2ecf20Sopenharmony_ci struct ucc_fast_info *uf_info; 848c2ecf20Sopenharmony_ci u32 cecr_subblock; 858c2ecf20Sopenharmony_ci u16 bd_status; 868c2ecf20Sopenharmony_ci int ret, i; 878c2ecf20Sopenharmony_ci void *bd_buffer; 888c2ecf20Sopenharmony_ci dma_addr_t bd_dma_addr; 898c2ecf20Sopenharmony_ci s32 riptr; 908c2ecf20Sopenharmony_ci s32 tiptr; 918c2ecf20Sopenharmony_ci u32 gumr; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci ut_info = priv->ut_info; 948c2ecf20Sopenharmony_ci uf_info = &ut_info->uf_info; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (priv->tsa) { 978c2ecf20Sopenharmony_ci uf_info->tsa = 1; 988c2ecf20Sopenharmony_ci uf_info->ctsp = 1; 998c2ecf20Sopenharmony_ci uf_info->cds = 1; 1008c2ecf20Sopenharmony_ci uf_info->ctss = 1; 1018c2ecf20Sopenharmony_ci } else { 1028c2ecf20Sopenharmony_ci uf_info->cds = 0; 1038c2ecf20Sopenharmony_ci uf_info->ctsp = 0; 1048c2ecf20Sopenharmony_ci uf_info->ctss = 0; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* This sets HPM register in CMXUCR register which configures a 1088c2ecf20Sopenharmony_ci * open drain connected HDLC bus 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_ci if (priv->hdlc_bus) 1118c2ecf20Sopenharmony_ci uf_info->brkpt_support = 1; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci uf_info->uccm_mask = ((UCC_HDLC_UCCE_RXB | UCC_HDLC_UCCE_RXF | 1148c2ecf20Sopenharmony_ci UCC_HDLC_UCCE_TXB) << 16); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci ret = ucc_fast_init(uf_info, &priv->uccf); 1178c2ecf20Sopenharmony_ci if (ret) { 1188c2ecf20Sopenharmony_ci dev_err(priv->dev, "Failed to init uccf."); 1198c2ecf20Sopenharmony_ci return ret; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci priv->uf_regs = priv->uccf->uf_regs; 1238c2ecf20Sopenharmony_ci ucc_fast_disable(priv->uccf, COMM_DIR_RX | COMM_DIR_TX); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* Loopback mode */ 1268c2ecf20Sopenharmony_ci if (priv->loopback) { 1278c2ecf20Sopenharmony_ci dev_info(priv->dev, "Loopback Mode\n"); 1288c2ecf20Sopenharmony_ci /* use the same clock when work in loopback */ 1298c2ecf20Sopenharmony_ci qe_setbrg(ut_info->uf_info.rx_clock, 20000000, 1); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci gumr = ioread32be(&priv->uf_regs->gumr); 1328c2ecf20Sopenharmony_ci gumr |= (UCC_FAST_GUMR_LOOPBACK | UCC_FAST_GUMR_CDS | 1338c2ecf20Sopenharmony_ci UCC_FAST_GUMR_TCI); 1348c2ecf20Sopenharmony_ci gumr &= ~(UCC_FAST_GUMR_CTSP | UCC_FAST_GUMR_RSYN); 1358c2ecf20Sopenharmony_ci iowrite32be(gumr, &priv->uf_regs->gumr); 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* Initialize SI */ 1398c2ecf20Sopenharmony_ci if (priv->tsa) 1408c2ecf20Sopenharmony_ci ucc_tdm_init(priv->utdm, priv->ut_info); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* Write to QE CECR, UCCx channel to Stop Transmission */ 1438c2ecf20Sopenharmony_ci cecr_subblock = ucc_fast_get_qe_cr_subblock(uf_info->ucc_num); 1448c2ecf20Sopenharmony_ci ret = qe_issue_cmd(QE_STOP_TX, cecr_subblock, 1458c2ecf20Sopenharmony_ci QE_CR_PROTOCOL_UNSPECIFIED, 0); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* Set UPSMR normal mode (need fixed)*/ 1488c2ecf20Sopenharmony_ci iowrite32be(0, &priv->uf_regs->upsmr); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* hdlc_bus mode */ 1518c2ecf20Sopenharmony_ci if (priv->hdlc_bus) { 1528c2ecf20Sopenharmony_ci u32 upsmr; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci dev_info(priv->dev, "HDLC bus Mode\n"); 1558c2ecf20Sopenharmony_ci upsmr = ioread32be(&priv->uf_regs->upsmr); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* bus mode and retransmit enable, with collision window 1588c2ecf20Sopenharmony_ci * set to 8 bytes 1598c2ecf20Sopenharmony_ci */ 1608c2ecf20Sopenharmony_ci upsmr |= UCC_HDLC_UPSMR_RTE | UCC_HDLC_UPSMR_BUS | 1618c2ecf20Sopenharmony_ci UCC_HDLC_UPSMR_CW8; 1628c2ecf20Sopenharmony_ci iowrite32be(upsmr, &priv->uf_regs->upsmr); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci /* explicitly disable CDS & CTSP */ 1658c2ecf20Sopenharmony_ci gumr = ioread32be(&priv->uf_regs->gumr); 1668c2ecf20Sopenharmony_ci gumr &= ~(UCC_FAST_GUMR_CDS | UCC_FAST_GUMR_CTSP); 1678c2ecf20Sopenharmony_ci /* set automatic sync to explicitly ignore CD signal */ 1688c2ecf20Sopenharmony_ci gumr |= UCC_FAST_GUMR_SYNL_AUTO; 1698c2ecf20Sopenharmony_ci iowrite32be(gumr, &priv->uf_regs->gumr); 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci priv->rx_ring_size = RX_BD_RING_LEN; 1738c2ecf20Sopenharmony_ci priv->tx_ring_size = TX_BD_RING_LEN; 1748c2ecf20Sopenharmony_ci /* Alloc Rx BD */ 1758c2ecf20Sopenharmony_ci priv->rx_bd_base = dma_alloc_coherent(priv->dev, 1768c2ecf20Sopenharmony_ci RX_BD_RING_LEN * sizeof(struct qe_bd), 1778c2ecf20Sopenharmony_ci &priv->dma_rx_bd, GFP_KERNEL); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (!priv->rx_bd_base) { 1808c2ecf20Sopenharmony_ci dev_err(priv->dev, "Cannot allocate MURAM memory for RxBDs\n"); 1818c2ecf20Sopenharmony_ci ret = -ENOMEM; 1828c2ecf20Sopenharmony_ci goto free_uccf; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* Alloc Tx BD */ 1868c2ecf20Sopenharmony_ci priv->tx_bd_base = dma_alloc_coherent(priv->dev, 1878c2ecf20Sopenharmony_ci TX_BD_RING_LEN * sizeof(struct qe_bd), 1888c2ecf20Sopenharmony_ci &priv->dma_tx_bd, GFP_KERNEL); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (!priv->tx_bd_base) { 1918c2ecf20Sopenharmony_ci dev_err(priv->dev, "Cannot allocate MURAM memory for TxBDs\n"); 1928c2ecf20Sopenharmony_ci ret = -ENOMEM; 1938c2ecf20Sopenharmony_ci goto free_rx_bd; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* Alloc parameter ram for ucc hdlc */ 1978c2ecf20Sopenharmony_ci priv->ucc_pram_offset = qe_muram_alloc(sizeof(struct ucc_hdlc_param), 1988c2ecf20Sopenharmony_ci ALIGNMENT_OF_UCC_HDLC_PRAM); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (priv->ucc_pram_offset < 0) { 2018c2ecf20Sopenharmony_ci dev_err(priv->dev, "Can not allocate MURAM for hdlc parameter.\n"); 2028c2ecf20Sopenharmony_ci ret = -ENOMEM; 2038c2ecf20Sopenharmony_ci goto free_tx_bd; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci priv->rx_skbuff = kcalloc(priv->rx_ring_size, 2078c2ecf20Sopenharmony_ci sizeof(*priv->rx_skbuff), 2088c2ecf20Sopenharmony_ci GFP_KERNEL); 2098c2ecf20Sopenharmony_ci if (!priv->rx_skbuff) { 2108c2ecf20Sopenharmony_ci ret = -ENOMEM; 2118c2ecf20Sopenharmony_ci goto free_ucc_pram; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci priv->tx_skbuff = kcalloc(priv->tx_ring_size, 2158c2ecf20Sopenharmony_ci sizeof(*priv->tx_skbuff), 2168c2ecf20Sopenharmony_ci GFP_KERNEL); 2178c2ecf20Sopenharmony_ci if (!priv->tx_skbuff) { 2188c2ecf20Sopenharmony_ci ret = -ENOMEM; 2198c2ecf20Sopenharmony_ci goto free_rx_skbuff; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci priv->skb_curtx = 0; 2238c2ecf20Sopenharmony_ci priv->skb_dirtytx = 0; 2248c2ecf20Sopenharmony_ci priv->curtx_bd = priv->tx_bd_base; 2258c2ecf20Sopenharmony_ci priv->dirty_tx = priv->tx_bd_base; 2268c2ecf20Sopenharmony_ci priv->currx_bd = priv->rx_bd_base; 2278c2ecf20Sopenharmony_ci priv->currx_bdnum = 0; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci /* init parameter base */ 2308c2ecf20Sopenharmony_ci cecr_subblock = ucc_fast_get_qe_cr_subblock(uf_info->ucc_num); 2318c2ecf20Sopenharmony_ci ret = qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, cecr_subblock, 2328c2ecf20Sopenharmony_ci QE_CR_PROTOCOL_UNSPECIFIED, priv->ucc_pram_offset); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci priv->ucc_pram = (struct ucc_hdlc_param __iomem *) 2358c2ecf20Sopenharmony_ci qe_muram_addr(priv->ucc_pram_offset); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* Zero out parameter ram */ 2388c2ecf20Sopenharmony_ci memset_io(priv->ucc_pram, 0, sizeof(struct ucc_hdlc_param)); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* Alloc riptr, tiptr */ 2418c2ecf20Sopenharmony_ci riptr = qe_muram_alloc(32, 32); 2428c2ecf20Sopenharmony_ci if (riptr < 0) { 2438c2ecf20Sopenharmony_ci dev_err(priv->dev, "Cannot allocate MURAM mem for Receive internal temp data pointer\n"); 2448c2ecf20Sopenharmony_ci ret = -ENOMEM; 2458c2ecf20Sopenharmony_ci goto free_tx_skbuff; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci tiptr = qe_muram_alloc(32, 32); 2498c2ecf20Sopenharmony_ci if (tiptr < 0) { 2508c2ecf20Sopenharmony_ci dev_err(priv->dev, "Cannot allocate MURAM mem for Transmit internal temp data pointer\n"); 2518c2ecf20Sopenharmony_ci ret = -ENOMEM; 2528c2ecf20Sopenharmony_ci goto free_riptr; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci if (riptr != (u16)riptr || tiptr != (u16)tiptr) { 2558c2ecf20Sopenharmony_ci dev_err(priv->dev, "MURAM allocation out of addressable range\n"); 2568c2ecf20Sopenharmony_ci ret = -ENOMEM; 2578c2ecf20Sopenharmony_ci goto free_tiptr; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* Set RIPTR, TIPTR */ 2618c2ecf20Sopenharmony_ci iowrite16be(riptr, &priv->ucc_pram->riptr); 2628c2ecf20Sopenharmony_ci iowrite16be(tiptr, &priv->ucc_pram->tiptr); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci /* Set MRBLR */ 2658c2ecf20Sopenharmony_ci iowrite16be(MAX_RX_BUF_LENGTH, &priv->ucc_pram->mrblr); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci /* Set RBASE, TBASE */ 2688c2ecf20Sopenharmony_ci iowrite32be(priv->dma_rx_bd, &priv->ucc_pram->rbase); 2698c2ecf20Sopenharmony_ci iowrite32be(priv->dma_tx_bd, &priv->ucc_pram->tbase); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* Set RSTATE, TSTATE */ 2728c2ecf20Sopenharmony_ci iowrite32be(BMR_GBL | BMR_BIG_ENDIAN, &priv->ucc_pram->rstate); 2738c2ecf20Sopenharmony_ci iowrite32be(BMR_GBL | BMR_BIG_ENDIAN, &priv->ucc_pram->tstate); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci /* Set C_MASK, C_PRES for 16bit CRC */ 2768c2ecf20Sopenharmony_ci iowrite32be(CRC_16BIT_MASK, &priv->ucc_pram->c_mask); 2778c2ecf20Sopenharmony_ci iowrite32be(CRC_16BIT_PRES, &priv->ucc_pram->c_pres); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci iowrite16be(MAX_FRAME_LENGTH, &priv->ucc_pram->mflr); 2808c2ecf20Sopenharmony_ci iowrite16be(DEFAULT_RFTHR, &priv->ucc_pram->rfthr); 2818c2ecf20Sopenharmony_ci iowrite16be(DEFAULT_RFTHR, &priv->ucc_pram->rfcnt); 2828c2ecf20Sopenharmony_ci iowrite16be(priv->hmask, &priv->ucc_pram->hmask); 2838c2ecf20Sopenharmony_ci iowrite16be(DEFAULT_HDLC_ADDR, &priv->ucc_pram->haddr1); 2848c2ecf20Sopenharmony_ci iowrite16be(DEFAULT_HDLC_ADDR, &priv->ucc_pram->haddr2); 2858c2ecf20Sopenharmony_ci iowrite16be(DEFAULT_HDLC_ADDR, &priv->ucc_pram->haddr3); 2868c2ecf20Sopenharmony_ci iowrite16be(DEFAULT_HDLC_ADDR, &priv->ucc_pram->haddr4); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* Get BD buffer */ 2898c2ecf20Sopenharmony_ci bd_buffer = dma_alloc_coherent(priv->dev, 2908c2ecf20Sopenharmony_ci (RX_BD_RING_LEN + TX_BD_RING_LEN) * MAX_RX_BUF_LENGTH, 2918c2ecf20Sopenharmony_ci &bd_dma_addr, GFP_KERNEL); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (!bd_buffer) { 2948c2ecf20Sopenharmony_ci dev_err(priv->dev, "Could not allocate buffer descriptors\n"); 2958c2ecf20Sopenharmony_ci ret = -ENOMEM; 2968c2ecf20Sopenharmony_ci goto free_tiptr; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci priv->rx_buffer = bd_buffer; 3008c2ecf20Sopenharmony_ci priv->tx_buffer = bd_buffer + RX_BD_RING_LEN * MAX_RX_BUF_LENGTH; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci priv->dma_rx_addr = bd_dma_addr; 3038c2ecf20Sopenharmony_ci priv->dma_tx_addr = bd_dma_addr + RX_BD_RING_LEN * MAX_RX_BUF_LENGTH; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci for (i = 0; i < RX_BD_RING_LEN; i++) { 3068c2ecf20Sopenharmony_ci if (i < (RX_BD_RING_LEN - 1)) 3078c2ecf20Sopenharmony_ci bd_status = R_E_S | R_I_S; 3088c2ecf20Sopenharmony_ci else 3098c2ecf20Sopenharmony_ci bd_status = R_E_S | R_I_S | R_W_S; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci iowrite16be(bd_status, &priv->rx_bd_base[i].status); 3128c2ecf20Sopenharmony_ci iowrite32be(priv->dma_rx_addr + i * MAX_RX_BUF_LENGTH, 3138c2ecf20Sopenharmony_ci &priv->rx_bd_base[i].buf); 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci for (i = 0; i < TX_BD_RING_LEN; i++) { 3178c2ecf20Sopenharmony_ci if (i < (TX_BD_RING_LEN - 1)) 3188c2ecf20Sopenharmony_ci bd_status = T_I_S | T_TC_S; 3198c2ecf20Sopenharmony_ci else 3208c2ecf20Sopenharmony_ci bd_status = T_I_S | T_TC_S | T_W_S; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci iowrite16be(bd_status, &priv->tx_bd_base[i].status); 3238c2ecf20Sopenharmony_ci iowrite32be(priv->dma_tx_addr + i * MAX_RX_BUF_LENGTH, 3248c2ecf20Sopenharmony_ci &priv->tx_bd_base[i].buf); 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci return 0; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cifree_tiptr: 3308c2ecf20Sopenharmony_ci qe_muram_free(tiptr); 3318c2ecf20Sopenharmony_cifree_riptr: 3328c2ecf20Sopenharmony_ci qe_muram_free(riptr); 3338c2ecf20Sopenharmony_cifree_tx_skbuff: 3348c2ecf20Sopenharmony_ci kfree(priv->tx_skbuff); 3358c2ecf20Sopenharmony_cifree_rx_skbuff: 3368c2ecf20Sopenharmony_ci kfree(priv->rx_skbuff); 3378c2ecf20Sopenharmony_cifree_ucc_pram: 3388c2ecf20Sopenharmony_ci qe_muram_free(priv->ucc_pram_offset); 3398c2ecf20Sopenharmony_cifree_tx_bd: 3408c2ecf20Sopenharmony_ci dma_free_coherent(priv->dev, 3418c2ecf20Sopenharmony_ci TX_BD_RING_LEN * sizeof(struct qe_bd), 3428c2ecf20Sopenharmony_ci priv->tx_bd_base, priv->dma_tx_bd); 3438c2ecf20Sopenharmony_cifree_rx_bd: 3448c2ecf20Sopenharmony_ci dma_free_coherent(priv->dev, 3458c2ecf20Sopenharmony_ci RX_BD_RING_LEN * sizeof(struct qe_bd), 3468c2ecf20Sopenharmony_ci priv->rx_bd_base, priv->dma_rx_bd); 3478c2ecf20Sopenharmony_cifree_uccf: 3488c2ecf20Sopenharmony_ci ucc_fast_free(priv->uccf); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci return ret; 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic netdev_tx_t ucc_hdlc_tx(struct sk_buff *skb, struct net_device *dev) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci hdlc_device *hdlc = dev_to_hdlc(dev); 3568c2ecf20Sopenharmony_ci struct ucc_hdlc_private *priv = (struct ucc_hdlc_private *)hdlc->priv; 3578c2ecf20Sopenharmony_ci struct qe_bd __iomem *bd; 3588c2ecf20Sopenharmony_ci u16 bd_status; 3598c2ecf20Sopenharmony_ci unsigned long flags; 3608c2ecf20Sopenharmony_ci u16 *proto_head; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci switch (dev->type) { 3638c2ecf20Sopenharmony_ci case ARPHRD_RAWHDLC: 3648c2ecf20Sopenharmony_ci if (skb_headroom(skb) < HDLC_HEAD_LEN) { 3658c2ecf20Sopenharmony_ci dev->stats.tx_dropped++; 3668c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 3678c2ecf20Sopenharmony_ci netdev_err(dev, "No enough space for hdlc head\n"); 3688c2ecf20Sopenharmony_ci return -ENOMEM; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci skb_push(skb, HDLC_HEAD_LEN); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci proto_head = (u16 *)skb->data; 3748c2ecf20Sopenharmony_ci *proto_head = htons(DEFAULT_HDLC_HEAD); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci dev->stats.tx_bytes += skb->len; 3778c2ecf20Sopenharmony_ci break; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci case ARPHRD_PPP: 3808c2ecf20Sopenharmony_ci proto_head = (u16 *)skb->data; 3818c2ecf20Sopenharmony_ci if (*proto_head != htons(DEFAULT_PPP_HEAD)) { 3828c2ecf20Sopenharmony_ci dev->stats.tx_dropped++; 3838c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 3848c2ecf20Sopenharmony_ci netdev_err(dev, "Wrong ppp header\n"); 3858c2ecf20Sopenharmony_ci return -ENOMEM; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci dev->stats.tx_bytes += skb->len; 3898c2ecf20Sopenharmony_ci break; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci case ARPHRD_ETHER: 3928c2ecf20Sopenharmony_ci dev->stats.tx_bytes += skb->len; 3938c2ecf20Sopenharmony_ci break; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci default: 3968c2ecf20Sopenharmony_ci dev->stats.tx_dropped++; 3978c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 3988c2ecf20Sopenharmony_ci return -ENOMEM; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci netdev_sent_queue(dev, skb->len); 4018c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->lock, flags); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci /* Start from the next BD that should be filled */ 4048c2ecf20Sopenharmony_ci bd = priv->curtx_bd; 4058c2ecf20Sopenharmony_ci bd_status = ioread16be(&bd->status); 4068c2ecf20Sopenharmony_ci /* Save the skb pointer so we can free it later */ 4078c2ecf20Sopenharmony_ci priv->tx_skbuff[priv->skb_curtx] = skb; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci /* Update the current skb pointer (wrapping if this was the last) */ 4108c2ecf20Sopenharmony_ci priv->skb_curtx = 4118c2ecf20Sopenharmony_ci (priv->skb_curtx + 1) & TX_RING_MOD_MASK(TX_BD_RING_LEN); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci /* copy skb data to tx buffer for sdma processing */ 4148c2ecf20Sopenharmony_ci memcpy(priv->tx_buffer + (be32_to_cpu(bd->buf) - priv->dma_tx_addr), 4158c2ecf20Sopenharmony_ci skb->data, skb->len); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci /* set bd status and length */ 4188c2ecf20Sopenharmony_ci bd_status = (bd_status & T_W_S) | T_R_S | T_I_S | T_L_S | T_TC_S; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci iowrite16be(skb->len, &bd->length); 4218c2ecf20Sopenharmony_ci iowrite16be(bd_status, &bd->status); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci /* Move to next BD in the ring */ 4248c2ecf20Sopenharmony_ci if (!(bd_status & T_W_S)) 4258c2ecf20Sopenharmony_ci bd += 1; 4268c2ecf20Sopenharmony_ci else 4278c2ecf20Sopenharmony_ci bd = priv->tx_bd_base; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci if (bd == priv->dirty_tx) { 4308c2ecf20Sopenharmony_ci if (!netif_queue_stopped(dev)) 4318c2ecf20Sopenharmony_ci netif_stop_queue(dev); 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci priv->curtx_bd = bd; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->lock, flags); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cistatic int hdlc_tx_restart(struct ucc_hdlc_private *priv) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci u32 cecr_subblock; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci cecr_subblock = 4468c2ecf20Sopenharmony_ci ucc_fast_get_qe_cr_subblock(priv->ut_info->uf_info.ucc_num); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci qe_issue_cmd(QE_RESTART_TX, cecr_subblock, 4498c2ecf20Sopenharmony_ci QE_CR_PROTOCOL_UNSPECIFIED, 0); 4508c2ecf20Sopenharmony_ci return 0; 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cistatic int hdlc_tx_done(struct ucc_hdlc_private *priv) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci /* Start from the next BD that should be filled */ 4568c2ecf20Sopenharmony_ci struct net_device *dev = priv->ndev; 4578c2ecf20Sopenharmony_ci unsigned int bytes_sent = 0; 4588c2ecf20Sopenharmony_ci int howmany = 0; 4598c2ecf20Sopenharmony_ci struct qe_bd *bd; /* BD pointer */ 4608c2ecf20Sopenharmony_ci u16 bd_status; 4618c2ecf20Sopenharmony_ci int tx_restart = 0; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci bd = priv->dirty_tx; 4648c2ecf20Sopenharmony_ci bd_status = ioread16be(&bd->status); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci /* Normal processing. */ 4678c2ecf20Sopenharmony_ci while ((bd_status & T_R_S) == 0) { 4688c2ecf20Sopenharmony_ci struct sk_buff *skb; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if (bd_status & T_UN_S) { /* Underrun */ 4718c2ecf20Sopenharmony_ci dev->stats.tx_fifo_errors++; 4728c2ecf20Sopenharmony_ci tx_restart = 1; 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci if (bd_status & T_CT_S) { /* Carrier lost */ 4758c2ecf20Sopenharmony_ci dev->stats.tx_carrier_errors++; 4768c2ecf20Sopenharmony_ci tx_restart = 1; 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci /* BD contains already transmitted buffer. */ 4808c2ecf20Sopenharmony_ci /* Handle the transmitted buffer and release */ 4818c2ecf20Sopenharmony_ci /* the BD to be used with the current frame */ 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci skb = priv->tx_skbuff[priv->skb_dirtytx]; 4848c2ecf20Sopenharmony_ci if (!skb) 4858c2ecf20Sopenharmony_ci break; 4868c2ecf20Sopenharmony_ci howmany++; 4878c2ecf20Sopenharmony_ci bytes_sent += skb->len; 4888c2ecf20Sopenharmony_ci dev->stats.tx_packets++; 4898c2ecf20Sopenharmony_ci memset(priv->tx_buffer + 4908c2ecf20Sopenharmony_ci (be32_to_cpu(bd->buf) - priv->dma_tx_addr), 4918c2ecf20Sopenharmony_ci 0, skb->len); 4928c2ecf20Sopenharmony_ci dev_consume_skb_irq(skb); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci priv->tx_skbuff[priv->skb_dirtytx] = NULL; 4958c2ecf20Sopenharmony_ci priv->skb_dirtytx = 4968c2ecf20Sopenharmony_ci (priv->skb_dirtytx + 4978c2ecf20Sopenharmony_ci 1) & TX_RING_MOD_MASK(TX_BD_RING_LEN); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci /* We freed a buffer, so now we can restart transmission */ 5008c2ecf20Sopenharmony_ci if (netif_queue_stopped(dev)) 5018c2ecf20Sopenharmony_ci netif_wake_queue(dev); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci /* Advance the confirmation BD pointer */ 5048c2ecf20Sopenharmony_ci if (!(bd_status & T_W_S)) 5058c2ecf20Sopenharmony_ci bd += 1; 5068c2ecf20Sopenharmony_ci else 5078c2ecf20Sopenharmony_ci bd = priv->tx_bd_base; 5088c2ecf20Sopenharmony_ci bd_status = ioread16be(&bd->status); 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci priv->dirty_tx = bd; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci if (tx_restart) 5138c2ecf20Sopenharmony_ci hdlc_tx_restart(priv); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci netdev_completed_queue(dev, howmany, bytes_sent); 5168c2ecf20Sopenharmony_ci return 0; 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cistatic int hdlc_rx_done(struct ucc_hdlc_private *priv, int rx_work_limit) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci struct net_device *dev = priv->ndev; 5228c2ecf20Sopenharmony_ci struct sk_buff *skb = NULL; 5238c2ecf20Sopenharmony_ci hdlc_device *hdlc = dev_to_hdlc(dev); 5248c2ecf20Sopenharmony_ci struct qe_bd *bd; 5258c2ecf20Sopenharmony_ci u16 bd_status; 5268c2ecf20Sopenharmony_ci u16 length, howmany = 0; 5278c2ecf20Sopenharmony_ci u8 *bdbuffer; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci bd = priv->currx_bd; 5308c2ecf20Sopenharmony_ci bd_status = ioread16be(&bd->status); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci /* while there are received buffers and BD is full (~R_E) */ 5338c2ecf20Sopenharmony_ci while (!((bd_status & (R_E_S)) || (--rx_work_limit < 0))) { 5348c2ecf20Sopenharmony_ci if (bd_status & (RX_BD_ERRORS)) { 5358c2ecf20Sopenharmony_ci dev->stats.rx_errors++; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci if (bd_status & R_CD_S) 5388c2ecf20Sopenharmony_ci dev->stats.collisions++; 5398c2ecf20Sopenharmony_ci if (bd_status & R_OV_S) 5408c2ecf20Sopenharmony_ci dev->stats.rx_fifo_errors++; 5418c2ecf20Sopenharmony_ci if (bd_status & R_CR_S) 5428c2ecf20Sopenharmony_ci dev->stats.rx_crc_errors++; 5438c2ecf20Sopenharmony_ci if (bd_status & R_AB_S) 5448c2ecf20Sopenharmony_ci dev->stats.rx_over_errors++; 5458c2ecf20Sopenharmony_ci if (bd_status & R_NO_S) 5468c2ecf20Sopenharmony_ci dev->stats.rx_frame_errors++; 5478c2ecf20Sopenharmony_ci if (bd_status & R_LG_S) 5488c2ecf20Sopenharmony_ci dev->stats.rx_length_errors++; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci goto recycle; 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci bdbuffer = priv->rx_buffer + 5538c2ecf20Sopenharmony_ci (priv->currx_bdnum * MAX_RX_BUF_LENGTH); 5548c2ecf20Sopenharmony_ci length = ioread16be(&bd->length); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci switch (dev->type) { 5578c2ecf20Sopenharmony_ci case ARPHRD_RAWHDLC: 5588c2ecf20Sopenharmony_ci bdbuffer += HDLC_HEAD_LEN; 5598c2ecf20Sopenharmony_ci length -= (HDLC_HEAD_LEN + HDLC_CRC_SIZE); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci skb = dev_alloc_skb(length); 5628c2ecf20Sopenharmony_ci if (!skb) { 5638c2ecf20Sopenharmony_ci dev->stats.rx_dropped++; 5648c2ecf20Sopenharmony_ci return -ENOMEM; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci skb_put(skb, length); 5688c2ecf20Sopenharmony_ci skb->len = length; 5698c2ecf20Sopenharmony_ci skb->dev = dev; 5708c2ecf20Sopenharmony_ci memcpy(skb->data, bdbuffer, length); 5718c2ecf20Sopenharmony_ci break; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci case ARPHRD_PPP: 5748c2ecf20Sopenharmony_ci case ARPHRD_ETHER: 5758c2ecf20Sopenharmony_ci length -= HDLC_CRC_SIZE; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci skb = dev_alloc_skb(length); 5788c2ecf20Sopenharmony_ci if (!skb) { 5798c2ecf20Sopenharmony_ci dev->stats.rx_dropped++; 5808c2ecf20Sopenharmony_ci return -ENOMEM; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci skb_put(skb, length); 5848c2ecf20Sopenharmony_ci skb->len = length; 5858c2ecf20Sopenharmony_ci skb->dev = dev; 5868c2ecf20Sopenharmony_ci memcpy(skb->data, bdbuffer, length); 5878c2ecf20Sopenharmony_ci break; 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci dev->stats.rx_packets++; 5918c2ecf20Sopenharmony_ci dev->stats.rx_bytes += skb->len; 5928c2ecf20Sopenharmony_ci howmany++; 5938c2ecf20Sopenharmony_ci if (hdlc->proto) 5948c2ecf20Sopenharmony_ci skb->protocol = hdlc_type_trans(skb, dev); 5958c2ecf20Sopenharmony_ci netif_receive_skb(skb); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_cirecycle: 5988c2ecf20Sopenharmony_ci iowrite16be((bd_status & R_W_S) | R_E_S | R_I_S, &bd->status); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci /* update to point at the next bd */ 6018c2ecf20Sopenharmony_ci if (bd_status & R_W_S) { 6028c2ecf20Sopenharmony_ci priv->currx_bdnum = 0; 6038c2ecf20Sopenharmony_ci bd = priv->rx_bd_base; 6048c2ecf20Sopenharmony_ci } else { 6058c2ecf20Sopenharmony_ci if (priv->currx_bdnum < (RX_BD_RING_LEN - 1)) 6068c2ecf20Sopenharmony_ci priv->currx_bdnum += 1; 6078c2ecf20Sopenharmony_ci else 6088c2ecf20Sopenharmony_ci priv->currx_bdnum = RX_BD_RING_LEN - 1; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci bd += 1; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci bd_status = ioread16be(&bd->status); 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci priv->currx_bd = bd; 6178c2ecf20Sopenharmony_ci return howmany; 6188c2ecf20Sopenharmony_ci} 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_cistatic int ucc_hdlc_poll(struct napi_struct *napi, int budget) 6218c2ecf20Sopenharmony_ci{ 6228c2ecf20Sopenharmony_ci struct ucc_hdlc_private *priv = container_of(napi, 6238c2ecf20Sopenharmony_ci struct ucc_hdlc_private, 6248c2ecf20Sopenharmony_ci napi); 6258c2ecf20Sopenharmony_ci int howmany; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci /* Tx event processing */ 6288c2ecf20Sopenharmony_ci spin_lock(&priv->lock); 6298c2ecf20Sopenharmony_ci hdlc_tx_done(priv); 6308c2ecf20Sopenharmony_ci spin_unlock(&priv->lock); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci howmany = 0; 6338c2ecf20Sopenharmony_ci howmany += hdlc_rx_done(priv, budget - howmany); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci if (howmany < budget) { 6368c2ecf20Sopenharmony_ci napi_complete_done(napi, howmany); 6378c2ecf20Sopenharmony_ci qe_setbits_be32(priv->uccf->p_uccm, 6388c2ecf20Sopenharmony_ci (UCCE_HDLC_RX_EVENTS | UCCE_HDLC_TX_EVENTS) << 16); 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci return howmany; 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_cistatic irqreturn_t ucc_hdlc_irq_handler(int irq, void *dev_id) 6458c2ecf20Sopenharmony_ci{ 6468c2ecf20Sopenharmony_ci struct ucc_hdlc_private *priv = (struct ucc_hdlc_private *)dev_id; 6478c2ecf20Sopenharmony_ci struct net_device *dev = priv->ndev; 6488c2ecf20Sopenharmony_ci struct ucc_fast_private *uccf; 6498c2ecf20Sopenharmony_ci u32 ucce; 6508c2ecf20Sopenharmony_ci u32 uccm; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci uccf = priv->uccf; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci ucce = ioread32be(uccf->p_ucce); 6558c2ecf20Sopenharmony_ci uccm = ioread32be(uccf->p_uccm); 6568c2ecf20Sopenharmony_ci ucce &= uccm; 6578c2ecf20Sopenharmony_ci iowrite32be(ucce, uccf->p_ucce); 6588c2ecf20Sopenharmony_ci if (!ucce) 6598c2ecf20Sopenharmony_ci return IRQ_NONE; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci if ((ucce >> 16) & (UCCE_HDLC_RX_EVENTS | UCCE_HDLC_TX_EVENTS)) { 6628c2ecf20Sopenharmony_ci if (napi_schedule_prep(&priv->napi)) { 6638c2ecf20Sopenharmony_ci uccm &= ~((UCCE_HDLC_RX_EVENTS | UCCE_HDLC_TX_EVENTS) 6648c2ecf20Sopenharmony_ci << 16); 6658c2ecf20Sopenharmony_ci iowrite32be(uccm, uccf->p_uccm); 6668c2ecf20Sopenharmony_ci __napi_schedule(&priv->napi); 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci /* Errors and other events */ 6718c2ecf20Sopenharmony_ci if (ucce >> 16 & UCC_HDLC_UCCE_BSY) 6728c2ecf20Sopenharmony_ci dev->stats.rx_missed_errors++; 6738c2ecf20Sopenharmony_ci if (ucce >> 16 & UCC_HDLC_UCCE_TXE) 6748c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_cistatic int uhdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci const size_t size = sizeof(te1_settings); 6828c2ecf20Sopenharmony_ci te1_settings line; 6838c2ecf20Sopenharmony_ci struct ucc_hdlc_private *priv = netdev_priv(dev); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci if (cmd != SIOCWANDEV) 6868c2ecf20Sopenharmony_ci return hdlc_ioctl(dev, ifr, cmd); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci switch (ifr->ifr_settings.type) { 6898c2ecf20Sopenharmony_ci case IF_GET_IFACE: 6908c2ecf20Sopenharmony_ci ifr->ifr_settings.type = IF_IFACE_E1; 6918c2ecf20Sopenharmony_ci if (ifr->ifr_settings.size < size) { 6928c2ecf20Sopenharmony_ci ifr->ifr_settings.size = size; /* data size wanted */ 6938c2ecf20Sopenharmony_ci return -ENOBUFS; 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci memset(&line, 0, sizeof(line)); 6968c2ecf20Sopenharmony_ci line.clock_type = priv->clocking; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci if (copy_to_user(ifr->ifr_settings.ifs_ifsu.sync, &line, size)) 6998c2ecf20Sopenharmony_ci return -EFAULT; 7008c2ecf20Sopenharmony_ci return 0; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci default: 7038c2ecf20Sopenharmony_ci return hdlc_ioctl(dev, ifr, cmd); 7048c2ecf20Sopenharmony_ci } 7058c2ecf20Sopenharmony_ci} 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_cistatic int uhdlc_open(struct net_device *dev) 7088c2ecf20Sopenharmony_ci{ 7098c2ecf20Sopenharmony_ci u32 cecr_subblock; 7108c2ecf20Sopenharmony_ci hdlc_device *hdlc = dev_to_hdlc(dev); 7118c2ecf20Sopenharmony_ci struct ucc_hdlc_private *priv = hdlc->priv; 7128c2ecf20Sopenharmony_ci struct ucc_tdm *utdm = priv->utdm; 7138c2ecf20Sopenharmony_ci int rc = 0; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci if (priv->hdlc_busy != 1) { 7168c2ecf20Sopenharmony_ci if (request_irq(priv->ut_info->uf_info.irq, 7178c2ecf20Sopenharmony_ci ucc_hdlc_irq_handler, 0, "hdlc", priv)) 7188c2ecf20Sopenharmony_ci return -ENODEV; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci cecr_subblock = ucc_fast_get_qe_cr_subblock( 7218c2ecf20Sopenharmony_ci priv->ut_info->uf_info.ucc_num); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci qe_issue_cmd(QE_INIT_TX_RX, cecr_subblock, 7248c2ecf20Sopenharmony_ci QE_CR_PROTOCOL_UNSPECIFIED, 0); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci ucc_fast_enable(priv->uccf, COMM_DIR_RX | COMM_DIR_TX); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci /* Enable the TDM port */ 7298c2ecf20Sopenharmony_ci if (priv->tsa) 7308c2ecf20Sopenharmony_ci utdm->si_regs->siglmr1_h |= (0x1 << utdm->tdm_port); 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci priv->hdlc_busy = 1; 7338c2ecf20Sopenharmony_ci netif_device_attach(priv->ndev); 7348c2ecf20Sopenharmony_ci napi_enable(&priv->napi); 7358c2ecf20Sopenharmony_ci netdev_reset_queue(dev); 7368c2ecf20Sopenharmony_ci netif_start_queue(dev); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci rc = hdlc_open(dev); 7398c2ecf20Sopenharmony_ci if (rc) 7408c2ecf20Sopenharmony_ci uhdlc_close(dev); 7418c2ecf20Sopenharmony_ci } 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci return rc; 7448c2ecf20Sopenharmony_ci} 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_cistatic void uhdlc_memclean(struct ucc_hdlc_private *priv) 7478c2ecf20Sopenharmony_ci{ 7488c2ecf20Sopenharmony_ci qe_muram_free(ioread16be(&priv->ucc_pram->riptr)); 7498c2ecf20Sopenharmony_ci qe_muram_free(ioread16be(&priv->ucc_pram->tiptr)); 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci if (priv->rx_bd_base) { 7528c2ecf20Sopenharmony_ci dma_free_coherent(priv->dev, 7538c2ecf20Sopenharmony_ci RX_BD_RING_LEN * sizeof(struct qe_bd), 7548c2ecf20Sopenharmony_ci priv->rx_bd_base, priv->dma_rx_bd); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci priv->rx_bd_base = NULL; 7578c2ecf20Sopenharmony_ci priv->dma_rx_bd = 0; 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci if (priv->tx_bd_base) { 7618c2ecf20Sopenharmony_ci dma_free_coherent(priv->dev, 7628c2ecf20Sopenharmony_ci TX_BD_RING_LEN * sizeof(struct qe_bd), 7638c2ecf20Sopenharmony_ci priv->tx_bd_base, priv->dma_tx_bd); 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci priv->tx_bd_base = NULL; 7668c2ecf20Sopenharmony_ci priv->dma_tx_bd = 0; 7678c2ecf20Sopenharmony_ci } 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci if (priv->ucc_pram) { 7708c2ecf20Sopenharmony_ci qe_muram_free(priv->ucc_pram_offset); 7718c2ecf20Sopenharmony_ci priv->ucc_pram = NULL; 7728c2ecf20Sopenharmony_ci priv->ucc_pram_offset = 0; 7738c2ecf20Sopenharmony_ci } 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci kfree(priv->rx_skbuff); 7768c2ecf20Sopenharmony_ci priv->rx_skbuff = NULL; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci kfree(priv->tx_skbuff); 7798c2ecf20Sopenharmony_ci priv->tx_skbuff = NULL; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci if (priv->uf_regs) { 7828c2ecf20Sopenharmony_ci iounmap(priv->uf_regs); 7838c2ecf20Sopenharmony_ci priv->uf_regs = NULL; 7848c2ecf20Sopenharmony_ci } 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci if (priv->uccf) { 7878c2ecf20Sopenharmony_ci ucc_fast_free(priv->uccf); 7888c2ecf20Sopenharmony_ci priv->uccf = NULL; 7898c2ecf20Sopenharmony_ci } 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci if (priv->rx_buffer) { 7928c2ecf20Sopenharmony_ci dma_free_coherent(priv->dev, 7938c2ecf20Sopenharmony_ci RX_BD_RING_LEN * MAX_RX_BUF_LENGTH, 7948c2ecf20Sopenharmony_ci priv->rx_buffer, priv->dma_rx_addr); 7958c2ecf20Sopenharmony_ci priv->rx_buffer = NULL; 7968c2ecf20Sopenharmony_ci priv->dma_rx_addr = 0; 7978c2ecf20Sopenharmony_ci } 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci if (priv->tx_buffer) { 8008c2ecf20Sopenharmony_ci dma_free_coherent(priv->dev, 8018c2ecf20Sopenharmony_ci TX_BD_RING_LEN * MAX_RX_BUF_LENGTH, 8028c2ecf20Sopenharmony_ci priv->tx_buffer, priv->dma_tx_addr); 8038c2ecf20Sopenharmony_ci priv->tx_buffer = NULL; 8048c2ecf20Sopenharmony_ci priv->dma_tx_addr = 0; 8058c2ecf20Sopenharmony_ci } 8068c2ecf20Sopenharmony_ci} 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_cistatic int uhdlc_close(struct net_device *dev) 8098c2ecf20Sopenharmony_ci{ 8108c2ecf20Sopenharmony_ci struct ucc_hdlc_private *priv = dev_to_hdlc(dev)->priv; 8118c2ecf20Sopenharmony_ci struct ucc_tdm *utdm = priv->utdm; 8128c2ecf20Sopenharmony_ci u32 cecr_subblock; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci napi_disable(&priv->napi); 8158c2ecf20Sopenharmony_ci cecr_subblock = ucc_fast_get_qe_cr_subblock( 8168c2ecf20Sopenharmony_ci priv->ut_info->uf_info.ucc_num); 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci qe_issue_cmd(QE_GRACEFUL_STOP_TX, cecr_subblock, 8198c2ecf20Sopenharmony_ci (u8)QE_CR_PROTOCOL_UNSPECIFIED, 0); 8208c2ecf20Sopenharmony_ci qe_issue_cmd(QE_CLOSE_RX_BD, cecr_subblock, 8218c2ecf20Sopenharmony_ci (u8)QE_CR_PROTOCOL_UNSPECIFIED, 0); 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci if (priv->tsa) 8248c2ecf20Sopenharmony_ci utdm->si_regs->siglmr1_h &= ~(0x1 << utdm->tdm_port); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci ucc_fast_disable(priv->uccf, COMM_DIR_RX | COMM_DIR_TX); 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci free_irq(priv->ut_info->uf_info.irq, priv); 8298c2ecf20Sopenharmony_ci netif_stop_queue(dev); 8308c2ecf20Sopenharmony_ci netdev_reset_queue(dev); 8318c2ecf20Sopenharmony_ci priv->hdlc_busy = 0; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci hdlc_close(dev); 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci return 0; 8368c2ecf20Sopenharmony_ci} 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_cistatic int ucc_hdlc_attach(struct net_device *dev, unsigned short encoding, 8398c2ecf20Sopenharmony_ci unsigned short parity) 8408c2ecf20Sopenharmony_ci{ 8418c2ecf20Sopenharmony_ci struct ucc_hdlc_private *priv = dev_to_hdlc(dev)->priv; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci if (encoding != ENCODING_NRZ && 8448c2ecf20Sopenharmony_ci encoding != ENCODING_NRZI) 8458c2ecf20Sopenharmony_ci return -EINVAL; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci if (parity != PARITY_NONE && 8488c2ecf20Sopenharmony_ci parity != PARITY_CRC32_PR1_CCITT && 8498c2ecf20Sopenharmony_ci parity != PARITY_CRC16_PR0_CCITT && 8508c2ecf20Sopenharmony_ci parity != PARITY_CRC16_PR1_CCITT) 8518c2ecf20Sopenharmony_ci return -EINVAL; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci priv->encoding = encoding; 8548c2ecf20Sopenharmony_ci priv->parity = parity; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci return 0; 8578c2ecf20Sopenharmony_ci} 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 8608c2ecf20Sopenharmony_cistatic void store_clk_config(struct ucc_hdlc_private *priv) 8618c2ecf20Sopenharmony_ci{ 8628c2ecf20Sopenharmony_ci struct qe_mux *qe_mux_reg = &qe_immr->qmx; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci /* store si clk */ 8658c2ecf20Sopenharmony_ci priv->cmxsi1cr_h = ioread32be(&qe_mux_reg->cmxsi1cr_h); 8668c2ecf20Sopenharmony_ci priv->cmxsi1cr_l = ioread32be(&qe_mux_reg->cmxsi1cr_l); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci /* store si sync */ 8698c2ecf20Sopenharmony_ci priv->cmxsi1syr = ioread32be(&qe_mux_reg->cmxsi1syr); 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci /* store ucc clk */ 8728c2ecf20Sopenharmony_ci memcpy_fromio(priv->cmxucr, qe_mux_reg->cmxucr, 4 * sizeof(u32)); 8738c2ecf20Sopenharmony_ci} 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_cistatic void resume_clk_config(struct ucc_hdlc_private *priv) 8768c2ecf20Sopenharmony_ci{ 8778c2ecf20Sopenharmony_ci struct qe_mux *qe_mux_reg = &qe_immr->qmx; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci memcpy_toio(qe_mux_reg->cmxucr, priv->cmxucr, 4 * sizeof(u32)); 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci iowrite32be(priv->cmxsi1cr_h, &qe_mux_reg->cmxsi1cr_h); 8828c2ecf20Sopenharmony_ci iowrite32be(priv->cmxsi1cr_l, &qe_mux_reg->cmxsi1cr_l); 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci iowrite32be(priv->cmxsi1syr, &qe_mux_reg->cmxsi1syr); 8858c2ecf20Sopenharmony_ci} 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_cistatic int uhdlc_suspend(struct device *dev) 8888c2ecf20Sopenharmony_ci{ 8898c2ecf20Sopenharmony_ci struct ucc_hdlc_private *priv = dev_get_drvdata(dev); 8908c2ecf20Sopenharmony_ci struct ucc_fast __iomem *uf_regs; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci if (!priv) 8938c2ecf20Sopenharmony_ci return -EINVAL; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci if (!netif_running(priv->ndev)) 8968c2ecf20Sopenharmony_ci return 0; 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci netif_device_detach(priv->ndev); 8998c2ecf20Sopenharmony_ci napi_disable(&priv->napi); 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci uf_regs = priv->uf_regs; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci /* backup gumr guemr*/ 9048c2ecf20Sopenharmony_ci priv->gumr = ioread32be(&uf_regs->gumr); 9058c2ecf20Sopenharmony_ci priv->guemr = ioread8(&uf_regs->guemr); 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci priv->ucc_pram_bak = kmalloc(sizeof(*priv->ucc_pram_bak), 9088c2ecf20Sopenharmony_ci GFP_KERNEL); 9098c2ecf20Sopenharmony_ci if (!priv->ucc_pram_bak) 9108c2ecf20Sopenharmony_ci return -ENOMEM; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci /* backup HDLC parameter */ 9138c2ecf20Sopenharmony_ci memcpy_fromio(priv->ucc_pram_bak, priv->ucc_pram, 9148c2ecf20Sopenharmony_ci sizeof(struct ucc_hdlc_param)); 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci /* store the clk configuration */ 9178c2ecf20Sopenharmony_ci store_clk_config(priv); 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci /* save power */ 9208c2ecf20Sopenharmony_ci ucc_fast_disable(priv->uccf, COMM_DIR_RX | COMM_DIR_TX); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci return 0; 9238c2ecf20Sopenharmony_ci} 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_cistatic int uhdlc_resume(struct device *dev) 9268c2ecf20Sopenharmony_ci{ 9278c2ecf20Sopenharmony_ci struct ucc_hdlc_private *priv = dev_get_drvdata(dev); 9288c2ecf20Sopenharmony_ci struct ucc_tdm *utdm; 9298c2ecf20Sopenharmony_ci struct ucc_tdm_info *ut_info; 9308c2ecf20Sopenharmony_ci struct ucc_fast __iomem *uf_regs; 9318c2ecf20Sopenharmony_ci struct ucc_fast_private *uccf; 9328c2ecf20Sopenharmony_ci struct ucc_fast_info *uf_info; 9338c2ecf20Sopenharmony_ci int i; 9348c2ecf20Sopenharmony_ci u32 cecr_subblock; 9358c2ecf20Sopenharmony_ci u16 bd_status; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci if (!priv) 9388c2ecf20Sopenharmony_ci return -EINVAL; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci if (!netif_running(priv->ndev)) 9418c2ecf20Sopenharmony_ci return 0; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci utdm = priv->utdm; 9448c2ecf20Sopenharmony_ci ut_info = priv->ut_info; 9458c2ecf20Sopenharmony_ci uf_info = &ut_info->uf_info; 9468c2ecf20Sopenharmony_ci uf_regs = priv->uf_regs; 9478c2ecf20Sopenharmony_ci uccf = priv->uccf; 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci /* restore gumr guemr */ 9508c2ecf20Sopenharmony_ci iowrite8(priv->guemr, &uf_regs->guemr); 9518c2ecf20Sopenharmony_ci iowrite32be(priv->gumr, &uf_regs->gumr); 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci /* Set Virtual Fifo registers */ 9548c2ecf20Sopenharmony_ci iowrite16be(uf_info->urfs, &uf_regs->urfs); 9558c2ecf20Sopenharmony_ci iowrite16be(uf_info->urfet, &uf_regs->urfet); 9568c2ecf20Sopenharmony_ci iowrite16be(uf_info->urfset, &uf_regs->urfset); 9578c2ecf20Sopenharmony_ci iowrite16be(uf_info->utfs, &uf_regs->utfs); 9588c2ecf20Sopenharmony_ci iowrite16be(uf_info->utfet, &uf_regs->utfet); 9598c2ecf20Sopenharmony_ci iowrite16be(uf_info->utftt, &uf_regs->utftt); 9608c2ecf20Sopenharmony_ci /* utfb, urfb are offsets from MURAM base */ 9618c2ecf20Sopenharmony_ci iowrite32be(uccf->ucc_fast_tx_virtual_fifo_base_offset, &uf_regs->utfb); 9628c2ecf20Sopenharmony_ci iowrite32be(uccf->ucc_fast_rx_virtual_fifo_base_offset, &uf_regs->urfb); 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci /* Rx Tx and sync clock routing */ 9658c2ecf20Sopenharmony_ci resume_clk_config(priv); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci iowrite32be(uf_info->uccm_mask, &uf_regs->uccm); 9688c2ecf20Sopenharmony_ci iowrite32be(0xffffffff, &uf_regs->ucce); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci ucc_fast_disable(priv->uccf, COMM_DIR_RX | COMM_DIR_TX); 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci /* rebuild SIRAM */ 9738c2ecf20Sopenharmony_ci if (priv->tsa) 9748c2ecf20Sopenharmony_ci ucc_tdm_init(priv->utdm, priv->ut_info); 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci /* Write to QE CECR, UCCx channel to Stop Transmission */ 9778c2ecf20Sopenharmony_ci cecr_subblock = ucc_fast_get_qe_cr_subblock(uf_info->ucc_num); 9788c2ecf20Sopenharmony_ci qe_issue_cmd(QE_STOP_TX, cecr_subblock, 9798c2ecf20Sopenharmony_ci (u8)QE_CR_PROTOCOL_UNSPECIFIED, 0); 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci /* Set UPSMR normal mode */ 9828c2ecf20Sopenharmony_ci iowrite32be(0, &uf_regs->upsmr); 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci /* init parameter base */ 9858c2ecf20Sopenharmony_ci cecr_subblock = ucc_fast_get_qe_cr_subblock(uf_info->ucc_num); 9868c2ecf20Sopenharmony_ci qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, cecr_subblock, 9878c2ecf20Sopenharmony_ci QE_CR_PROTOCOL_UNSPECIFIED, priv->ucc_pram_offset); 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci priv->ucc_pram = (struct ucc_hdlc_param __iomem *) 9908c2ecf20Sopenharmony_ci qe_muram_addr(priv->ucc_pram_offset); 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci /* restore ucc parameter */ 9938c2ecf20Sopenharmony_ci memcpy_toio(priv->ucc_pram, priv->ucc_pram_bak, 9948c2ecf20Sopenharmony_ci sizeof(struct ucc_hdlc_param)); 9958c2ecf20Sopenharmony_ci kfree(priv->ucc_pram_bak); 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci /* rebuild BD entry */ 9988c2ecf20Sopenharmony_ci for (i = 0; i < RX_BD_RING_LEN; i++) { 9998c2ecf20Sopenharmony_ci if (i < (RX_BD_RING_LEN - 1)) 10008c2ecf20Sopenharmony_ci bd_status = R_E_S | R_I_S; 10018c2ecf20Sopenharmony_ci else 10028c2ecf20Sopenharmony_ci bd_status = R_E_S | R_I_S | R_W_S; 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci iowrite16be(bd_status, &priv->rx_bd_base[i].status); 10058c2ecf20Sopenharmony_ci iowrite32be(priv->dma_rx_addr + i * MAX_RX_BUF_LENGTH, 10068c2ecf20Sopenharmony_ci &priv->rx_bd_base[i].buf); 10078c2ecf20Sopenharmony_ci } 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci for (i = 0; i < TX_BD_RING_LEN; i++) { 10108c2ecf20Sopenharmony_ci if (i < (TX_BD_RING_LEN - 1)) 10118c2ecf20Sopenharmony_ci bd_status = T_I_S | T_TC_S; 10128c2ecf20Sopenharmony_ci else 10138c2ecf20Sopenharmony_ci bd_status = T_I_S | T_TC_S | T_W_S; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci iowrite16be(bd_status, &priv->tx_bd_base[i].status); 10168c2ecf20Sopenharmony_ci iowrite32be(priv->dma_tx_addr + i * MAX_RX_BUF_LENGTH, 10178c2ecf20Sopenharmony_ci &priv->tx_bd_base[i].buf); 10188c2ecf20Sopenharmony_ci } 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci /* if hdlc is busy enable TX and RX */ 10218c2ecf20Sopenharmony_ci if (priv->hdlc_busy == 1) { 10228c2ecf20Sopenharmony_ci cecr_subblock = ucc_fast_get_qe_cr_subblock( 10238c2ecf20Sopenharmony_ci priv->ut_info->uf_info.ucc_num); 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci qe_issue_cmd(QE_INIT_TX_RX, cecr_subblock, 10268c2ecf20Sopenharmony_ci (u8)QE_CR_PROTOCOL_UNSPECIFIED, 0); 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci ucc_fast_enable(priv->uccf, COMM_DIR_RX | COMM_DIR_TX); 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci /* Enable the TDM port */ 10318c2ecf20Sopenharmony_ci if (priv->tsa) 10328c2ecf20Sopenharmony_ci utdm->si_regs->siglmr1_h |= (0x1 << utdm->tdm_port); 10338c2ecf20Sopenharmony_ci } 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci napi_enable(&priv->napi); 10368c2ecf20Sopenharmony_ci netif_device_attach(priv->ndev); 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci return 0; 10398c2ecf20Sopenharmony_ci} 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_cistatic const struct dev_pm_ops uhdlc_pm_ops = { 10428c2ecf20Sopenharmony_ci .suspend = uhdlc_suspend, 10438c2ecf20Sopenharmony_ci .resume = uhdlc_resume, 10448c2ecf20Sopenharmony_ci .freeze = uhdlc_suspend, 10458c2ecf20Sopenharmony_ci .thaw = uhdlc_resume, 10468c2ecf20Sopenharmony_ci}; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci#define HDLC_PM_OPS (&uhdlc_pm_ops) 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci#else 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci#define HDLC_PM_OPS NULL 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci#endif 10558c2ecf20Sopenharmony_cistatic void uhdlc_tx_timeout(struct net_device *ndev, unsigned int txqueue) 10568c2ecf20Sopenharmony_ci{ 10578c2ecf20Sopenharmony_ci netdev_err(ndev, "%s\n", __func__); 10588c2ecf20Sopenharmony_ci} 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_cistatic const struct net_device_ops uhdlc_ops = { 10618c2ecf20Sopenharmony_ci .ndo_open = uhdlc_open, 10628c2ecf20Sopenharmony_ci .ndo_stop = uhdlc_close, 10638c2ecf20Sopenharmony_ci .ndo_start_xmit = hdlc_start_xmit, 10648c2ecf20Sopenharmony_ci .ndo_do_ioctl = uhdlc_ioctl, 10658c2ecf20Sopenharmony_ci .ndo_tx_timeout = uhdlc_tx_timeout, 10668c2ecf20Sopenharmony_ci}; 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_cistatic int hdlc_map_iomem(char *name, int init_flag, void __iomem **ptr) 10698c2ecf20Sopenharmony_ci{ 10708c2ecf20Sopenharmony_ci struct device_node *np; 10718c2ecf20Sopenharmony_ci struct platform_device *pdev; 10728c2ecf20Sopenharmony_ci struct resource *res; 10738c2ecf20Sopenharmony_ci static int siram_init_flag; 10748c2ecf20Sopenharmony_ci int ret = 0; 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, name); 10778c2ecf20Sopenharmony_ci if (!np) 10788c2ecf20Sopenharmony_ci return -EINVAL; 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci pdev = of_find_device_by_node(np); 10818c2ecf20Sopenharmony_ci if (!pdev) { 10828c2ecf20Sopenharmony_ci pr_err("%pOFn: failed to lookup pdev\n", np); 10838c2ecf20Sopenharmony_ci of_node_put(np); 10848c2ecf20Sopenharmony_ci return -EINVAL; 10858c2ecf20Sopenharmony_ci } 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci of_node_put(np); 10888c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 10898c2ecf20Sopenharmony_ci if (!res) { 10908c2ecf20Sopenharmony_ci ret = -EINVAL; 10918c2ecf20Sopenharmony_ci goto error_put_device; 10928c2ecf20Sopenharmony_ci } 10938c2ecf20Sopenharmony_ci *ptr = ioremap(res->start, resource_size(res)); 10948c2ecf20Sopenharmony_ci if (!*ptr) { 10958c2ecf20Sopenharmony_ci ret = -ENOMEM; 10968c2ecf20Sopenharmony_ci goto error_put_device; 10978c2ecf20Sopenharmony_ci } 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci /* We've remapped the addresses, and we don't need the device any 11008c2ecf20Sopenharmony_ci * more, so we should release it. 11018c2ecf20Sopenharmony_ci */ 11028c2ecf20Sopenharmony_ci put_device(&pdev->dev); 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci if (init_flag && siram_init_flag == 0) { 11058c2ecf20Sopenharmony_ci memset_io(*ptr, 0, resource_size(res)); 11068c2ecf20Sopenharmony_ci siram_init_flag = 1; 11078c2ecf20Sopenharmony_ci } 11088c2ecf20Sopenharmony_ci return 0; 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_cierror_put_device: 11118c2ecf20Sopenharmony_ci put_device(&pdev->dev); 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci return ret; 11148c2ecf20Sopenharmony_ci} 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_cistatic int ucc_hdlc_probe(struct platform_device *pdev) 11178c2ecf20Sopenharmony_ci{ 11188c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 11198c2ecf20Sopenharmony_ci struct ucc_hdlc_private *uhdlc_priv = NULL; 11208c2ecf20Sopenharmony_ci struct ucc_tdm_info *ut_info; 11218c2ecf20Sopenharmony_ci struct ucc_tdm *utdm = NULL; 11228c2ecf20Sopenharmony_ci struct resource res; 11238c2ecf20Sopenharmony_ci struct net_device *dev; 11248c2ecf20Sopenharmony_ci hdlc_device *hdlc; 11258c2ecf20Sopenharmony_ci int ucc_num; 11268c2ecf20Sopenharmony_ci const char *sprop; 11278c2ecf20Sopenharmony_ci int ret; 11288c2ecf20Sopenharmony_ci u32 val; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci ret = of_property_read_u32_index(np, "cell-index", 0, &val); 11318c2ecf20Sopenharmony_ci if (ret) { 11328c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Invalid ucc property\n"); 11338c2ecf20Sopenharmony_ci return -ENODEV; 11348c2ecf20Sopenharmony_ci } 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci ucc_num = val - 1; 11378c2ecf20Sopenharmony_ci if (ucc_num > (UCC_MAX_NUM - 1) || ucc_num < 0) { 11388c2ecf20Sopenharmony_ci dev_err(&pdev->dev, ": Invalid UCC num\n"); 11398c2ecf20Sopenharmony_ci return -EINVAL; 11408c2ecf20Sopenharmony_ci } 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci memcpy(&utdm_info[ucc_num], &utdm_primary_info, 11438c2ecf20Sopenharmony_ci sizeof(utdm_primary_info)); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci ut_info = &utdm_info[ucc_num]; 11468c2ecf20Sopenharmony_ci ut_info->uf_info.ucc_num = ucc_num; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci sprop = of_get_property(np, "rx-clock-name", NULL); 11498c2ecf20Sopenharmony_ci if (sprop) { 11508c2ecf20Sopenharmony_ci ut_info->uf_info.rx_clock = qe_clock_source(sprop); 11518c2ecf20Sopenharmony_ci if ((ut_info->uf_info.rx_clock < QE_CLK_NONE) || 11528c2ecf20Sopenharmony_ci (ut_info->uf_info.rx_clock > QE_CLK24)) { 11538c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Invalid rx-clock-name property\n"); 11548c2ecf20Sopenharmony_ci return -EINVAL; 11558c2ecf20Sopenharmony_ci } 11568c2ecf20Sopenharmony_ci } else { 11578c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Invalid rx-clock-name property\n"); 11588c2ecf20Sopenharmony_ci return -EINVAL; 11598c2ecf20Sopenharmony_ci } 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci sprop = of_get_property(np, "tx-clock-name", NULL); 11628c2ecf20Sopenharmony_ci if (sprop) { 11638c2ecf20Sopenharmony_ci ut_info->uf_info.tx_clock = qe_clock_source(sprop); 11648c2ecf20Sopenharmony_ci if ((ut_info->uf_info.tx_clock < QE_CLK_NONE) || 11658c2ecf20Sopenharmony_ci (ut_info->uf_info.tx_clock > QE_CLK24)) { 11668c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Invalid tx-clock-name property\n"); 11678c2ecf20Sopenharmony_ci return -EINVAL; 11688c2ecf20Sopenharmony_ci } 11698c2ecf20Sopenharmony_ci } else { 11708c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Invalid tx-clock-name property\n"); 11718c2ecf20Sopenharmony_ci return -EINVAL; 11728c2ecf20Sopenharmony_ci } 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci ret = of_address_to_resource(np, 0, &res); 11758c2ecf20Sopenharmony_ci if (ret) 11768c2ecf20Sopenharmony_ci return -EINVAL; 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci ut_info->uf_info.regs = res.start; 11798c2ecf20Sopenharmony_ci ut_info->uf_info.irq = irq_of_parse_and_map(np, 0); 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci uhdlc_priv = kzalloc(sizeof(*uhdlc_priv), GFP_KERNEL); 11828c2ecf20Sopenharmony_ci if (!uhdlc_priv) { 11838c2ecf20Sopenharmony_ci return -ENOMEM; 11848c2ecf20Sopenharmony_ci } 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci dev_set_drvdata(&pdev->dev, uhdlc_priv); 11878c2ecf20Sopenharmony_ci uhdlc_priv->dev = &pdev->dev; 11888c2ecf20Sopenharmony_ci uhdlc_priv->ut_info = ut_info; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci if (of_get_property(np, "fsl,tdm-interface", NULL)) 11918c2ecf20Sopenharmony_ci uhdlc_priv->tsa = 1; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci if (of_get_property(np, "fsl,ucc-internal-loopback", NULL)) 11948c2ecf20Sopenharmony_ci uhdlc_priv->loopback = 1; 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci if (of_get_property(np, "fsl,hdlc-bus", NULL)) 11978c2ecf20Sopenharmony_ci uhdlc_priv->hdlc_bus = 1; 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci if (uhdlc_priv->tsa == 1) { 12008c2ecf20Sopenharmony_ci utdm = kzalloc(sizeof(*utdm), GFP_KERNEL); 12018c2ecf20Sopenharmony_ci if (!utdm) { 12028c2ecf20Sopenharmony_ci ret = -ENOMEM; 12038c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No mem to alloc ucc tdm data\n"); 12048c2ecf20Sopenharmony_ci goto free_uhdlc_priv; 12058c2ecf20Sopenharmony_ci } 12068c2ecf20Sopenharmony_ci uhdlc_priv->utdm = utdm; 12078c2ecf20Sopenharmony_ci ret = ucc_of_parse_tdm(np, utdm, ut_info); 12088c2ecf20Sopenharmony_ci if (ret) 12098c2ecf20Sopenharmony_ci goto free_utdm; 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci ret = hdlc_map_iomem("fsl,t1040-qe-si", 0, 12128c2ecf20Sopenharmony_ci (void __iomem **)&utdm->si_regs); 12138c2ecf20Sopenharmony_ci if (ret) 12148c2ecf20Sopenharmony_ci goto free_utdm; 12158c2ecf20Sopenharmony_ci ret = hdlc_map_iomem("fsl,t1040-qe-siram", 1, 12168c2ecf20Sopenharmony_ci (void __iomem **)&utdm->siram); 12178c2ecf20Sopenharmony_ci if (ret) 12188c2ecf20Sopenharmony_ci goto unmap_si_regs; 12198c2ecf20Sopenharmony_ci } 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci if (of_property_read_u16(np, "fsl,hmask", &uhdlc_priv->hmask)) 12228c2ecf20Sopenharmony_ci uhdlc_priv->hmask = DEFAULT_ADDR_MASK; 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci ret = uhdlc_init(uhdlc_priv); 12258c2ecf20Sopenharmony_ci if (ret) { 12268c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to init uhdlc\n"); 12278c2ecf20Sopenharmony_ci goto undo_uhdlc_init; 12288c2ecf20Sopenharmony_ci } 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci dev = alloc_hdlcdev(uhdlc_priv); 12318c2ecf20Sopenharmony_ci if (!dev) { 12328c2ecf20Sopenharmony_ci ret = -ENOMEM; 12338c2ecf20Sopenharmony_ci pr_err("ucc_hdlc: unable to allocate memory\n"); 12348c2ecf20Sopenharmony_ci goto undo_uhdlc_init; 12358c2ecf20Sopenharmony_ci } 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci uhdlc_priv->ndev = dev; 12388c2ecf20Sopenharmony_ci hdlc = dev_to_hdlc(dev); 12398c2ecf20Sopenharmony_ci dev->tx_queue_len = 16; 12408c2ecf20Sopenharmony_ci dev->netdev_ops = &uhdlc_ops; 12418c2ecf20Sopenharmony_ci dev->watchdog_timeo = 2 * HZ; 12428c2ecf20Sopenharmony_ci hdlc->attach = ucc_hdlc_attach; 12438c2ecf20Sopenharmony_ci hdlc->xmit = ucc_hdlc_tx; 12448c2ecf20Sopenharmony_ci netif_napi_add(dev, &uhdlc_priv->napi, ucc_hdlc_poll, 32); 12458c2ecf20Sopenharmony_ci if (register_hdlc_device(dev)) { 12468c2ecf20Sopenharmony_ci ret = -ENOBUFS; 12478c2ecf20Sopenharmony_ci pr_err("ucc_hdlc: unable to register hdlc device\n"); 12488c2ecf20Sopenharmony_ci goto free_dev; 12498c2ecf20Sopenharmony_ci } 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci return 0; 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_cifree_dev: 12548c2ecf20Sopenharmony_ci free_netdev(dev); 12558c2ecf20Sopenharmony_ciundo_uhdlc_init: 12568c2ecf20Sopenharmony_ci if (utdm) 12578c2ecf20Sopenharmony_ci iounmap(utdm->siram); 12588c2ecf20Sopenharmony_ciunmap_si_regs: 12598c2ecf20Sopenharmony_ci if (utdm) 12608c2ecf20Sopenharmony_ci iounmap(utdm->si_regs); 12618c2ecf20Sopenharmony_cifree_utdm: 12628c2ecf20Sopenharmony_ci if (uhdlc_priv->tsa) 12638c2ecf20Sopenharmony_ci kfree(utdm); 12648c2ecf20Sopenharmony_cifree_uhdlc_priv: 12658c2ecf20Sopenharmony_ci kfree(uhdlc_priv); 12668c2ecf20Sopenharmony_ci return ret; 12678c2ecf20Sopenharmony_ci} 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_cistatic int ucc_hdlc_remove(struct platform_device *pdev) 12708c2ecf20Sopenharmony_ci{ 12718c2ecf20Sopenharmony_ci struct ucc_hdlc_private *priv = dev_get_drvdata(&pdev->dev); 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci uhdlc_memclean(priv); 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci if (priv->utdm->si_regs) { 12768c2ecf20Sopenharmony_ci iounmap(priv->utdm->si_regs); 12778c2ecf20Sopenharmony_ci priv->utdm->si_regs = NULL; 12788c2ecf20Sopenharmony_ci } 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci if (priv->utdm->siram) { 12818c2ecf20Sopenharmony_ci iounmap(priv->utdm->siram); 12828c2ecf20Sopenharmony_ci priv->utdm->siram = NULL; 12838c2ecf20Sopenharmony_ci } 12848c2ecf20Sopenharmony_ci kfree(priv); 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "UCC based hdlc module removed\n"); 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci return 0; 12898c2ecf20Sopenharmony_ci} 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_cistatic const struct of_device_id fsl_ucc_hdlc_of_match[] = { 12928c2ecf20Sopenharmony_ci { 12938c2ecf20Sopenharmony_ci .compatible = "fsl,ucc-hdlc", 12948c2ecf20Sopenharmony_ci }, 12958c2ecf20Sopenharmony_ci {}, 12968c2ecf20Sopenharmony_ci}; 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, fsl_ucc_hdlc_of_match); 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_cistatic struct platform_driver ucc_hdlc_driver = { 13018c2ecf20Sopenharmony_ci .probe = ucc_hdlc_probe, 13028c2ecf20Sopenharmony_ci .remove = ucc_hdlc_remove, 13038c2ecf20Sopenharmony_ci .driver = { 13048c2ecf20Sopenharmony_ci .name = DRV_NAME, 13058c2ecf20Sopenharmony_ci .pm = HDLC_PM_OPS, 13068c2ecf20Sopenharmony_ci .of_match_table = fsl_ucc_hdlc_of_match, 13078c2ecf20Sopenharmony_ci }, 13088c2ecf20Sopenharmony_ci}; 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_cimodule_platform_driver(ucc_hdlc_driver); 13118c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 13128c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRV_DESC); 1313