From 716ca530e1c4515d8683c9d5be3d56b301758b66 Mon Sep 17 00:00:00 2001 From: James <> Date: Wed, 4 Nov 2015 11:49:21 +0000 Subject: trunk-47381 --- .../kernel/lantiq/ltq-ptm/src/ifxmips_ptm_adsl.c | 1555 ++++++++++++++++++++ 1 file changed, 1555 insertions(+) create mode 100644 package/kernel/lantiq/ltq-ptm/src/ifxmips_ptm_adsl.c (limited to 'package/kernel/lantiq/ltq-ptm/src/ifxmips_ptm_adsl.c') diff --git a/package/kernel/lantiq/ltq-ptm/src/ifxmips_ptm_adsl.c b/package/kernel/lantiq/ltq-ptm/src/ifxmips_ptm_adsl.c new file mode 100644 index 0000000..38001c3 --- /dev/null +++ b/package/kernel/lantiq/ltq-ptm/src/ifxmips_ptm_adsl.c @@ -0,0 +1,1555 @@ +/****************************************************************************** +** +** FILE NAME : ifxmips_ptm_adsl.c +** PROJECT : UEIP +** MODULES : PTM +** +** DATE : 7 Jul 2009 +** AUTHOR : Xu Liang +** DESCRIPTION : PTM driver common source file (core functions for Danube/ +** Amazon-SE/AR9) +** COPYRIGHT : Copyright (c) 2006 +** Infineon Technologies AG +** Am Campeon 1-12, 85579 Neubiberg, Germany +** +** 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. +** +** HISTORY +** $Date $Author $Comment +** 07 JUL 2009 Xu Liang Init Version +*******************************************************************************/ + + + +/* + * #################################### + * Head File + * #################################### + */ + +/* + * Common Head File + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Chip Specific Head File + */ +#include "ifxmips_ptm_adsl.h" + + +#include + +/* + * #################################### + * Kernel Version Adaption + * #################################### + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11) + #define MODULE_PARM_ARRAY(a, b) module_param_array(a, int, NULL, 0) + #define MODULE_PARM(a, b) module_param(a, int, 0) +#else + #define MODULE_PARM_ARRAY(a, b) MODULE_PARM(a, b) +#endif + + + +/* + * #################################### + * Parameters to Configure PPE + * #################################### + */ + +static int write_desc_delay = 0x20; /* Write descriptor delay */ + +static int rx_max_packet_size = ETH_MAX_FRAME_LENGTH; + /* Max packet size for RX */ + +static int dma_rx_descriptor_length = 24; /* Number of descriptors per DMA RX channel */ +static int dma_tx_descriptor_length = 24; /* Number of descriptors per DMA TX channel */ + +static int eth_efmtc_crc_cfg = 0x03100710; /* default: tx_eth_crc_check: 1, tx_tc_crc_check: 1, tx_tc_crc_len = 16 */ + /* rx_eth_crc_present: 1, rx_eth_crc_check: 1, rx_tc_crc_check: 1, rx_tc_crc_len = 16 */ + +MODULE_PARM(write_desc_delay, "i"); +MODULE_PARM_DESC(write_desc_delay, "PPE core clock cycles between descriptor write and effectiveness in external RAM"); + +MODULE_PARM(rx_max_packet_size, "i"); +MODULE_PARM_DESC(rx_max_packet_size, "Max packet size in byte for downstream ethernet frames"); + +MODULE_PARM(dma_rx_descriptor_length, "i"); +MODULE_PARM_DESC(dma_rx_descriptor_length, "Number of descriptor assigned to DMA RX channel (>16)"); +MODULE_PARM(dma_tx_descriptor_length, "i"); +MODULE_PARM_DESC(dma_tx_descriptor_length, "Number of descriptor assigned to DMA TX channel (>16)"); + +MODULE_PARM(eth_efmtc_crc_cfg, "i"); +MODULE_PARM_DESC(eth_efmtc_crc_cfg, "Configuration for PTM TX/RX ethernet/efm-tc CRC"); + + + +/* + * #################################### + * Definition + * #################################### + */ + + +#define DUMP_SKB_LEN ~0 + + + +/* + * #################################### + * Declaration + * #################################### + */ + +/* + * Network Operations + */ +static void ptm_setup(struct net_device *, int); +static struct net_device_stats *ptm_get_stats(struct net_device *); +static int ptm_open(struct net_device *); +static int ptm_stop(struct net_device *); + static unsigned int ptm_poll(int, unsigned int); + static int ptm_napi_poll(struct napi_struct *, int); +static int ptm_hard_start_xmit(struct sk_buff *, struct net_device *); +static int ptm_ioctl(struct net_device *, struct ifreq *, int); +static void ptm_tx_timeout(struct net_device *); + +/* + * DSL Data LED + */ +static INLINE void adsl_led_flash(void); + +/* + * buffer manage functions + */ +static INLINE struct sk_buff* alloc_skb_rx(void); +//static INLINE struct sk_buff* alloc_skb_tx(unsigned int); +static INLINE struct sk_buff *get_skb_rx_pointer(unsigned int); +static INLINE int get_tx_desc(unsigned int, unsigned int *); + +/* + * Mailbox handler and signal function + */ +static INLINE int mailbox_rx_irq_handler(unsigned int); +static irqreturn_t mailbox_irq_handler(int, void *); +static INLINE void mailbox_signal(unsigned int, int); +#ifdef CONFIG_IFX_PTM_RX_TASKLET + static void do_ptm_tasklet(unsigned long); +#endif + +/* + * Debug Functions + */ +#if defined(DEBUG_DUMP_SKB) && DEBUG_DUMP_SKB + static void dump_skb(struct sk_buff *, u32, char *, int, int, int); +#else + #define dump_skb(skb, len, title, port, ch, is_tx) do {} while (0) +#endif +#if defined(ENABLE_DBG_PROC) && ENABLE_DBG_PROC + static void skb_swap(struct sk_buff *); +#else + #define skb_swap(skb) do {} while (0) +#endif + +/* + * Proc File Functions + */ +static INLINE void proc_file_create(void); +static INLINE void proc_file_delete(void); +static int proc_read_version(char *, char **, off_t, int, int *, void *); +static int proc_read_wanmib(char *, char **, off_t, int, int *, void *); +static int proc_write_wanmib(struct file *, const char *, unsigned long, void *); +#if defined(ENABLE_FW_PROC) && ENABLE_FW_PROC + static int proc_read_genconf(char *, char **, off_t, int, int *, void *); +#endif +#if defined(ENABLE_DBG_PROC) && ENABLE_DBG_PROC + static int proc_read_dbg(char *, char **, off_t, int, int *, void *); + static int proc_write_dbg(struct file *, const char *, unsigned long, void *); +#endif + +/* + * Proc Help Functions + */ +static INLINE int stricmp(const char *, const char *); +#if defined(ENABLE_DBG_PROC) && ENABLE_DBG_PROC + static INLINE int strincmp(const char *, const char *, int); +#endif +static INLINE int ifx_ptm_version(char *); + +/* + * Init & clean-up functions + */ +static INLINE void check_parameters(void); +static INLINE int init_priv_data(void); +static INLINE void clear_priv_data(void); +static INLINE void init_tables(void); + +/* + * Exteranl Function + */ +#if defined(CONFIG_IFXMIPS_DSL_CPE_MEI) || defined(CONFIG_IFXMIPS_DSL_CPE_MEI_MODULE) + extern int ifx_mei_atm_showtime_check(int *is_showtime, struct port_cell_info *port_cell, void **xdata_addr); +#else + static inline int ifx_mei_atm_showtime_check(int *is_showtime, struct port_cell_info *port_cell, void **xdata_addr) + { + if ( is_showtime != NULL ) + *is_showtime = 0; + return 0; + } +#endif + +/* + * External variable + */ +#if defined(CONFIG_IFXMIPS_DSL_CPE_MEI) || defined(CONFIG_IFXMIPS_DSL_CPE_MEI_MODULE) + extern int (*ifx_mei_atm_showtime_enter)(struct port_cell_info *, void *); + extern int (*ifx_mei_atm_showtime_exit)(void); +#else + int (*ifx_mei_atm_showtime_enter)(struct port_cell_info *, void *) = NULL; + EXPORT_SYMBOL(ifx_mei_atm_showtime_enter); + int (*ifx_mei_atm_showtime_exit)(void) = NULL; + EXPORT_SYMBOL(ifx_mei_atm_showtime_exit); +#endif + + + +/* + * #################################### + * Local Variable + * #################################### + */ + +static struct ptm_priv_data g_ptm_priv_data; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32) +static struct net_device_ops g_ptm_netdev_ops = { + .ndo_get_stats = ptm_get_stats, + .ndo_open = ptm_open, + .ndo_stop = ptm_stop, + .ndo_start_xmit = ptm_hard_start_xmit, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = eth_mac_addr, + .ndo_change_mtu = eth_change_mtu, + .ndo_do_ioctl = ptm_ioctl, + .ndo_tx_timeout = ptm_tx_timeout, +}; +#endif + +static struct net_device *g_net_dev[2] = {0}; +static char *g_net_dev_name[2] = {"ptm0", "ptmfast0"}; + +#ifdef CONFIG_IFX_PTM_RX_TASKLET + static struct tasklet_struct g_ptm_tasklet[] = { + {NULL, 0, ATOMIC_INIT(0), do_ptm_tasklet, 0}, + {NULL, 0, ATOMIC_INIT(0), do_ptm_tasklet, 1}, + }; +#endif + +unsigned int ifx_ptm_dbg_enable = DBG_ENABLE_MASK_ERR; + +static struct proc_dir_entry* g_ptm_dir = NULL; + +static int g_showtime = 0; + + + +/* + * #################################### + * Local Function + * #################################### + */ + +static void ptm_setup(struct net_device *dev, int ndev) +{ + /* hook network operations */ + dev->netdev_ops = &g_ptm_netdev_ops; + netif_napi_add(dev, &g_ptm_priv_data.itf[ndev].napi, ptm_napi_poll, 25); + dev->watchdog_timeo = ETH_WATCHDOG_TIMEOUT; + + dev->dev_addr[0] = 0x00; + dev->dev_addr[1] = 0x20; + dev->dev_addr[2] = 0xda; + dev->dev_addr[3] = 0x86; + dev->dev_addr[4] = 0x23; + dev->dev_addr[5] = 0x75 + ndev; +} + +static struct net_device_stats *ptm_get_stats(struct net_device *dev) +{ + int ndev; + + for ( ndev = 0; ndev < ARRAY_SIZE(g_net_dev) && g_net_dev[ndev] != dev; ndev++ ); + ASSERT(ndev >= 0 && ndev < ARRAY_SIZE(g_net_dev), "ndev = %d (wrong value)", ndev); + + g_ptm_priv_data.itf[ndev].stats.rx_errors = WAN_MIB_TABLE[ndev].wrx_tccrc_err_pdu + WAN_MIB_TABLE[ndev].wrx_ethcrc_err_pdu; + g_ptm_priv_data.itf[ndev].stats.rx_dropped = WAN_MIB_TABLE[ndev].wrx_nodesc_drop_pdu + WAN_MIB_TABLE[ndev].wrx_len_violation_drop_pdu + (WAN_MIB_TABLE[ndev].wrx_correct_pdu - g_ptm_priv_data.itf[ndev].stats.rx_packets); + + return &g_ptm_priv_data.itf[ndev].stats; +} + +static int ptm_open(struct net_device *dev) +{ + int ndev; + + for ( ndev = 0; ndev < ARRAY_SIZE(g_net_dev) && g_net_dev[ndev] != dev; ndev++ ); + ASSERT(ndev >= 0 && ndev < ARRAY_SIZE(g_net_dev), "ndev = %d (wrong value)", ndev); + + napi_enable(&g_ptm_priv_data.itf[ndev].napi); + + IFX_REG_W32_MASK(0, 1 << ndev, MBOX_IGU1_IER); + + netif_start_queue(dev); + + return 0; +} + +static int ptm_stop(struct net_device *dev) +{ + int ndev; + + for ( ndev = 0; ndev < ARRAY_SIZE(g_net_dev) && g_net_dev[ndev] != dev; ndev++ ); + ASSERT(ndev >= 0 && ndev < ARRAY_SIZE(g_net_dev), "ndev = %d (wrong value)", ndev); + + IFX_REG_W32_MASK((1 << ndev) | (1 << (ndev + 16)), 0, MBOX_IGU1_IER); + + napi_disable(&g_ptm_priv_data.itf[ndev].napi); + + netif_stop_queue(dev); + + return 0; +} + +static unsigned int ptm_poll(int ndev, unsigned int work_to_do) +{ + unsigned int work_done = 0; + + ASSERT(ndev >= 0 && ndev < ARRAY_SIZE(g_net_dev), "ndev = %d (wrong value)", ndev); + + while ( work_done < work_to_do && WRX_DMA_CHANNEL_CONFIG(ndev)->vlddes > 0 ) { + if ( mailbox_rx_irq_handler(ndev) < 0 ) + break; + + work_done++; + } + + return work_done; +} +static int ptm_napi_poll(struct napi_struct *napi, int budget) +{ + int ndev; + unsigned int work_done; + + for ( ndev = 0; ndev < ARRAY_SIZE(g_net_dev) && g_net_dev[ndev] != napi->dev; ndev++ ); + + work_done = ptm_poll(ndev, budget); + + // interface down + if ( !netif_running(napi->dev) ) { + napi_complete(napi); + return work_done; + } + + // no more traffic + if ( WRX_DMA_CHANNEL_CONFIG(ndev)->vlddes == 0 ) { + // clear interrupt + IFX_REG_W32_MASK(0, 1 << ndev, MBOX_IGU1_ISRC); + // double check + if ( WRX_DMA_CHANNEL_CONFIG(ndev)->vlddes == 0 ) { + napi_complete(napi); + IFX_REG_W32_MASK(0, 1 << ndev, MBOX_IGU1_IER); + return work_done; + } + } + + // next round + return work_done; +} + +static int ptm_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + int ndev; + unsigned int f_full; + int desc_base; + register struct tx_descriptor reg_desc = {0}; + + for ( ndev = 0; ndev < ARRAY_SIZE(g_net_dev) && g_net_dev[ndev] != dev; ndev++ ); + ASSERT(ndev >= 0 && ndev < ARRAY_SIZE(g_net_dev), "ndev = %d (wrong value)", ndev); + + if ( !g_showtime ) { + err("not in showtime"); + goto PTM_HARD_START_XMIT_FAIL; + } + + /* allocate descriptor */ + desc_base = get_tx_desc(ndev, &f_full); + if ( f_full ) { + dev->trans_start = jiffies; + netif_stop_queue(dev); + + IFX_REG_W32_MASK(0, 1 << (ndev + 16), MBOX_IGU1_ISRC); + IFX_REG_W32_MASK(0, 1 << (ndev + 16), MBOX_IGU1_IER); + } + if ( desc_base < 0 ) + goto PTM_HARD_START_XMIT_FAIL; + + if ( g_ptm_priv_data.itf[ndev].tx_skb[desc_base] != NULL ) + dev_kfree_skb_any(g_ptm_priv_data.itf[ndev].tx_skb[desc_base]); + g_ptm_priv_data.itf[ndev].tx_skb[desc_base] = skb; + + reg_desc.dataptr = (unsigned int)skb->data >> 2; + reg_desc.datalen = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len; + reg_desc.byteoff = (unsigned int)skb->data & (DATA_BUFFER_ALIGNMENT - 1); + reg_desc.own = 1; + reg_desc.c = 1; + reg_desc.sop = reg_desc.eop = 1; + + /* write discriptor to memory and write back cache */ + g_ptm_priv_data.itf[ndev].tx_desc[desc_base] = reg_desc; + dma_cache_wback((unsigned long)skb->data, skb->len); + wmb(); + + dump_skb(skb, DUMP_SKB_LEN, (char *)__func__, ndev, ndev, 1); + + if ( (ifx_ptm_dbg_enable & DBG_ENABLE_MASK_MAC_SWAP) ) { + skb_swap(skb); + } + + g_ptm_priv_data.itf[ndev].stats.tx_packets++; + g_ptm_priv_data.itf[ndev].stats.tx_bytes += reg_desc.datalen; + + dev->trans_start = jiffies; + mailbox_signal(ndev, 1); + + adsl_led_flash(); + + return NETDEV_TX_OK; + +PTM_HARD_START_XMIT_FAIL: + dev_kfree_skb_any(skb); + g_ptm_priv_data.itf[ndev].stats.tx_dropped++; + return NETDEV_TX_OK; +} + +static int ptm_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + int ndev; + + for ( ndev = 0; ndev < ARRAY_SIZE(g_net_dev) && g_net_dev[ndev] != dev; ndev++ ); + ASSERT(ndev >= 0 && ndev < ARRAY_SIZE(g_net_dev), "ndev = %d (wrong value)", ndev); + + switch ( cmd ) + { + case IFX_PTM_MIB_CW_GET: + ((PTM_CW_IF_ENTRY_T *)ifr->ifr_data)->ifRxNoIdleCodewords = WAN_MIB_TABLE[ndev].wrx_nonidle_cw; + ((PTM_CW_IF_ENTRY_T *)ifr->ifr_data)->ifRxIdleCodewords = WAN_MIB_TABLE[ndev].wrx_idle_cw; + ((PTM_CW_IF_ENTRY_T *)ifr->ifr_data)->ifRxCodingViolation = WAN_MIB_TABLE[ndev].wrx_err_cw; + ((PTM_CW_IF_ENTRY_T *)ifr->ifr_data)->ifTxNoIdleCodewords = 0; + ((PTM_CW_IF_ENTRY_T *)ifr->ifr_data)->ifTxIdleCodewords = 0; + break; + case IFX_PTM_MIB_FRAME_GET: + ((PTM_FRAME_MIB_T *)ifr->ifr_data)->RxCorrect = WAN_MIB_TABLE[ndev].wrx_correct_pdu; + ((PTM_FRAME_MIB_T *)ifr->ifr_data)->TC_CrcError = WAN_MIB_TABLE[ndev].wrx_tccrc_err_pdu; + ((PTM_FRAME_MIB_T *)ifr->ifr_data)->RxDropped = WAN_MIB_TABLE[ndev].wrx_nodesc_drop_pdu + WAN_MIB_TABLE[ndev].wrx_len_violation_drop_pdu; + ((PTM_FRAME_MIB_T *)ifr->ifr_data)->TxSend = WAN_MIB_TABLE[ndev].wtx_total_pdu; + break; + case IFX_PTM_CFG_GET: + ((IFX_PTM_CFG_T *)ifr->ifr_data)->RxEthCrcPresent = CFG_ETH_EFMTC_CRC->rx_eth_crc_present; + ((IFX_PTM_CFG_T *)ifr->ifr_data)->RxEthCrcCheck = CFG_ETH_EFMTC_CRC->rx_eth_crc_check; + ((IFX_PTM_CFG_T *)ifr->ifr_data)->RxTcCrcCheck = CFG_ETH_EFMTC_CRC->rx_tc_crc_check; + ((IFX_PTM_CFG_T *)ifr->ifr_data)->RxTcCrcLen = CFG_ETH_EFMTC_CRC->rx_tc_crc_len; + ((IFX_PTM_CFG_T *)ifr->ifr_data)->TxEthCrcGen = CFG_ETH_EFMTC_CRC->tx_eth_crc_gen; + ((IFX_PTM_CFG_T *)ifr->ifr_data)->TxTcCrcGen = CFG_ETH_EFMTC_CRC->tx_tc_crc_gen; + ((IFX_PTM_CFG_T *)ifr->ifr_data)->TxTcCrcLen = CFG_ETH_EFMTC_CRC->tx_tc_crc_len; + break; + case IFX_PTM_CFG_SET: + CFG_ETH_EFMTC_CRC->rx_eth_crc_present = ((IFX_PTM_CFG_T *)ifr->ifr_data)->RxEthCrcPresent ? 1 : 0; + CFG_ETH_EFMTC_CRC->rx_eth_crc_check = ((IFX_PTM_CFG_T *)ifr->ifr_data)->RxEthCrcCheck ? 1 : 0; + if ( ((IFX_PTM_CFG_T *)ifr->ifr_data)->RxTcCrcCheck && (((IFX_PTM_CFG_T *)ifr->ifr_data)->RxTcCrcLen == 16 || ((IFX_PTM_CFG_T *)ifr->ifr_data)->RxTcCrcLen == 32) ) + { + CFG_ETH_EFMTC_CRC->rx_tc_crc_check = 1; + CFG_ETH_EFMTC_CRC->rx_tc_crc_len = ((IFX_PTM_CFG_T *)ifr->ifr_data)->RxTcCrcLen; + } + else + { + CFG_ETH_EFMTC_CRC->rx_tc_crc_check = 0; + CFG_ETH_EFMTC_CRC->rx_tc_crc_len = 0; + } + CFG_ETH_EFMTC_CRC->tx_eth_crc_gen = ((IFX_PTM_CFG_T *)ifr->ifr_data)->TxEthCrcGen ? 1 : 0; + if ( ((IFX_PTM_CFG_T *)ifr->ifr_data)->TxTcCrcGen && (((IFX_PTM_CFG_T *)ifr->ifr_data)->TxTcCrcLen == 16 || ((IFX_PTM_CFG_T *)ifr->ifr_data)->TxTcCrcLen == 32) ) + { + CFG_ETH_EFMTC_CRC->tx_tc_crc_gen = 1; + CFG_ETH_EFMTC_CRC->tx_tc_crc_len = ((IFX_PTM_CFG_T *)ifr->ifr_data)->TxTcCrcLen; + } + else + { + CFG_ETH_EFMTC_CRC->tx_tc_crc_gen = 0; + CFG_ETH_EFMTC_CRC->tx_tc_crc_len = 0; + } + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static void ptm_tx_timeout(struct net_device *dev) +{ + int ndev; + + for ( ndev = 0; ndev < ARRAY_SIZE(g_net_dev) && g_net_dev[ndev] != dev; ndev++ ); + ASSERT(ndev >= 0 && ndev < ARRAY_SIZE(g_net_dev), "ndev = %d (wrong value)", ndev); + + /* disable TX irq, release skb when sending new packet */ + IFX_REG_W32_MASK(1 << (ndev + 16), 0, MBOX_IGU1_IER); + + /* wake up TX queue */ + netif_wake_queue(dev); + + return; +} + +static INLINE void adsl_led_flash(void) +{ +} + +static INLINE struct sk_buff* alloc_skb_rx(void) +{ + struct sk_buff *skb; + + /* allocate memroy including trailer and padding */ + skb = dev_alloc_skb(rx_max_packet_size + RX_HEAD_MAC_ADDR_ALIGNMENT + DATA_BUFFER_ALIGNMENT); + if ( skb != NULL ) { + /* must be burst length alignment and reserve two more bytes for MAC address alignment */ + if ( ((unsigned int)skb->data & (DATA_BUFFER_ALIGNMENT - 1)) != 0 ) + skb_reserve(skb, ~((unsigned int)skb->data + (DATA_BUFFER_ALIGNMENT - 1)) & (DATA_BUFFER_ALIGNMENT - 1)); + /* pub skb in reserved area "skb->data - 4" */ + *((struct sk_buff **)skb->data - 1) = skb; + wmb(); + /* write back and invalidate cache */ + dma_cache_wback_inv((unsigned long)skb->data - sizeof(skb), sizeof(skb)); + /* invalidate cache */ + dma_cache_inv((unsigned long)skb->data, (unsigned int)skb->end - (unsigned int)skb->data); + } + + return skb; +} + +#if 0 +static INLINE struct sk_buff* alloc_skb_tx(unsigned int size) +{ + struct sk_buff *skb; + + /* allocate memory including padding */ + size = (size + DATA_BUFFER_ALIGNMENT - 1) & ~(DATA_BUFFER_ALIGNMENT - 1); + skb = dev_alloc_skb(size + DATA_BUFFER_ALIGNMENT); + /* must be burst length alignment */ + if ( skb != NULL ) + skb_reserve(skb, ~((unsigned int)skb->data + (DATA_BUFFER_ALIGNMENT - 1)) & (DATA_BUFFER_ALIGNMENT - 1)); + return skb; +} +#endif + +static INLINE struct sk_buff *get_skb_rx_pointer(unsigned int dataptr) +{ + unsigned int skb_dataptr; + struct sk_buff *skb; + + skb_dataptr = ((dataptr - 1) << 2) | KSEG1; + skb = *(struct sk_buff **)skb_dataptr; + + ASSERT((unsigned int)skb >= KSEG0, "invalid skb - skb = %#08x, dataptr = %#08x", (unsigned int)skb, dataptr); + ASSERT(((unsigned int)skb->data | KSEG1) == ((dataptr << 2) | KSEG1), "invalid skb - skb = %#08x, skb->data = %#08x, dataptr = %#08x", (unsigned int)skb, (unsigned int)skb->data, dataptr); + + return skb; +} + +static INLINE int get_tx_desc(unsigned int itf, unsigned int *f_full) +{ + int desc_base = -1; + struct ptm_itf *p_itf = &g_ptm_priv_data.itf[itf]; + + // assume TX is serial operation + // no protection provided + + *f_full = 1; + + if ( p_itf->tx_desc[p_itf->tx_desc_pos].own == 0 ) { + desc_base = p_itf->tx_desc_pos; + if ( ++(p_itf->tx_desc_pos) == dma_tx_descriptor_length ) + p_itf->tx_desc_pos = 0; + if ( p_itf->tx_desc[p_itf->tx_desc_pos].own == 0 ) + *f_full = 0; + } + + return desc_base; +} + +static INLINE int mailbox_rx_irq_handler(unsigned int ch) // return: < 0 - descriptor not available, 0 - received one packet +{ + unsigned int ndev = ch; + struct sk_buff *skb; + struct sk_buff *new_skb; + volatile struct rx_descriptor *desc; + struct rx_descriptor reg_desc; + int netif_rx_ret; + + desc = &g_ptm_priv_data.itf[ndev].rx_desc[g_ptm_priv_data.itf[ndev].rx_desc_pos]; + if ( desc->own || !desc->c ) // if PP32 hold descriptor or descriptor not completed + return -EAGAIN; + if ( ++g_ptm_priv_data.itf[ndev].rx_desc_pos == dma_rx_descriptor_length ) + g_ptm_priv_data.itf[ndev].rx_desc_pos = 0; + + reg_desc = *desc; + skb = get_skb_rx_pointer(reg_desc.dataptr); + + if ( !reg_desc.err ) { + new_skb = alloc_skb_rx(); + if ( new_skb != NULL ) { + skb_reserve(skb, reg_desc.byteoff); + skb_put(skb, reg_desc.datalen); + + dump_skb(skb, DUMP_SKB_LEN, (char *)__func__, ndev, ndev, 0); + + // parse protocol header + skb->dev = g_net_dev[ndev]; + skb->protocol = eth_type_trans(skb, skb->dev); + + g_net_dev[ndev]->last_rx = jiffies; + + netif_rx_ret = netif_receive_skb(skb); + + if ( netif_rx_ret != NET_RX_DROP ) { + g_ptm_priv_data.itf[ndev].stats.rx_packets++; + g_ptm_priv_data.itf[ndev].stats.rx_bytes += reg_desc.datalen; + } + + reg_desc.dataptr = ((unsigned int)new_skb->data >> 2) & 0x0FFFFFFF; + reg_desc.byteoff = RX_HEAD_MAC_ADDR_ALIGNMENT; + } + } + else + reg_desc.err = 0; + + reg_desc.datalen = rx_max_packet_size; + reg_desc.own = 1; + reg_desc.c = 0; + + // update descriptor + *desc = reg_desc; + wmb(); + + mailbox_signal(ndev, 0); + + adsl_led_flash(); + + return 0; +} + +static irqreturn_t mailbox_irq_handler(int irq, void *dev_id) +{ + unsigned int isr; + int i; + + isr = IFX_REG_R32(MBOX_IGU1_ISR); + IFX_REG_W32(isr, MBOX_IGU1_ISRC); + isr &= IFX_REG_R32(MBOX_IGU1_IER); + + while ( (i = __fls(isr)) >= 0 ) { + isr ^= 1 << i; + + if ( i >= 16 ) { + // TX + IFX_REG_W32_MASK(1 << i, 0, MBOX_IGU1_IER); + i -= 16; + if ( i < MAX_ITF_NUMBER ) + netif_wake_queue(g_net_dev[i]); + } + else { + // RX +#ifdef CONFIG_IFX_PTM_RX_INTERRUPT + while ( WRX_DMA_CHANNEL_CONFIG(i)->vlddes > 0 ) + mailbox_rx_irq_handler(i); +#else + IFX_REG_W32_MASK(1 << i, 0, MBOX_IGU1_IER); + napi_schedule(&g_ptm_priv_data.itf[i].napi); +#endif + } + } + + return IRQ_HANDLED; +} + +static INLINE void mailbox_signal(unsigned int itf, int is_tx) +{ + int count = 1000; + + if ( is_tx ) { + while ( MBOX_IGU3_ISR_ISR(itf + 16) && count > 0 ) + count--; + IFX_REG_W32(MBOX_IGU3_ISRS_SET(itf + 16), MBOX_IGU3_ISRS); + } + else { + while ( MBOX_IGU3_ISR_ISR(itf) && count > 0 ) + count--; + IFX_REG_W32(MBOX_IGU3_ISRS_SET(itf), MBOX_IGU3_ISRS); + } + + ASSERT(count != 0, "MBOX_IGU3_ISR = 0x%08x", IFX_REG_R32(MBOX_IGU3_ISR)); +} + +#ifdef CONFIG_IFX_PTM_RX_TASKLET +static void do_ptm_tasklet(unsigned long arg) +{ + unsigned int work_to_do = 25; + unsigned int work_done = 0; + + ASSERT(arg >= 0 && arg < ARRAY_SIZE(g_net_dev), "arg = %lu (wrong value)", arg); + + while ( work_done < work_to_do && WRX_DMA_CHANNEL_CONFIG(arg)->vlddes > 0 ) { + if ( mailbox_rx_irq_handler(arg) < 0 ) + break; + + work_done++; + } + + // interface down + if ( !netif_running(g_net_dev[arg]) ) + return; + + // no more traffic + if ( WRX_DMA_CHANNEL_CONFIG(arg)->vlddes == 0 ) { + // clear interrupt + IFX_REG_W32_MASK(0, 1 << arg, MBOX_IGU1_ISRC); + // double check + if ( WRX_DMA_CHANNEL_CONFIG(arg)->vlddes == 0 ) { + IFX_REG_W32_MASK(0, 1 << arg, MBOX_IGU1_IER); + return; + } + } + + // next round + tasklet_schedule(&g_ptm_tasklet[arg]); +} +#endif + +#if defined(DEBUG_DUMP_SKB) && DEBUG_DUMP_SKB +static void dump_skb(struct sk_buff *skb, u32 len, char *title, int port, int ch, int is_tx) +{ + int i; + + if ( !(ifx_ptm_dbg_enable & (is_tx ? DBG_ENABLE_MASK_DUMP_SKB_TX : DBG_ENABLE_MASK_DUMP_SKB_RX)) ) + return; + + if ( skb->len < len ) + len = skb->len; + + if ( len > rx_max_packet_size ) { + printk("too big data length: skb = %08x, skb->data = %08x, skb->len = %d\n", (u32)skb, (u32)skb->data, skb->len); + return; + } + + if ( ch >= 0 ) + printk("%s (port %d, ch %d)\n", title, port, ch); + else + printk("%s\n", title); + printk(" skb->data = %08X, skb->tail = %08X, skb->len = %d\n", (u32)skb->data, (u32)skb->tail, (int)skb->len); + for ( i = 1; i <= len; i++ ) { + if ( i % 16 == 1 ) + printk(" %4d:", i - 1); + printk(" %02X", (int)(*((char*)skb->data + i - 1) & 0xFF)); + if ( i % 16 == 0 ) + printk("\n"); + } + if ( (i - 1) % 16 != 0 ) + printk("\n"); +} +#endif + +#if defined(ENABLE_DBG_PROC) && ENABLE_DBG_PROC +static void skb_swap(struct sk_buff *skb) +{ + unsigned char tmp[8]; + unsigned char *p = skb->data; + + if ( !(p[0] & 0x01) ) { // bypass broadcast/multicast + // swap MAC + memcpy(tmp, p, 6); + memcpy(p, p + 6, 6); + memcpy(p + 6, tmp, 6); + p += 12; + + // bypass VLAN + while ( p[0] == 0x81 && p[1] == 0x00 ) + p += 4; + + // IP + if ( p[0] == 0x08 && p[1] == 0x00 ) { + p += 14; + memcpy(tmp, p, 4); + memcpy(p, p + 4, 4); + memcpy(p + 4, tmp, 4); + p += 8; + } + + dma_cache_wback((unsigned long)skb->data, (unsigned long)p - (unsigned long)skb->data); + } +} +#endif + +static INLINE void proc_file_create(void) +{ +#if defined(ENABLE_DBG_PROC) && ENABLE_DBG_PROC + struct proc_dir_entry *res; + + g_ptm_dir = proc_mkdir("driver/ifx_ptm", NULL); + + create_proc_read_entry("version", + 0, + g_ptm_dir, + proc_read_version, + NULL); + + res = create_proc_entry("wanmib", + 0, + g_ptm_dir); + if ( res != NULL ) { + res->read_proc = proc_read_wanmib; + res->write_proc = proc_write_wanmib; + } + +#if defined(ENABLE_FW_PROC) && ENABLE_FW_PROC + create_proc_read_entry("genconf", + 0, + g_ptm_dir, + proc_read_genconf, + NULL); + + #ifdef CONFIG_AR9 + create_proc_read_entry("regs", + 0, + g_ptm_dir, + ifx_ptm_proc_read_regs, + NULL); + #endif +#endif + + res = create_proc_entry("dbg", + 0, + g_ptm_dir); + if ( res != NULL ) { + res->read_proc = proc_read_dbg; + res->write_proc = proc_write_dbg; + } +#endif +} + +static INLINE void proc_file_delete(void) +{ +#if defined(ENABLE_DBG_PROC) && ENABLE_DBG_PROC + remove_proc_entry("dbg", g_ptm_dir); +#endif + +#if defined(ENABLE_FW_PROC) && ENABLE_FW_PROC + #ifdef CONFIG_AR9 + remove_proc_entry("regs", g_ptm_dir); + #endif + + remove_proc_entry("genconf", g_ptm_dir); +#endif + + remove_proc_entry("wanmib", g_ptm_dir); + + remove_proc_entry("version", g_ptm_dir); + + remove_proc_entry("driver/ifx_ptm", NULL); +} + +static int proc_read_version(char *buf, char **start, off_t offset, int count, int *eof, void *data) +{ + int len = 0; + + len += ifx_ptm_version(buf + len); + + if ( offset >= len ) { + *start = buf; + *eof = 1; + return 0; + } + *start = buf + offset; + if ( (len -= offset) > count ) + return count; + *eof = 1; + return len; +} + +static int proc_read_wanmib(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + int i; + char *title[] = { + "ptm0\n", + "ptmfast0\n" + }; + + for ( i = 0; i < ARRAY_SIZE(title); i++ ) { + len += sprintf(page + off + len, title[i]); + len += sprintf(page + off + len, " wrx_correct_pdu = %d\n", WAN_MIB_TABLE[i].wrx_correct_pdu); + len += sprintf(page + off + len, " wrx_correct_pdu_bytes = %d\n", WAN_MIB_TABLE[i].wrx_correct_pdu_bytes); + len += sprintf(page + off + len, " wrx_tccrc_err_pdu = %d\n", WAN_MIB_TABLE[i].wrx_tccrc_err_pdu); + len += sprintf(page + off + len, " wrx_tccrc_err_pdu_bytes = %d\n", WAN_MIB_TABLE[i].wrx_tccrc_err_pdu_bytes); + len += sprintf(page + off + len, " wrx_ethcrc_err_pdu = %d\n", WAN_MIB_TABLE[i].wrx_ethcrc_err_pdu); + len += sprintf(page + off + len, " wrx_ethcrc_err_pdu_bytes = %d\n", WAN_MIB_TABLE[i].wrx_ethcrc_err_pdu_bytes); + len += sprintf(page + off + len, " wrx_nodesc_drop_pdu = %d\n", WAN_MIB_TABLE[i].wrx_nodesc_drop_pdu); + len += sprintf(page + off + len, " wrx_len_violation_drop_pdu = %d\n", WAN_MIB_TABLE[i].wrx_len_violation_drop_pdu); + len += sprintf(page + off + len, " wrx_idle_bytes = %d\n", WAN_MIB_TABLE[i].wrx_idle_bytes); + len += sprintf(page + off + len, " wrx_nonidle_cw = %d\n", WAN_MIB_TABLE[i].wrx_nonidle_cw); + len += sprintf(page + off + len, " wrx_idle_cw = %d\n", WAN_MIB_TABLE[i].wrx_idle_cw); + len += sprintf(page + off + len, " wrx_err_cw = %d\n", WAN_MIB_TABLE[i].wrx_err_cw); + len += sprintf(page + off + len, " wtx_total_pdu = %d\n", WAN_MIB_TABLE[i].wtx_total_pdu); + len += sprintf(page + off + len, " wtx_total_bytes = %d\n", WAN_MIB_TABLE[i].wtx_total_bytes); + } + + *eof = 1; + + return len; +} + +static int proc_write_wanmib(struct file *file, const char *buf, unsigned long count, void *data) +{ + char str[2048]; + char *p; + int len, rlen; + + int i; + + len = count < sizeof(str) ? count : sizeof(str) - 1; + rlen = len - copy_from_user(str, buf, len); + while ( rlen && str[rlen - 1] <= ' ' ) + rlen--; + str[rlen] = 0; + for ( p = str; *p && *p <= ' '; p++, rlen-- ); + if ( !*p ) + return count; + + if ( stricmp(p, "clear") == 0 || stricmp(p, "clean") == 0 ) { + for ( i = 0; i < 2; i++ ) + memset((void*)&WAN_MIB_TABLE[i], 0, sizeof(WAN_MIB_TABLE[i])); + } + + return count; +} + +#if defined(ENABLE_FW_PROC) && ENABLE_FW_PROC + +static int proc_read_genconf(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + int len_max = off + count; + char *pstr; + char str[2048]; + int llen = 0; + int i; + unsigned long bit; + + pstr = *start = page; + + __sync(); + + llen += sprintf(str + llen, "CFG_WAN_WRDES_DELAY (0x%08X): %d\n", (unsigned int)CFG_WAN_WRDES_DELAY, IFX_REG_R32(CFG_WAN_WRDES_DELAY)); + llen += sprintf(str + llen, "CFG_WRX_DMACH_ON (0x%08X):", (unsigned int)CFG_WRX_DMACH_ON); + for ( i = 0, bit = 1; i < MAX_RX_DMA_CHANNEL_NUMBER; i++, bit <<= 1 ) + llen += sprintf(str + llen, " %d - %s", i, (IFX_REG_R32(CFG_WRX_DMACH_ON) & bit) ? "on " : "off"); + llen += sprintf(str + llen, "\n"); + llen += sprintf(str + llen, "CFG_WTX_DMACH_ON (0x%08X):", (unsigned int)CFG_WTX_DMACH_ON); + for ( i = 0, bit = 1; i < MAX_TX_DMA_CHANNEL_NUMBER; i++, bit <<= 1 ) + llen += sprintf(str + llen, " %d - %s", i, (IFX_REG_R32(CFG_WTX_DMACH_ON) & bit) ? "on " : "off"); + llen += sprintf(str + llen, "\n"); + llen += sprintf(str + llen, "CFG_WRX_LOOK_BITTH (0x%08X): %d\n", (unsigned int)CFG_WRX_LOOK_BITTH, IFX_REG_R32(CFG_WRX_LOOK_BITTH)); + llen += sprintf(str + llen, "CFG_ETH_EFMTC_CRC (0x%08X): rx_tc_crc_len - %2d, rx_tc_crc_check - %s\n", (unsigned int)CFG_ETH_EFMTC_CRC, CFG_ETH_EFMTC_CRC->rx_tc_crc_len, CFG_ETH_EFMTC_CRC->rx_tc_crc_check ? " on" : "off"); + llen += sprintf(str + llen, " rx_eth_crc_check - %s, rx_eth_crc_present - %s\n", CFG_ETH_EFMTC_CRC->rx_eth_crc_check ? " on" : "off", CFG_ETH_EFMTC_CRC->rx_eth_crc_present ? " on" : "off"); + llen += sprintf(str + llen, " tx_tc_crc_len - %2d, tx_tc_crc_gen - %s\n", CFG_ETH_EFMTC_CRC->tx_tc_crc_len, CFG_ETH_EFMTC_CRC->tx_tc_crc_gen ? " on" : "off"); + llen += sprintf(str + llen, " tx_eth_crc_gen - %s\n", CFG_ETH_EFMTC_CRC->tx_eth_crc_gen ? " on" : "off"); + + llen += sprintf(str + llen, "RX Port:\n"); + for ( i = 0; i < MAX_RX_DMA_CHANNEL_NUMBER; i++ ) + llen += sprintf(str + llen, " %d (0x%08X). mfs - %5d, dmach - %d, local_state - %d, partner_state - %d\n", i, (unsigned int)WRX_PORT_CONFIG(i), WRX_PORT_CONFIG(i)->mfs, WRX_PORT_CONFIG(i)->dmach, WRX_PORT_CONFIG(i)->local_state, WRX_PORT_CONFIG(i)->partner_state); + llen += sprintf(str + llen, "RX DMA Channel:\n"); + for ( i = 0; i < MAX_RX_DMA_CHANNEL_NUMBER; i++ ) + llen += sprintf(str + llen, " %d (0x%08X). desba - 0x%08X (0x%08X), deslen - %d, vlddes - %d\n", i, (unsigned int)WRX_DMA_CHANNEL_CONFIG(i), WRX_DMA_CHANNEL_CONFIG(i)->desba, ((unsigned int)WRX_DMA_CHANNEL_CONFIG(i)->desba << 2) | KSEG1, WRX_DMA_CHANNEL_CONFIG(i)->deslen, WRX_DMA_CHANNEL_CONFIG(i)->vlddes); + + llen += sprintf(str + llen, "TX Port:\n"); + for ( i = 0; i < MAX_TX_DMA_CHANNEL_NUMBER; i++ ) + llen += sprintf(str + llen, " %d (0x%08X). tx_cwth2 - %d, tx_cwth1 - %d\n", i, (unsigned int)WTX_PORT_CONFIG(i), WTX_PORT_CONFIG(i)->tx_cwth2, WTX_PORT_CONFIG(i)->tx_cwth1); + llen += sprintf(str + llen, "TX DMA Channel:\n"); + for ( i = 0; i < MAX_TX_DMA_CHANNEL_NUMBER; i++ ) + llen += sprintf(str + llen, " %d (0x%08X). desba - 0x%08X (0x%08X), deslen - %d, vlddes - %d\n", i, (unsigned int)WTX_DMA_CHANNEL_CONFIG(i), WTX_DMA_CHANNEL_CONFIG(i)->desba, ((unsigned int)WTX_DMA_CHANNEL_CONFIG(i)->desba << 2) | KSEG1, WTX_DMA_CHANNEL_CONFIG(i)->deslen, WTX_DMA_CHANNEL_CONFIG(i)->vlddes); + + if ( len <= off && len + llen > off ) + { + memcpy(pstr, str + off - len, len + llen - off); + pstr += len + llen - off; + } + else if ( len > off ) + { + memcpy(pstr, str, llen); + pstr += llen; + } + len += llen; + if ( len >= len_max ) + goto PROC_READ_GENCONF_OVERRUN_END; + + *eof = 1; + + return len - off; + +PROC_READ_GENCONF_OVERRUN_END: + return len - llen - off; +} + +#endif // defined(ENABLE_FW_PROC) && ENABLE_FW_PROC + +#if defined(ENABLE_DBG_PROC) && ENABLE_DBG_PROC + +static int proc_read_dbg(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + + len += sprintf(page + off + len, "error print - %s\n", (ifx_ptm_dbg_enable & DBG_ENABLE_MASK_ERR) ? "enabled" : "disabled"); + len += sprintf(page + off + len, "debug print - %s\n", (ifx_ptm_dbg_enable & DBG_ENABLE_MASK_DEBUG_PRINT) ? "enabled" : "disabled"); + len += sprintf(page + off + len, "assert - %s\n", (ifx_ptm_dbg_enable & DBG_ENABLE_MASK_ASSERT) ? "enabled" : "disabled"); + len += sprintf(page + off + len, "dump rx skb - %s\n", (ifx_ptm_dbg_enable & DBG_ENABLE_MASK_DUMP_SKB_RX) ? "enabled" : "disabled"); + len += sprintf(page + off + len, "dump tx skb - %s\n", (ifx_ptm_dbg_enable & DBG_ENABLE_MASK_DUMP_SKB_TX) ? "enabled" : "disabled"); + len += sprintf(page + off + len, "mac swap - %s\n", (ifx_ptm_dbg_enable & DBG_ENABLE_MASK_MAC_SWAP) ? "enabled" : "disabled"); + + *eof = 1; + + return len; +} + +static int proc_write_dbg(struct file *file, const char *buf, unsigned long count, void *data) +{ + static const char *dbg_enable_mask_str[] = { + " error print", + " err", + " debug print", + " dbg", + " assert", + " assert", + " dump rx skb", + " rx", + " dump tx skb", + " tx", + " dump init", + " init", + " dump qos", + " qos", + " mac swap", + " swap", + " all" + }; + static const int dbg_enable_mask_str_len[] = { + 12, 4, + 12, 4, + 7, 7, + 12, 3, + 12, 3, + 10, 5, + 9, 4, + 9, 5, + 4 + }; + unsigned int dbg_enable_mask[] = { + DBG_ENABLE_MASK_ERR, + DBG_ENABLE_MASK_DEBUG_PRINT, + DBG_ENABLE_MASK_ASSERT, + DBG_ENABLE_MASK_DUMP_SKB_RX, + DBG_ENABLE_MASK_DUMP_SKB_TX, + DBG_ENABLE_MASK_DUMP_INIT, + DBG_ENABLE_MASK_DUMP_QOS, + DBG_ENABLE_MASK_MAC_SWAP, + DBG_ENABLE_MASK_ALL + }; + + char str[2048]; + char *p; + + int len, rlen; + + int f_enable = 0; + int i; + + len = count < sizeof(str) ? count : sizeof(str) - 1; + rlen = len - copy_from_user(str, buf, len); + while ( rlen && str[rlen - 1] <= ' ' ) + rlen--; + str[rlen] = 0; + for ( p = str; *p && *p <= ' '; p++, rlen-- ); + if ( !*p ) + return 0; + + // debugging feature for enter/leave showtime + if ( strincmp(p, "enter", 5) == 0 && ifx_mei_atm_showtime_enter != NULL ) + ifx_mei_atm_showtime_enter(NULL, NULL); + else if ( strincmp(p, "leave", 5) == 0 && ifx_mei_atm_showtime_exit != NULL ) + ifx_mei_atm_showtime_exit(); + + if ( strincmp(p, "enable", 6) == 0 ) { + p += 6; + f_enable = 1; + } + else if ( strincmp(p, "disable", 7) == 0 ) { + p += 7; + f_enable = -1; + } + else if ( strincmp(p, "help", 4) == 0 || *p == '?' ) { + printk("echo [err/dbg/assert/rx/tx/init/qos/swap/all] > /proc/driver/ifx_ptm/dbg\n"); + } + + if ( f_enable ) { + if ( *p == 0 ) { + if ( f_enable > 0 ) + ifx_ptm_dbg_enable |= DBG_ENABLE_MASK_ALL & ~DBG_ENABLE_MASK_MAC_SWAP; + else + ifx_ptm_dbg_enable &= ~DBG_ENABLE_MASK_ALL | DBG_ENABLE_MASK_MAC_SWAP; + } + else { + do { + for ( i = 0; i < ARRAY_SIZE(dbg_enable_mask_str); i++ ) + if ( strincmp(p, dbg_enable_mask_str[i], dbg_enable_mask_str_len[i]) == 0 ) { + if ( f_enable > 0 ) + ifx_ptm_dbg_enable |= dbg_enable_mask[i >> 1]; + else + ifx_ptm_dbg_enable &= ~dbg_enable_mask[i >> 1]; + p += dbg_enable_mask_str_len[i]; + break; + } + } while ( i < ARRAY_SIZE(dbg_enable_mask_str) ); + } + } + + return count; +} + +#endif // defined(ENABLE_DBG_PROC) && ENABLE_DBG_PROC + +static INLINE int stricmp(const char *p1, const char *p2) +{ + int c1, c2; + + while ( *p1 && *p2 ) + { + c1 = *p1 >= 'A' && *p1 <= 'Z' ? *p1 + 'a' - 'A' : *p1; + c2 = *p2 >= 'A' && *p2 <= 'Z' ? *p2 + 'a' - 'A' : *p2; + if ( (c1 -= c2) ) + return c1; + p1++; + p2++; + } + + return *p1 - *p2; +} + +#if defined(ENABLE_DBG_PROC) && ENABLE_DBG_PROC +static INLINE int strincmp(const char *p1, const char *p2, int n) +{ + int c1 = 0, c2; + + while ( n && *p1 && *p2 ) + { + c1 = *p1 >= 'A' && *p1 <= 'Z' ? *p1 + 'a' - 'A' : *p1; + c2 = *p2 >= 'A' && *p2 <= 'Z' ? *p2 + 'a' - 'A' : *p2; + if ( (c1 -= c2) ) + return c1; + p1++; + p2++; + n--; + } + + return n ? *p1 - *p2 : c1; +} +#endif + +static INLINE int ifx_ptm_version(char *buf) +{ + int len = 0; + unsigned int major, minor; + + ifx_ptm_get_fw_ver(&major, &minor); + + len += sprintf(buf + len, "PTM %d.%d.%d", IFX_PTM_VER_MAJOR, IFX_PTM_VER_MID, IFX_PTM_VER_MINOR); + len += sprintf(buf + len, " PTM (E1) firmware version %d.%d\n", major, minor); + + return len; +} + +static INLINE void check_parameters(void) +{ + /* There is a delay between PPE write descriptor and descriptor is */ + /* really stored in memory. Host also has this delay when writing */ + /* descriptor. So PPE will use this value to determine if the write */ + /* operation makes effect. */ + if ( write_desc_delay < 0 ) + write_desc_delay = 0; + + /* Because of the limitation of length field in descriptors, the packet */ + /* size could not be larger than 64K minus overhead size. */ + if ( rx_max_packet_size < ETH_MIN_FRAME_LENGTH ) + rx_max_packet_size = ETH_MIN_FRAME_LENGTH; + else if ( rx_max_packet_size > 65536 - 1 ) + rx_max_packet_size = 65536 - 1; + + if ( dma_rx_descriptor_length < 2 ) + dma_rx_descriptor_length = 2; + if ( dma_tx_descriptor_length < 2 ) + dma_tx_descriptor_length = 2; +} + +static INLINE int init_priv_data(void) +{ + void *p; + int i; + struct rx_descriptor rx_desc = {0}; + struct sk_buff *skb; + volatile struct rx_descriptor *p_rx_desc; + volatile struct tx_descriptor *p_tx_desc; + struct sk_buff **ppskb; + + // clear ptm private data structure + memset(&g_ptm_priv_data, 0, sizeof(g_ptm_priv_data)); + + // allocate memory for RX descriptors + p = kzalloc(MAX_ITF_NUMBER * dma_rx_descriptor_length * sizeof(struct rx_descriptor) + DESC_ALIGNMENT, GFP_KERNEL); + if ( p == NULL ) + return -1; + dma_cache_inv((unsigned long)p, MAX_ITF_NUMBER * dma_rx_descriptor_length * sizeof(struct rx_descriptor) + DESC_ALIGNMENT); + g_ptm_priv_data.rx_desc_base = p; + //p = (void *)((((unsigned int)p + DESC_ALIGNMENT - 1) & ~(DESC_ALIGNMENT - 1)) | KSEG1); + + // allocate memory for TX descriptors + p = kzalloc(MAX_ITF_NUMBER * dma_tx_descriptor_length * sizeof(struct tx_descriptor) + DESC_ALIGNMENT, GFP_KERNEL); + if ( p == NULL ) + return -1; + dma_cache_inv((unsigned long)p, MAX_ITF_NUMBER * dma_tx_descriptor_length * sizeof(struct tx_descriptor) + DESC_ALIGNMENT); + g_ptm_priv_data.tx_desc_base = p; + + // allocate memroy for TX skb pointers + p = kzalloc(MAX_ITF_NUMBER * dma_tx_descriptor_length * sizeof(struct sk_buff *) + 4, GFP_KERNEL); + if ( p == NULL ) + return -1; + dma_cache_wback_inv((unsigned long)p, MAX_ITF_NUMBER * dma_tx_descriptor_length * sizeof(struct sk_buff *) + 4); + g_ptm_priv_data.tx_skb_base = p; + + p_rx_desc = (volatile struct rx_descriptor *)((((unsigned int)g_ptm_priv_data.rx_desc_base + DESC_ALIGNMENT - 1) & ~(DESC_ALIGNMENT - 1)) | KSEG1); + p_tx_desc = (volatile struct tx_descriptor *)((((unsigned int)g_ptm_priv_data.tx_desc_base + DESC_ALIGNMENT - 1) & ~(DESC_ALIGNMENT - 1)) | KSEG1); + ppskb = (struct sk_buff **)(((unsigned int)g_ptm_priv_data.tx_skb_base + 3) & ~3); + for ( i = 0; i < MAX_ITF_NUMBER; i++ ) { + g_ptm_priv_data.itf[i].rx_desc = &p_rx_desc[i * dma_rx_descriptor_length]; + g_ptm_priv_data.itf[i].tx_desc = &p_tx_desc[i * dma_tx_descriptor_length]; + g_ptm_priv_data.itf[i].tx_skb = &ppskb[i * dma_tx_descriptor_length]; + } + + rx_desc.own = 1; + rx_desc.c = 0; + rx_desc.sop = 1; + rx_desc.eop = 1; + rx_desc.byteoff = RX_HEAD_MAC_ADDR_ALIGNMENT; + rx_desc.id = 0; + rx_desc.err = 0; + rx_desc.datalen = rx_max_packet_size; + for ( i = 0; i < MAX_ITF_NUMBER * dma_rx_descriptor_length; i++ ) { + skb = alloc_skb_rx(); + if ( skb == NULL ) + return -1; + rx_desc.dataptr = ((unsigned int)skb->data >> 2) & 0x0FFFFFFF; + p_rx_desc[i] = rx_desc; + } + + return 0; +} + +static INLINE void clear_priv_data(void) +{ + int i, j; + struct sk_buff *skb; + + for ( i = 0; i < MAX_ITF_NUMBER; i++ ) { + if ( g_ptm_priv_data.itf[i].tx_skb != NULL ) { + for ( j = 0; j < dma_tx_descriptor_length; j++ ) + if ( g_ptm_priv_data.itf[i].tx_skb[j] != NULL ) + dev_kfree_skb_any(g_ptm_priv_data.itf[i].tx_skb[j]); + } + if ( g_ptm_priv_data.itf[i].rx_desc != NULL ) { + for ( j = 0; j < dma_rx_descriptor_length; j++ ) { + if ( g_ptm_priv_data.itf[i].rx_desc[j].sop || g_ptm_priv_data.itf[i].rx_desc[j].eop ) { // descriptor initialized + skb = get_skb_rx_pointer(g_ptm_priv_data.itf[i].rx_desc[j].dataptr); + dev_kfree_skb_any(skb); + } + } + } + } + + if ( g_ptm_priv_data.rx_desc_base != NULL ) + kfree(g_ptm_priv_data.rx_desc_base); + + if ( g_ptm_priv_data.tx_desc_base != NULL ) + kfree(g_ptm_priv_data.tx_desc_base); + + if ( g_ptm_priv_data.tx_skb_base != NULL ) + kfree(g_ptm_priv_data.tx_skb_base); +} + +static INLINE void init_tables(void) +{ + int i; + volatile unsigned int *p; + struct wrx_dma_channel_config rx_config = {0}; + struct wtx_dma_channel_config tx_config = {0}; + struct wrx_port_cfg_status rx_port_cfg = { 0 }; + struct wtx_port_cfg tx_port_cfg = { 0 }; + + /* + * CDM Block 1 + */ + IFX_REG_W32(CDM_CFG_RAM1_SET(0x00) | CDM_CFG_RAM0_SET(0x00), CDM_CFG); // CDM block 1 must be data memory and mapped to 0x5000 (dword addr) + p = CDM_DATA_MEMORY(0, 0); // Clear CDM block 1 + for ( i = 0; i < CDM_DATA_MEMORY_DWLEN; i++, p++ ) + IFX_REG_W32(0, p); + + /* + * General Registers + */ + IFX_REG_W32(write_desc_delay, CFG_WAN_WRDES_DELAY); + IFX_REG_W32((1 << MAX_RX_DMA_CHANNEL_NUMBER) - 1, CFG_WRX_DMACH_ON); + IFX_REG_W32((1 << MAX_TX_DMA_CHANNEL_NUMBER) - 1, CFG_WTX_DMACH_ON); + + IFX_REG_W32(8, CFG_WRX_LOOK_BITTH); // WAN RX EFM-TC Looking Threshold + + IFX_REG_W32(eth_efmtc_crc_cfg, CFG_ETH_EFMTC_CRC); + + /* + * WRX DMA Channel Configuration Table + */ + rx_config.deslen = dma_rx_descriptor_length; + rx_port_cfg.mfs = ETH_MAX_FRAME_LENGTH; + rx_port_cfg.local_state = 0; // looking for sync + rx_port_cfg.partner_state = 0; // parter receiver is out of sync + + for ( i = 0; i < MAX_RX_DMA_CHANNEL_NUMBER; i++ ) { + rx_config.desba = ((unsigned int)g_ptm_priv_data.itf[i].rx_desc >> 2) & 0x0FFFFFFF; + *WRX_DMA_CHANNEL_CONFIG(i) = rx_config; + + rx_port_cfg.dmach = i; + *WRX_PORT_CONFIG(i) = rx_port_cfg; + } + + /* + * WTX DMA Channel Configuration Table + */ + tx_config.deslen = dma_tx_descriptor_length; + tx_port_cfg.tx_cwth1 = 5; + tx_port_cfg.tx_cwth2 = 4; + + for ( i = 0; i < MAX_TX_DMA_CHANNEL_NUMBER; i++ ) { + tx_config.desba = ((unsigned int)g_ptm_priv_data.itf[i].tx_desc >> 2) & 0x0FFFFFFF; + *WTX_DMA_CHANNEL_CONFIG(i) = tx_config; + + *WTX_PORT_CONFIG(i) = tx_port_cfg; + } +} + + + +/* + * #################################### + * Global Function + * #################################### + */ + +static int ptm_showtime_enter(struct port_cell_info *port_cell, void *xdata_addr) +{ + + g_showtime = 1; + + printk("enter showtime\n"); + + return 0; +} + +static int ptm_showtime_exit(void) +{ + if ( !g_showtime ) + return -1; + + g_showtime = 0; + + printk("leave showtime\n"); + + return 0; +} + + + +/* + * #################################### + * Init/Cleanup API + * #################################### + */ + +/* + * Description: + * Initialize global variables, PP32, comunication structures, register IRQ + * and register device. + * Input: + * none + * Output: + * 0 --- successful + * else --- failure, usually it is negative value of error code + */ +static int ifx_ptm_init(void) +{ + int ret; + struct port_cell_info port_cell = {0}; + void *xdata_addr = NULL; + int i; + char ver_str[256]; + + check_parameters(); + + ret = init_priv_data(); + if ( ret != 0 ) { + err("INIT_PRIV_DATA_FAIL"); + goto INIT_PRIV_DATA_FAIL; + } + + ifx_ptm_init_chip(); + init_tables(); + + for ( i = 0; i < ARRAY_SIZE(g_net_dev); i++ ) { + g_net_dev[i] = alloc_netdev(0, g_net_dev_name[i], NET_NAME_UNKNOWN, ether_setup); + if ( g_net_dev[i] == NULL ) + goto ALLOC_NETDEV_FAIL; + ptm_setup(g_net_dev[i], i); + } + + for ( i = 0; i < ARRAY_SIZE(g_net_dev); i++ ) { + ret = register_netdev(g_net_dev[i]); + if ( ret != 0 ) + goto REGISTER_NETDEV_FAIL; + } + + /* register interrupt handler */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0) + ret = request_irq(PPE_MAILBOX_IGU1_INT, mailbox_irq_handler, 0, "ptm_mailbox_isr", &g_ptm_priv_data); +#else + ret = request_irq(PPE_MAILBOX_IGU1_INT, mailbox_irq_handler, IRQF_DISABLED, "ptm_mailbox_isr", &g_ptm_priv_data); +#endif + if ( ret ) { + if ( ret == -EBUSY ) { + err("IRQ may be occupied by other driver, please reconfig to disable it."); + } + else { + err("request_irq fail"); + } + goto REQUEST_IRQ_PPE_MAILBOX_IGU1_INT_FAIL; + } + disable_irq(PPE_MAILBOX_IGU1_INT); + + ret = ifx_pp32_start(0); + if ( ret ) { + err("ifx_pp32_start fail!"); + goto PP32_START_FAIL; + } + IFX_REG_W32(0, MBOX_IGU1_IER); + IFX_REG_W32(~0, MBOX_IGU1_ISRC); + + enable_irq(PPE_MAILBOX_IGU1_INT); + + + proc_file_create(); + + port_cell.port_num = 1; + ifx_mei_atm_showtime_check(&g_showtime, &port_cell, &xdata_addr); + + ifx_mei_atm_showtime_enter = ptm_showtime_enter; + ifx_mei_atm_showtime_exit = ptm_showtime_exit; + + ifx_ptm_version(ver_str); + printk(KERN_INFO "%s", ver_str); + + printk("ifxmips_ptm: PTM init succeed\n"); + + return 0; + +PP32_START_FAIL: + free_irq(PPE_MAILBOX_IGU1_INT, &g_ptm_priv_data); +REQUEST_IRQ_PPE_MAILBOX_IGU1_INT_FAIL: + i = ARRAY_SIZE(g_net_dev); +REGISTER_NETDEV_FAIL: + while ( i-- ) + unregister_netdev(g_net_dev[i]); + i = ARRAY_SIZE(g_net_dev); +ALLOC_NETDEV_FAIL: + while ( i-- ) { + free_netdev(g_net_dev[i]); + g_net_dev[i] = NULL; + } +INIT_PRIV_DATA_FAIL: + clear_priv_data(); + printk("ifxmips_ptm: PTM init failed\n"); + return ret; +} + +/* + * Description: + * Release memory, free IRQ, and deregister device. + * Input: + * none + * Output: + * none + */ +static void __exit ifx_ptm_exit(void) +{ + int i; + + ifx_mei_atm_showtime_enter = NULL; + ifx_mei_atm_showtime_exit = NULL; + + proc_file_delete(); + + + ifx_pp32_stop(0); + + free_irq(PPE_MAILBOX_IGU1_INT, &g_ptm_priv_data); + + for ( i = 0; i < ARRAY_SIZE(g_net_dev); i++ ) + unregister_netdev(g_net_dev[i]); + + for ( i = 0; i < ARRAY_SIZE(g_net_dev); i++ ) { + free_netdev(g_net_dev[i]); + g_net_dev[i] = NULL; + } + + ifx_ptm_uninit_chip(); + + clear_priv_data(); +} + +module_init(ifx_ptm_init); +module_exit(ifx_ptm_exit); -- cgit v1.2.3