2016-06-06 14:30:02 +08:00
/* Freescale QUICC Engine HDLC Device Driver
*
* Copyright 2016 Freescale Semiconductor Inc .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*/
# include <linux/delay.h>
# include <linux/dma-mapping.h>
# include <linux/hdlc.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/irq.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/netdevice.h>
# include <linux/of_address.h>
# include <linux/of_irq.h>
# include <linux/of_platform.h>
# include <linux/platform_device.h>
# include <linux/sched.h>
# include <linux/skbuff.h>
# include <linux/slab.h>
# include <linux/spinlock.h>
# include <linux/stddef.h>
# include <soc/fsl/qe/qe_tdm.h>
# include <uapi/linux/if_arp.h>
# include "fsl_ucc_hdlc.h"
# define DRV_DESC "Freescale QE UCC HDLC Driver"
# define DRV_NAME "ucc_hdlc"
# define TDM_PPPOHT_SLIC_MAXIN
static struct ucc_tdm_info utdm_primary_info = {
. uf_info = {
. tsa = 0 ,
. cdp = 0 ,
. cds = 1 ,
. ctsp = 1 ,
. ctss = 1 ,
. revd = 0 ,
. urfs = 256 ,
. utfs = 256 ,
. urfet = 128 ,
. urfset = 192 ,
. utfet = 128 ,
. utftt = 0x40 ,
. ufpt = 256 ,
. mode = UCC_FAST_PROTOCOL_MODE_HDLC ,
. ttx_trx = UCC_FAST_GUMR_TRANSPARENT_TTX_TRX_NORMAL ,
. tenc = UCC_FAST_TX_ENCODING_NRZ ,
. renc = UCC_FAST_RX_ENCODING_NRZ ,
. tcrc = UCC_FAST_16_BIT_CRC ,
. synl = UCC_FAST_SYNC_LEN_NOT_USED ,
} ,
. si_info = {
# ifdef TDM_PPPOHT_SLIC_MAXIN
. simr_rfsd = 1 ,
. simr_tfsd = 2 ,
# else
. simr_rfsd = 0 ,
. simr_tfsd = 0 ,
# endif
. simr_crt = 0 ,
. simr_sl = 0 ,
. simr_ce = 1 ,
. simr_fe = 1 ,
. simr_gm = 0 ,
} ,
} ;
static struct ucc_tdm_info utdm_info [ MAX_HDLC_NUM ] ;
static int uhdlc_init ( struct ucc_hdlc_private * priv )
{
struct ucc_tdm_info * ut_info ;
struct ucc_fast_info * uf_info ;
u32 cecr_subblock ;
u16 bd_status ;
int ret , i ;
void * bd_buffer ;
dma_addr_t bd_dma_addr ;
u32 riptr ;
u32 tiptr ;
u32 gumr ;
ut_info = priv - > ut_info ;
uf_info = & ut_info - > uf_info ;
if ( priv - > tsa ) {
uf_info - > tsa = 1 ;
uf_info - > ctsp = 1 ;
}
2017-05-17 17:24:38 +02:00
/* This sets HPM register in CMXUCR register which configures a
* open drain connected HDLC bus
*/
if ( priv - > hdlc_bus )
uf_info - > brkpt_support = 1 ;
2016-06-06 14:30:02 +08:00
uf_info - > uccm_mask = ( ( UCC_HDLC_UCCE_RXB | UCC_HDLC_UCCE_RXF |
UCC_HDLC_UCCE_TXB ) < < 16 ) ;
ret = ucc_fast_init ( uf_info , & priv - > uccf ) ;
if ( ret ) {
dev_err ( priv - > dev , " Failed to init uccf. " ) ;
return ret ;
}
priv - > uf_regs = priv - > uccf - > uf_regs ;
ucc_fast_disable ( priv - > uccf , COMM_DIR_RX | COMM_DIR_TX ) ;
/* Loopback mode */
if ( priv - > loopback ) {
dev_info ( priv - > dev , " Loopback Mode \n " ) ;
2017-05-17 17:24:36 +02:00
/* use the same clock when work in loopback */
qe_setbrg ( ut_info - > uf_info . rx_clock , 20000000 , 1 ) ;
2016-06-06 14:30:02 +08:00
gumr = ioread32be ( & priv - > uf_regs - > gumr ) ;
gumr | = ( UCC_FAST_GUMR_LOOPBACK | UCC_FAST_GUMR_CDS |
UCC_FAST_GUMR_TCI ) ;
gumr & = ~ ( UCC_FAST_GUMR_CTSP | UCC_FAST_GUMR_RSYN ) ;
iowrite32be ( gumr , & priv - > uf_regs - > gumr ) ;
}
/* Initialize SI */
if ( priv - > tsa )
ucc_tdm_init ( priv - > utdm , priv - > ut_info ) ;
/* Write to QE CECR, UCCx channel to Stop Transmission */
cecr_subblock = ucc_fast_get_qe_cr_subblock ( uf_info - > ucc_num ) ;
ret = qe_issue_cmd ( QE_STOP_TX , cecr_subblock ,
QE_CR_PROTOCOL_UNSPECIFIED , 0 ) ;
/* Set UPSMR normal mode (need fixed)*/
iowrite32be ( 0 , & priv - > uf_regs - > upsmr ) ;
2017-05-17 17:24:38 +02:00
/* hdlc_bus mode */
if ( priv - > hdlc_bus ) {
u32 upsmr ;
dev_info ( priv - > dev , " HDLC bus Mode \n " ) ;
upsmr = ioread32be ( & priv - > uf_regs - > upsmr ) ;
/* bus mode and retransmit enable, with collision window
* set to 8 bytes
*/
upsmr | = UCC_HDLC_UPSMR_RTE | UCC_HDLC_UPSMR_BUS |
UCC_HDLC_UPSMR_CW8 ;
iowrite32be ( upsmr , & priv - > uf_regs - > upsmr ) ;
/* explicitly disable CDS & CTSP */
gumr = ioread32be ( & priv - > uf_regs - > gumr ) ;
gumr & = ~ ( UCC_FAST_GUMR_CDS | UCC_FAST_GUMR_CTSP ) ;
/* set automatic sync to explicitly ignore CD signal */
gumr | = UCC_FAST_GUMR_SYNL_AUTO ;
iowrite32be ( gumr , & priv - > uf_regs - > gumr ) ;
}
2016-06-06 14:30:02 +08:00
priv - > rx_ring_size = RX_BD_RING_LEN ;
priv - > tx_ring_size = TX_BD_RING_LEN ;
/* Alloc Rx BD */
priv - > rx_bd_base = dma_alloc_coherent ( priv - > dev ,
2017-05-17 17:24:35 +02:00
RX_BD_RING_LEN * sizeof ( struct qe_bd ) ,
2016-06-06 14:30:02 +08:00
& priv - > dma_rx_bd , GFP_KERNEL ) ;
if ( ! priv - > rx_bd_base ) {
dev_err ( priv - > dev , " Cannot allocate MURAM memory for RxBDs \n " ) ;
ret = - ENOMEM ;
2016-07-15 10:38:25 +08:00
goto free_uccf ;
2016-06-06 14:30:02 +08:00
}
/* Alloc Tx BD */
priv - > tx_bd_base = dma_alloc_coherent ( priv - > dev ,
2017-05-17 17:24:35 +02:00
TX_BD_RING_LEN * sizeof ( struct qe_bd ) ,
2016-06-06 14:30:02 +08:00
& priv - > dma_tx_bd , GFP_KERNEL ) ;
if ( ! priv - > tx_bd_base ) {
dev_err ( priv - > dev , " Cannot allocate MURAM memory for TxBDs \n " ) ;
ret = - ENOMEM ;
2016-07-15 10:38:25 +08:00
goto free_rx_bd ;
2016-06-06 14:30:02 +08:00
}
/* Alloc parameter ram for ucc hdlc */
2017-05-22 09:31:15 +02:00
priv - > ucc_pram_offset = qe_muram_alloc ( sizeof ( struct ucc_hdlc_param ) ,
2016-06-06 14:30:02 +08:00
ALIGNMENT_OF_UCC_HDLC_PRAM ) ;
if ( priv - > ucc_pram_offset < 0 ) {
2016-08-28 11:40:41 +01:00
dev_err ( priv - > dev , " Can not allocate MURAM for hdlc parameter. \n " ) ;
2016-06-06 14:30:02 +08:00
ret = - ENOMEM ;
2016-07-15 10:38:25 +08:00
goto free_tx_bd ;
2016-06-06 14:30:02 +08:00
}
priv - > rx_skbuff = kzalloc ( priv - > rx_ring_size * sizeof ( * priv - > rx_skbuff ) ,
GFP_KERNEL ) ;
if ( ! priv - > rx_skbuff )
2016-07-15 10:38:25 +08:00
goto free_ucc_pram ;
2016-06-06 14:30:02 +08:00
priv - > tx_skbuff = kzalloc ( priv - > tx_ring_size * sizeof ( * priv - > tx_skbuff ) ,
GFP_KERNEL ) ;
if ( ! priv - > tx_skbuff )
2016-07-15 10:38:25 +08:00
goto free_rx_skbuff ;
2016-06-06 14:30:02 +08:00
priv - > skb_curtx = 0 ;
priv - > skb_dirtytx = 0 ;
priv - > curtx_bd = priv - > tx_bd_base ;
priv - > dirty_tx = priv - > tx_bd_base ;
priv - > currx_bd = priv - > rx_bd_base ;
priv - > currx_bdnum = 0 ;
/* init parameter base */
cecr_subblock = ucc_fast_get_qe_cr_subblock ( uf_info - > ucc_num ) ;
ret = qe_issue_cmd ( QE_ASSIGN_PAGE_TO_DEVICE , cecr_subblock ,
QE_CR_PROTOCOL_UNSPECIFIED , priv - > ucc_pram_offset ) ;
priv - > ucc_pram = ( struct ucc_hdlc_param __iomem * )
qe_muram_addr ( priv - > ucc_pram_offset ) ;
/* Zero out parameter ram */
memset_io ( priv - > ucc_pram , 0 , sizeof ( struct ucc_hdlc_param ) ) ;
/* Alloc riptr, tiptr */
riptr = qe_muram_alloc ( 32 , 32 ) ;
if ( riptr < 0 ) {
dev_err ( priv - > dev , " Cannot allocate MURAM mem for Receive internal temp data pointer \n " ) ;
ret = - ENOMEM ;
2016-07-15 10:38:25 +08:00
goto free_tx_skbuff ;
2016-06-06 14:30:02 +08:00
}
tiptr = qe_muram_alloc ( 32 , 32 ) ;
if ( tiptr < 0 ) {
dev_err ( priv - > dev , " Cannot allocate MURAM mem for Transmit internal temp data pointer \n " ) ;
ret = - ENOMEM ;
2016-07-15 10:38:25 +08:00
goto free_riptr ;
2016-06-06 14:30:02 +08:00
}
/* Set RIPTR, TIPTR */
iowrite16be ( riptr , & priv - > ucc_pram - > riptr ) ;
iowrite16be ( tiptr , & priv - > ucc_pram - > tiptr ) ;
/* Set MRBLR */
iowrite16be ( MAX_RX_BUF_LENGTH , & priv - > ucc_pram - > mrblr ) ;
/* Set RBASE, TBASE */
iowrite32be ( priv - > dma_rx_bd , & priv - > ucc_pram - > rbase ) ;
iowrite32be ( priv - > dma_tx_bd , & priv - > ucc_pram - > tbase ) ;
/* Set RSTATE, TSTATE */
iowrite32be ( BMR_GBL | BMR_BIG_ENDIAN , & priv - > ucc_pram - > rstate ) ;
iowrite32be ( BMR_GBL | BMR_BIG_ENDIAN , & priv - > ucc_pram - > tstate ) ;
/* Set C_MASK, C_PRES for 16bit CRC */
iowrite32be ( CRC_16BIT_MASK , & priv - > ucc_pram - > c_mask ) ;
iowrite32be ( CRC_16BIT_PRES , & priv - > ucc_pram - > c_pres ) ;
iowrite16be ( MAX_FRAME_LENGTH , & priv - > ucc_pram - > mflr ) ;
iowrite16be ( DEFAULT_RFTHR , & priv - > ucc_pram - > rfthr ) ;
iowrite16be ( DEFAULT_RFTHR , & priv - > ucc_pram - > rfcnt ) ;
iowrite16be ( DEFAULT_ADDR_MASK , & priv - > ucc_pram - > hmask ) ;
iowrite16be ( DEFAULT_HDLC_ADDR , & priv - > ucc_pram - > haddr1 ) ;
iowrite16be ( DEFAULT_HDLC_ADDR , & priv - > ucc_pram - > haddr2 ) ;
iowrite16be ( DEFAULT_HDLC_ADDR , & priv - > ucc_pram - > haddr3 ) ;
iowrite16be ( DEFAULT_HDLC_ADDR , & priv - > ucc_pram - > haddr4 ) ;
/* Get BD buffer */
bd_buffer = dma_alloc_coherent ( priv - > dev ,
( RX_BD_RING_LEN + TX_BD_RING_LEN ) *
MAX_RX_BUF_LENGTH ,
& bd_dma_addr , GFP_KERNEL ) ;
if ( ! bd_buffer ) {
dev_err ( priv - > dev , " Could not allocate buffer descriptors \n " ) ;
ret = - ENOMEM ;
2016-07-15 10:38:25 +08:00
goto free_tiptr ;
2016-06-06 14:30:02 +08:00
}
memset ( bd_buffer , 0 , ( RX_BD_RING_LEN + TX_BD_RING_LEN )
* MAX_RX_BUF_LENGTH ) ;
priv - > rx_buffer = bd_buffer ;
priv - > tx_buffer = bd_buffer + RX_BD_RING_LEN * MAX_RX_BUF_LENGTH ;
priv - > dma_rx_addr = bd_dma_addr ;
priv - > dma_tx_addr = bd_dma_addr + RX_BD_RING_LEN * MAX_RX_BUF_LENGTH ;
for ( i = 0 ; i < RX_BD_RING_LEN ; i + + ) {
if ( i < ( RX_BD_RING_LEN - 1 ) )
bd_status = R_E_S | R_I_S ;
else
bd_status = R_E_S | R_I_S | R_W_S ;
iowrite16be ( bd_status , & priv - > rx_bd_base [ i ] . status ) ;
iowrite32be ( priv - > dma_rx_addr + i * MAX_RX_BUF_LENGTH ,
& priv - > rx_bd_base [ i ] . buf ) ;
}
for ( i = 0 ; i < TX_BD_RING_LEN ; i + + ) {
if ( i < ( TX_BD_RING_LEN - 1 ) )
bd_status = T_I_S | T_TC_S ;
else
bd_status = T_I_S | T_TC_S | T_W_S ;
iowrite16be ( bd_status , & priv - > tx_bd_base [ i ] . status ) ;
iowrite32be ( priv - > dma_tx_addr + i * MAX_RX_BUF_LENGTH ,
& priv - > tx_bd_base [ i ] . buf ) ;
}
return 0 ;
2016-07-15 10:38:25 +08:00
free_tiptr :
2016-06-06 14:30:02 +08:00
qe_muram_free ( tiptr ) ;
2016-07-15 10:38:25 +08:00
free_riptr :
2016-06-06 14:30:02 +08:00
qe_muram_free ( riptr ) ;
2016-07-15 10:38:25 +08:00
free_tx_skbuff :
2016-06-06 14:30:02 +08:00
kfree ( priv - > tx_skbuff ) ;
2016-07-15 10:38:25 +08:00
free_rx_skbuff :
2016-06-06 14:30:02 +08:00
kfree ( priv - > rx_skbuff ) ;
2016-07-15 10:38:25 +08:00
free_ucc_pram :
2016-06-06 14:30:02 +08:00
qe_muram_free ( priv - > ucc_pram_offset ) ;
2016-07-15 10:38:25 +08:00
free_tx_bd :
2016-06-06 14:30:02 +08:00
dma_free_coherent ( priv - > dev ,
2017-05-17 17:24:35 +02:00
TX_BD_RING_LEN * sizeof ( struct qe_bd ) ,
2016-06-06 14:30:02 +08:00
priv - > tx_bd_base , priv - > dma_tx_bd ) ;
2016-07-15 10:38:25 +08:00
free_rx_bd :
2016-06-06 14:30:02 +08:00
dma_free_coherent ( priv - > dev ,
2017-05-17 17:24:35 +02:00
RX_BD_RING_LEN * sizeof ( struct qe_bd ) ,
2016-06-06 14:30:02 +08:00
priv - > rx_bd_base , priv - > dma_rx_bd ) ;
2016-07-15 10:38:25 +08:00
free_uccf :
2016-06-06 14:30:02 +08:00
ucc_fast_free ( priv - > uccf ) ;
return ret ;
}
static netdev_tx_t ucc_hdlc_tx ( struct sk_buff * skb , struct net_device * dev )
{
hdlc_device * hdlc = dev_to_hdlc ( dev ) ;
struct ucc_hdlc_private * priv = ( struct ucc_hdlc_private * ) hdlc - > priv ;
struct qe_bd __iomem * bd ;
u16 bd_status ;
unsigned long flags ;
u16 * proto_head ;
switch ( dev - > type ) {
case ARPHRD_RAWHDLC :
if ( skb_headroom ( skb ) < HDLC_HEAD_LEN ) {
dev - > stats . tx_dropped + + ;
dev_kfree_skb ( skb ) ;
netdev_err ( dev , " No enough space for hdlc head \n " ) ;
return - ENOMEM ;
}
skb_push ( skb , HDLC_HEAD_LEN ) ;
proto_head = ( u16 * ) skb - > data ;
* proto_head = htons ( DEFAULT_HDLC_HEAD ) ;
dev - > stats . tx_bytes + = skb - > len ;
break ;
case ARPHRD_PPP :
proto_head = ( u16 * ) skb - > data ;
if ( * proto_head ! = htons ( DEFAULT_PPP_HEAD ) ) {
dev - > stats . tx_dropped + + ;
dev_kfree_skb ( skb ) ;
netdev_err ( dev , " Wrong ppp header \n " ) ;
return - ENOMEM ;
}
dev - > stats . tx_bytes + = skb - > len ;
break ;
default :
dev - > stats . tx_dropped + + ;
dev_kfree_skb ( skb ) ;
return - ENOMEM ;
}
spin_lock_irqsave ( & priv - > lock , flags ) ;
/* Start from the next BD that should be filled */
bd = priv - > curtx_bd ;
bd_status = ioread16be ( & bd - > status ) ;
/* Save the skb pointer so we can free it later */
priv - > tx_skbuff [ priv - > skb_curtx ] = skb ;
/* Update the current skb pointer (wrapping if this was the last) */
priv - > skb_curtx =
( priv - > skb_curtx + 1 ) & TX_RING_MOD_MASK ( TX_BD_RING_LEN ) ;
/* copy skb data to tx buffer for sdma processing */
memcpy ( priv - > tx_buffer + ( be32_to_cpu ( bd - > buf ) - priv - > dma_tx_addr ) ,
skb - > data , skb - > len ) ;
/* set bd status and length */
bd_status = ( bd_status & T_W_S ) | T_R_S | T_I_S | T_L_S | T_TC_S ;
iowrite16be ( skb - > len , & bd - > length ) ;
2017-03-14 09:38:33 +08:00
iowrite16be ( bd_status , & bd - > status ) ;
2016-06-06 14:30:02 +08:00
/* Move to next BD in the ring */
if ( ! ( bd_status & T_W_S ) )
bd + = 1 ;
else
bd = priv - > tx_bd_base ;
if ( bd = = priv - > dirty_tx ) {
if ( ! netif_queue_stopped ( dev ) )
netif_stop_queue ( dev ) ;
}
priv - > curtx_bd = bd ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
return NETDEV_TX_OK ;
}
static int hdlc_tx_done ( struct ucc_hdlc_private * priv )
{
/* Start from the next BD that should be filled */
struct net_device * dev = priv - > ndev ;
struct qe_bd * bd ; /* BD pointer */
u16 bd_status ;
bd = priv - > dirty_tx ;
bd_status = ioread16be ( & bd - > status ) ;
/* Normal processing. */
while ( ( bd_status & T_R_S ) = = 0 ) {
struct sk_buff * skb ;
/* BD contains already transmitted buffer. */
/* Handle the transmitted buffer and release */
/* the BD to be used with the current frame */
skb = priv - > tx_skbuff [ priv - > skb_dirtytx ] ;
if ( ! skb )
break ;
dev - > stats . tx_packets + + ;
memset ( priv - > tx_buffer +
( be32_to_cpu ( bd - > buf ) - priv - > dma_tx_addr ) ,
0 , skb - > len ) ;
dev_kfree_skb_irq ( skb ) ;
priv - > tx_skbuff [ priv - > skb_dirtytx ] = NULL ;
priv - > skb_dirtytx =
( priv - > skb_dirtytx +
1 ) & TX_RING_MOD_MASK ( TX_BD_RING_LEN ) ;
/* We freed a buffer, so now we can restart transmission */
if ( netif_queue_stopped ( dev ) )
netif_wake_queue ( dev ) ;
/* Advance the confirmation BD pointer */
if ( ! ( bd_status & T_W_S ) )
bd + = 1 ;
else
bd = priv - > tx_bd_base ;
bd_status = ioread16be ( & bd - > status ) ;
}
priv - > dirty_tx = bd ;
return 0 ;
}
static int hdlc_rx_done ( struct ucc_hdlc_private * priv , int rx_work_limit )
{
struct net_device * dev = priv - > ndev ;
2017-05-17 17:24:33 +02:00
struct sk_buff * skb = NULL ;
2016-06-06 14:30:02 +08:00
hdlc_device * hdlc = dev_to_hdlc ( dev ) ;
struct qe_bd * bd ;
2017-03-14 09:38:33 +08:00
u16 bd_status ;
2016-06-06 14:30:02 +08:00
u16 length , howmany = 0 ;
u8 * bdbuffer ;
bd = priv - > currx_bd ;
bd_status = ioread16be ( & bd - > status ) ;
/* while there are received buffers and BD is full (~R_E) */
while ( ! ( ( bd_status & ( R_E_S ) ) | | ( - - rx_work_limit < 0 ) ) ) {
if ( bd_status & R_OV_S )
dev - > stats . rx_over_errors + + ;
if ( bd_status & R_CR_S ) {
dev - > stats . rx_crc_errors + + ;
dev - > stats . rx_dropped + + ;
goto recycle ;
}
bdbuffer = priv - > rx_buffer +
( priv - > currx_bdnum * MAX_RX_BUF_LENGTH ) ;
length = ioread16be ( & bd - > length ) ;
switch ( dev - > type ) {
case ARPHRD_RAWHDLC :
bdbuffer + = HDLC_HEAD_LEN ;
length - = ( HDLC_HEAD_LEN + HDLC_CRC_SIZE ) ;
skb = dev_alloc_skb ( length ) ;
if ( ! skb ) {
dev - > stats . rx_dropped + + ;
return - ENOMEM ;
}
skb_put ( skb , length ) ;
skb - > len = length ;
skb - > dev = dev ;
memcpy ( skb - > data , bdbuffer , length ) ;
break ;
case ARPHRD_PPP :
length - = HDLC_CRC_SIZE ;
skb = dev_alloc_skb ( length ) ;
if ( ! skb ) {
dev - > stats . rx_dropped + + ;
return - ENOMEM ;
}
skb_put ( skb , length ) ;
skb - > len = length ;
skb - > dev = dev ;
memcpy ( skb - > data , bdbuffer , length ) ;
break ;
}
dev - > stats . rx_packets + + ;
dev - > stats . rx_bytes + = skb - > len ;
howmany + + ;
if ( hdlc - > proto )
skb - > protocol = hdlc_type_trans ( skb , dev ) ;
netif_receive_skb ( skb ) ;
recycle :
iowrite16be ( bd_status | R_E_S | R_I_S , & bd - > status ) ;
/* update to point at the next bd */
if ( bd_status & R_W_S ) {
priv - > currx_bdnum = 0 ;
bd = priv - > rx_bd_base ;
} else {
if ( priv - > currx_bdnum < ( RX_BD_RING_LEN - 1 ) )
priv - > currx_bdnum + = 1 ;
else
priv - > currx_bdnum = RX_BD_RING_LEN - 1 ;
bd + = 1 ;
}
bd_status = ioread16be ( & bd - > status ) ;
}
priv - > currx_bd = bd ;
return howmany ;
}
static int ucc_hdlc_poll ( struct napi_struct * napi , int budget )
{
struct ucc_hdlc_private * priv = container_of ( napi ,
struct ucc_hdlc_private ,
napi ) ;
int howmany ;
/* Tx event processing */
spin_lock ( & priv - > lock ) ;
2017-05-17 17:24:34 +02:00
hdlc_tx_done ( priv ) ;
2016-06-06 14:30:02 +08:00
spin_unlock ( & priv - > lock ) ;
howmany = 0 ;
howmany + = hdlc_rx_done ( priv , budget - howmany ) ;
if ( howmany < budget ) {
2017-01-30 08:22:01 -08:00
napi_complete_done ( napi , howmany ) ;
2016-06-06 14:30:02 +08:00
qe_setbits32 ( priv - > uccf - > p_uccm ,
( UCCE_HDLC_RX_EVENTS | UCCE_HDLC_TX_EVENTS ) < < 16 ) ;
}
return howmany ;
}
static irqreturn_t ucc_hdlc_irq_handler ( int irq , void * dev_id )
{
struct ucc_hdlc_private * priv = ( struct ucc_hdlc_private * ) dev_id ;
struct net_device * dev = priv - > ndev ;
struct ucc_fast_private * uccf ;
struct ucc_tdm_info * ut_info ;
u32 ucce ;
u32 uccm ;
ut_info = priv - > ut_info ;
uccf = priv - > uccf ;
ucce = ioread32be ( uccf - > p_ucce ) ;
uccm = ioread32be ( uccf - > p_uccm ) ;
ucce & = uccm ;
iowrite32be ( ucce , uccf - > p_ucce ) ;
if ( ! ucce )
return IRQ_NONE ;
if ( ( ucce > > 16 ) & ( UCCE_HDLC_RX_EVENTS | UCCE_HDLC_TX_EVENTS ) ) {
if ( napi_schedule_prep ( & priv - > napi ) ) {
uccm & = ~ ( ( UCCE_HDLC_RX_EVENTS | UCCE_HDLC_TX_EVENTS )
< < 16 ) ;
iowrite32be ( uccm , uccf - > p_uccm ) ;
__napi_schedule ( & priv - > napi ) ;
}
}
/* Errors and other events */
if ( ucce > > 16 & UCC_HDLC_UCCE_BSY )
dev - > stats . rx_errors + + ;
if ( ucce > > 16 & UCC_HDLC_UCCE_TXE )
dev - > stats . tx_errors + + ;
return IRQ_HANDLED ;
}
static int uhdlc_ioctl ( struct net_device * dev , struct ifreq * ifr , int cmd )
{
const size_t size = sizeof ( te1_settings ) ;
te1_settings line ;
struct ucc_hdlc_private * priv = netdev_priv ( dev ) ;
if ( cmd ! = SIOCWANDEV )
return hdlc_ioctl ( dev , ifr , cmd ) ;
switch ( ifr - > ifr_settings . type ) {
case IF_GET_IFACE :
ifr - > ifr_settings . type = IF_IFACE_E1 ;
if ( ifr - > ifr_settings . size < size ) {
ifr - > ifr_settings . size = size ; /* data size wanted */
return - ENOBUFS ;
}
2016-07-14 14:16:53 +03:00
memset ( & line , 0 , sizeof ( line ) ) ;
2016-06-06 14:30:02 +08:00
line . clock_type = priv - > clocking ;
if ( copy_to_user ( ifr - > ifr_settings . ifs_ifsu . sync , & line , size ) )
return - EFAULT ;
return 0 ;
default :
return hdlc_ioctl ( dev , ifr , cmd ) ;
}
}
static int uhdlc_open ( struct net_device * dev )
{
u32 cecr_subblock ;
hdlc_device * hdlc = dev_to_hdlc ( dev ) ;
struct ucc_hdlc_private * priv = hdlc - > priv ;
struct ucc_tdm * utdm = priv - > utdm ;
if ( priv - > hdlc_busy ! = 1 ) {
if ( request_irq ( priv - > ut_info - > uf_info . irq ,
ucc_hdlc_irq_handler , 0 , " hdlc " , priv ) )
return - ENODEV ;
cecr_subblock = ucc_fast_get_qe_cr_subblock (
priv - > ut_info - > uf_info . ucc_num ) ;
qe_issue_cmd ( QE_INIT_TX_RX , cecr_subblock ,
QE_CR_PROTOCOL_UNSPECIFIED , 0 ) ;
ucc_fast_enable ( priv - > uccf , COMM_DIR_RX | COMM_DIR_TX ) ;
/* Enable the TDM port */
if ( priv - > tsa )
utdm - > si_regs - > siglmr1_h | = ( 0x1 < < utdm - > tdm_port ) ;
priv - > hdlc_busy = 1 ;
netif_device_attach ( priv - > ndev ) ;
napi_enable ( & priv - > napi ) ;
netif_start_queue ( dev ) ;
hdlc_open ( dev ) ;
}
return 0 ;
}
static void uhdlc_memclean ( struct ucc_hdlc_private * priv )
{
qe_muram_free ( priv - > ucc_pram - > riptr ) ;
qe_muram_free ( priv - > ucc_pram - > tiptr ) ;
if ( priv - > rx_bd_base ) {
dma_free_coherent ( priv - > dev ,
2017-05-17 17:24:35 +02:00
RX_BD_RING_LEN * sizeof ( struct qe_bd ) ,
2016-06-06 14:30:02 +08:00
priv - > rx_bd_base , priv - > dma_rx_bd ) ;
priv - > rx_bd_base = NULL ;
priv - > dma_rx_bd = 0 ;
}
if ( priv - > tx_bd_base ) {
dma_free_coherent ( priv - > dev ,
2017-05-17 17:24:35 +02:00
TX_BD_RING_LEN * sizeof ( struct qe_bd ) ,
2016-06-06 14:30:02 +08:00
priv - > tx_bd_base , priv - > dma_tx_bd ) ;
priv - > tx_bd_base = NULL ;
priv - > dma_tx_bd = 0 ;
}
if ( priv - > ucc_pram ) {
qe_muram_free ( priv - > ucc_pram_offset ) ;
priv - > ucc_pram = NULL ;
priv - > ucc_pram_offset = 0 ;
}
kfree ( priv - > rx_skbuff ) ;
priv - > rx_skbuff = NULL ;
kfree ( priv - > tx_skbuff ) ;
priv - > tx_skbuff = NULL ;
if ( priv - > uf_regs ) {
iounmap ( priv - > uf_regs ) ;
priv - > uf_regs = NULL ;
}
if ( priv - > uccf ) {
ucc_fast_free ( priv - > uccf ) ;
priv - > uccf = NULL ;
}
if ( priv - > rx_buffer ) {
dma_free_coherent ( priv - > dev ,
RX_BD_RING_LEN * MAX_RX_BUF_LENGTH ,
priv - > rx_buffer , priv - > dma_rx_addr ) ;
priv - > rx_buffer = NULL ;
priv - > dma_rx_addr = 0 ;
}
if ( priv - > tx_buffer ) {
dma_free_coherent ( priv - > dev ,
TX_BD_RING_LEN * MAX_RX_BUF_LENGTH ,
priv - > tx_buffer , priv - > dma_tx_addr ) ;
priv - > tx_buffer = NULL ;
priv - > dma_tx_addr = 0 ;
}
}
static int uhdlc_close ( struct net_device * dev )
{
struct ucc_hdlc_private * priv = dev_to_hdlc ( dev ) - > priv ;
struct ucc_tdm * utdm = priv - > utdm ;
u32 cecr_subblock ;
napi_disable ( & priv - > napi ) ;
cecr_subblock = ucc_fast_get_qe_cr_subblock (
priv - > ut_info - > uf_info . ucc_num ) ;
qe_issue_cmd ( QE_GRACEFUL_STOP_TX , cecr_subblock ,
( u8 ) QE_CR_PROTOCOL_UNSPECIFIED , 0 ) ;
qe_issue_cmd ( QE_CLOSE_RX_BD , cecr_subblock ,
( u8 ) QE_CR_PROTOCOL_UNSPECIFIED , 0 ) ;
if ( priv - > tsa )
utdm - > si_regs - > siglmr1_h & = ~ ( 0x1 < < utdm - > tdm_port ) ;
ucc_fast_disable ( priv - > uccf , COMM_DIR_RX | COMM_DIR_TX ) ;
free_irq ( priv - > ut_info - > uf_info . irq , priv ) ;
netif_stop_queue ( dev ) ;
priv - > hdlc_busy = 0 ;
return 0 ;
}
static int ucc_hdlc_attach ( struct net_device * dev , unsigned short encoding ,
unsigned short parity )
{
struct ucc_hdlc_private * priv = dev_to_hdlc ( dev ) - > priv ;
if ( encoding ! = ENCODING_NRZ & &
encoding ! = ENCODING_NRZI )
return - EINVAL ;
if ( parity ! = PARITY_NONE & &
parity ! = PARITY_CRC32_PR1_CCITT & &
parity ! = PARITY_CRC16_PR1_CCITT )
return - EINVAL ;
priv - > encoding = encoding ;
priv - > parity = parity ;
return 0 ;
}
# ifdef CONFIG_PM
static void store_clk_config ( struct ucc_hdlc_private * priv )
{
struct qe_mux * qe_mux_reg = & qe_immr - > qmx ;
/* store si clk */
priv - > cmxsi1cr_h = ioread32be ( & qe_mux_reg - > cmxsi1cr_h ) ;
priv - > cmxsi1cr_l = ioread32be ( & qe_mux_reg - > cmxsi1cr_l ) ;
/* store si sync */
priv - > cmxsi1syr = ioread32be ( & qe_mux_reg - > cmxsi1syr ) ;
/* store ucc clk */
memcpy_fromio ( priv - > cmxucr , qe_mux_reg - > cmxucr , 4 * sizeof ( u32 ) ) ;
}
static void resume_clk_config ( struct ucc_hdlc_private * priv )
{
struct qe_mux * qe_mux_reg = & qe_immr - > qmx ;
memcpy_toio ( qe_mux_reg - > cmxucr , priv - > cmxucr , 4 * sizeof ( u32 ) ) ;
iowrite32be ( priv - > cmxsi1cr_h , & qe_mux_reg - > cmxsi1cr_h ) ;
iowrite32be ( priv - > cmxsi1cr_l , & qe_mux_reg - > cmxsi1cr_l ) ;
iowrite32be ( priv - > cmxsi1syr , & qe_mux_reg - > cmxsi1syr ) ;
}
static int uhdlc_suspend ( struct device * dev )
{
struct ucc_hdlc_private * priv = dev_get_drvdata ( dev ) ;
struct ucc_tdm_info * ut_info ;
struct ucc_fast __iomem * uf_regs ;
if ( ! priv )
return - EINVAL ;
if ( ! netif_running ( priv - > ndev ) )
return 0 ;
netif_device_detach ( priv - > ndev ) ;
napi_disable ( & priv - > napi ) ;
ut_info = priv - > ut_info ;
uf_regs = priv - > uf_regs ;
/* backup gumr guemr*/
priv - > gumr = ioread32be ( & uf_regs - > gumr ) ;
priv - > guemr = ioread8 ( & uf_regs - > guemr ) ;
priv - > ucc_pram_bak = kmalloc ( sizeof ( * priv - > ucc_pram_bak ) ,
GFP_KERNEL ) ;
if ( ! priv - > ucc_pram_bak )
return - ENOMEM ;
/* backup HDLC parameter */
memcpy_fromio ( priv - > ucc_pram_bak , priv - > ucc_pram ,
sizeof ( struct ucc_hdlc_param ) ) ;
/* store the clk configuration */
store_clk_config ( priv ) ;
/* save power */
ucc_fast_disable ( priv - > uccf , COMM_DIR_RX | COMM_DIR_TX ) ;
return 0 ;
}
static int uhdlc_resume ( struct device * dev )
{
struct ucc_hdlc_private * priv = dev_get_drvdata ( dev ) ;
2016-07-31 13:14:23 +02:00
struct ucc_tdm * utdm ;
2016-06-06 14:30:02 +08:00
struct ucc_tdm_info * ut_info ;
struct ucc_fast __iomem * uf_regs ;
struct ucc_fast_private * uccf ;
struct ucc_fast_info * uf_info ;
int ret , i ;
u32 cecr_subblock ;
u16 bd_status ;
if ( ! priv )
return - EINVAL ;
if ( ! netif_running ( priv - > ndev ) )
return 0 ;
2016-07-31 13:14:23 +02:00
utdm = priv - > utdm ;
2016-06-06 14:30:02 +08:00
ut_info = priv - > ut_info ;
uf_info = & ut_info - > uf_info ;
uf_regs = priv - > uf_regs ;
uccf = priv - > uccf ;
/* restore gumr guemr */
iowrite8 ( priv - > guemr , & uf_regs - > guemr ) ;
iowrite32be ( priv - > gumr , & uf_regs - > gumr ) ;
/* Set Virtual Fifo registers */
iowrite16be ( uf_info - > urfs , & uf_regs - > urfs ) ;
iowrite16be ( uf_info - > urfet , & uf_regs - > urfet ) ;
iowrite16be ( uf_info - > urfset , & uf_regs - > urfset ) ;
iowrite16be ( uf_info - > utfs , & uf_regs - > utfs ) ;
iowrite16be ( uf_info - > utfet , & uf_regs - > utfet ) ;
iowrite16be ( uf_info - > utftt , & uf_regs - > utftt ) ;
/* utfb, urfb are offsets from MURAM base */
iowrite32be ( uccf - > ucc_fast_tx_virtual_fifo_base_offset , & uf_regs - > utfb ) ;
iowrite32be ( uccf - > ucc_fast_rx_virtual_fifo_base_offset , & uf_regs - > urfb ) ;
/* Rx Tx and sync clock routing */
resume_clk_config ( priv ) ;
iowrite32be ( uf_info - > uccm_mask , & uf_regs - > uccm ) ;
iowrite32be ( 0xffffffff , & uf_regs - > ucce ) ;
ucc_fast_disable ( priv - > uccf , COMM_DIR_RX | COMM_DIR_TX ) ;
/* rebuild SIRAM */
if ( priv - > tsa )
ucc_tdm_init ( priv - > utdm , priv - > ut_info ) ;
/* Write to QE CECR, UCCx channel to Stop Transmission */
cecr_subblock = ucc_fast_get_qe_cr_subblock ( uf_info - > ucc_num ) ;
ret = qe_issue_cmd ( QE_STOP_TX , cecr_subblock ,
( u8 ) QE_CR_PROTOCOL_UNSPECIFIED , 0 ) ;
/* Set UPSMR normal mode */
iowrite32be ( 0 , & uf_regs - > upsmr ) ;
/* init parameter base */
cecr_subblock = ucc_fast_get_qe_cr_subblock ( uf_info - > ucc_num ) ;
ret = qe_issue_cmd ( QE_ASSIGN_PAGE_TO_DEVICE , cecr_subblock ,
QE_CR_PROTOCOL_UNSPECIFIED , priv - > ucc_pram_offset ) ;
priv - > ucc_pram = ( struct ucc_hdlc_param __iomem * )
qe_muram_addr ( priv - > ucc_pram_offset ) ;
/* restore ucc parameter */
memcpy_toio ( priv - > ucc_pram , priv - > ucc_pram_bak ,
sizeof ( struct ucc_hdlc_param ) ) ;
kfree ( priv - > ucc_pram_bak ) ;
/* rebuild BD entry */
for ( i = 0 ; i < RX_BD_RING_LEN ; i + + ) {
if ( i < ( RX_BD_RING_LEN - 1 ) )
bd_status = R_E_S | R_I_S ;
else
bd_status = R_E_S | R_I_S | R_W_S ;
iowrite16be ( bd_status , & priv - > rx_bd_base [ i ] . status ) ;
iowrite32be ( priv - > dma_rx_addr + i * MAX_RX_BUF_LENGTH ,
& priv - > rx_bd_base [ i ] . buf ) ;
}
for ( i = 0 ; i < TX_BD_RING_LEN ; i + + ) {
if ( i < ( TX_BD_RING_LEN - 1 ) )
bd_status = T_I_S | T_TC_S ;
else
bd_status = T_I_S | T_TC_S | T_W_S ;
iowrite16be ( bd_status , & priv - > tx_bd_base [ i ] . status ) ;
iowrite32be ( priv - > dma_tx_addr + i * MAX_RX_BUF_LENGTH ,
& priv - > tx_bd_base [ i ] . buf ) ;
}
/* if hdlc is busy enable TX and RX */
if ( priv - > hdlc_busy = = 1 ) {
cecr_subblock = ucc_fast_get_qe_cr_subblock (
priv - > ut_info - > uf_info . ucc_num ) ;
qe_issue_cmd ( QE_INIT_TX_RX , cecr_subblock ,
( u8 ) QE_CR_PROTOCOL_UNSPECIFIED , 0 ) ;
ucc_fast_enable ( priv - > uccf , COMM_DIR_RX | COMM_DIR_TX ) ;
/* Enable the TDM port */
if ( priv - > tsa )
utdm - > si_regs - > siglmr1_h | = ( 0x1 < < utdm - > tdm_port ) ;
}
napi_enable ( & priv - > napi ) ;
netif_device_attach ( priv - > ndev ) ;
return 0 ;
}
static const struct dev_pm_ops uhdlc_pm_ops = {
. suspend = uhdlc_suspend ,
. resume = uhdlc_resume ,
. freeze = uhdlc_suspend ,
. thaw = uhdlc_resume ,
} ;
# define HDLC_PM_OPS (&uhdlc_pm_ops)
# else
# define HDLC_PM_OPS NULL
# endif
static const struct net_device_ops uhdlc_ops = {
. ndo_open = uhdlc_open ,
. ndo_stop = uhdlc_close ,
. ndo_start_xmit = hdlc_start_xmit ,
. ndo_do_ioctl = uhdlc_ioctl ,
} ;
static int ucc_hdlc_probe ( struct platform_device * pdev )
{
struct device_node * np = pdev - > dev . of_node ;
struct ucc_hdlc_private * uhdlc_priv = NULL ;
struct ucc_tdm_info * ut_info ;
2017-05-17 17:24:33 +02:00
struct ucc_tdm * utdm = NULL ;
2016-06-06 14:30:02 +08:00
struct resource res ;
struct net_device * dev ;
hdlc_device * hdlc ;
int ucc_num ;
const char * sprop ;
int ret ;
u32 val ;
ret = of_property_read_u32_index ( np , " cell-index " , 0 , & val ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Invalid ucc property \n " ) ;
return - ENODEV ;
}
ucc_num = val - 1 ;
if ( ( ucc_num > 3 ) | | ( ucc_num < 0 ) ) {
dev_err ( & pdev - > dev , " : Invalid UCC num \n " ) ;
return - EINVAL ;
}
memcpy ( & utdm_info [ ucc_num ] , & utdm_primary_info ,
sizeof ( utdm_primary_info ) ) ;
ut_info = & utdm_info [ ucc_num ] ;
ut_info - > uf_info . ucc_num = ucc_num ;
sprop = of_get_property ( np , " rx-clock-name " , NULL ) ;
if ( sprop ) {
ut_info - > uf_info . rx_clock = qe_clock_source ( sprop ) ;
if ( ( ut_info - > uf_info . rx_clock < QE_CLK_NONE ) | |
( ut_info - > uf_info . rx_clock > QE_CLK24 ) ) {
dev_err ( & pdev - > dev , " Invalid rx-clock-name property \n " ) ;
return - EINVAL ;
}
} else {
dev_err ( & pdev - > dev , " Invalid rx-clock-name property \n " ) ;
return - EINVAL ;
}
sprop = of_get_property ( np , " tx-clock-name " , NULL ) ;
if ( sprop ) {
ut_info - > uf_info . tx_clock = qe_clock_source ( sprop ) ;
if ( ( ut_info - > uf_info . tx_clock < QE_CLK_NONE ) | |
( ut_info - > uf_info . tx_clock > QE_CLK24 ) ) {
dev_err ( & pdev - > dev , " Invalid tx-clock-name property \n " ) ;
return - EINVAL ;
}
} else {
dev_err ( & pdev - > dev , " Invalid tx-clock-name property \n " ) ;
return - EINVAL ;
}
ret = of_address_to_resource ( np , 0 , & res ) ;
if ( ret )
return - EINVAL ;
ut_info - > uf_info . regs = res . start ;
ut_info - > uf_info . irq = irq_of_parse_and_map ( np , 0 ) ;
uhdlc_priv = kzalloc ( sizeof ( * uhdlc_priv ) , GFP_KERNEL ) ;
if ( ! uhdlc_priv ) {
2016-07-15 10:38:25 +08:00
return - ENOMEM ;
2016-06-06 14:30:02 +08:00
}
dev_set_drvdata ( & pdev - > dev , uhdlc_priv ) ;
uhdlc_priv - > dev = & pdev - > dev ;
uhdlc_priv - > ut_info = ut_info ;
if ( of_get_property ( np , " fsl,tdm-interface " , NULL ) )
uhdlc_priv - > tsa = 1 ;
if ( of_get_property ( np , " fsl,ucc-internal-loopback " , NULL ) )
uhdlc_priv - > loopback = 1 ;
2017-05-17 17:24:38 +02:00
if ( of_get_property ( np , " fsl,hdlc-bus " , NULL ) )
uhdlc_priv - > hdlc_bus = 1 ;
2016-06-06 14:30:02 +08:00
if ( uhdlc_priv - > tsa = = 1 ) {
utdm = kzalloc ( sizeof ( * utdm ) , GFP_KERNEL ) ;
if ( ! utdm ) {
ret = - ENOMEM ;
dev_err ( & pdev - > dev , " No mem to alloc ucc tdm data \n " ) ;
2016-07-15 10:38:25 +08:00
goto free_uhdlc_priv ;
2016-06-06 14:30:02 +08:00
}
uhdlc_priv - > utdm = utdm ;
ret = ucc_of_parse_tdm ( np , utdm , ut_info ) ;
if ( ret )
2016-07-15 10:38:25 +08:00
goto free_utdm ;
2016-06-06 14:30:02 +08:00
}
ret = uhdlc_init ( uhdlc_priv ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Failed to init uhdlc \n " ) ;
2016-07-15 10:38:25 +08:00
goto free_utdm ;
2016-06-06 14:30:02 +08:00
}
dev = alloc_hdlcdev ( uhdlc_priv ) ;
if ( ! dev ) {
ret = - ENOMEM ;
pr_err ( " ucc_hdlc: unable to allocate memory \n " ) ;
2016-07-15 10:38:25 +08:00
goto undo_uhdlc_init ;
2016-06-06 14:30:02 +08:00
}
uhdlc_priv - > ndev = dev ;
hdlc = dev_to_hdlc ( dev ) ;
dev - > tx_queue_len = 16 ;
dev - > netdev_ops = & uhdlc_ops ;
hdlc - > attach = ucc_hdlc_attach ;
hdlc - > xmit = ucc_hdlc_tx ;
netif_napi_add ( dev , & uhdlc_priv - > napi , ucc_hdlc_poll , 32 ) ;
if ( register_hdlc_device ( dev ) ) {
ret = - ENOBUFS ;
pr_err ( " ucc_hdlc: unable to register hdlc device \n " ) ;
free_netdev ( dev ) ;
2016-07-15 10:38:25 +08:00
goto free_dev ;
2016-06-06 14:30:02 +08:00
}
return 0 ;
2016-07-15 10:38:25 +08:00
free_dev :
free_netdev ( dev ) ;
undo_uhdlc_init :
free_utdm :
2016-06-06 14:30:02 +08:00
if ( uhdlc_priv - > tsa )
kfree ( utdm ) ;
2016-07-15 10:38:25 +08:00
free_uhdlc_priv :
2016-06-06 14:30:02 +08:00
kfree ( uhdlc_priv ) ;
return ret ;
}
static int ucc_hdlc_remove ( struct platform_device * pdev )
{
struct ucc_hdlc_private * priv = dev_get_drvdata ( & pdev - > dev ) ;
uhdlc_memclean ( priv ) ;
if ( priv - > utdm - > si_regs ) {
iounmap ( priv - > utdm - > si_regs ) ;
priv - > utdm - > si_regs = NULL ;
}
if ( priv - > utdm - > siram ) {
iounmap ( priv - > utdm - > siram ) ;
priv - > utdm - > siram = NULL ;
}
kfree ( priv ) ;
dev_info ( & pdev - > dev , " UCC based hdlc module removed \n " ) ;
return 0 ;
}
static const struct of_device_id fsl_ucc_hdlc_of_match [ ] = {
{
. compatible = " fsl,ucc-hdlc " ,
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , fsl_ucc_hdlc_of_match ) ;
static struct platform_driver ucc_hdlc_driver = {
. probe = ucc_hdlc_probe ,
. remove = ucc_hdlc_remove ,
. driver = {
. name = DRV_NAME ,
. pm = HDLC_PM_OPS ,
. of_match_table = fsl_ucc_hdlc_of_match ,
} ,
} ;
2016-07-19 11:25:16 +00:00
module_platform_driver ( ucc_hdlc_driver ) ;
2017-02-17 11:31:22 +01:00
MODULE_LICENSE ( " GPL " ) ;