/*
* Copyright (C) 2006-2007 Freescale Semicondutor, Inc. All rights reserved.
*
* Author: Shlomi Gridish <gridish@freescale.com>
* Li Yang <leoli@freescale.com>
*
* Description:
* QE UCC Gigabit Ethernet Driver
*
* 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/kernel.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/stddef.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/mm.h>
#include <linux/dma-mapping.h>
#include <linux/mii.h>
#include <linux/phy.h>
#include <linux/workqueue.h>
#include <linux/of_mdio.h>
#include <linux/of_platform.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/immap_qe.h>
#include <asm/qe.h>
#include <asm/ucc.h>
#include <asm/ucc_fast.h>
#include "ucc_geth.h"
#include "fsl_pq_mdio.h"
#undef DEBUG
#define ugeth_printk(level, format, arg...) \
printk(level format "\n", ## arg)
#define ugeth_dbg(format, arg...) \
ugeth_printk(KERN_DEBUG , format , ## arg)
#define ugeth_err(format, arg...) \
ugeth_printk(KERN_ERR , format , ## arg)
#define ugeth_info(format, arg...) \
ugeth_printk(KERN_INFO , format , ## arg)
#define ugeth_warn(format, arg...) \
ugeth_printk(KERN_WARNING , format , ## arg)
#ifdef UGETH_VERBOSE_DEBUG
#define ugeth_vdbg ugeth_dbg
#else
#define ugeth_vdbg(fmt, args...) do { } while (0)
#endif /* UGETH_VERBOSE_DEBUG */
#define UGETH_MSG_DEFAULT (NETIF_MSG_IFUP << 1 ) - 1
static DEFINE_SPINLOCK(ugeth_lock);
static struct {
u32 msg_enable;
} debug = { -1 };
module_param_named(debug, debug.msg_enable, int, 0);
MODULE_PARM_DESC(debug, "Debug verbosity level (0=none, ..., 0xffff=all)");
static struct ucc_geth_info ugeth_primary_info = {
.uf_info = {
.bd_mem_part = MEM_PART_SYSTEM,
.rtsm = UCC_FAST_SEND_IDLES_BETWEEN_FRAMES,
.max_rx_buf_length = 1536,
/* adjusted at startup if max-speed 1000 */
.urfs = UCC_GETH_URFS_INIT,
.urfet = UCC_GETH_URFET_INIT,
.urfset = UCC_GETH_URFSET_INIT,
.utfs = UCC_GETH_UTFS_INIT,
.utfet = UCC_GETH_UTFET_INIT,
.utftt = UCC_GETH_UTFTT_INIT,
.ufpt = 256,
.mode = UCC_FAST_PROTOCOL_MODE_ETHERNET,
.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,
},
.numQueuesTx = 1,
.numQueuesRx = 1,
.extendedFilteringChainPointer = ((uint32_t) NULL),
.typeorlen = 3072 /*1536 */ ,
.nonBackToBackIfgPart1 = 0x40,
.nonBackToBackIfgPart2 = 0x60,
.miminumInterFrameGapEnforcement = 0x50,
.backToBackInterFrameGap = 0x60,
.mblinterval = 128,
.nortsrbytetime = 5,
.fracsiz = 1,
.strictpriorityq = 0xff,
.altBebTruncation = 0xa,
.excessDefer = 1,
.maxRetransmission = 0xf,
.collisionWindow = 0x37,
.receiveFlowControl = 1,
.transmitFlowControl = 1,
.maxGroupAddrInHash = 4,
.maxIndAddrInHash = 4,
.prel = 7,
.maxFrameLength = 1518,
.minFrameLength = 64,
.maxD1Length = 1520,
.maxD2Length = 1520,
.vlantype = 0x8100,
.ecamptr = ((uint32_t) NULL),
.eventRegMask = UCCE_OTHER,
.pausePeriod = 0xf000,
.interruptcoalescingmaxvalue = {1, 1, 1, 1, 1, 1, 1, 1},
.bdRingLenTx = {
TX_BD_RING_LEN,
TX_BD_RING_LEN,
TX_BD_RING_LEN,
TX_BD_RING_LEN,
TX_BD_RING_LEN,
TX_BD_RING_LEN,
TX_BD_RING_LEN,
TX_BD_RING_LEN},
.bdRingLenRx = {
RX_BD_RING_LEN,
RX_BD_RING_LEN,
RX_BD_RING_LEN,
RX_BD_RING_LEN,
RX_BD_RING_LEN,
RX_BD_RING_LEN,
RX_BD_RING_LEN,
RX_BD_RING_LEN},
.numStationAddresses = UCC_GETH_NUM_OF_STATION_ADDRESSES_1,
.largestexternallookupkeysize =
QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_NONE,
.statisticsMode = UCC_GETH_STATISTICS_GATHERING_MODE_HARDWARE |
UCC_GETH_STATISTICS_GATHERING_MODE_FIRMWARE_TX |
UCC_GETH_STATISTICS_GATHERING_MODE_FIRMWARE_RX,
.vlanOperationTagged = UCC_GETH_VLAN_OPERATION_TAGGED_NOP,
.vlanOperationNonTagged = UCC_GETH_VLAN_OPERATION_NON_TAGGED_NOP,
.rxQoSMode = UCC_GETH_QOS_MODE_DEFAULT,
.aufc = UPSMR_AUTOMATIC_FLOW_CONTROL_MODE_NONE,
.padAndCrc = MACCFG2_PAD_AND_CRC_MODE_PAD_AND_CRC,
.numThreadsTx = UCC_GETH_NUM_OF_THREADS_1,
.numThreadsRx = UCC_GETH_NUM_OF_THREADS_1,
.riscTx = QE_RISC_ALLOCATION_RISC1_AND_RISC2,
.riscRx = QE_RISC_ALLOCATION_RISC1_AND_RISC2,
};
static struct ucc_geth_info ugeth_info[8];
#ifdef DEBUG
static void mem_disp(u8 *addr, int size)
{
u8 *i;
int size16Aling = (size >> 4) << 4;
int size4Aling = (size >> 2) << 2;
int notAlign = 0;
if (size % 16)
notAlign = 1;
for (i = addr; (u32) i < (u32) addr + size16Aling; i += 16)
printk("0x%08x: %08x %08x %08x %08x\r\n",
(u32) i,
*((u32 *) (i)),
*((u32 *) (i + 4)),
*((u32 *) (i + 8)), *((u32 *) (i + 12)));
if (notAlign == 1)
printk("0x%08x: ", (u32) i);
for (; (u32) i < (u32) addr + size4Aling; i += 4)
printk("%08x ", *((u32 *) (i)));
for (; (u32) i < (u32) addr + size; i++)
printk("%02x", *((u8 *) (i)));
if (notAlign == 1)
printk("\r\n");
}
#endif /* DEBUG */
static struct list_head *dequeue(struct list_head *lh)
{
unsigned long flags;
spin_lock_irqsave(&ugeth_lock, flags);
if (!list_empty(lh)) {
struct list_head *node = lh->next;
list_del(node);
spin_unlock_irqrestore(&ugeth_lock, flags);
return node;
} else {
spin_unlock_irqrestore(&ugeth_lock, flags);
return NULL;
}
}
static struct sk_buff *get_new_skb(struct ucc_geth_private *ugeth,
u8 __iomem *bd)
{
struct sk_buff *skb = NULL;
skb = dev_alloc_skb(ugeth->ug_info->uf_info.max_rx_buf_length +
UCC_GETH_RX_DATA_BUF_ALIGNMENT);
if (skb == NULL)
return NULL;
/* We need the data buffer to be aligned properly. We will reserve
* as many bytes as needed to align the data properly
*/
skb_reserve(skb,
UCC_GETH_RX_DATA_BUF_ALIGNMENT -
(((unsigned)skb->data) & (UCC_GETH_RX_DATA_BUF_ALIGNMENT -
1)));
skb->dev = ugeth->ndev;
out_be32(&((struct qe_bd __iomem *)bd)->buf,
dma_map_single(ugeth->dev,
skb->data,
ugeth->ug_info->uf_info.max_rx_buf_length +
UCC_GETH_RX_DATA_BUF_ALIGNMENT,
DMA_FROM_DEVICE));
out_be32((u32 __iomem *)bd,
(R_E | R_I | (in_be32((u32 __iomem*)bd) & R_W)));
return skb;
}
static int rx_bd_buffer_set(struct ucc_geth_private *ugeth, u8 rxQ)
{
u8 __iomem *bd;
u32 bd_status;
struct sk_buff *skb;
int i;
bd = ugeth->p_rx_bd_ring[rxQ];
i = 0;
do {
bd_status = in_be32((u32 __iomem *)bd);
skb = get_new_skb(ugeth, bd);
if (!skb) /* If can not allocate data buffer,
abort. Cleanup will be elsewhere */
return -ENOMEM;
ugeth->rx_skbuff[rxQ][i] = skb;
/* advance the BD pointer */
bd += sizeof(struct qe_bd);
i++;
} while (!(bd_status & R_W));
return 0;
}
static int fill_init_enet_entries(struct ucc_geth_private *ugeth,
u32 *p_start,
u8 num_entries,
u32 thread_size,
u32 thread_alignment,
enum qe_risc_allocation risc,
int skip_page_for_first_entry)
{
u32 init_enet_offset;
u8 i;
int snum;
for (i = 0; i < num_entries; i++) {
if ((snum = qe_get_snum()) < 0) {
if (netif_msg_ifup(ugeth))
ugeth_err("fill_init_enet_entries: Can not get SNUM.");
return snum;
}
if ((i == 0) && skip_page_for_first_entry)
/* First entry of Rx does not have page */
init_enet_offset = 0;
else {
init_enet_offset =
qe_muram_alloc(thread_size, thread_alignment);
if (IS_ERR_VALUE(init_enet_offset)) {
if (netif_msg_ifup(ugeth))
ugeth_err("fill_init_enet_entries: Can not allocate DPRAM memory.");
qe_put_snum((u8) snum);
return -ENOMEM;
}
}
*(p_start++) =
((u8) snum << ENET_INIT_PARAM_SNUM_SHIFT) | init_enet_offset
| risc;
}
return 0;
}
static int return_init_enet_entries(struct ucc_geth_private *ugeth,
u32 *p_start,
u8 num_entries,
enum qe_risc_allocation risc,
int skip_page_for_first_entry)
{
u32 init_enet_offset;
u8 i;
int snum;
for (i = 0; i < num_entries; i++) {
u32 val = *p_start;
/* Check that this entry was actually valid --
needed in case failed in allocations */
if ((val & ENET_INIT_PARAM_RISC_MASK) == risc) {
snum =
(u32) (val & ENET_INIT_PARAM_SNUM_MASK) >>
ENET_INIT_PARAM_SNUM_SHIFT;
qe_put_snum((u8) snum);
if (!((i == 0) && skip_page_for_first_entry)) {
/* First entry of Rx does not have page */
init_enet_offset =
(val & ENET_INIT_PARAM_PTR_MASK);
qe_muram_free(init_enet_offset);
}
*p_start++ = 0;
}
}
return 0;
}
#ifdef DEBUG
static int dump_init_enet_entries(struct ucc_geth_private *ugeth,
u32 __iomem *p_start,
u8 num_entries,
u32 thread_size,
enum qe_risc_allocation risc,
int skip_page_for_first_entry)
{
u32 init_enet_offset;
u8 i;
int snum;
for (i = 0; i < num_entries; i++) {
u32 val = in_be32(p_start);
/* Check that this entry was actually valid --
needed in case failed in allocations */
if ((val & ENET_INIT_PARAM_RISC_MASK) == risc) {
snum =
(u32) (val & ENET_INIT_PARAM_SNUM_MASK) >>
ENET_INIT_PARAM_SNUM_SHIFT;
qe_put_snum((u8) snum);
if (!((i == 0) && skip_page_for_first_entry)) {
/* First entry of Rx does not have page */
init_enet_offset =
(in_be32(p_start) &
ENET_INIT_PARAM_PTR_MASK);
ugeth_info("Init enet entry %d:", i);
ugeth_info("Base address: 0x%08x",
(u32)
qe_muram_addr(init_enet_offset));
mem_disp(qe_muram_addr(init_enet_offset),
thread_size);
}
p_start++;
}
}
return 0;
}
#endif
static void put_enet_addr_container(struct enet_addr_container *enet_addr_cont)
{
kfree(enet_addr_cont);
}
static void set_mac_addr(__be16 __iomem *reg, u8 *mac)
{
out_be16(®[0], ((u16)mac[5] << 8) | mac[4]);
out_be16(®[1], ((u16)mac[3] << 8) | mac[2]);
out_be16(®[2], ((u16)mac[1] << 8) | mac[0]);
}
static int hw_clear_addr_in_paddr(struct ucc_geth_private *ugeth, u8 paddr_num)
{
struct ucc_geth_82xx_address_filtering_pram __iomem *p_82xx_addr_filt;
if (!(paddr_num < NUM_OF_PADDRS)) {
ugeth_warn("%s: Illagel paddr_num.", __func__);
return -EINVAL;
}
p_82xx_addr_filt =
(struct ucc_geth_82xx_address_filtering_pram __iomem *) ugeth->p_rx_glbl_pram->
addressfiltering;
/* Writing address ff.ff.ff.ff.ff.ff disables address
recognition for this register */
out_be16(&p_82xx_addr_filt->paddr[paddr_num].h, 0xffff);
out_be16(&p_82xx_addr_filt->paddr[paddr_num].m, 0xffff);
out_be16(&p_82xx_addr_filt->paddr[paddr_num].l, 0xffff);
|