提交 1edb9ca6 编写于 作者: S Siva Reddy 提交者: David S. Miller

net: sxgbe: add basic framework for Samsung 10Gb ethernet driver

This patch adds support for Samsung 10Gb ethernet driver(sxgbe).

- sxgbe core initialization
- Tx and Rx support
- MDIO support
- ISRs for Tx and Rx
- ifconfig support to driver
Signed-off-by: NSiva Reddy Kallam <siva.kallam@samsung.com>
Signed-off-by: NVipul Pandya <vipul.pandya@samsung.com>
Signed-off-by: NGirish K S <ks.giri@samsung.com>
Neatening-by: NJoe Perches <joe@perches.com>
Signed-off-by: NByungho An <bh74.an@samsung.com>
Signed-off-by: NDavid S. Miller <davem@davemloft.net>
上级 5221d3e6
......@@ -150,6 +150,7 @@ config S6GMAC
To compile this driver as a module, choose M here. The module
will be called s6gmac.
source "drivers/net/ethernet/samsung/Kconfig"
source "drivers/net/ethernet/seeq/Kconfig"
source "drivers/net/ethernet/silan/Kconfig"
source "drivers/net/ethernet/sis/Kconfig"
......
......@@ -61,6 +61,7 @@ obj-$(CONFIG_NET_VENDOR_REALTEK) += realtek/
obj-$(CONFIG_SH_ETH) += renesas/
obj-$(CONFIG_NET_VENDOR_RDC) += rdc/
obj-$(CONFIG_S6GMAC) += s6gmac.o
obj-$(CONFIG_NET_VENDOR_SAMSUNG) += samsung/
obj-$(CONFIG_NET_VENDOR_SEEQ) += seeq/
obj-$(CONFIG_NET_VENDOR_SILAN) += silan/
obj-$(CONFIG_NET_VENDOR_SIS) += sis/
......
#
# Samsung Ethernet device configuration
#
config NET_VENDOR_SAMSUNG
bool "Samsung Ethernet device"
default y
---help---
This is the driver for the SXGBE 10G Ethernet IP block found on Samsung
platforms.
if NET_VENDOR_SAMSUNG
source "drivers/net/ethernet/samsung/sxgbe/Kconfig"
endif # NET_VENDOR_SAMSUNG
#
# Makefile for the Samsung Ethernet device drivers.
#
obj-$(CONFIG_SXGBE_ETH) += sxgbe/
config SXGBE_ETH
tristate "Samsung 10G/2.5G/1G SXGBE Ethernet driver"
depends on HAS_IOMEM && HAS_DMA
select PHYLIB
select CRC32
select PTP_1588_CLOCK
---help---
This is the driver for the SXGBE 10G Ethernet IP block found on Samsung
platforms.
obj-$(CONFIG_SXGBE_ETH) += samsung-sxgbe.o
samsung-sxgbe-objs:= sxgbe_platform.o sxgbe_main.o sxgbe_desc.o \
sxgbe_dma.o sxgbe_core.o sxgbe_mtl.o sxgbe_mdio.o \
sxgbe_ethtool.o sxgbe_xpcs.o $(samsung-sxgbe-y)
/* 10G controller driver for Samsung SoCs
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Siva Reddy Kallam <siva.kallam@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __SXGBE_COMMON_H__
#define __SXGBE_COMMON_H__
/* forward references */
struct sxgbe_desc_ops;
struct sxgbe_dma_ops;
struct sxgbe_mtl_ops;
#define SXGBE_RESOURCE_NAME "sam_sxgbeeth"
#define DRV_MODULE_VERSION "November_2013"
/* MAX HW feature words */
#define SXGBE_HW_WORDS 3
#define SXGBE_RX_COE_NONE 0
/* CSR Frequency Access Defines*/
#define SXGBE_CSR_F_150M 150000000
#define SXGBE_CSR_F_250M 250000000
#define SXGBE_CSR_F_300M 300000000
#define SXGBE_CSR_F_350M 350000000
#define SXGBE_CSR_F_400M 400000000
#define SXGBE_CSR_F_500M 500000000
/* pause time */
#define SXGBE_PAUSE_TIME 0x200
/* tx queues */
#define SXGBE_TX_QUEUES 8
#define SXGBE_RX_QUEUES 16
/* Calculated based how much time does it take to fill 256KB Rx memory
* at 10Gb speed at 156MHz clock rate and considered little less then
* the actual value.
*/
#define SXGBE_MAX_DMA_RIWT 0x70
#define SXGBE_MIN_DMA_RIWT 0x01
/* Tx coalesce parameters */
#define SXGBE_COAL_TX_TIMER 40000
#define SXGBE_MAX_COAL_TX_TICK 100000
#define SXGBE_TX_MAX_FRAMES 512
#define SXGBE_TX_FRAMES 128
/* SXGBE TX FIFO is 8K, Rx FIFO is 16K */
#define BUF_SIZE_16KiB 16384
#define BUF_SIZE_8KiB 8192
#define BUF_SIZE_4KiB 4096
#define BUF_SIZE_2KiB 2048
#define SXGBE_DEFAULT_LIT_LS 0x3E8
#define SXGBE_DEFAULT_TWT_LS 0x0
/* Flow Control defines */
#define SXGBE_FLOW_OFF 0
#define SXGBE_FLOW_RX 1
#define SXGBE_FLOW_TX 2
#define SXGBE_FLOW_AUTO (SXGBE_FLOW_TX | SXGBE_FLOW_RX)
#define SF_DMA_MODE 1 /* DMA STORE-AND-FORWARD Operation Mode */
/* errors */
#define RX_GMII_ERR 0x01
#define RX_WATCHDOG_ERR 0x02
#define RX_CRC_ERR 0x03
#define RX_GAINT_ERR 0x04
#define RX_IP_HDR_ERR 0x05
#define RX_PAYLOAD_ERR 0x06
#define RX_OVERFLOW_ERR 0x07
/* pkt type */
#define RX_LEN_PKT 0x00
#define RX_MACCTL_PKT 0x01
#define RX_DCBCTL_PKT 0x02
#define RX_ARP_PKT 0x03
#define RX_OAM_PKT 0x04
#define RX_UNTAG_PKT 0x05
#define RX_OTHER_PKT 0x07
#define RX_SVLAN_PKT 0x08
#define RX_CVLAN_PKT 0x09
#define RX_DVLAN_OCVLAN_ICVLAN_PKT 0x0A
#define RX_DVLAN_OSVLAN_ISVLAN_PKT 0x0B
#define RX_DVLAN_OSVLAN_ICVLAN_PKT 0x0C
#define RX_DVLAN_OCVLAN_ISVLAN_PKT 0x0D
#define RX_NOT_IP_PKT 0x00
#define RX_IPV4_TCP_PKT 0x01
#define RX_IPV4_UDP_PKT 0x02
#define RX_IPV4_ICMP_PKT 0x03
#define RX_IPV4_UNKNOWN_PKT 0x07
#define RX_IPV6_TCP_PKT 0x09
#define RX_IPV6_UDP_PKT 0x0A
#define RX_IPV6_ICMP_PKT 0x0B
#define RX_IPV6_UNKNOWN_PKT 0x0F
#define RX_NO_PTP 0x00
#define RX_PTP_SYNC 0x01
#define RX_PTP_FOLLOW_UP 0x02
#define RX_PTP_DELAY_REQ 0x03
#define RX_PTP_DELAY_RESP 0x04
#define RX_PTP_PDELAY_REQ 0x05
#define RX_PTP_PDELAY_RESP 0x06
#define RX_PTP_PDELAY_FOLLOW_UP 0x07
#define RX_PTP_ANNOUNCE 0x08
#define RX_PTP_MGMT 0x09
#define RX_PTP_SIGNAL 0x0A
#define RX_PTP_RESV_MSG 0x0F
enum dma_irq_status {
tx_hard_error = BIT(0),
tx_bump_tc = BIT(1),
handle_tx = BIT(2),
rx_hard_error = BIT(3),
rx_bump_tc = BIT(4),
handle_rx = BIT(5),
};
#define NETIF_F_HW_VLAN_ALL (NETIF_F_HW_VLAN_CTAG_RX | \
NETIF_F_HW_VLAN_STAG_RX | \
NETIF_F_HW_VLAN_CTAG_TX | \
NETIF_F_HW_VLAN_STAG_TX | \
NETIF_F_HW_VLAN_CTAG_FILTER | \
NETIF_F_HW_VLAN_STAG_FILTER)
/* MMC control defines */
#define SXGBE_MMC_CTRL_CNT_FRZ 0x00000008
/* SXGBE HW ADDR regs */
#define SXGBE_ADDR_HIGH(reg) (((reg > 15) ? 0x00000800 : 0x00000040) + \
(reg * 8))
#define SXGBE_ADDR_LOW(reg) (((reg > 15) ? 0x00000804 : 0x00000044) + \
(reg * 8))
#define SXGBE_MAX_PERFECT_ADDRESSES 32 /* Maximum unicast perfect filtering */
#define SXGBE_FRAME_FILTER 0x00000004 /* Frame Filter */
/* SXGBE Frame Filter defines */
#define SXGBE_FRAME_FILTER_PR 0x00000001 /* Promiscuous Mode */
#define SXGBE_FRAME_FILTER_HUC 0x00000002 /* Hash Unicast */
#define SXGBE_FRAME_FILTER_HMC 0x00000004 /* Hash Multicast */
#define SXGBE_FRAME_FILTER_DAIF 0x00000008 /* DA Inverse Filtering */
#define SXGBE_FRAME_FILTER_PM 0x00000010 /* Pass all multicast */
#define SXGBE_FRAME_FILTER_DBF 0x00000020 /* Disable Broadcast frames */
#define SXGBE_FRAME_FILTER_SAIF 0x00000100 /* Inverse Filtering */
#define SXGBE_FRAME_FILTER_SAF 0x00000200 /* Source Address Filter */
#define SXGBE_FRAME_FILTER_HPF 0x00000400 /* Hash or perfect Filter */
#define SXGBE_FRAME_FILTER_RA 0x80000000 /* Receive all mode */
#define SXGBE_HASH_TABLE_SIZE 64
#define SXGBE_HASH_HIGH 0x00000008 /* Multicast Hash Table High */
#define SXGBE_HASH_LOW 0x0000000c /* Multicast Hash Table Low */
#define SXGBE_HI_REG_AE 0x80000000
/* Minimum and maximum MTU */
#define MIN_MTU 68
#define MAX_MTU 9000
#define SXGBE_FOR_EACH_QUEUE(max_queues, queue_num) \
for (queue_num = 0; queue_num < max_queues; queue_num++)
/* sxgbe statistics counters */
struct sxgbe_extra_stats {
/* TX/RX IRQ events */
unsigned long tx_underflow_irq;
unsigned long tx_process_stopped_irq;
unsigned long tx_ctxt_desc_err;
unsigned long tx_threshold;
unsigned long rx_threshold;
unsigned long tx_pkt_n;
unsigned long rx_pkt_n;
unsigned long normal_irq_n;
unsigned long tx_normal_irq_n;
unsigned long rx_normal_irq_n;
unsigned long napi_poll;
unsigned long tx_clean;
unsigned long tx_reset_ic_bit;
unsigned long rx_process_stopped_irq;
unsigned long rx_underflow_irq;
/* Bus access errors */
unsigned long fatal_bus_error_irq;
unsigned long tx_read_transfer_err;
unsigned long tx_write_transfer_err;
unsigned long tx_desc_access_err;
unsigned long tx_buffer_access_err;
unsigned long tx_data_transfer_err;
unsigned long rx_read_transfer_err;
unsigned long rx_write_transfer_err;
unsigned long rx_desc_access_err;
unsigned long rx_buffer_access_err;
unsigned long rx_data_transfer_err;
/* RX specific */
/* L2 error */
unsigned long rx_code_gmii_err;
unsigned long rx_watchdog_err;
unsigned long rx_crc_err;
unsigned long rx_gaint_pkt_err;
unsigned long ip_hdr_err;
unsigned long ip_payload_err;
unsigned long overflow_error;
/* L2 Pkt type */
unsigned long len_pkt;
unsigned long mac_ctl_pkt;
unsigned long dcb_ctl_pkt;
unsigned long arp_pkt;
unsigned long oam_pkt;
unsigned long untag_okt;
unsigned long other_pkt;
unsigned long svlan_tag_pkt;
unsigned long cvlan_tag_pkt;
unsigned long dvlan_ocvlan_icvlan_pkt;
unsigned long dvlan_osvlan_isvlan_pkt;
unsigned long dvlan_osvlan_icvlan_pkt;
unsigned long dvan_ocvlan_icvlan_pkt;
/* L3/L4 Pkt type */
unsigned long not_ip_pkt;
unsigned long ip4_tcp_pkt;
unsigned long ip4_udp_pkt;
unsigned long ip4_icmp_pkt;
unsigned long ip4_unknown_pkt;
unsigned long ip6_tcp_pkt;
unsigned long ip6_udp_pkt;
unsigned long ip6_icmp_pkt;
unsigned long ip6_unknown_pkt;
/* Filter specific */
unsigned long vlan_filter_match;
unsigned long sa_filter_fail;
unsigned long da_filter_fail;
unsigned long hash_filter_pass;
unsigned long l3_filter_match;
unsigned long l4_filter_match;
/* RX context specific */
unsigned long timestamp_dropped;
unsigned long rx_msg_type_no_ptp;
unsigned long rx_ptp_type_sync;
unsigned long rx_ptp_type_follow_up;
unsigned long rx_ptp_type_delay_req;
unsigned long rx_ptp_type_delay_resp;
unsigned long rx_ptp_type_pdelay_req;
unsigned long rx_ptp_type_pdelay_resp;
unsigned long rx_ptp_type_pdelay_follow_up;
unsigned long rx_ptp_announce;
unsigned long rx_ptp_mgmt;
unsigned long rx_ptp_signal;
unsigned long rx_ptp_resv_msg_type;
};
struct mac_link {
int port;
int duplex;
int speed;
};
struct mii_regs {
unsigned int addr; /* MII Address */
unsigned int data; /* MII Data */
};
struct sxgbe_core_ops {
/* MAC core initialization */
void (*core_init)(void __iomem *ioaddr);
/* Dump MAC registers */
void (*dump_regs)(void __iomem *ioaddr);
/* Handle extra events on specific interrupts hw dependent */
int (*host_irq_status)(void __iomem *ioaddr,
struct sxgbe_extra_stats *x);
/* Set power management mode (e.g. magic frame) */
void (*pmt)(void __iomem *ioaddr, unsigned long mode);
/* Set/Get Unicast MAC addresses */
void (*set_umac_addr)(void __iomem *ioaddr, unsigned char *addr,
unsigned int reg_n);
void (*get_umac_addr)(void __iomem *ioaddr, unsigned char *addr,
unsigned int reg_n);
void (*enable_rx)(void __iomem *ioaddr, bool enable);
void (*enable_tx)(void __iomem *ioaddr, bool enable);
/* controller version specific operations */
int (*get_controller_version)(void __iomem *ioaddr);
/* If supported then get the optional core features */
unsigned int (*get_hw_feature)(void __iomem *ioaddr,
unsigned char feature_index);
/* adjust SXGBE speed */
void (*set_speed)(void __iomem *ioaddr, unsigned char speed);
};
const struct sxgbe_core_ops *sxgbe_get_core_ops(void);
struct sxgbe_ops {
const struct sxgbe_core_ops *mac;
const struct sxgbe_desc_ops *desc;
const struct sxgbe_dma_ops *dma;
const struct sxgbe_mtl_ops *mtl;
struct mii_regs mii; /* MII register Addresses */
struct mac_link link;
unsigned int ctrl_uid;
unsigned int ctrl_id;
};
/* SXGBE private data structures */
struct sxgbe_tx_queue {
unsigned int irq_no;
struct sxgbe_priv_data *priv_ptr;
struct sxgbe_tx_norm_desc *dma_tx;
dma_addr_t dma_tx_phy;
dma_addr_t *tx_skbuff_dma;
struct sk_buff **tx_skbuff;
struct timer_list txtimer;
spinlock_t tx_lock; /* lock for tx queues */
unsigned int cur_tx;
unsigned int dirty_tx;
u32 tx_count_frames;
u32 tx_coal_frames;
u32 tx_coal_timer;
int hwts_tx_en;
u8 queue_no;
};
struct sxgbe_rx_queue {
struct sxgbe_priv_data *priv_ptr;
struct sxgbe_rx_norm_desc *dma_rx;
struct sk_buff **rx_skbuff;
unsigned int cur_rx;
unsigned int dirty_rx;
unsigned int irq_no;
u32 rx_riwt;
dma_addr_t *rx_skbuff_dma;
dma_addr_t dma_rx_phy;
u8 queue_no;
};
/* SXGBE HW capabilities */
struct sxgbe_hw_features {
/****** CAP [0] *******/
unsigned int pmt_remote_wake_up;
unsigned int pmt_magic_frame;
/* IEEE 1588-2008 */
unsigned int atime_stamp;
unsigned int tx_csum_offload;
unsigned int rx_csum_offload;
unsigned int multi_macaddr;
unsigned int tstamp_srcselect;
unsigned int sa_vlan_insert;
/****** CAP [1] *******/
unsigned int rxfifo_size;
unsigned int txfifo_size;
unsigned int atstmap_hword;
unsigned int dcb_enable;
unsigned int splithead_enable;
unsigned int tcpseg_offload;
unsigned int debug_mem;
unsigned int rss_enable;
unsigned int hash_tsize;
unsigned int l3l4_filer_size;
/* This value is in bytes and
* as mentioned in HW features
* of SXGBE data book
*/
unsigned int rx_mtl_qsize;
unsigned int tx_mtl_qsize;
/****** CAP [2] *******/
/* TX and RX number of channels */
unsigned int rx_mtl_queues;
unsigned int tx_mtl_queues;
unsigned int rx_dma_channels;
unsigned int tx_dma_channels;
unsigned int pps_output_count;
unsigned int aux_input_count;
};
struct sxgbe_priv_data {
/* DMA descriptos */
struct sxgbe_tx_queue *txq[SXGBE_TX_QUEUES];
struct sxgbe_rx_queue *rxq[SXGBE_RX_QUEUES];
u8 cur_rx_qnum;
unsigned int dma_tx_size;
unsigned int dma_rx_size;
unsigned int dma_buf_sz;
u32 rx_riwt;
struct napi_struct napi;
void __iomem *ioaddr;
struct net_device *dev;
struct device *device;
struct sxgbe_ops *hw; /* sxgbe specific ops */
int no_csum_insertion;
int irq;
spinlock_t stats_lock; /* lock for tx/rx statatics */
struct phy_device *phydev;
int oldlink;
int speed;
int oldduplex;
struct mii_bus *mii;
int mii_irq[PHY_MAX_ADDR];
u8 rx_pause;
u8 tx_pause;
struct sxgbe_extra_stats xstats;
struct sxgbe_plat_data *plat;
struct sxgbe_hw_features hw_cap;
u32 msg_enable;
struct clk *sxgbe_clk;
int clk_csr;
unsigned int mode;
unsigned int default_addend;
/* advanced time stamp support */
u32 adv_ts;
int use_riwt;
/* tc control */
int tx_tc;
int rx_tc;
};
/* Function prototypes */
struct sxgbe_priv_data *sxgbe_drv_probe(struct device *device,
struct sxgbe_plat_data *plat_dat,
void __iomem *addr);
int sxgbe_drv_remove(struct net_device *ndev);
void sxgbe_set_ethtool_ops(struct net_device *netdev);
int sxgbe_mdio_unregister(struct net_device *ndev);
int sxgbe_mdio_register(struct net_device *ndev);
int sxgbe_register_platform(void);
void sxgbe_unregister_platform(void);
#ifdef CONFIG_PM
int sxgbe_suspend(struct net_device *ndev);
int sxgbe_resume(struct net_device *ndev);
int sxgbe_freeze(struct net_device *ndev);
int sxgbe_restore(struct net_device *ndev);
#endif /* CONFIG_PM */
const struct sxgbe_mtl_ops *sxgbe_get_mtl_ops(void);
#endif /* __SXGBE_COMMON_H__ */
/* 10G controller driver for Samsung SoCs
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Siva Reddy Kallam <siva.kallam@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/export.h>
#include <linux/io.h>
#include <linux/netdevice.h>
#include <linux/phy.h>
#include "sxgbe_common.h"
#include "sxgbe_reg.h"
/* MAC core initialization */
static void sxgbe_core_init(void __iomem *ioaddr)
{
u32 regval;
/* TX configuration */
regval = readl(ioaddr + SXGBE_CORE_TX_CONFIG_REG);
/* Other configurable parameters IFP, IPG, ISR, ISM
* needs to be set if needed
*/
regval |= SXGBE_TX_JABBER_DISABLE;
writel(regval, ioaddr + SXGBE_CORE_TX_CONFIG_REG);
/* RX configuration */
regval = readl(ioaddr + SXGBE_CORE_RX_CONFIG_REG);
/* Other configurable parameters CST, SPEN, USP, GPSLCE
* WD, LM, S2KP, HDSMS, GPSL, ELEN, ARPEN needs to be
* set if needed
*/
regval |= SXGBE_RX_JUMBPKT_ENABLE | SXGBE_RX_ACS_ENABLE;
writel(regval, ioaddr + SXGBE_CORE_RX_CONFIG_REG);
}
/* Dump MAC registers */
static void sxgbe_core_dump_regs(void __iomem *ioaddr)
{
}
/* Handle extra events on specific interrupts hw dependent */
static int sxgbe_core_host_irq_status(void __iomem *ioaddr,
struct sxgbe_extra_stats *x)
{
return 0;
}
/* Set power management mode (e.g. magic frame) */
static void sxgbe_core_pmt(void __iomem *ioaddr, unsigned long mode)
{
}
/* Set/Get Unicast MAC addresses */
static void sxgbe_core_set_umac_addr(void __iomem *ioaddr, unsigned char *addr,
unsigned int reg_n)
{
u32 high_word, low_word;
high_word = (addr[5] << 8) || (addr[4]);
low_word = ((addr[3] << 24) || (addr[2] << 16) ||
(addr[1] << 8) || (addr[0]));
writel(high_word, ioaddr + SXGBE_CORE_ADD_HIGHOFFSET(reg_n));
writel(low_word, ioaddr + SXGBE_CORE_ADD_LOWOFFSET(reg_n));
}
static void sxgbe_core_get_umac_addr(void __iomem *ioaddr, unsigned char *addr,
unsigned int reg_n)
{
u32 high_word, low_word;
high_word = readl(ioaddr + SXGBE_CORE_ADD_HIGHOFFSET(reg_n));
low_word = readl(ioaddr + SXGBE_CORE_ADD_LOWOFFSET(reg_n));
/* extract and assign address */
addr[5] = (high_word & 0x0000FF00) >> 8;
addr[4] = (high_word & 0x000000FF);
addr[3] = (low_word & 0xFF000000) >> 24;
addr[2] = (low_word & 0x00FF0000) >> 16;
addr[1] = (low_word & 0x0000FF00) >> 8;
addr[0] = (low_word & 0x000000FF);
}
static void sxgbe_enable_tx(void __iomem *ioaddr, bool enable)
{
u32 tx_config;
tx_config = readl(ioaddr + SXGBE_CORE_TX_CONFIG_REG);
tx_config &= ~SXGBE_TX_ENABLE;
if (enable)
tx_config |= SXGBE_TX_ENABLE;
writel(tx_config, ioaddr + SXGBE_CORE_TX_CONFIG_REG);
}
static void sxgbe_enable_rx(void __iomem *ioaddr, bool enable)
{
u32 rx_config;
rx_config = readl(ioaddr + SXGBE_CORE_RX_CONFIG_REG);
rx_config &= ~SXGBE_RX_ENABLE;
if (enable)
rx_config |= SXGBE_RX_ENABLE;
writel(rx_config, ioaddr + SXGBE_CORE_RX_CONFIG_REG);
}
static int sxgbe_get_controller_version(void __iomem *ioaddr)
{
return readl(ioaddr + SXGBE_CORE_VERSION_REG);
}
/* If supported then get the optional core features */
static unsigned int sxgbe_get_hw_feature(void __iomem *ioaddr,
unsigned char feature_index)
{
return readl(ioaddr + (SXGBE_CORE_HW_FEA_REG(feature_index)));
}
static void sxgbe_core_set_speed(void __iomem *ioaddr, unsigned char speed)
{
u32 tx_cfg = readl(ioaddr + SXGBE_CORE_TX_CONFIG_REG);
/* clear the speed bits */
tx_cfg &= ~0x60000000;
tx_cfg |= (speed << SXGBE_SPEED_LSHIFT);
/* set the speed */
writel(tx_cfg, ioaddr + SXGBE_CORE_TX_CONFIG_REG);
}
const struct sxgbe_core_ops core_ops = {
.core_init = sxgbe_core_init,
.dump_regs = sxgbe_core_dump_regs,
.host_irq_status = sxgbe_core_host_irq_status,
.pmt = sxgbe_core_pmt,
.set_umac_addr = sxgbe_core_set_umac_addr,
.get_umac_addr = sxgbe_core_get_umac_addr,
.enable_rx = sxgbe_enable_rx,
.enable_tx = sxgbe_enable_tx,
.get_controller_version = sxgbe_get_controller_version,
.get_hw_feature = sxgbe_get_hw_feature,
.set_speed = sxgbe_core_set_speed,
};
const struct sxgbe_core_ops *sxgbe_get_core_ops(void)
{
return &core_ops;
}
/* 10G controller driver for Samsung SoCs
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Siva Reddy Kallam <siva.kallam@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/bitops.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/netdevice.h>
#include <linux/phy.h>
#include "sxgbe_common.h"
#include "sxgbe_dma.h"
#include "sxgbe_desc.h"
/* DMA TX descriptor ring initialization */
static void sxgbe_init_tx_desc(struct sxgbe_tx_norm_desc *p)
{
p->tdes23.tx_rd_des23.own_bit = 0;
}
static void sxgbe_tx_desc_enable_tse(struct sxgbe_tx_norm_desc *p, u8 is_tse,
u32 total_hdr_len, u32 tcp_hdr_len,
u32 tcp_payload_len)
{
p->tdes23.tx_rd_des23.tse_bit = is_tse;
p->tdes23.tx_rd_des23.buf1_size = total_hdr_len;
p->tdes23.tx_rd_des23.tcp_hdr_len = tcp_hdr_len / 4;
p->tdes23.tx_rd_des23.tx_pkt_len.tcp_payload_len = tcp_payload_len;
}
/* Assign buffer lengths for descriptor */
static void sxgbe_prepare_tx_desc(struct sxgbe_tx_norm_desc *p, u8 is_fd,
int buf1_len, int pkt_len, int cksum)
{
p->tdes23.tx_rd_des23.first_desc = is_fd;
p->tdes23.tx_rd_des23.buf1_size = buf1_len;
p->tdes23.tx_rd_des23.tx_pkt_len.cksum_pktlen.total_pkt_len = pkt_len;
if (cksum)
p->tdes23.tx_rd_des23.tx_pkt_len.cksum_pktlen.cksum_ctl = cic_full;
}
/* Set VLAN control information */
static void sxgbe_tx_vlanctl_desc(struct sxgbe_tx_norm_desc *p, int vlan_ctl)
{
p->tdes23.tx_rd_des23.vlan_tag_ctl = vlan_ctl;
}
/* Set the owner of Normal descriptor */
static void sxgbe_set_tx_owner(struct sxgbe_tx_norm_desc *p)
{
p->tdes23.tx_rd_des23.own_bit = 1;
}
/* Get the owner of Normal descriptor */
static int sxgbe_get_tx_owner(struct sxgbe_tx_norm_desc *p)
{
return p->tdes23.tx_rd_des23.own_bit;
}
/* Invoked by the xmit function to close the tx descriptor */
static void sxgbe_close_tx_desc(struct sxgbe_tx_norm_desc *p)
{
p->tdes23.tx_rd_des23.last_desc = 1;
p->tdes23.tx_rd_des23.int_on_com = 1;
}
/* Clean the tx descriptor as soon as the tx irq is received */
static void sxgbe_release_tx_desc(struct sxgbe_tx_norm_desc *p)
{
memset(p, 0, sizeof(*p));
}
/* Clear interrupt on tx frame completion. When this bit is
* set an interrupt happens as soon as the frame is transmitted
*/
static void sxgbe_clear_tx_ic(struct sxgbe_tx_norm_desc *p)
{
p->tdes23.tx_rd_des23.int_on_com = 0;
}
/* Last tx segment reports the transmit status */
static int sxgbe_get_tx_ls(struct sxgbe_tx_norm_desc *p)
{
return p->tdes23.tx_rd_des23.last_desc;
}
/* Get the buffer size from the descriptor */
static int sxgbe_get_tx_len(struct sxgbe_tx_norm_desc *p)
{
return p->tdes23.tx_rd_des23.buf1_size;
}
/* Set tx timestamp enable bit */
static void sxgbe_tx_enable_tstamp(struct sxgbe_tx_norm_desc *p)
{
p->tdes23.tx_rd_des23.timestmp_enable = 1;
}
/* get tx timestamp status */
static int sxgbe_get_tx_timestamp_status(struct sxgbe_tx_norm_desc *p)
{
return p->tdes23.tx_rd_des23.timestmp_enable;
}
/* TX Context Descripto Specific */
static void sxgbe_tx_ctxt_desc_set_ctxt(struct sxgbe_tx_ctxt_desc *p)
{
p->ctxt_bit = 1;
}
/* Set the owner of TX context descriptor */
static void sxgbe_tx_ctxt_desc_set_owner(struct sxgbe_tx_ctxt_desc *p)
{
p->own_bit = 1;
}
/* Get the owner of TX context descriptor */
static int sxgbe_tx_ctxt_desc_get_owner(struct sxgbe_tx_ctxt_desc *p)
{
return p->own_bit;
}
/* Set TX mss in TX context Descriptor */
static void sxgbe_tx_ctxt_desc_set_mss(struct sxgbe_tx_ctxt_desc *p, int mss)
{
p->maxseg_size = mss;
}
/* Get TX mss from TX context Descriptor */
static int sxgbe_tx_ctxt_desc_get_mss(struct sxgbe_tx_ctxt_desc *p)
{
return p->maxseg_size;
}
/* Set TX tcmssv in TX context Descriptor */
static void sxgbe_tx_ctxt_desc_set_tcmssv(struct sxgbe_tx_ctxt_desc *p)
{
p->tcmssv = 1;
}
/* Reset TX ostc in TX context Descriptor */
static void sxgbe_tx_ctxt_desc_reset_ostc(struct sxgbe_tx_ctxt_desc *p)
{
p->ostc = 0;
}
/* Set IVLAN information */
static void sxgbe_tx_ctxt_desc_set_ivlantag(struct sxgbe_tx_ctxt_desc *p,
int is_ivlanvalid, int ivlan_tag,
int ivlan_ctl)
{
if (is_ivlanvalid) {
p->ivlan_tag_valid = is_ivlanvalid;
p->ivlan_tag = ivlan_tag;
p->ivlan_tag_ctl = ivlan_ctl;
}
}
/* Return IVLAN Tag */
static int sxgbe_tx_ctxt_desc_get_ivlantag(struct sxgbe_tx_ctxt_desc *p)
{
return p->ivlan_tag;
}
/* Set VLAN Tag */
static void sxgbe_tx_ctxt_desc_set_vlantag(struct sxgbe_tx_ctxt_desc *p,
int is_vlanvalid, int vlan_tag)
{
if (is_vlanvalid) {
p->vltag_valid = is_vlanvalid;
p->vlan_tag = vlan_tag;
}
}
/* Return VLAN Tag */
static int sxgbe_tx_ctxt_desc_get_vlantag(struct sxgbe_tx_ctxt_desc *p)
{
return p->vlan_tag;
}
/* Set Time stamp */
static void sxgbe_tx_ctxt_desc_set_tstamp(struct sxgbe_tx_ctxt_desc *p,
u8 ostc_enable, u64 tstamp)
{
if (ostc_enable) {
p->ostc = ostc_enable;
p->tstamp_lo = (u32) tstamp;
p->tstamp_hi = (u32) (tstamp>>32);
}
}
/* Close TX context descriptor */
static void sxgbe_tx_ctxt_desc_close(struct sxgbe_tx_ctxt_desc *p)
{
p->own_bit = 1;
}
/* WB status of context descriptor */
static int sxgbe_tx_ctxt_desc_get_cde(struct sxgbe_tx_ctxt_desc *p)
{
return p->ctxt_desc_err;
}
/* DMA RX descriptor ring initialization */
static void sxgbe_init_rx_desc(struct sxgbe_rx_norm_desc *p, int disable_rx_ic,
int mode, int end)
{
p->rdes23.rx_rd_des23.own_bit = 1;
if (disable_rx_ic)
p->rdes23.rx_rd_des23.int_on_com = disable_rx_ic;
}
/* Get RX own bit */
static int sxgbe_get_rx_owner(struct sxgbe_rx_norm_desc *p)
{
return p->rdes23.rx_rd_des23.own_bit;
}
/* Set RX own bit */
static void sxgbe_set_rx_owner(struct sxgbe_rx_norm_desc *p)
{
p->rdes23.rx_rd_des23.own_bit = 1;
}
/* Get the receive frame size */
static int sxgbe_get_rx_frame_len(struct sxgbe_rx_norm_desc *p)
{
return p->rdes23.rx_wb_des23.pkt_len;
}
/* Return first Descriptor status */
static int sxgbe_get_rx_fd_status(struct sxgbe_rx_norm_desc *p)
{
return p->rdes23.rx_wb_des23.first_desc;
}
/* Return Last Descriptor status */
static int sxgbe_get_rx_ld_status(struct sxgbe_rx_norm_desc *p)
{
return p->rdes23.rx_wb_des23.last_desc;
}
/* Return the RX status looking at the WB fields */
static int sxgbe_rx_wbstatus(struct sxgbe_rx_norm_desc *p,
struct sxgbe_extra_stats *x, int *checksum)
{
int status = 0;
*checksum = CHECKSUM_UNNECESSARY;
if (p->rdes23.rx_wb_des23.err_summary) {
switch (p->rdes23.rx_wb_des23.err_l2_type) {
case RX_GMII_ERR:
status = -EINVAL;
x->rx_code_gmii_err++;
break;
case RX_WATCHDOG_ERR:
status = -EINVAL;
x->rx_watchdog_err++;
break;
case RX_CRC_ERR:
status = -EINVAL;
x->rx_crc_err++;
break;
case RX_GAINT_ERR:
status = -EINVAL;
x->rx_gaint_pkt_err++;
break;
case RX_IP_HDR_ERR:
*checksum = CHECKSUM_NONE;
x->ip_hdr_err++;
break;
case RX_PAYLOAD_ERR:
*checksum = CHECKSUM_NONE;
x->ip_payload_err++;
break;
case RX_OVERFLOW_ERR:
status = -EINVAL;
x->overflow_error++;
break;
default:
pr_err("Invalid Error type\n");
break;
}
} else {
switch (p->rdes23.rx_wb_des23.err_l2_type) {
case RX_LEN_PKT:
x->len_pkt++;
break;
case RX_MACCTL_PKT:
x->mac_ctl_pkt++;
break;
case RX_DCBCTL_PKT:
x->dcb_ctl_pkt++;
break;
case RX_ARP_PKT:
x->arp_pkt++;
break;
case RX_OAM_PKT:
x->oam_pkt++;
break;
case RX_UNTAG_PKT:
x->untag_okt++;
break;
case RX_OTHER_PKT:
x->other_pkt++;
break;
case RX_SVLAN_PKT:
x->svlan_tag_pkt++;
break;
case RX_CVLAN_PKT:
x->cvlan_tag_pkt++;
break;
case RX_DVLAN_OCVLAN_ICVLAN_PKT:
x->dvlan_ocvlan_icvlan_pkt++;
break;
case RX_DVLAN_OSVLAN_ISVLAN_PKT:
x->dvlan_osvlan_isvlan_pkt++;
break;
case RX_DVLAN_OSVLAN_ICVLAN_PKT:
x->dvlan_osvlan_icvlan_pkt++;
break;
case RX_DVLAN_OCVLAN_ISVLAN_PKT:
x->dvlan_ocvlan_icvlan_pkt++;
break;
default:
pr_err("Invalid L2 Packet type\n");
break;
}
}
/* L3/L4 Pkt type */
switch (p->rdes23.rx_wb_des23.layer34_pkt_type) {
case RX_NOT_IP_PKT:
x->not_ip_pkt++;
break;
case RX_IPV4_TCP_PKT:
x->ip4_tcp_pkt++;
break;
case RX_IPV4_UDP_PKT:
x->ip4_udp_pkt++;
break;
case RX_IPV4_ICMP_PKT:
x->ip4_icmp_pkt++;
break;
case RX_IPV4_UNKNOWN_PKT:
x->ip4_unknown_pkt++;
break;
case RX_IPV6_TCP_PKT:
x->ip6_tcp_pkt++;
break;
case RX_IPV6_UDP_PKT:
x->ip6_udp_pkt++;
break;
case RX_IPV6_ICMP_PKT:
x->ip6_icmp_pkt++;
break;
case RX_IPV6_UNKNOWN_PKT:
x->ip6_unknown_pkt++;
break;
default:
pr_err("Invalid L3/L4 Packet type\n");
break;
}
/* Filter */
if (p->rdes23.rx_wb_des23.vlan_filter_match)
x->vlan_filter_match++;
if (p->rdes23.rx_wb_des23.sa_filter_fail) {
status = -EINVAL;
x->sa_filter_fail++;
}
if (p->rdes23.rx_wb_des23.da_filter_fail) {
status = -EINVAL;
x->da_filter_fail++;
}
if (p->rdes23.rx_wb_des23.hash_filter_pass)
x->hash_filter_pass++;
if (p->rdes23.rx_wb_des23.l3_filter_match)
x->l3_filter_match++;
if (p->rdes23.rx_wb_des23.l4_filter_match)
x->l4_filter_match++;
return status;
}
/* Get own bit of context descriptor */
static int sxgbe_get_rx_ctxt_owner(struct sxgbe_rx_ctxt_desc *p)
{
return p->own_bit;
}
/* Set own bit for context descriptor */
static void sxgbe_set_ctxt_rx_owner(struct sxgbe_rx_ctxt_desc *p)
{
p->own_bit = 1;
}
/* Return the reception status looking at Context control information */
static void sxgbe_rx_ctxt_wbstatus(struct sxgbe_rx_ctxt_desc *p,
struct sxgbe_extra_stats *x)
{
if (p->tstamp_dropped)
x->timestamp_dropped++;
/* ptp */
if (p->ptp_msgtype == RX_NO_PTP)
x->rx_msg_type_no_ptp++;
else if (p->ptp_msgtype == RX_PTP_SYNC)
x->rx_ptp_type_sync++;
else if (p->ptp_msgtype == RX_PTP_FOLLOW_UP)
x->rx_ptp_type_follow_up++;
else if (p->ptp_msgtype == RX_PTP_DELAY_REQ)
x->rx_ptp_type_delay_req++;
else if (p->ptp_msgtype == RX_PTP_DELAY_RESP)
x->rx_ptp_type_delay_resp++;
else if (p->ptp_msgtype == RX_PTP_PDELAY_REQ)
x->rx_ptp_type_pdelay_req++;
else if (p->ptp_msgtype == RX_PTP_PDELAY_RESP)
x->rx_ptp_type_pdelay_resp++;
else if (p->ptp_msgtype == RX_PTP_PDELAY_FOLLOW_UP)
x->rx_ptp_type_pdelay_follow_up++;
else if (p->ptp_msgtype == RX_PTP_ANNOUNCE)
x->rx_ptp_announce++;
else if (p->ptp_msgtype == RX_PTP_MGMT)
x->rx_ptp_mgmt++;
else if (p->ptp_msgtype == RX_PTP_SIGNAL)
x->rx_ptp_signal++;
else if (p->ptp_msgtype == RX_PTP_RESV_MSG)
x->rx_ptp_resv_msg_type++;
}
/* Get rx timestamp status */
static int sxgbe_get_rx_ctxt_tstamp_status(struct sxgbe_rx_ctxt_desc *p)
{
if ((p->tstamp_hi == 0xffffffff) && (p->tstamp_lo == 0xffffffff)) {
pr_err("Time stamp corrupted\n");
return 0;
}
return p->tstamp_available;
}
static u64 sxgbe_get_rx_timestamp(struct sxgbe_rx_ctxt_desc *p)
{
u64 ns;
ns = p->tstamp_lo;
ns |= ((u64)p->tstamp_hi) << 32;
return ns;
}
static const struct sxgbe_desc_ops desc_ops = {
.init_tx_desc = sxgbe_init_tx_desc,
.tx_desc_enable_tse = sxgbe_tx_desc_enable_tse,
.prepare_tx_desc = sxgbe_prepare_tx_desc,
.tx_vlanctl_desc = sxgbe_tx_vlanctl_desc,
.set_tx_owner = sxgbe_set_tx_owner,
.get_tx_owner = sxgbe_get_tx_owner,
.close_tx_desc = sxgbe_close_tx_desc,
.release_tx_desc = sxgbe_release_tx_desc,
.clear_tx_ic = sxgbe_clear_tx_ic,
.get_tx_ls = sxgbe_get_tx_ls,
.get_tx_len = sxgbe_get_tx_len,
.tx_enable_tstamp = sxgbe_tx_enable_tstamp,
.get_tx_timestamp_status = sxgbe_get_tx_timestamp_status,
.tx_ctxt_desc_set_ctxt = sxgbe_tx_ctxt_desc_set_ctxt,
.tx_ctxt_desc_set_owner = sxgbe_tx_ctxt_desc_set_owner,
.get_tx_ctxt_owner = sxgbe_tx_ctxt_desc_get_owner,
.tx_ctxt_desc_set_mss = sxgbe_tx_ctxt_desc_set_mss,
.tx_ctxt_desc_get_mss = sxgbe_tx_ctxt_desc_get_mss,
.tx_ctxt_desc_set_tcmssv = sxgbe_tx_ctxt_desc_set_tcmssv,
.tx_ctxt_desc_reset_ostc = sxgbe_tx_ctxt_desc_reset_ostc,
.tx_ctxt_desc_set_ivlantag = sxgbe_tx_ctxt_desc_set_ivlantag,
.tx_ctxt_desc_get_ivlantag = sxgbe_tx_ctxt_desc_get_ivlantag,
.tx_ctxt_desc_set_vlantag = sxgbe_tx_ctxt_desc_set_vlantag,
.tx_ctxt_desc_get_vlantag = sxgbe_tx_ctxt_desc_get_vlantag,
.tx_ctxt_set_tstamp = sxgbe_tx_ctxt_desc_set_tstamp,
.close_tx_ctxt_desc = sxgbe_tx_ctxt_desc_close,
.get_tx_ctxt_cde = sxgbe_tx_ctxt_desc_get_cde,
.init_rx_desc = sxgbe_init_rx_desc,
.get_rx_owner = sxgbe_get_rx_owner,
.set_rx_owner = sxgbe_set_rx_owner,
.get_rx_frame_len = sxgbe_get_rx_frame_len,
.get_rx_fd_status = sxgbe_get_rx_fd_status,
.get_rx_ld_status = sxgbe_get_rx_ld_status,
.rx_wbstatus = sxgbe_rx_wbstatus,
.get_rx_ctxt_owner = sxgbe_get_rx_ctxt_owner,
.set_rx_ctxt_owner = sxgbe_set_ctxt_rx_owner,
.rx_ctxt_wbstatus = sxgbe_rx_ctxt_wbstatus,
.get_rx_ctxt_tstamp_status = sxgbe_get_rx_ctxt_tstamp_status,
.get_timestamp = sxgbe_get_rx_timestamp,
};
const struct sxgbe_desc_ops *sxgbe_get_desc_ops(void)
{
return &desc_ops;
}
/* 10G controller driver for Samsung SoCs
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Siva Reddy Kallam <siva.kallam@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __SXGBE_DESC_H__
#define __SXGBE_DESC_H__
#define SXGBE_DESC_SIZE_BYTES 16
/* forward declaration */
struct sxgbe_extra_stats;
/* Transmit checksum insertion control */
enum tdes_csum_insertion {
cic_disabled = 0, /* Checksum Insertion Control */
cic_only_ip = 1, /* Only IP header */
/* IP header but pseudoheader is not calculated */
cic_no_pseudoheader = 2,
cic_full = 3, /* IP header and pseudoheader */
};
struct sxgbe_tx_norm_desc {
u64 tdes01; /* buf1 address */
union {
/* TX Read-Format Desc 2,3 */
struct {
/* TDES2 */
u32 buf1_size:14;
u32 vlan_tag_ctl:2;
u32 buf2_size:14;
u32 timestmp_enable:1;
u32 int_on_com:1;
/* TDES3 */
union {
u32 tcp_payload_len:18;
struct {
u32 total_pkt_len:15;
u32 reserved1:1;
u32 cksum_ctl:2;
} cksum_pktlen;
} tx_pkt_len;
u32 tse_bit:1;
u32 tcp_hdr_len:4;
u32 sa_insert_ctl:3;
u32 crc_pad_ctl:2;
u32 last_desc:1;
u32 first_desc:1;
u32 ctxt_bit:1;
u32 own_bit:1;
} tx_rd_des23;
/* tx write back Desc 2,3 */
struct {
/* WB TES2 */
u32 reserved1;
/* WB TES3 */
u32 reserved2:31;
u32 own_bit:1;
} tx_wb_des23;
} tdes23;
};
struct sxgbe_rx_norm_desc {
union {
u32 rdes0; /* buf1 address */
struct {
u32 out_vlan_tag:16;
u32 in_vlan_tag:16;
} wb_rx_des0;
} rd_wb_des0;
union {
u32 rdes1; /* buf2 address or buf1[63:32] */
u32 rss_hash; /* Write-back RX */
} rd_wb_des1;
union {
/* RX Read format Desc 2,3 */
struct{
/* RDES2 */
u32 buf2_addr;
/* RDES3 */
u32 buf2_hi_addr:30;
u32 int_on_com:1;
u32 own_bit:1;
} rx_rd_des23;
/* RX write back */
struct{
/* WB RDES2 */
u32 hdr_len:10;
u32 rdes2_reserved:2;
u32 elrd_val:1;
u32 iovt_sel:1;
u32 res_pkt:1;
u32 vlan_filter_match:1;
u32 sa_filter_fail:1;
u32 da_filter_fail:1;
u32 hash_filter_pass:1;
u32 macaddr_filter_match:8;
u32 l3_filter_match:1;
u32 l4_filter_match:1;
u32 l34_filter_num:3;
/* WB RDES3 */
u32 pkt_len:14;
u32 rdes3_reserved:1;
u32 err_summary:15;
u32 err_l2_type:4;
u32 layer34_pkt_type:4;
u32 no_coagulation_pkt:1;
u32 in_seq_pkt:1;
u32 rss_valid:1;
u32 context_des_avail:1;
u32 last_desc:1;
u32 first_desc:1;
u32 recv_context_desc:1;
u32 own_bit:1;
} rx_wb_des23;
} rdes23;
};
/* Context descriptor structure */
struct sxgbe_tx_ctxt_desc {
u32 tstamp_lo;
u32 tstamp_hi;
u32 maxseg_size:15;
u32 reserved1:1;
u32 ivlan_tag:16;
u32 vlan_tag:16;
u32 vltag_valid:1;
u32 ivlan_tag_valid:1;
u32 ivlan_tag_ctl:2;
u32 reserved2:3;
u32 ctxt_desc_err:1;
u32 reserved3:2;
u32 ostc:1;
u32 tcmssv:1;
u32 reserved4:2;
u32 ctxt_bit:1;
u32 own_bit:1;
};
struct sxgbe_rx_ctxt_desc {
u32 tstamp_lo;
u32 tstamp_hi;
u32 reserved1;
u32 ptp_msgtype:4;
u32 tstamp_available:1;
u32 ptp_rsp_err:1;
u32 tstamp_dropped:1;
u32 reserved2:23;
u32 rx_ctxt_desc:1;
u32 own_bit:1;
};
struct sxgbe_desc_ops {
/* DMA TX descriptor ring initialization */
void (*init_tx_desc)(struct sxgbe_tx_norm_desc *p);
/* Invoked by the xmit function to prepare the tx descriptor */
void (*tx_desc_enable_tse)(struct sxgbe_tx_norm_desc *p, u8 is_tse,
u32 total_hdr_len, u32 payload_len,
u32 tcp_payload_len);
/* Assign buffer lengths for descriptor */
void (*prepare_tx_desc)(struct sxgbe_tx_norm_desc *p, u8 is_fd,
int buf1_len, int pkt_len, int cksum);
/* Set VLAN control information */
void (*tx_vlanctl_desc)(struct sxgbe_tx_norm_desc *p, int vlan_ctl);
/* Set the owner of the descriptor */
void (*set_tx_owner)(struct sxgbe_tx_norm_desc *p);
/* Get the owner of the descriptor */
int (*get_tx_owner)(struct sxgbe_tx_norm_desc *p);
/* Invoked by the xmit function to close the tx descriptor */
void (*close_tx_desc)(struct sxgbe_tx_norm_desc *p);
/* Clean the tx descriptor as soon as the tx irq is received */
void (*release_tx_desc)(struct sxgbe_tx_norm_desc *p);
/* Clear interrupt on tx frame completion. When this bit is
* set an interrupt happens as soon as the frame is transmitted
*/
void (*clear_tx_ic)(struct sxgbe_tx_norm_desc *p);
/* Last tx segment reports the transmit status */
int (*get_tx_ls)(struct sxgbe_tx_norm_desc *p);
/* Get the buffer size from the descriptor */
int (*get_tx_len)(struct sxgbe_tx_norm_desc *p);
/* Set tx timestamp enable bit */
void (*tx_enable_tstamp)(struct sxgbe_tx_norm_desc *p);
/* get tx timestamp status */
int (*get_tx_timestamp_status)(struct sxgbe_tx_norm_desc *p);
/* TX Context Descripto Specific */
void (*tx_ctxt_desc_set_ctxt)(struct sxgbe_tx_ctxt_desc *p);
/* Set the owner of the TX context descriptor */
void (*tx_ctxt_desc_set_owner)(struct sxgbe_tx_ctxt_desc *p);
/* Get the owner of the TX context descriptor */
int (*get_tx_ctxt_owner)(struct sxgbe_tx_ctxt_desc *p);
/* Set TX mss */
void (*tx_ctxt_desc_set_mss)(struct sxgbe_tx_ctxt_desc *p, int mss);
/* Set TX mss */
int (*tx_ctxt_desc_get_mss)(struct sxgbe_tx_ctxt_desc *p);
/* Set TX tcmssv */
void (*tx_ctxt_desc_set_tcmssv)(struct sxgbe_tx_ctxt_desc *p);
/* Reset TX ostc */
void (*tx_ctxt_desc_reset_ostc)(struct sxgbe_tx_ctxt_desc *p);
/* Set IVLAN information */
void (*tx_ctxt_desc_set_ivlantag)(struct sxgbe_tx_ctxt_desc *p,
int is_ivlanvalid, int ivlan_tag,
int ivlan_ctl);
/* Return IVLAN Tag */
int (*tx_ctxt_desc_get_ivlantag)(struct sxgbe_tx_ctxt_desc *p);
/* Set VLAN Tag */
void (*tx_ctxt_desc_set_vlantag)(struct sxgbe_tx_ctxt_desc *p,
int is_vlanvalid, int vlan_tag);
/* Return VLAN Tag */
int (*tx_ctxt_desc_get_vlantag)(struct sxgbe_tx_ctxt_desc *p);
/* Set Time stamp */
void (*tx_ctxt_set_tstamp)(struct sxgbe_tx_ctxt_desc *p,
u8 ostc_enable, u64 tstamp);
/* Close TX context descriptor */
void (*close_tx_ctxt_desc)(struct sxgbe_tx_ctxt_desc *p);
/* WB status of context descriptor */
int (*get_tx_ctxt_cde)(struct sxgbe_tx_ctxt_desc *p);
/* DMA RX descriptor ring initialization */
void (*init_rx_desc)(struct sxgbe_rx_norm_desc *p, int disable_rx_ic,
int mode, int end);
/* Get own bit */
int (*get_rx_owner)(struct sxgbe_rx_norm_desc *p);
/* Set own bit */
void (*set_rx_owner)(struct sxgbe_rx_norm_desc *p);
/* Get the receive frame size */
int (*get_rx_frame_len)(struct sxgbe_rx_norm_desc *p);
/* Return first Descriptor status */
int (*get_rx_fd_status)(struct sxgbe_rx_norm_desc *p);
/* Return first Descriptor status */
int (*get_rx_ld_status)(struct sxgbe_rx_norm_desc *p);
/* Return the reception status looking at the RDES1 */
int (*rx_wbstatus)(struct sxgbe_rx_norm_desc *p,
struct sxgbe_extra_stats *x, int *checksum);
/* Get own bit */
int (*get_rx_ctxt_owner)(struct sxgbe_rx_ctxt_desc *p);
/* Set own bit */
void (*set_rx_ctxt_owner)(struct sxgbe_rx_ctxt_desc *p);
/* Return the reception status looking at Context control information */
void (*rx_ctxt_wbstatus)(struct sxgbe_rx_ctxt_desc *p,
struct sxgbe_extra_stats *x);
/* Get rx timestamp status */
int (*get_rx_ctxt_tstamp_status)(struct sxgbe_rx_ctxt_desc *p);
/* Get timestamp value for rx, need to check this */
u64 (*get_timestamp)(struct sxgbe_rx_ctxt_desc *p);
};
const struct sxgbe_desc_ops *sxgbe_get_desc_ops(void);
#endif /* __SXGBE_DESC_H__ */
/* 10G controller driver for Samsung SoCs
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Siva Reddy Kallam <siva.kallam@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/netdevice.h>
#include <linux/phy.h>
#include "sxgbe_common.h"
#include "sxgbe_dma.h"
#include "sxgbe_reg.h"
#include "sxgbe_desc.h"
/* DMA core initialization */
static int sxgbe_dma_init(void __iomem *ioaddr, int fix_burst, int burst_map)
{
int retry_count = 10;
u32 reg_val;
/* reset the DMA */
writel(SXGBE_DMA_SOFT_RESET, ioaddr + SXGBE_DMA_MODE_REG);
while (retry_count--) {
if (!(readl(ioaddr + SXGBE_DMA_MODE_REG) &
SXGBE_DMA_SOFT_RESET))
break;
mdelay(10);
}
if (retry_count < 0)
return -EBUSY;
reg_val = readl(ioaddr + SXGBE_DMA_SYSBUS_MODE_REG);
/* if fix_burst = 0, Set UNDEF = 1 of DMA_Sys_Mode Register.
* if fix_burst = 1, Set UNDEF = 0 of DMA_Sys_Mode Register.
* burst_map is bitmap for BLEN[4, 8, 16, 32, 64, 128 and 256].
* Set burst_map irrespective of fix_burst value.
*/
if (!fix_burst)
reg_val |= SXGBE_DMA_AXI_UNDEF_BURST;
/* write burst len map */
reg_val |= (burst_map << SXGBE_DMA_BLENMAP_LSHIFT);
writel(reg_val, ioaddr + SXGBE_DMA_SYSBUS_MODE_REG);
return 0;
}
static void sxgbe_dma_channel_init(void __iomem *ioaddr, int cha_num,
int fix_burst, int pbl, dma_addr_t dma_tx,
dma_addr_t dma_rx, int t_rsize, int r_rsize)
{
u32 reg_val;
dma_addr_t dma_addr;
reg_val = readl(ioaddr + SXGBE_DMA_CHA_CTL_REG(cha_num));
/* set the pbl */
if (fix_burst) {
reg_val |= SXGBE_DMA_PBL_X8MODE;
writel(reg_val, ioaddr + SXGBE_DMA_CHA_CTL_REG(cha_num));
/* program the TX pbl */
reg_val = readl(ioaddr + SXGBE_DMA_CHA_TXCTL_REG(cha_num));
reg_val |= (pbl << SXGBE_DMA_TXPBL_LSHIFT);
writel(reg_val, ioaddr + SXGBE_DMA_CHA_TXCTL_REG(cha_num));
/* program the RX pbl */
reg_val = readl(ioaddr + SXGBE_DMA_CHA_RXCTL_REG(cha_num));
reg_val |= (pbl << SXGBE_DMA_RXPBL_LSHIFT);
writel(reg_val, ioaddr + SXGBE_DMA_CHA_RXCTL_REG(cha_num));
}
/* program desc registers */
writel(upper_32_bits(dma_tx),
ioaddr + SXGBE_DMA_CHA_TXDESC_HADD_REG(cha_num));
writel(lower_32_bits(dma_tx),
ioaddr + SXGBE_DMA_CHA_TXDESC_LADD_REG(cha_num));
writel(upper_32_bits(dma_rx),
ioaddr + SXGBE_DMA_CHA_RXDESC_HADD_REG(cha_num));
writel(lower_32_bits(dma_rx),
ioaddr + SXGBE_DMA_CHA_RXDESC_LADD_REG(cha_num));
/* program tail pointers */
/* assumption: upper 32 bits are constant and
* same as TX/RX desc list
*/
dma_addr = dma_tx + ((t_rsize - 1) * SXGBE_DESC_SIZE_BYTES);
writel(lower_32_bits(dma_addr),
ioaddr + SXGBE_DMA_CHA_TXDESC_TAILPTR_REG(cha_num));
dma_addr = dma_rx + ((r_rsize - 1) * SXGBE_DESC_SIZE_BYTES);
writel(lower_32_bits(dma_addr),
ioaddr + SXGBE_DMA_CHA_RXDESC_LADD_REG(cha_num));
/* program the ring sizes */
writel(t_rsize - 1, ioaddr + SXGBE_DMA_CHA_TXDESC_RINGLEN_REG(cha_num));
writel(r_rsize - 1, ioaddr + SXGBE_DMA_CHA_RXDESC_RINGLEN_REG(cha_num));
/* Enable TX/RX interrupts */
writel(SXGBE_DMA_ENA_INT,
ioaddr + SXGBE_DMA_CHA_INT_ENABLE_REG(cha_num));
}
static void sxgbe_enable_dma_transmission(void __iomem *ioaddr, int cha_num)
{
u32 tx_config;
tx_config = readl(ioaddr + SXGBE_DMA_CHA_TXCTL_REG(cha_num));
tx_config |= SXGBE_TX_START_DMA;
writel(tx_config, ioaddr + SXGBE_DMA_CHA_TXCTL_REG(cha_num));
}
static void sxgbe_enable_dma_irq(void __iomem *ioaddr, int dma_cnum)
{
/* Enable TX/RX interrupts */
writel(SXGBE_DMA_ENA_INT,
ioaddr + SXGBE_DMA_CHA_INT_ENABLE_REG(dma_cnum));
}
static void sxgbe_disable_dma_irq(void __iomem *ioaddr, int dma_cnum)
{
/* Disable TX/RX interrupts */
writel(0, ioaddr + SXGBE_DMA_CHA_INT_ENABLE_REG(dma_cnum));
}
static void sxgbe_dma_start_tx(void __iomem *ioaddr, int tchannels)
{
int cnum;
u32 tx_ctl_reg;
for (cnum = 0; cnum < tchannels; cnum++) {
tx_ctl_reg = readl(ioaddr + SXGBE_DMA_CHA_TXCTL_REG(cnum));
tx_ctl_reg |= SXGBE_TX_ENABLE;
writel(tx_ctl_reg,
ioaddr + SXGBE_DMA_CHA_TXCTL_REG(cnum));
}
}
static void sxgbe_dma_start_tx_queue(void __iomem *ioaddr, int dma_cnum)
{
u32 tx_ctl_reg;
tx_ctl_reg = readl(ioaddr + SXGBE_DMA_CHA_TXCTL_REG(dma_cnum));
tx_ctl_reg |= SXGBE_TX_ENABLE;
writel(tx_ctl_reg, ioaddr + SXGBE_DMA_CHA_TXCTL_REG(dma_cnum));
}
static void sxgbe_dma_stop_tx_queue(void __iomem *ioaddr, int dma_cnum)
{
u32 tx_ctl_reg;
tx_ctl_reg = readl(ioaddr + SXGBE_DMA_CHA_TXCTL_REG(dma_cnum));
tx_ctl_reg &= ~(SXGBE_TX_ENABLE);
writel(tx_ctl_reg, ioaddr + SXGBE_DMA_CHA_TXCTL_REG(dma_cnum));
}
static void sxgbe_dma_stop_tx(void __iomem *ioaddr, int tchannels)
{
int cnum;
u32 tx_ctl_reg;
for (cnum = 0; cnum < tchannels; cnum++) {
tx_ctl_reg = readl(ioaddr + SXGBE_DMA_CHA_TXCTL_REG(cnum));
tx_ctl_reg &= ~(SXGBE_TX_ENABLE);
writel(tx_ctl_reg, ioaddr + SXGBE_DMA_CHA_TXCTL_REG(cnum));
}
}
static void sxgbe_dma_start_rx(void __iomem *ioaddr, int rchannels)
{
int cnum;
u32 rx_ctl_reg;
for (cnum = 0; cnum < rchannels; cnum++) {
rx_ctl_reg = readl(ioaddr + SXGBE_DMA_CHA_RXCTL_REG(cnum));
rx_ctl_reg |= SXGBE_RX_ENABLE;
writel(rx_ctl_reg,
ioaddr + SXGBE_DMA_CHA_RXCTL_REG(cnum));
}
}
static void sxgbe_dma_stop_rx(void __iomem *ioaddr, int rchannels)
{
int cnum;
u32 rx_ctl_reg;
for (cnum = 0; cnum < rchannels; cnum++) {
rx_ctl_reg = readl(ioaddr + SXGBE_DMA_CHA_RXCTL_REG(cnum));
rx_ctl_reg &= ~(SXGBE_RX_ENABLE);
writel(rx_ctl_reg, ioaddr + SXGBE_DMA_CHA_RXCTL_REG(cnum));
}
}
static int sxgbe_tx_dma_int_status(void __iomem *ioaddr, int channel_no,
struct sxgbe_extra_stats *x)
{
u32 int_status = readl(ioaddr + SXGBE_DMA_CHA_STATUS_REG(channel_no));
u32 clear_val = 0;
u32 ret_val = 0;
/* TX Normal Interrupt Summary */
if (likely(int_status & SXGBE_DMA_INT_STATUS_NIS)) {
x->normal_irq_n++;
if (int_status & SXGBE_DMA_INT_STATUS_TI) {
ret_val |= handle_tx;
x->tx_normal_irq_n++;
clear_val |= SXGBE_DMA_INT_STATUS_TI;
}
if (int_status & SXGBE_DMA_INT_STATUS_TBU) {
x->tx_underflow_irq++;
ret_val |= tx_bump_tc;
clear_val |= SXGBE_DMA_INT_STATUS_TBU;
}
} else if (unlikely(int_status & SXGBE_DMA_INT_STATUS_AIS)) {
/* TX Abnormal Interrupt Summary */
if (int_status & SXGBE_DMA_INT_STATUS_TPS) {
ret_val |= tx_hard_error;
clear_val |= SXGBE_DMA_INT_STATUS_TPS;
x->tx_process_stopped_irq++;
}
if (int_status & SXGBE_DMA_INT_STATUS_FBE) {
ret_val |= tx_hard_error;
x->fatal_bus_error_irq++;
/* Assumption: FBE bit is the combination of
* all the bus access erros and cleared when
* the respective error bits cleared
*/
/* check for actual cause */
if (int_status & SXGBE_DMA_INT_STATUS_TEB0) {
x->tx_read_transfer_err++;
clear_val |= SXGBE_DMA_INT_STATUS_TEB0;
} else {
x->tx_write_transfer_err++;
}
if (int_status & SXGBE_DMA_INT_STATUS_TEB1) {
x->tx_desc_access_err++;
clear_val |= SXGBE_DMA_INT_STATUS_TEB1;
} else {
x->tx_buffer_access_err++;
}
if (int_status & SXGBE_DMA_INT_STATUS_TEB2) {
x->tx_data_transfer_err++;
clear_val |= SXGBE_DMA_INT_STATUS_TEB2;
}
}
/* context descriptor error */
if (int_status & SXGBE_DMA_INT_STATUS_CTXTERR) {
x->tx_ctxt_desc_err++;
clear_val |= SXGBE_DMA_INT_STATUS_CTXTERR;
}
}
/* clear the served bits */
writel(clear_val, ioaddr + SXGBE_DMA_CHA_STATUS_REG(channel_no));
return ret_val;
}
static int sxgbe_rx_dma_int_status(void __iomem *ioaddr, int channel_no,
struct sxgbe_extra_stats *x)
{
u32 int_status = readl(ioaddr + SXGBE_DMA_CHA_STATUS_REG(channel_no));
u32 clear_val = 0;
u32 ret_val = 0;
/* RX Normal Interrupt Summary */
if (likely(int_status & SXGBE_DMA_INT_STATUS_NIS)) {
x->normal_irq_n++;
if (int_status & SXGBE_DMA_INT_STATUS_RI) {
ret_val |= handle_rx;
x->rx_normal_irq_n++;
clear_val |= SXGBE_DMA_INT_STATUS_RI;
}
} else if (unlikely(int_status & SXGBE_DMA_INT_STATUS_AIS)) {
/* RX Abnormal Interrupt Summary */
if (int_status & SXGBE_DMA_INT_STATUS_RBU) {
ret_val |= rx_bump_tc;
clear_val |= SXGBE_DMA_INT_STATUS_RBU;
x->rx_underflow_irq++;
}
if (int_status & SXGBE_DMA_INT_STATUS_RPS) {
ret_val |= rx_hard_error;
clear_val |= SXGBE_DMA_INT_STATUS_RPS;
x->rx_process_stopped_irq++;
}
if (int_status & SXGBE_DMA_INT_STATUS_FBE) {
ret_val |= rx_hard_error;
x->fatal_bus_error_irq++;
/* Assumption: FBE bit is the combination of
* all the bus access erros and cleared when
* the respective error bits cleared
*/
/* check for actual cause */
if (int_status & SXGBE_DMA_INT_STATUS_REB0) {
x->rx_read_transfer_err++;
clear_val |= SXGBE_DMA_INT_STATUS_REB0;
} else {
x->rx_write_transfer_err++;
}
if (int_status & SXGBE_DMA_INT_STATUS_REB1) {
x->rx_desc_access_err++;
clear_val |= SXGBE_DMA_INT_STATUS_REB1;
} else {
x->rx_buffer_access_err++;
}
if (int_status & SXGBE_DMA_INT_STATUS_REB2) {
x->rx_data_transfer_err++;
clear_val |= SXGBE_DMA_INT_STATUS_REB2;
}
}
}
/* clear the served bits */
writel(clear_val, ioaddr + SXGBE_DMA_CHA_STATUS_REG(channel_no));
return ret_val;
}
/* Program the HW RX Watchdog */
static void sxgbe_dma_rx_watchdog(void __iomem *ioaddr, u32 riwt)
{
u32 que_num;
SXGBE_FOR_EACH_QUEUE(SXGBE_RX_QUEUES, que_num) {
writel(riwt,
ioaddr + SXGBE_DMA_CHA_INT_RXWATCHTMR_REG(que_num));
}
}
static const struct sxgbe_dma_ops sxgbe_dma_ops = {
.init = sxgbe_dma_init,
.cha_init = sxgbe_dma_channel_init,
.enable_dma_transmission = sxgbe_enable_dma_transmission,
.enable_dma_irq = sxgbe_enable_dma_irq,
.disable_dma_irq = sxgbe_disable_dma_irq,
.start_tx = sxgbe_dma_start_tx,
.start_tx_queue = sxgbe_dma_start_tx_queue,
.stop_tx = sxgbe_dma_stop_tx,
.stop_tx_queue = sxgbe_dma_stop_tx_queue,
.start_rx = sxgbe_dma_start_rx,
.stop_rx = sxgbe_dma_stop_rx,
.tx_dma_int_status = sxgbe_tx_dma_int_status,
.rx_dma_int_status = sxgbe_rx_dma_int_status,
.rx_watchdog = sxgbe_dma_rx_watchdog,
};
const struct sxgbe_dma_ops *sxgbe_get_dma_ops(void)
{
return &sxgbe_dma_ops;
}
/* 10G controller driver for Samsung SoCs
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Siva Reddy Kallam <siva.kallam@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __SXGBE_DMA_H__
#define __SXGBE_DMA_H__
/* forward declaration */
struct sxgbe_extra_stats;
#define SXGBE_DMA_BLENMAP_LSHIFT 1
#define SXGBE_DMA_TXPBL_LSHIFT 16
#define SXGBE_DMA_RXPBL_LSHIFT 16
#define DEFAULT_DMA_PBL 8
struct sxgbe_dma_ops {
/* DMA core initialization */
int (*init)(void __iomem *ioaddr, int fix_burst, int burst_map);
void (*cha_init)(void __iomem *ioaddr, int cha_num, int fix_burst,
int pbl, dma_addr_t dma_tx, dma_addr_t dma_rx,
int t_rzie, int r_rsize);
void (*enable_dma_transmission)(void __iomem *ioaddr, int dma_cnum);
void (*enable_dma_irq)(void __iomem *ioaddr, int dma_cnum);
void (*disable_dma_irq)(void __iomem *ioaddr, int dma_cnum);
void (*start_tx)(void __iomem *ioaddr, int tchannels);
void (*start_tx_queue)(void __iomem *ioaddr, int dma_cnum);
void (*stop_tx)(void __iomem *ioaddr, int tchannels);
void (*stop_tx_queue)(void __iomem *ioaddr, int dma_cnum);
void (*start_rx)(void __iomem *ioaddr, int rchannels);
void (*stop_rx)(void __iomem *ioaddr, int rchannels);
int (*tx_dma_int_status)(void __iomem *ioaddr, int channel_no,
struct sxgbe_extra_stats *x);
int (*rx_dma_int_status)(void __iomem *ioaddr, int channel_no,
struct sxgbe_extra_stats *x);
/* Program the HW RX Watchdog */
void (*rx_watchdog)(void __iomem *ioaddr, u32 riwt);
};
const struct sxgbe_dma_ops *sxgbe_get_dma_ops(void);
#endif /* __SXGBE_CORE_H__ */
/* 10G controller driver for Samsung SoCs
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Siva Reddy Kallam <siva.kallam@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/phy.h>
#include "sxgbe_common.h"
struct sxgbe_stats {
char stat_string[ETH_GSTRING_LEN];
int sizeof_stat;
int stat_offset;
};
#define SXGBE_STAT(m) \
{ \
#m, \
FIELD_SIZEOF(struct sxgbe_extra_stats, m), \
offsetof(struct sxgbe_priv_data, xstats.m) \
}
static const struct sxgbe_stats sxgbe_gstrings_stats[] = {
};
#define SXGBE_STATS_LEN ARRAY_SIZE(sxgbe_gstrings_stats)
static const struct ethtool_ops sxgbe_ethtool_ops = {
};
void sxgbe_set_ethtool_ops(struct net_device *netdev)
{
SET_ETHTOOL_OPS(netdev, &sxgbe_ethtool_ops);
}
此差异已折叠。
/* 10G controller driver for Samsung SoCs
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Siva Reddy Kallam <siva.kallam@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/io.h>
#include <linux/mii.h>
#include <linux/netdevice.h>
#include <linux/platform_device.h>
#include <linux/phy.h>
#include <linux/slab.h>
#include <linux/sxgbe_platform.h>
#include "sxgbe_common.h"
#include "sxgbe_reg.h"
#define SXGBE_SMA_WRITE_CMD 0x01 /* write command */
#define SXGBE_SMA_PREAD_CMD 0x02 /* post read increament address */
#define SXGBE_SMA_READ_CMD 0x03 /* read command */
#define SXGBE_SMA_SKIP_ADDRFRM 0x00040000 /* skip the address frame */
#define SXGBE_MII_BUSY 0x00800000 /* mii busy */
static int sxgbe_mdio_busy_wait(void __iomem *ioaddr, unsigned int mii_data)
{
unsigned long fin_time = jiffies + 3 * HZ; /* 3 seconds */
while (!time_after(jiffies, fin_time)) {
if (!(readl(ioaddr + mii_data) & SXGBE_MII_BUSY))
return 0;
cpu_relax();
}
return -EBUSY;
}
static void sxgbe_mdio_ctrl_data(struct sxgbe_priv_data *sp, u32 cmd,
u16 phydata)
{
u32 reg = phydata;
reg |= (cmd << 16) | SXGBE_SMA_SKIP_ADDRFRM |
((sp->clk_csr & 0x7) << 19) | SXGBE_MII_BUSY;
writel(reg, sp->ioaddr + sp->hw->mii.data);
}
static void sxgbe_mdio_c45(struct sxgbe_priv_data *sp, u32 cmd, int phyaddr,
int phyreg, u16 phydata)
{
u32 reg;
/* set mdio address register */
reg = ((phyreg >> 16) & 0x1f) << 21;
reg |= (phyaddr << 16) | (phyreg & 0xffff);
writel(reg, sp->ioaddr + sp->hw->mii.addr);
sxgbe_mdio_ctrl_data(sp, cmd, phydata);
}
static void sxgbe_mdio_c22(struct sxgbe_priv_data *sp, u32 cmd, int phyaddr,
int phyreg, u16 phydata)
{
u32 reg;
writel(1 << phyaddr, sp->ioaddr + SXGBE_MDIO_CLAUSE22_PORT_REG);
/* set mdio address register */
reg = (phyaddr << 16) | (phyreg & 0x1f);
writel(reg, sp->ioaddr + sp->hw->mii.addr);
sxgbe_mdio_ctrl_data(sp, cmd, phydata);
}
static int sxgbe_mdio_access(struct sxgbe_priv_data *sp, u32 cmd, int phyaddr,
int phyreg, u16 phydata)
{
const struct mii_regs *mii = &sp->hw->mii;
int rc;
rc = sxgbe_mdio_busy_wait(sp->ioaddr, mii->data);
if (rc < 0)
return rc;
if (phyreg & MII_ADDR_C45) {
sxgbe_mdio_c45(sp, cmd, phyaddr, phyreg, phydata);
} else {
/* Ports 0-3 only support C22. */
if (phyaddr >= 4)
return -ENODEV;
sxgbe_mdio_c22(sp, cmd, phyaddr, phyreg, phydata);
}
return sxgbe_mdio_busy_wait(sp->ioaddr, mii->data);
}
/**
* sxgbe_mdio_read
* @bus: points to the mii_bus structure
* @phyaddr: address of phy port
* @phyreg: address of register with in phy register
* Description: this function used for C45 and C22 MDIO Read
*/
static int sxgbe_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg)
{
struct net_device *ndev = bus->priv;
struct sxgbe_priv_data *priv = netdev_priv(ndev);
int rc;
rc = sxgbe_mdio_access(priv, SXGBE_SMA_READ_CMD, phyaddr, phyreg, 0);
if (rc < 0)
return rc;
return readl(priv->ioaddr + priv->hw->mii.data) & 0xffff;
}
/**
* sxgbe_mdio_write
* @bus: points to the mii_bus structure
* @phyaddr: address of phy port
* @phyreg: address of phy registers
* @phydata: data to be written into phy register
* Description: this function is used for C45 and C22 MDIO write
*/
static int sxgbe_mdio_write(struct mii_bus *bus, int phyaddr, int phyreg,
u16 phydata)
{
struct net_device *ndev = bus->priv;
struct sxgbe_priv_data *priv = netdev_priv(ndev);
return sxgbe_mdio_access(priv, SXGBE_SMA_WRITE_CMD, phyaddr, phyreg,
phydata);
}
int sxgbe_mdio_register(struct net_device *ndev)
{
struct mii_bus *mdio_bus;
struct sxgbe_priv_data *priv = netdev_priv(ndev);
struct sxgbe_mdio_bus_data *mdio_data = priv->plat->mdio_bus_data;
int err, phy_addr;
int *irqlist;
bool act;
/* allocate the new mdio bus */
mdio_bus = mdiobus_alloc();
if (!mdio_bus) {
netdev_err(ndev, "%s: mii bus allocation failed\n", __func__);
return -ENOMEM;
}
if (mdio_data->irqs)
irqlist = mdio_data->irqs;
else
irqlist = priv->mii_irq;
/* assign mii bus fields */
mdio_bus->name = "samsxgbe";
mdio_bus->read = &sxgbe_mdio_read;
mdio_bus->write = &sxgbe_mdio_write;
snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s-%x",
mdio_bus->name, priv->plat->bus_id);
mdio_bus->priv = ndev;
mdio_bus->phy_mask = mdio_data->phy_mask;
mdio_bus->parent = priv->device;
/* register with kernel subsystem */
err = mdiobus_register(mdio_bus);
if (err != 0) {
netdev_err(ndev, "mdiobus register failed\n");
goto mdiobus_err;
}
for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
struct phy_device *phy = mdio_bus->phy_map[phy_addr];
if (phy) {
char irq_num[4];
char *irq_str;
/* If an IRQ was provided to be assigned after
* the bus probe, do it here.
*/
if ((mdio_data->irqs == NULL) &&
(mdio_data->probed_phy_irq > 0)) {
irqlist[phy_addr] = mdio_data->probed_phy_irq;
phy->irq = mdio_data->probed_phy_irq;
}
/* If we're going to bind the MAC to this PHY bus,
* and no PHY number was provided to the MAC,
* use the one probed here.
*/
if (priv->plat->phy_addr == -1)
priv->plat->phy_addr = phy_addr;
act = (priv->plat->phy_addr == phy_addr);
switch (phy->irq) {
case PHY_POLL:
irq_str = "POLL";
break;
case PHY_IGNORE_INTERRUPT:
irq_str = "IGNORE";
break;
default:
sprintf(irq_num, "%d", phy->irq);
irq_str = irq_num;
break;
}
netdev_info(ndev, "PHY ID %08x at %d IRQ %s (%s)%s\n",
phy->phy_id, phy_addr, irq_str,
dev_name(&phy->dev), act ? " active" : "");
}
}
if (!err) {
netdev_err(ndev, "PHY not found\n");
mdiobus_unregister(mdio_bus);
mdiobus_free(mdio_bus);
goto mdiobus_err;
}
priv->mii = mdio_bus;
return 0;
mdiobus_err:
mdiobus_free(mdio_bus);
return err;
}
int sxgbe_mdio_unregister(struct net_device *ndev)
{
struct sxgbe_priv_data *priv = netdev_priv(ndev);
if (!priv->mii)
return 0;
mdiobus_unregister(priv->mii);
priv->mii->priv = NULL;
mdiobus_free(priv->mii);
priv->mii = NULL;
return 0;
}
/* 10G controller driver for Samsung SoCs
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Siva Reddy Kallam <siva.kallam@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/io.h>
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/jiffies.h>
#include "sxgbe_mtl.h"
#include "sxgbe_reg.h"
static void sxgbe_mtl_init(void __iomem *ioaddr, unsigned int etsalg,
unsigned int raa)
{
u32 reg_val;
reg_val = readl(ioaddr + SXGBE_MTL_OP_MODE_REG);
reg_val &= ETS_RST;
/* ETS Algorith */
switch (etsalg & SXGBE_MTL_OPMODE_ESTMASK) {
case ETS_WRR:
reg_val &= ETS_WRR;
break;
case ETS_WFQ:
reg_val |= ETS_WFQ;
break;
case ETS_DWRR:
reg_val |= ETS_DWRR;
break;
}
writel(reg_val, ioaddr + SXGBE_MTL_OP_MODE_REG);
switch (raa & SXGBE_MTL_OPMODE_RAAMASK) {
case RAA_SP:
reg_val &= RAA_SP;
break;
case RAA_WSP:
reg_val |= RAA_WSP;
break;
}
writel(reg_val, ioaddr + SXGBE_MTL_OP_MODE_REG);
}
/* For Dynamic DMA channel mapping for Rx queue */
static void sxgbe_mtl_dma_dm_rxqueue(void __iomem *ioaddr)
{
writel(RX_QUEUE_DYNAMIC, ioaddr + SXGBE_MTL_RXQ_DMAMAP0_REG);
writel(RX_QUEUE_DYNAMIC, ioaddr + SXGBE_MTL_RXQ_DMAMAP1_REG);
writel(RX_QUEUE_DYNAMIC, ioaddr + SXGBE_MTL_RXQ_DMAMAP2_REG);
}
static void sxgbe_mtl_set_txfifosize(void __iomem *ioaddr, int queue_num,
int queue_fifo)
{
u32 fifo_bits, reg_val;
/* 0 means 256 bytes */
fifo_bits = (queue_fifo / SXGBE_MTL_TX_FIFO_DIV) - 1;
reg_val = readl(ioaddr + SXGBE_MTL_TXQ_OPMODE_REG(queue_num));
reg_val |= (fifo_bits << SXGBE_MTL_FIFO_LSHIFT);
writel(reg_val, ioaddr + SXGBE_MTL_TXQ_OPMODE_REG(queue_num));
}
static void sxgbe_mtl_set_rxfifosize(void __iomem *ioaddr, int queue_num,
int queue_fifo)
{
u32 fifo_bits, reg_val;
/* 0 means 256 bytes */
fifo_bits = (queue_fifo / SXGBE_MTL_RX_FIFO_DIV)-1;
reg_val = readl(ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
reg_val |= (fifo_bits << SXGBE_MTL_FIFO_LSHIFT);
writel(reg_val, ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
}
static void sxgbe_mtl_enable_txqueue(void __iomem *ioaddr, int queue_num)
{
u32 reg_val;
reg_val = readl(ioaddr + SXGBE_MTL_TXQ_OPMODE_REG(queue_num));
reg_val |= SXGBE_MTL_ENABLE_QUEUE;
writel(reg_val, ioaddr + SXGBE_MTL_TXQ_OPMODE_REG(queue_num));
}
static void sxgbe_mtl_disable_txqueue(void __iomem *ioaddr, int queue_num)
{
u32 reg_val;
reg_val = readl(ioaddr + SXGBE_MTL_TXQ_OPMODE_REG(queue_num));
reg_val &= ~SXGBE_MTL_ENABLE_QUEUE;
writel(reg_val, ioaddr + SXGBE_MTL_TXQ_OPMODE_REG(queue_num));
}
static void sxgbe_mtl_fc_active(void __iomem *ioaddr, int queue_num,
int threshold)
{
u32 reg_val;
reg_val = readl(ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
reg_val &= ~(SXGBE_MTL_FCMASK << RX_FC_ACTIVE);
reg_val |= (threshold << RX_FC_ACTIVE);
writel(reg_val, ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
}
static void sxgbe_mtl_fc_enable(void __iomem *ioaddr, int queue_num)
{
u32 reg_val;
reg_val = readl(ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
reg_val |= SXGBE_MTL_ENABLE_FC;
writel(reg_val, ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
}
static void sxgbe_mtl_fc_deactive(void __iomem *ioaddr, int queue_num,
int threshold)
{
u32 reg_val;
reg_val = readl(ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
reg_val &= ~(SXGBE_MTL_FCMASK << RX_FC_DEACTIVE);
reg_val |= (threshold << RX_FC_DEACTIVE);
writel(reg_val, ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
}
static void sxgbe_mtl_fep_enable(void __iomem *ioaddr, int queue_num)
{
u32 reg_val;
reg_val = readl(ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
reg_val |= SXGBE_MTL_RXQ_OP_FEP;
writel(reg_val, ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
}
static void sxgbe_mtl_fep_disable(void __iomem *ioaddr, int queue_num)
{
u32 reg_val;
reg_val = readl(ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
reg_val &= ~(SXGBE_MTL_RXQ_OP_FEP);
writel(reg_val, ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
}
static void sxgbe_mtl_fup_enable(void __iomem *ioaddr, int queue_num)
{
u32 reg_val;
reg_val = readl(ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
reg_val |= SXGBE_MTL_RXQ_OP_FUP;
writel(reg_val, ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
}
static void sxgbe_mtl_fup_disable(void __iomem *ioaddr, int queue_num)
{
u32 reg_val;
reg_val = readl(ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
reg_val &= ~(SXGBE_MTL_RXQ_OP_FUP);
writel(reg_val, ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
}
static void sxgbe_set_tx_mtl_mode(void __iomem *ioaddr, int queue_num,
int tx_mode)
{
u32 reg_val;
reg_val = readl(ioaddr + SXGBE_MTL_TXQ_OPMODE_REG(queue_num));
/* TX specific MTL mode settings */
if (tx_mode == SXGBE_MTL_SFMODE) {
reg_val |= SXGBE_MTL_SFMODE;
} else {
/* set the TTC values */
if (tx_mode <= 64)
reg_val |= MTL_CONTROL_TTC_64;
else if (tx_mode <= 96)
reg_val |= MTL_CONTROL_TTC_96;
else if (tx_mode <= 128)
reg_val |= MTL_CONTROL_TTC_128;
else if (tx_mode <= 192)
reg_val |= MTL_CONTROL_TTC_192;
else if (tx_mode <= 256)
reg_val |= MTL_CONTROL_TTC_256;
else if (tx_mode <= 384)
reg_val |= MTL_CONTROL_TTC_384;
else
reg_val |= MTL_CONTROL_TTC_512;
}
/* write into TXQ operation register */
writel(reg_val, ioaddr + SXGBE_MTL_TXQ_OPMODE_REG(queue_num));
}
static void sxgbe_set_rx_mtl_mode(void __iomem *ioaddr, int queue_num,
int rx_mode)
{
u32 reg_val;
reg_val = readl(ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
/* RX specific MTL mode settings */
if (rx_mode == SXGBE_RX_MTL_SFMODE) {
reg_val |= SXGBE_RX_MTL_SFMODE;
} else {
if (rx_mode <= 64)
reg_val |= MTL_CONTROL_RTC_64;
else if (rx_mode <= 96)
reg_val |= MTL_CONTROL_RTC_96;
else if (rx_mode <= 128)
reg_val |= MTL_CONTROL_RTC_128;
}
/* write into RXQ operation register */
writel(reg_val, ioaddr + SXGBE_MTL_RXQ_OPMODE_REG(queue_num));
}
static const struct sxgbe_mtl_ops mtl_ops = {
.mtl_set_txfifosize = sxgbe_mtl_set_txfifosize,
.mtl_set_rxfifosize = sxgbe_mtl_set_rxfifosize,
.mtl_enable_txqueue = sxgbe_mtl_enable_txqueue,
.mtl_disable_txqueue = sxgbe_mtl_disable_txqueue,
.mtl_dynamic_dma_rxqueue = sxgbe_mtl_dma_dm_rxqueue,
.set_tx_mtl_mode = sxgbe_set_tx_mtl_mode,
.set_rx_mtl_mode = sxgbe_set_rx_mtl_mode,
.mtl_init = sxgbe_mtl_init,
.mtl_fc_active = sxgbe_mtl_fc_active,
.mtl_fc_deactive = sxgbe_mtl_fc_deactive,
.mtl_fc_enable = sxgbe_mtl_fc_enable,
.mtl_fep_enable = sxgbe_mtl_fep_enable,
.mtl_fep_disable = sxgbe_mtl_fep_disable,
.mtl_fup_enable = sxgbe_mtl_fup_enable,
.mtl_fup_disable = sxgbe_mtl_fup_disable
};
const struct sxgbe_mtl_ops *sxgbe_get_mtl_ops(void)
{
return &mtl_ops;
}
/* 10G controller driver for Samsung SoCs
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Siva Reddy Kallam <siva.kallam@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __SXGBE_MTL_H__
#define __SXGBE_MTL_H__
#define SXGBE_MTL_OPMODE_ESTMASK 0x3
#define SXGBE_MTL_OPMODE_RAAMASK 0x1
#define SXGBE_MTL_FCMASK 0x7
#define SXGBE_MTL_TX_FIFO_DIV 256
#define SXGBE_MTL_RX_FIFO_DIV 256
#define SXGBE_MTL_RXQ_OP_FEP BIT(4)
#define SXGBE_MTL_RXQ_OP_FUP BIT(3)
#define SXGBE_MTL_ENABLE_FC 0x80
#define ETS_WRR 0xFFFFFF9F
#define ETS_RST 0xFFFFFF9F
#define ETS_WFQ 0x00000020
#define ETS_DWRR 0x00000040
#define RAA_SP 0xFFFFFFFB
#define RAA_WSP 0x00000004
#define RX_QUEUE_DYNAMIC 0x80808080
#define RX_FC_ACTIVE 8
#define RX_FC_DEACTIVE 13
enum ttc_control {
MTL_CONTROL_TTC_64 = 0x00000000,
MTL_CONTROL_TTC_96 = 0x00000020,
MTL_CONTROL_TTC_128 = 0x00000030,
MTL_CONTROL_TTC_192 = 0x00000040,
MTL_CONTROL_TTC_256 = 0x00000050,
MTL_CONTROL_TTC_384 = 0x00000060,
MTL_CONTROL_TTC_512 = 0x00000070,
};
enum rtc_control {
MTL_CONTROL_RTC_64 = 0x00000000,
MTL_CONTROL_RTC_96 = 0x00000002,
MTL_CONTROL_RTC_128 = 0x00000003,
};
enum flow_control_th {
MTL_FC_FULL_1K = 0x00000000,
MTL_FC_FULL_2K = 0x00000001,
MTL_FC_FULL_4K = 0x00000002,
MTL_FC_FULL_5K = 0x00000003,
MTL_FC_FULL_6K = 0x00000004,
MTL_FC_FULL_8K = 0x00000005,
MTL_FC_FULL_16K = 0x00000006,
MTL_FC_FULL_24K = 0x00000007,
};
struct sxgbe_mtl_ops {
void (*mtl_init)(void __iomem *ioaddr, unsigned int etsalg,
unsigned int raa);
void (*mtl_set_txfifosize)(void __iomem *ioaddr, int queue_num,
int mtl_fifo);
void (*mtl_set_rxfifosize)(void __iomem *ioaddr, int queue_num,
int queue_fifo);
void (*mtl_enable_txqueue)(void __iomem *ioaddr, int queue_num);
void (*mtl_disable_txqueue)(void __iomem *ioaddr, int queue_num);
void (*set_tx_mtl_mode)(void __iomem *ioaddr, int queue_num,
int tx_mode);
void (*set_rx_mtl_mode)(void __iomem *ioaddr, int queue_num,
int rx_mode);
void (*mtl_dynamic_dma_rxqueue)(void __iomem *ioaddr);
void (*mtl_fc_active)(void __iomem *ioaddr, int queue_num,
int threshold);
void (*mtl_fc_deactive)(void __iomem *ioaddr, int queue_num,
int threshold);
void (*mtl_fc_enable)(void __iomem *ioaddr, int queue_num);
void (*mtl_fep_enable)(void __iomem *ioaddr, int queue_num);
void (*mtl_fep_disable)(void __iomem *ioaddr, int queue_num);
void (*mtl_fup_enable)(void __iomem *ioaddr, int queue_num);
void (*mtl_fup_disable)(void __iomem *ioaddr, int queue_num);
};
const struct sxgbe_mtl_ops *sxgbe_get_mtl_ops(void);
#endif /* __SXGBE_MTL_H__ */
/* 10G controller driver for Samsung SoCs
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Siva Reddy Kallam <siva.kallam@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/etherdevice.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_net.h>
#include <linux/phy.h>
#include <linux/platform_device.h>
#include <linux/sxgbe_platform.h>
#include "sxgbe_common.h"
#include "sxgbe_reg.h"
#ifdef CONFIG_OF
static int sxgbe_probe_config_dt(struct platform_device *pdev,
struct sxgbe_plat_data *plat,
const char **mac)
{
struct device_node *np = pdev->dev.of_node;
struct sxgbe_dma_cfg *dma_cfg;
if (!np)
return -ENODEV;
*mac = of_get_mac_address(np);
plat->interface = of_get_phy_mode(np);
plat->bus_id = of_alias_get_id(np, "ethernet");
if (plat->bus_id < 0)
plat->bus_id = 0;
plat->mdio_bus_data = devm_kzalloc(&pdev->dev,
sizeof(*plat->mdio_bus_data),
GFP_KERNEL);
dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg), GFP_KERNEL);
if (!dma_cfg)
return -ENOMEM;
plat->dma_cfg = dma_cfg;
of_property_read_u32(np, "samsung,pbl", &dma_cfg->pbl);
if (of_property_read_u32(np, "samsung,burst-map", &dma_cfg->burst_map) == 0)
dma_cfg->fixed_burst = true;
return 0;
}
#else
static int sxgbe_probe_config_dt(struct platform_device *pdev,
struct sxgbe_plat_data *plat,
const char **mac)
{
return -ENOSYS;
}
#endif /* CONFIG_OF */
/**
* sxgbe_platform_probe
* @pdev: platform device pointer
* Description: platform_device probe function. It allocates
* the necessary resources and invokes the main to init
* the net device, register the mdio bus etc.
*/
static int sxgbe_platform_probe(struct platform_device *pdev)
{
int ret;
int i, chan;
struct resource *res;
struct device *dev = &pdev->dev;
void __iomem *addr;
struct sxgbe_priv_data *priv = NULL;
struct sxgbe_plat_data *plat_dat = NULL;
const char *mac = NULL;
struct net_device *ndev = platform_get_drvdata(pdev);
struct device_node *node = dev->of_node;
/* Get memory resource */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
goto err_out;
addr = devm_ioremap_resource(dev, res);
if (IS_ERR(addr))
return PTR_ERR(addr);
if (pdev->dev.of_node) {
plat_dat = devm_kzalloc(&pdev->dev,
sizeof(struct sxgbe_plat_data),
GFP_KERNEL);
if (!plat_dat)
return -ENOMEM;
ret = sxgbe_probe_config_dt(pdev, plat_dat, &mac);
if (ret) {
pr_err("%s: main dt probe failed\n", __func__);
return ret;
}
}
/* Get MAC address if available (DT) */
if (mac)
ether_addr_copy(priv->dev->dev_addr, mac);
priv = sxgbe_drv_probe(&(pdev->dev), plat_dat, addr);
if (!priv) {
pr_err("%s: main driver probe failed\n", __func__);
goto err_out;
}
/* Get the SXGBE common INT information */
priv->irq = irq_of_parse_and_map(node, 0);
if (priv->irq <= 0) {
dev_err(dev, "sxgbe common irq parsing failed\n");
goto err_drv_remove;
}
/* Get the TX/RX IRQ numbers */
for (i = 0, chan = 1; i < SXGBE_TX_QUEUES; i++) {
priv->txq[i]->irq_no = irq_of_parse_and_map(node, chan++);
if (priv->txq[i]->irq_no <= 0) {
dev_err(dev, "sxgbe tx irq parsing failed\n");
goto err_tx_irq_unmap;
}
}
for (i = 0; i < SXGBE_RX_QUEUES; i++) {
priv->rxq[i]->irq_no = irq_of_parse_and_map(node, chan++);
if (priv->rxq[i]->irq_no <= 0) {
dev_err(dev, "sxgbe rx irq parsing failed\n");
goto err_rx_irq_unmap;
}
}
platform_set_drvdata(pdev, priv->dev);
pr_debug("platform driver registration completed\n");
return 0;
err_rx_irq_unmap:
while (--i)
irq_dispose_mapping(priv->rxq[i]->irq_no);
i = SXGBE_TX_QUEUES;
err_tx_irq_unmap:
while (--i)
irq_dispose_mapping(priv->txq[i]->irq_no);
irq_dispose_mapping(priv->irq);
err_drv_remove:
sxgbe_drv_remove(ndev);
err_out:
return -ENODEV;
}
/**
* sxgbe_platform_remove
* @pdev: platform device pointer
* Description: this function calls the main to free the net resources
* and calls the platforms hook and release the resources (e.g. mem).
*/
static int sxgbe_platform_remove(struct platform_device *pdev)
{
struct net_device *ndev = platform_get_drvdata(pdev);
int ret = sxgbe_drv_remove(ndev);
return ret;
}
#ifdef CONFIG_PM
static int sxgbe_platform_suspend(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
return sxgbe_suspend(ndev);
}
static int sxgbe_platform_resume(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
return sxgbe_resume(ndev);
}
int sxgbe_platform_freeze(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
return sxgbe_freeze(ndev);
}
int sxgbe_platform_restore(struct device *dev)
{
struct net_device *ndev = dev_get_drvdata(dev);
return sxgbe_restore(ndev);
}
static const struct dev_pm_ops sxgbe_platform_pm_ops = {
.suspend = sxgbe_platform_suspend,
.resume = sxgbe_platform_resume,
.freeze = sxgbe_platform_freeze,
.thaw = sxgbe_platform_restore,
.restore = sxgbe_platform_restore,
};
#else
static const struct dev_pm_ops sxgbe_platform_pm_ops;
#endif /* CONFIG_PM */
static const struct of_device_id sxgbe_dt_ids[] = {
{ .compatible = "samsung,sxgbe-v2.0a"},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sxgbe_dt_ids);
struct platform_driver sxgbe_platform_driver = {
.probe = sxgbe_platform_probe,
.remove = sxgbe_platform_remove,
.driver = {
.name = SXGBE_RESOURCE_NAME,
.owner = THIS_MODULE,
.pm = &sxgbe_platform_pm_ops,
.of_match_table = of_match_ptr(sxgbe_dt_ids),
},
};
int sxgbe_register_platform(void)
{
int err;
err = platform_driver_register(&sxgbe_platform_driver);
if (err)
pr_err("failed to register the platform driver\n");
return err;
}
void sxgbe_unregister_platform(void)
{
platform_driver_unregister(&sxgbe_platform_driver);
}
此差异已折叠。
/* 10G controller driver for Samsung SoCs
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Siva Reddy Kallam <siva.kallam@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/phy.h>
#include "sxgbe_common.h"
#include "sxgbe_xpcs.h"
static int sxgbe_xpcs_read(struct net_device *ndev, unsigned int reg)
{
u32 value;
struct sxgbe_priv_data *priv = netdev_priv(ndev);
value = readl(priv->ioaddr + XPCS_OFFSET + reg);
return value;
}
static int sxgbe_xpcs_write(struct net_device *ndev, int reg, int data)
{
struct sxgbe_priv_data *priv = netdev_priv(ndev);
writel(data, priv->ioaddr + XPCS_OFFSET + reg);
return 0;
}
int sxgbe_xpcs_init(struct net_device *ndev)
{
u32 value;
value = sxgbe_xpcs_read(ndev, SR_PCS_MMD_CONTROL1);
/* 10G XAUI mode */
sxgbe_xpcs_write(ndev, SR_PCS_CONTROL2, XPCS_TYPE_SEL_X);
sxgbe_xpcs_write(ndev, VR_PCS_MMD_XAUI_MODE_CONTROL, XPCS_XAUI_MODE);
sxgbe_xpcs_write(ndev, VR_PCS_MMD_XAUI_MODE_CONTROL, value | BIT(13));
sxgbe_xpcs_write(ndev, SR_PCS_MMD_CONTROL1, value | BIT(11));
do {
value = sxgbe_xpcs_read(ndev, VR_PCS_MMD_DIGITAL_STATUS);
} while ((value & XPCS_QSEQ_STATE_MPLLOFF) == XPCS_QSEQ_STATE_STABLE);
value = sxgbe_xpcs_read(ndev, SR_PCS_MMD_CONTROL1);
sxgbe_xpcs_write(ndev, SR_PCS_MMD_CONTROL1, value & ~BIT(11));
do {
value = sxgbe_xpcs_read(ndev, VR_PCS_MMD_DIGITAL_STATUS);
} while ((value & XPCS_QSEQ_STATE_MPLLOFF) != XPCS_QSEQ_STATE_STABLE);
return 0;
}
int sxgbe_xpcs_init_1G(struct net_device *ndev)
{
int value;
/* 10GBASE-X PCS (1G) mode */
sxgbe_xpcs_write(ndev, SR_PCS_CONTROL2, XPCS_TYPE_SEL_X);
sxgbe_xpcs_write(ndev, VR_PCS_MMD_XAUI_MODE_CONTROL, XPCS_XAUI_MODE);
value = sxgbe_xpcs_read(ndev, SR_PCS_MMD_CONTROL1);
sxgbe_xpcs_write(ndev, SR_PCS_MMD_CONTROL1, value & ~BIT(13));
value = sxgbe_xpcs_read(ndev, SR_MII_MMD_CONTROL);
sxgbe_xpcs_write(ndev, SR_MII_MMD_CONTROL, value | BIT(6));
sxgbe_xpcs_write(ndev, SR_MII_MMD_CONTROL, value & ~BIT(13));
value = sxgbe_xpcs_read(ndev, SR_PCS_MMD_CONTROL1);
sxgbe_xpcs_write(ndev, SR_PCS_MMD_CONTROL1, value | BIT(11));
do {
value = sxgbe_xpcs_read(ndev, VR_PCS_MMD_DIGITAL_STATUS);
} while ((value & XPCS_QSEQ_STATE_MPLLOFF) != XPCS_QSEQ_STATE_STABLE);
value = sxgbe_xpcs_read(ndev, SR_PCS_MMD_CONTROL1);
sxgbe_xpcs_write(ndev, SR_PCS_MMD_CONTROL1, value & ~BIT(11));
/* Auto Negotiation cluase 37 enable */
value = sxgbe_xpcs_read(ndev, SR_MII_MMD_CONTROL);
sxgbe_xpcs_write(ndev, SR_MII_MMD_CONTROL, value | BIT(12));
return 0;
}
/* 10G controller driver for Samsung SoCs
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Byungho An <bh74.an@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __SXGBE_XPCS_H__
#define __SXGBE_XPCS_H__
/* XPCS Registers */
#define XPCS_OFFSET 0x1A060000
#define SR_PCS_MMD_CONTROL1 0x030000
#define SR_PCS_CONTROL2 0x030007
#define VR_PCS_MMD_XAUI_MODE_CONTROL 0x038004
#define VR_PCS_MMD_DIGITAL_STATUS 0x038010
#define SR_MII_MMD_CONTROL 0x1F0000
#define SR_MII_MMD_AN_ADV 0x1F0004
#define SR_MII_MMD_AN_LINK_PARTNER_BA 0x1F0005
#define VR_MII_MMD_AN_CONTROL 0x1F8001
#define VR_MII_MMD_AN_INT_STATUS 0x1F8002
#define XPCS_QSEQ_STATE_STABLE 0x10
#define XPCS_QSEQ_STATE_MPLLOFF 0x1c
#define XPCS_TYPE_SEL_R 0x00
#define XPCS_TYPE_SEL_X 0x01
#define XPCS_TYPE_SEL_W 0x02
#define XPCS_XAUI_MODE 0x00
#define XPCS_RXAUI_MODE 0x01
int sxgbe_xpcs_init(struct net_device *ndev);
int sxgbe_xpcs_init_1G(struct net_device *ndev);
#endif /* __SXGBE_XPCS_H__ */
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册