blob: ec577095b36fbc6780954ceb5870b2f9492531ea [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* asr emac driver
*
* Copyright (C) 2019 ASR Micro Limited
*
*/
#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/in.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/ip.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_net.h>
#include <linux/of_mdio.h>
#include <linux/of_irq.h>
#include <linux/of_device.h>
#include <linux/phy.h>
#include <linux/platform_device.h>
#include <linux/tcp.h>
#include <linux/timer.h>
#include <linux/types.h>
#include <linux/udp.h>
#include <linux/workqueue.h>
#include <linux/phy_fixed.h>
#include <linux/pm_qos.h>
#include <asm/cacheflush.h>
#include <linux/cputype.h>
#include <linux/iopoll.h>
#include <linux/genalloc.h>
#include <linux/regulator/consumer.h>
#ifdef CONFIG_DEBUG_FS
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#endif /* CONFIG_DEBUG_FS */
#include <asm/atomic.h>
#include "emac_eth.h"
#include <linux/skbrb.h>
#ifdef WAN_LAN_AUTO_ADAPT
#include <linux/if_vlan.h>
#include <linux/if_ether.h>
#include <linux/kobject.h>
#endif
#define DRIVER_NAME "asr_emac"
#define CLOSE_AIB_POWER_DOMAIN 1
#define AXI_PHYS_BASE 0xd4200000
#define AIB_GMAC_IO_REG 0xD401E804
#define APBC_ASFAR 0xD4015050
#define AKEY_ASFAR 0xbaba
#define AKEY_ASSAR 0xeb10
#define EMAC_DIRECT_MAP
#define TUNING_CMD_LEN 50
#define CLK_PHASE_CNT 8
#define TXCLK_PHASE_DEFAULT 0
#define RXCLK_PHASE_DEFAULT 0
#define TX_PHASE 1
#define RX_PHASE 0
#define EMAC_DMA_REG_CNT 16
#define EMAC_MAC_REG_CNT 61
#define EMAC_EMPTY_FROM_DMA_TO_MAC 48
#define EMAC_REG_SPACE_SIZE ((EMAC_DMA_REG_CNT + \
EMAC_MAC_REG_CNT + EMAC_EMPTY_FROM_DMA_TO_MAC) * 4)
#define EMAC_ETHTOOL_STAT(x) { #x, \
offsetof(struct emac_hw_stats, x) / sizeof(u32) }
#define EMAC_SKBRB_SLOT_SIZE 1600
#define EMAC_EXTRA_ROOM 72
#define EMAC_SKBRB_MAX_PAYLOAD (EMAC_SKBRB_SLOT_SIZE - EMAC_EXTRA_ROOM - NET_IP_ALIGN)
#define EMAC_RX_FILL_TIMER_US 0
#define EMAC_TX_COAL_TIMER_US (1000)
#define EMAC_TX_FRAMES (64)
#ifdef WAN_LAN_AUTO_ADAPT
#define DHCP_DISCOVER 1
#define DHCP_OFFER 2
#define DHCP_REQUEST 3
#define DHCP_ACK 5
#define IP175D_PHY_ID 0x02430d80
enum emac_SIG {
CARRIER_DOWN = 0,
CARRIER_UP,
DHCP_EVENT_CLIENT,
DHCP_EVENT_SERVER,
PHY_IP175D_CONNECT,
CARRIER_DOWN_IP175D,
CARRIER_UP_IP175D,
};
enum emac_DHCP {
DHCP_SEND_REQ = 1,
DHCP_REC_RESP = 2,
};
struct emac_event {
const char *name;
char *action;
int port;
struct sk_buff *skb;
struct work_struct work;
};
extern u64 uevent_next_seqnum(void);
static int emac_sig_workq(int event, int port);
#endif
static int emac_set_axi_bus_clock(struct emac_priv *priv, bool enable);
static int clk_phase_set(struct emac_priv *priv, bool is_tx);
#ifdef CONFIG_ASR_EMAC_NAPI
static int emac_rx_clean_desc(struct emac_priv *priv, int budget);
#else
static int emac_rx_clean_desc(struct emac_priv *priv);
#endif
static void emac_alloc_rx_desc_buffers(struct emac_priv *priv);
static int emac_phy_connect(struct net_device *dev);
struct regulator *g_vcc3v3_gmac= NULL;
/* for falcon */
struct emac_regdata asr_emac_regdata_v1 = {
.support_dual_vol_power = 1,
.ptp_rx_ts_all_events = 0,
.clk_rst_ctrl_reg_offset = 0x160,
.axi_mst_single_id_shift = 17,
.phy_intr_enable_shift = 16,
.int_clk_src_sel_shift = -1,
.rgmii_tx_clk_src_sel_shift = 5,
.rgmii_rx_clk_src_sel_shift = 4,
.rmii_rx_clk_sel_shift = 7,
.rmii_tx_clk_sel_shift = 6,
.rmii_ref_clk_sel_shift = -1,
.mac_intf_sel_shift = 2,
.rgmii_tx_dline_reg_offset = -1,
.rgmii_tx_delay_code_shift = -1,
.rgmii_tx_delay_code_mask =-1,
.rgmii_tx_delay_step_shift = -1,
.rgmii_tx_delay_step_mask = -1,
.rgmii_tx_delay_enable_shift = -1,
.rgmii_rx_dline_reg_offset = -1,
.rgmii_rx_delay_code_shift = -1,
.rgmii_rx_delay_code_mask = -1,
.rgmii_rx_delay_step_shift = -1,
.rgmii_rx_delay_step_mask = -1,
.rgmii_rx_delay_enable_shift = -1,
};
/* for kagu */
struct emac_regdata asr_emac_regdata_v2 = {
.support_dual_vol_power = 0,
.ptp_rx_ts_all_events = 0,
.clk_rst_ctrl_reg_offset = 0x160,
.axi_mst_single_id_shift = 13,
.phy_intr_enable_shift = 12,
.int_clk_src_sel_shift = 9,
.rgmii_tx_clk_src_sel_shift = 8,
.rgmii_rx_clk_src_sel_shift = -1,
.rmii_rx_clk_sel_shift = 7,
.rmii_tx_clk_sel_shift = 6,
.rmii_ref_clk_sel_shift = 3,
.mac_intf_sel_shift = 2,
.rgmii_tx_dline_reg_offset = 0x178,
.rgmii_tx_delay_code_shift = 24,
.rgmii_tx_delay_code_mask = 0xff,
.rgmii_tx_delay_step_shift = 20,
.rgmii_tx_delay_step_mask = 0x3,
.rgmii_tx_delay_enable_shift = 16,
.rgmii_rx_dline_reg_offset = 0x178,
.rgmii_rx_delay_code_shift = 8,
.rgmii_rx_delay_code_mask = 0xff,
.rgmii_rx_delay_step_shift = 4,
.rgmii_rx_delay_step_mask = 0x3,
.rgmii_rx_delay_enable_shift = 0,
};
/* for lapwing */
struct emac_regdata asr_emac_regdata_v3 = {
.support_dual_vol_power = 1,
.ptp_rx_ts_all_events = 1,
.clk_rst_ctrl_reg_offset = 0x164,
.axi_mst_single_id_shift = 13,
.phy_intr_enable_shift = 12,
.int_clk_src_sel_shift = 9,
.rgmii_tx_clk_src_sel_shift = 8,
.rgmii_rx_clk_src_sel_shift = -1,
.rmii_rx_clk_sel_shift = 7,
.rmii_tx_clk_sel_shift = 6,
.rmii_ref_clk_sel_shift = 3,
.mac_intf_sel_shift = 2,
.rgmii_tx_dline_reg_offset = 0x16c,
.rgmii_tx_delay_code_shift = 8,
.rgmii_tx_delay_code_mask = 0xff,
.rgmii_tx_delay_step_shift = 0,
.rgmii_tx_delay_step_mask = 0x3,
.rgmii_tx_delay_enable_shift = 31,
.rgmii_rx_dline_reg_offset = 0x168,
.rgmii_rx_delay_code_shift = 8,
.rgmii_rx_delay_code_mask = 0xff,
.rgmii_rx_delay_step_shift = 0,
.rgmii_rx_delay_step_mask = 0x3,
.rgmii_rx_delay_enable_shift = 31,
};
static const struct of_device_id emac_of_match[] = {
{
.compatible = "asr,asr-eth",
.data = (void *)&asr_emac_regdata_v1,
},
{
.compatible = "asr,asr-eth-v2",
.data = (void *)&asr_emac_regdata_v2,
},
{
.compatible = "asr,asr-eth-v3",
.data = (void *)&asr_emac_regdata_v3,
},
{ },
};
MODULE_DEVICE_TABLE(of, emac_of_match);
#ifdef EMAC_DIRECT_MAP
dma_addr_t inline emac_map_direct(unsigned buf, unsigned len)
{
unsigned ret;
ret = mv_cp_virtual_to_physical(buf);
BUG_ON(ret == buf);
__cpuc_flush_dcache_area((void *)(buf & ~ 31),
((len + (buf & 31) + 31) & ~ 31));
return (dma_addr_t)ret;
}
#endif
static inline void emac_unmap_single(struct device *dev, dma_addr_t handle,
size_t size, enum dma_data_direction dir)
{
#ifdef EMAC_DIRECT_MAP
if (dir == DMA_TO_DEVICE)
return;
#endif
dma_unmap_single(dev, handle, size ,dir);
}
static inline dma_addr_t emac_map_single(struct device *dev, void *ptr,
size_t size,enum dma_data_direction dir)
{
if (dir == DMA_FROM_DEVICE)
return dma_map_single(dev, ptr, size, dir);
#ifndef EMAC_DIRECT_MAP
return dma_map_single(dev, ptr, size, dir);
#else
return emac_map_direct((unsigned)ptr, (unsigned)size);
#endif
}
#ifdef CONFIG_DDR_DEVFREQ
static void emac_ddr_qos_work(struct work_struct *work)
{
struct emac_priv *priv;
int val;
priv = container_of(work, struct emac_priv, qos_work);
val = priv->clk_scaling.qos_val;
if (val == PM_QOS_DEFAULT_VALUE)
pm_qos_update_request(&priv->clk_scaling.ddr_qos, val);
else
pm_qos_update_request_timeout(
&priv->clk_scaling.ddr_qos, val, (2 * USEC_PER_SEC));
}
static void emac_ddr_clk_scaling(struct emac_priv *priv)
{
struct net_device *ndev = priv->ndev;
unsigned long rx_bytes, tx_bytes;
unsigned long last_rx_bytes, last_tx_bytes;
unsigned long total_time_ms = 0;
unsigned int cur_rx_threshold, cur_tx_threshold;
unsigned long polling_jiffies;
int qos_val;
polling_jiffies = msecs_to_jiffies(priv->clk_scaling.polling_delay_ms);
if (time_is_after_jiffies(priv->clk_scaling.window_time +
polling_jiffies))
return;
total_time_ms = jiffies_to_msecs((long)jiffies -
(long)priv->clk_scaling.window_time);
if (!ndev) {
pr_err("%s: dev or net is not ready\n", __func__);
return;
}
qos_val = priv->clk_scaling.qos_val;
last_rx_bytes = priv->clk_scaling.rx_bytes;
last_tx_bytes = priv->clk_scaling.tx_bytes;
if (!last_rx_bytes && !last_tx_bytes)
goto out;
if (likely(ndev->stats.rx_bytes > last_rx_bytes))
rx_bytes = ndev->stats.rx_bytes - last_rx_bytes;
else
rx_bytes = ULONG_MAX - last_rx_bytes + ndev->stats.rx_bytes + 1;
if (likely(ndev->stats.tx_bytes > last_tx_bytes))
tx_bytes = ndev->stats.tx_bytes - last_tx_bytes;
else
tx_bytes = ULONG_MAX - last_tx_bytes + ndev->stats.tx_bytes + 1;
cur_tx_threshold = tx_bytes * 8 / (total_time_ms * 1000);
pr_debug("%s: tx_rate=%dMbps, up_threshold=%dMbps\n",
__func__, cur_tx_threshold, priv->clk_scaling.tx_up_threshold);
if (cur_tx_threshold >= priv->clk_scaling.tx_up_threshold) {
qos_val = ASR_EMAC_DDR_BOOST_FREQ;
goto out;
}
cur_rx_threshold = rx_bytes * 8 / (total_time_ms * 1000);
pr_debug("%s: rx_rate=%dMbps, up_threshold=%dMbps\n",
__func__, cur_rx_threshold, priv->clk_scaling.rx_up_threshold);
if (cur_rx_threshold >= priv->clk_scaling.rx_up_threshold) {
qos_val = ASR_EMAC_DDR_BOOST_FREQ;
goto out;
}
if (cur_tx_threshold < priv->clk_scaling.tx_down_threshold &&
cur_rx_threshold < priv->clk_scaling.rx_down_threshold)
qos_val = PM_QOS_DEFAULT_VALUE;
out:
priv->clk_scaling.rx_bytes = ndev->stats.rx_bytes;
priv->clk_scaling.tx_bytes = ndev->stats.tx_bytes;
priv->clk_scaling.window_time = jiffies;
if (qos_val != priv->clk_scaling.qos_val) {
priv->clk_scaling.qos_val = qos_val;
schedule_work(&priv->qos_work);
}
return;
}
#endif
/* strings used by ethtool */
static const struct emac_ethtool_stats {
char str[ETH_GSTRING_LEN];
u32 offset;
} emac_ethtool_stats[] = {
EMAC_ETHTOOL_STAT(tx_ok_pkts),
EMAC_ETHTOOL_STAT(tx_total_pkts),
EMAC_ETHTOOL_STAT(tx_ok_bytes),
EMAC_ETHTOOL_STAT(tx_err_pkts),
EMAC_ETHTOOL_STAT(tx_singleclsn_pkts),
EMAC_ETHTOOL_STAT(tx_multiclsn_pkts),
EMAC_ETHTOOL_STAT(tx_lateclsn_pkts),
EMAC_ETHTOOL_STAT(tx_excessclsn_pkts),
EMAC_ETHTOOL_STAT(tx_unicast_pkts),
EMAC_ETHTOOL_STAT(tx_multicast_pkts),
EMAC_ETHTOOL_STAT(tx_broadcast_pkts),
EMAC_ETHTOOL_STAT(tx_pause_pkts),
EMAC_ETHTOOL_STAT(rx_ok_pkts),
EMAC_ETHTOOL_STAT(rx_total_pkts),
EMAC_ETHTOOL_STAT(rx_crc_err_pkts),
EMAC_ETHTOOL_STAT(rx_align_err_pkts),
EMAC_ETHTOOL_STAT(rx_err_total_pkts),
EMAC_ETHTOOL_STAT(rx_ok_bytes),
EMAC_ETHTOOL_STAT(rx_total_bytes),
EMAC_ETHTOOL_STAT(rx_unicast_pkts),
EMAC_ETHTOOL_STAT(rx_multicast_pkts),
EMAC_ETHTOOL_STAT(rx_broadcast_pkts),
EMAC_ETHTOOL_STAT(rx_pause_pkts),
EMAC_ETHTOOL_STAT(rx_len_err_pkts),
EMAC_ETHTOOL_STAT(rx_len_undersize_pkts),
EMAC_ETHTOOL_STAT(rx_len_oversize_pkts),
EMAC_ETHTOOL_STAT(rx_len_fragment_pkts),
EMAC_ETHTOOL_STAT(rx_len_jabber_pkts),
EMAC_ETHTOOL_STAT(rx_64_pkts),
EMAC_ETHTOOL_STAT(rx_65_127_pkts),
EMAC_ETHTOOL_STAT(rx_128_255_pkts),
EMAC_ETHTOOL_STAT(rx_256_511_pkts),
EMAC_ETHTOOL_STAT(rx_512_1023_pkts),
EMAC_ETHTOOL_STAT(rx_1024_1518_pkts),
EMAC_ETHTOOL_STAT(rx_1519_plus_pkts),
EMAC_ETHTOOL_STAT(rx_drp_fifo_full_pkts),
EMAC_ETHTOOL_STAT(rx_truncate_fifo_full_pkts),
EMAC_ETHTOOL_STAT(rx_dma_missed_frame_cnt),
EMAC_ETHTOOL_STAT(tx_tso_pkts),
EMAC_ETHTOOL_STAT(tx_tso_bytes),
};
static int emac_set_speed_duplex(struct emac_priv *priv)
{
u32 ctrl;
ctrl = emac_rd(priv, MAC_GLOBAL_CONTROL);
if (priv->duplex)
ctrl |= MREGBIT_FULL_DUPLEX_MODE;
else
ctrl &= ~MREGBIT_FULL_DUPLEX_MODE;
switch (priv->speed) {
case SPEED_1000:
ctrl |= MREGBIT_SPEED_1000M;
break;
case SPEED_100:
ctrl |= MREGBIT_SPEED_100M;
break;
case SPEED_10:
ctrl |= MREGBIT_SPEED_10M;
break;
default:
pr_err("broken speed: %d\n", priv->speed);
return 0;
}
emac_wr(priv, MAC_GLOBAL_CONTROL, ctrl);
pr_info("emac: force link speed:%dM duplex:%s\n",
priv->speed, priv->duplex ? "Full": "Half");
return 0;
}
static int emac_set_fixed_link(struct device_node *np, struct emac_priv *priv)
{
struct fixed_phy_status status = {};
struct device_node *fixed_link_node;
u32 fixed_link_prop[5];
const char *managed;
int interface;
if (of_property_read_string(np, "managed", &managed) == 0 &&
strcmp(managed, "in-band-status") == 0) {
/* status is zeroed, namely its .link member */
goto fix_link;
}
/* New binding */
fixed_link_node = of_get_child_by_name(np, "fixed-link");
if (fixed_link_node) {
status.link = 1;
status.duplex = of_property_read_bool(fixed_link_node,
"full-duplex");
if (of_property_read_u32(fixed_link_node, "speed",
&status.speed)) {
of_node_put(fixed_link_node);
return -EINVAL;
}
status.pause = of_property_read_bool(fixed_link_node, "pause");
status.asym_pause = of_property_read_bool(fixed_link_node,
"asym-pause");
interface = of_get_phy_mode(fixed_link_node);
if (interface < 0) {
priv->interface = PHY_INTERFACE_MODE_RGMII;
pr_info("no interface for fix-link, use RGMII\n");
} else {
priv->interface = interface;
}
of_node_put(fixed_link_node);
goto fix_link;
}
/* Old binding */
if (of_property_read_u32_array(np, "fixed-link", fixed_link_prop,
ARRAY_SIZE(fixed_link_prop)) == 0) {
status.link = 1;
status.duplex = fixed_link_prop[1];
status.speed = fixed_link_prop[2];
status.pause = fixed_link_prop[3];
status.asym_pause = fixed_link_prop[4];
goto fix_link;
}
return -ENODEV;
fix_link:
priv->speed = status.speed;
priv->duplex = status.duplex;
return emac_set_speed_duplex(priv);
}
void register_dump(struct emac_priv *priv)
{
int i;
void __iomem *base = priv->iobase;
for (i = 0; i < 16; i++) {
pr_info("DMA:0x%x:0x%x\n",
DMA_CONFIGURATION + i * 4,
readl(base + DMA_CONFIGURATION + i * 4));
}
for (i = 0; i < 60; i++) {
pr_info("MAC:0x%x:0x%x\n",
MAC_GLOBAL_CONTROL + i * 4,
readl(base + MAC_GLOBAL_CONTROL + i * 4));
}
for (i = 0; i < 4; i++) {
pr_info("1588:0x%x:0x%x\n",
PTP_1588_CTRL + i * 4,
readl(base + PTP_1588_CTRL + i * 4));
}
for (i = 0; i < 6; i++) {
pr_info("1588:0x%x:0x%x\n",
SYS_TIME_GET_LOW + i * 4,
readl(base + SYS_TIME_GET_LOW + i * 4));
}
for (i = 0; i < 5; i++) {
pr_info("1588:0x%x:0x%x\n",
RX_TIMESTAMP_LOW + i * 4,
readl(base + RX_TIMESTAMP_LOW + i * 4));
}
for (i = 0; i < 2; i++) {
pr_info("1588:0x%x:0x%x\n",
PTP_1588_IRQ_STS + i * 4,
readl(base + PTP_1588_IRQ_STS + i * 4));
}
if (priv->tso) {
for (i = 0; i < 18; i++) {
pr_info("TSO:0x%x:0x%x\n", i * 4,
emac_rd_tso(priv, i * 4));
}
}
}
void print_pkt(unsigned char *buf, int len)
{
int i = 0;
pr_debug("data len = %d byte, buf addr: 0x%x\n",
len, (unsigned int)buf);
for (i = 0; i < len; i = i + 8) {
pr_debug("0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
*(buf + i),
*(buf + i + 1),
*(buf + i + 2),
*(buf + i + 3),
*(buf + i + 4),
*(buf + i + 5),
*(buf + i + 6),
*(buf + i + 7)
);
}
}
#ifdef EMAC_DEBUG
void print_desc(unsigned char *buf, int len)
{
int i;
pr_info("descriptor len = %d byte, buf addr: 0x%x\n",
len, (unsigned int)buf);
for (i = 0; i < len; i = i + 4) {
pr_info("0x%02x%02x%02x%02x\n",
*(buf + i + 3),
*(buf + i + 2),
*(buf + i + 1),
*(buf + i));
}
}
#else
void print_desc(unsigned char *buf, int len)
{
}
#endif
/* Name emac_reset_hw
* Arguments priv : pointer to hardware data structure
* Return Status: 0 - Success; non-zero - Fail
* Description TBDL
*/
int emac_reset_hw(struct emac_priv *priv)
{
mutex_lock(&priv->mii_mutex);
/* disable all the interrupts */
emac_wr(priv, MAC_INTERRUPT_ENABLE, 0x0000);
emac_wr(priv, DMA_INTERRUPT_ENABLE, 0x0000);
/* disable transmit and receive units */
emac_wr(priv, MAC_RECEIVE_CONTROL, 0x0000);
emac_wr(priv, MAC_TRANSMIT_CONTROL, 0x0000);
/* stop the DMA */
emac_wr(priv, DMA_CONTROL, 0x0000);
/* reset mac, statistic counters */
emac_wr(priv, MAC_GLOBAL_CONTROL, 0x0018);
emac_wr(priv, MAC_GLOBAL_CONTROL, 0x0000);
emac_wr(priv, MAC_MDIO_CLK_DIV,
priv->mdio_clk_div & MREGBIT_MAC_MDIO_CLK_DIV_MASK);
mutex_unlock(&priv->mii_mutex);
return 0;
}
/* Name emac_init_hw
* Arguments pstHWData : pointer to hardware data structure
* Return Status: 0 - Success; non-zero - Fail
* Description TBDL
* Assumes that the controller has previously been reset
* and is in apost-reset uninitialized state.
* Initializes the receive address registers,
* multicast table, and VLAN filter table.
* Calls routines to setup link
* configuration and flow control settings.
* Clears all on-chip counters. Leaves
* the transmit and receive units disabled and uninitialized.
*/
int emac_init_hw(struct emac_priv *priv)
{
u32 val = 0, threshold;
mutex_lock(&priv->mii_mutex);
/* MAC Init
* disable transmit and receive units
*/
emac_wr(priv, MAC_RECEIVE_CONTROL, 0x0000);
emac_wr(priv, MAC_TRANSMIT_CONTROL, 0x0000);
/* enable mac address 1 filtering */
//emac_wr(priv, MAC_ADDRESS_CONTROL, 0x0001);
emac_wr(priv, MAC_ADDRESS_CONTROL, 0x0100);
/* zero initialize the multicast hash table */
emac_wr(priv, MAC_MULTICAST_HASH_TABLE1, 0x0000);
emac_wr(priv, MAC_MULTICAST_HASH_TABLE2, 0x0000);
emac_wr(priv, MAC_MULTICAST_HASH_TABLE3, 0x0000);
emac_wr(priv, MAC_MULTICAST_HASH_TABLE4, 0x0000);
emac_wr(priv, MAC_TRANSMIT_FIFO_ALMOST_FULL, EMAC_TX_FIFO_DWORDS - 8);
if (priv->speed == SPEED_1000)
threshold = 1024;
else if (priv->speed == SPEED_100)
threshold = 256;
else
threshold = TX_STORE_FORWARD_MODE;
emac_wr(priv, MAC_TRANSMIT_PACKET_START_THRESHOLD, threshold);
emac_wr(priv, MAC_RECEIVE_PACKET_START_THRESHOLD, 0xc);
/* reset dma */
emac_wr(priv, DMA_CONTROL, 0x0000);
emac_wr(priv, DMA_CONFIGURATION, 0x01);
mdelay(10);
emac_wr(priv, DMA_CONFIGURATION, 0x00);
mdelay(10);
val |= MREGBIT_WAIT_FOR_DONE;
val |= MREGBIT_STRICT_BURST;
val |= MREGBIT_DMA_64BIT_MODE;
val |= MREGBIT_BURST_16WORD; //MREGBIT_BURST_1WORD;
emac_wr(priv, DMA_CONFIGURATION, val);
/* MDC Clock Division: AXI-312M/96 = 3.25M */
emac_wr(priv, MAC_MDIO_CLK_DIV,
priv->mdio_clk_div & MREGBIT_MAC_MDIO_CLK_DIV_MASK);
mutex_unlock(&priv->mii_mutex);
printk("MDIO clock div: 0x%x\n", emac_rd(priv, MAC_MDIO_CLK_DIV));
return 0;
}
int emac_set_mac_addr(struct emac_priv *priv, unsigned char *addr)
{
emac_wr(priv, MAC_ADDRESS1_HIGH, (addr[1] << 8 | addr[0]));
emac_wr(priv, MAC_ADDRESS1_MED, (addr[3] << 8 | addr[2]));
emac_wr(priv, MAC_ADDRESS1_LOW, (addr[5] << 8 | addr[4]));
return 0;
}
void emac_set_fc_source_addr(struct emac_priv *priv, unsigned char *addr)
{
emac_wr(priv, MAC_FC_SOURCE_ADDRESS_HIGH, (addr[1] << 8 | addr[0]));
emac_wr(priv, MAC_FC_SOURCE_ADDRESS_MED, (addr[3] << 8 | addr[2]));
emac_wr(priv, MAC_FC_SOURCE_ADDRESS_LOW, (addr[5] << 8 | addr[4]));
return;
}
static inline void emac_dma_start_transmit(struct emac_priv *priv)
{
emac_wr(priv, DMA_TRANSMIT_POLL_DEMAND, 0xFF);
}
static inline void emac_dma_start_receive(struct emac_priv *priv)
{
emac_wr(priv, DMA_RECEIVE_POLL_DEMAND, 0xFF);
}
#ifdef CONFIG_ASR_EMAC_NAPI
void emac_enable_interrupt(struct emac_priv *priv, int tx)
{
u32 val;
val = emac_rd(priv, DMA_INTERRUPT_ENABLE);
if (tx) {
val |= MREGBIT_TRANSMIT_TRANSFER_DONE_INTR_ENABLE;
} else {
val |= MREGBIT_RECEIVE_TRANSFER_DONE_INTR_ENABLE |
MREGBIT_RECEIVE_MISSED_FRAME_INTR_ENABLE;
if (priv->tso)
emac_wr_tso(priv, TSO_AP_RX_INTR_ENA,
TSO_AP_RX_INTR_ENA_CSUM_DONE |
TSO_AP_RX_INTR_ENA_CSUM_ERR);
}
emac_wr(priv, DMA_INTERRUPT_ENABLE, val);
}
void emac_disable_interrupt(struct emac_priv *priv, int tx)
{
u32 val;
val = emac_rd(priv, DMA_INTERRUPT_ENABLE);
if (tx) {
val &= ~MREGBIT_TRANSMIT_TRANSFER_DONE_INTR_ENABLE;
} else {
val &= ~(MREGBIT_RECEIVE_TRANSFER_DONE_INTR_ENABLE |
MREGBIT_RECEIVE_MISSED_FRAME_INTR_ENABLE);
if (priv->tso)
emac_wr_tso(priv, TSO_AP_RX_INTR_ENA, 0x0);
}
emac_wr(priv, DMA_INTERRUPT_ENABLE, val);
}
#endif
bool emac_is_rmii_interface(struct emac_priv *priv)
{
const struct emac_regdata *regdata = priv->regdata;
void __iomem* apmu;
u32 val;
apmu = ioremap(AXI_PHYS_BASE + 0x82800, SZ_4K);
if (apmu == NULL) {
pr_err("error to ioremap APMU base\n");
return -ENOMEM;
}
val = readl(apmu + regdata->clk_rst_ctrl_reg_offset);
val &= (0x1 << regdata->mac_intf_sel_shift);
if (val)
return false;
else
return true;
}
void emac_config_phy_interrupt(struct emac_priv *priv, int enable)
{
const struct emac_regdata *regdata = priv->regdata;
void __iomem* apmu;
u32 val;
apmu = ioremap(AXI_PHYS_BASE + 0x82800, SZ_4K);
if (apmu == NULL) {
pr_err("error to ioremap APMU base\n");
return;
}
val = readl(apmu + regdata->clk_rst_ctrl_reg_offset);
if (enable)
val |= 0x1 << regdata->phy_intr_enable_shift;
else
val &= ~(0x1 << regdata->phy_intr_enable_shift);
writel(val, apmu + regdata->clk_rst_ctrl_reg_offset);
iounmap(apmu);
return;
}
void emac_phy_interface_config(struct emac_priv *priv, int phy_interface)
{
const struct emac_regdata *regdata = priv->regdata;
void __iomem* apmu;
u32 val;
apmu = ioremap(AXI_PHYS_BASE + 0x82800, SZ_4K);
if (apmu == NULL) {
pr_err("error to ioremap APMU base\n");
return;
}
val = readl(apmu + regdata->clk_rst_ctrl_reg_offset);
if (PHY_INTERFACE_MODE_RMII == phy_interface) {
val &= ~(0x1 << regdata->mac_intf_sel_shift);
printk("===> set eamc interface: rmii\n");
} else {
val |= 0x1 << regdata->mac_intf_sel_shift;
printk("===> set eamc interface: rgmii\n");
}
val |= 0x1 << regdata->axi_mst_single_id_shift;
writel(val, apmu + regdata->clk_rst_ctrl_reg_offset);
iounmap(apmu);
priv->interface = phy_interface;
return;
}
static void emac_set_aib_power_domain(struct emac_priv *priv)
{
const struct emac_regdata *regdata = priv->regdata;
void __iomem *aib_emac_io;
void __iomem *apbc_asfar;
u32 tmp;
if (!regdata->support_dual_vol_power)
return;
aib_emac_io = ioremap(AIB_GMAC_IO_REG, 4);
apbc_asfar = ioremap(APBC_ASFAR, 8);
writel(AKEY_ASFAR, apbc_asfar);
writel(AKEY_ASSAR, apbc_asfar + 4);
tmp = readl(aib_emac_io);
priv->power_domain = 0;
/* 0= power down, only set power down when vol = 0 */
if (priv->power_domain) {
tmp &= ~(0x1 << 2); /* 3.3v */
printk("===> emac set io to 3.3v\n");
} else {
tmp |= 0x1 << 2; /* 1.8v */
printk("===> emac set io to 1.8v\n");
}
writel(AKEY_ASFAR, apbc_asfar);
writel(AKEY_ASSAR, apbc_asfar + 4);
writel(tmp, aib_emac_io);
writel(AKEY_ASFAR, apbc_asfar);
writel(AKEY_ASSAR, apbc_asfar + 4);
tmp = readl(aib_emac_io);
printk("===> emac AIB read back: 0x%x\n", tmp);
iounmap(apbc_asfar);
iounmap(aib_emac_io);
}
static void emac_pause_generate_work_fuc(struct work_struct *work)
{
struct emac_priv *priv= container_of(work, struct emac_priv, emac_pause_work.work);
int time_nxt = 0;
/* because pause time value = 0XFFFF,equal to stopping for 336ms(100M)/34ms(1000M) to transmit */
/* by a repeated testing, delay 20ms(1000M)/300ms(100M) satisfy making the neighbor stop transmission */
time_nxt = (priv->speed == SPEED_1000) ? 20 : 300;
if (!priv->pause.pause_time_max) {
emac_wr(priv, MAC_FC_PAUSE_TIME_VALUE, 0xffff);
priv->pause.pause_time_max = 1;
}
emac_wr(priv, MAC_FC_PAUSE_FRAME_GENERATE, 0x1);
schedule_delayed_work(&priv->emac_pause_work, msecs_to_jiffies(time_nxt));
return;
}
static inline void emac_check_ring_and_send_pause(struct emac_priv *priv)
{
int pos;
int high_water;
int low_water;
struct emac_rx_desc *rx_desc;
struct emac_desc_ring *rx_ring;
rx_ring = &priv->rx_ring;
pos = rx_ring->nxt_clean;
high_water = (pos + priv->pause.high_water) % priv->rx_ring.total_cnt;
low_water = (pos + priv->pause.low_water) % priv->rx_ring.total_cnt;
rx_desc = emac_get_rx_desc(priv, high_water);
if (priv->pause.pause_sending == 0 && rx_desc->OWN == 0) {
schedule_delayed_work(&priv->emac_pause_work, 0);
priv->pause.pause_sending = 1;
}
rx_desc = emac_get_rx_desc(priv, low_water);
if (rx_desc->OWN && priv->pause.pause_sending) {
cancel_delayed_work_sync(&priv->emac_pause_work);
emac_wr(priv, MAC_FC_PAUSE_TIME_VALUE, 0);
emac_wr(priv, MAC_FC_PAUSE_FRAME_GENERATE, 0x1);
priv->pause.pause_time_max = 0;
priv->pause.pause_sending = 0;
}
}
/* Name emac_sw_init
* Arguments priv : pointer to driver private data structure
* Return Status: 0 - Success; non-zero - Fail
* Description Reads PCI space configuration information and
* initializes the variables with
* their default values
*/
static int emac_sw_init(struct emac_priv *priv)
{
priv->u32RxBufferLen = EMAC_SKBRB_MAX_PAYLOAD;
mutex_init(&priv->mii_mutex);
spin_lock_init(&priv->spStatsLock);
spin_lock_init(&priv->spTxLock);
spin_lock_init(&priv->intr_lock);
return 0;
}
static int emac_check_ptp_packet(struct emac_priv *priv,
struct sk_buff *skb, int txrx)
{
struct ethhdr *eth = (struct ethhdr *)skb->data;
struct ptp_header *ptph = NULL;
struct iphdr *iph;
struct udphdr *udph;
int msg_type, msg_id;
int ts;
if (eth->h_proto == htons(ETH_P_1588)) {
netdev_dbg(priv->ndev, "get PTP packet over ETH\n");
ptph = (struct ptp_header *)((u8 *)eth + sizeof(struct ethhdr));
} else if (eth->h_proto == htons(ETH_P_IP)) {
iph = (struct iphdr *)((u8 *)eth + sizeof(struct ethhdr));
if (iph->protocol != IPPROTO_UDP)
return -1;
udph = (struct udphdr *)((u8 *)iph + (iph->ihl << 2));
if ((htons(udph->dest) != PTP_EVENT_PORT ||
htons(udph->source) != PTP_EVENT_PORT))
return -1;
netdev_dbg(priv->ndev, "get PTP packet over UDP\n");
ptph = (struct ptp_header *)((u8 *)udph + sizeof(struct udphdr));
} else {
return -1;
}
msg_id = -1;
ts = ptph->tsmt & 0xF0;
msg_type = (ptph->tsmt) & 0x0F;
if (txrx) {
if (msg_type == MSG_SYNC) {
if (ts)
msg_id = MSG_PDELAY_REQ;
else
msg_id = MSG_DELAY_REQ;
} else if (msg_type == MSG_DELAY_REQ) {
msg_id = MSG_SYNC;
} else if (msg_type == MSG_PDELAY_REQ) {
msg_id = MSG_PDELAY_RESP;
memcpy(&priv->sourcePortIdentity,
&ptph->sourcePortIdentity,
sizeof(struct PortIdentity));
} else if (msg_type == MSG_PDELAY_RESP) {
msg_id = MSG_PDELAY_REQ;
}
} else {
netdev_dbg(priv->ndev, "RX timestamp for message type %d\n",
ptph->tsmt);
if (msg_type == MSG_PDELAY_RESP) {
struct pdelay_resp_msg *presp = (struct pdelay_resp_msg *)ptph;
/*
* Change to monitor SYNC packet if pdelay response
* received for same clock indentity.
*/
if (!memcmp(&presp->requestingPortIdentity.clockIdentity,
&priv->sourcePortIdentity.clockIdentity,
sizeof(struct ClockIdentity))) {
msg_id = MSG_SYNC;
}
}
}
/*
* Since some platform not support to timestamp two or more
* message type, so change here.
*/
if (msg_id >= 0) {
if (priv->regdata->ptp_rx_ts_all_events) {
msg_id = ALL_EVENTS;
msg_id |= ts | ts << 8 | ts << 16 | ts << 24;
} else {
msg_id |= ts;
}
priv->hwptp->config_hw_tstamping(priv, 1, PTP_V2_L2_L4, msg_id);
}
return ptph->tsmt;
}
/* emac_get_tx_hwtstamp - get HW TX timestamps
* @priv: driver private structure
* @skb : the socket buffer
* Description :
* This function will read timestamp from the register & pass it to stack.
* and also perform some sanity checks.
*/
static void emac_get_tx_hwtstamp(struct emac_priv *priv, struct sk_buff *skb)
{
struct skb_shared_hwtstamps shhwtstamp;
u64 ns;
if (!priv->hwts_tx_en)
return;
/* exit if skb doesn't support hw tstamp */
if (likely(!skb || !(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)))
return;
emac_check_ptp_packet(priv, skb, 1);
/* get the valid tstamp */
ns = priv->hwptp->get_tx_timestamp(priv);
memset(&shhwtstamp, 0, sizeof(struct skb_shared_hwtstamps));
shhwtstamp.hwtstamp = ns_to_ktime(ns);
wmb();
netdev_dbg(priv->ndev, "get valid TX hw timestamp %llu\n", ns);
/* pass tstamp to stack */
skb_tstamp_tx(skb, &shhwtstamp);
return;
}
/* emac_get_rx_hwtstamp - get HW RX timestamps
* @priv: driver private structure
* @p : descriptor pointer
* @skb : the socket buffer
* Description :
* This function will read received packet's timestamp from the descriptor
* and pass it to stack. It also perform some sanity checks.
*/
static void emac_get_rx_hwtstamp(struct emac_priv *priv, struct emac_rx_desc *p,
struct sk_buff *skb)
{
struct skb_shared_hwtstamps *shhwtstamp = NULL;
u64 ns;
if (!priv->hwts_rx_en)
return;
/* Check if timestamp is available */
if (p->ptp_pkt && p->rx_timestamp) {
emac_check_ptp_packet(priv, skb, 0);
ns = priv->hwptp->get_rx_timestamp(priv);
netdev_dbg(priv->ndev, "get valid RX hw timestamp %llu\n", ns);
shhwtstamp = skb_hwtstamps(skb);
memset(shhwtstamp, 0, sizeof(struct skb_shared_hwtstamps));
shhwtstamp->hwtstamp = ns_to_ktime(ns);
} else {
netdev_dbg(priv->ndev, "cannot get RX hw timestamp\n");
}
}
/**
* emac_hwtstamp_set - control hardware timestamping.
* @dev: device pointer.
* @ifr: An IOCTL specific structure, that can contain a pointer to
* a proprietary structure used to pass information to the driver.
* Description:
* This function configures the MAC to enable/disable both outgoing(TX)
* and incoming(RX) packets time stamping based on user input.
* Return Value:
* 0 on success and an appropriate -ve integer on failure.
*/
static int emac_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
{
struct emac_priv *priv = netdev_priv(dev);
struct hwtstamp_config config;
struct timespec64 now;
u64 ns_ptp;
u32 ptp_event_msg_id = 0;
u32 rx_ptp_type = 0;
if (!priv->ptp_support) {
netdev_alert(priv->ndev, "No support for HW time stamping\n");
priv->hwts_tx_en = 0;
priv->hwts_rx_en = 0;
return -EOPNOTSUPP;
}
if (copy_from_user(&config, ifr->ifr_data,
sizeof(struct hwtstamp_config)))
return -EFAULT;
netdev_dbg(priv->ndev, "%s config flags:0x%x, tx_type:0x%x, rx_filter:0x%x\n",
__func__, config.flags, config.tx_type, config.rx_filter);
/* reserved for future extensions */
if (config.flags)
return -EINVAL;
if (config.tx_type != HWTSTAMP_TX_OFF &&
config.tx_type != HWTSTAMP_TX_ON)
return -ERANGE;
switch (config.rx_filter) {
case HWTSTAMP_FILTER_NONE:
/* time stamp no incoming packet at all */
config.rx_filter = HWTSTAMP_FILTER_NONE;
break;
case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
/* PTP v1, UDP, Sync packet */
config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_SYNC;
/* take time stamp for SYNC messages only */
ptp_event_msg_id = MSG_SYNC;
rx_ptp_type = PTP_V1_L4_ONLY;
break;
case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
/* PTP v1, UDP, Delay_req packet */
config.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ;
/* take time stamp for Delay_Req messages only */
ptp_event_msg_id = MSG_DELAY_REQ;
rx_ptp_type = PTP_V1_L4_ONLY;
break;
case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
/* PTP v2, UDP, Sync packet */
config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_SYNC;
/* take time stamp for SYNC messages only */
ptp_event_msg_id = MSG_SYNC;
rx_ptp_type = PTP_V2_L2_L4;
break;
case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
/* PTP v2, UDP, Delay_req packet */
config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ;
/* take time stamp for Delay_Req messages only */
ptp_event_msg_id = MSG_DELAY_REQ;
rx_ptp_type = PTP_V2_L2_L4;
break;
case HWTSTAMP_FILTER_PTP_V2_EVENT:
/* PTP v2/802.AS1 any layer, any kind of event packet */
config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
/*
* IF not support ALL EVENTS, default timestamp SYNC packet,
* changed to MSG_DELAY_REQ automactically if needed
*/
if (priv->regdata->ptp_rx_ts_all_events)
ptp_event_msg_id = ALL_EVENTS;
else
ptp_event_msg_id = MSG_SYNC;
rx_ptp_type = PTP_V2_L2_L4;
break;
case HWTSTAMP_FILTER_PTP_V2_SYNC:
/* PTP v2/802.AS1, any layer, Sync packet */
config.rx_filter = HWTSTAMP_FILTER_PTP_V2_SYNC;
/* take time stamp for SYNC messages only */
ptp_event_msg_id = MSG_SYNC;
rx_ptp_type = PTP_V2_L2_L4;
break;
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
/* PTP v2/802.AS1, any layer, Delay_req packet */
config.rx_filter = HWTSTAMP_FILTER_PTP_V2_DELAY_REQ;
/* take time stamp for Delay_Req messages only */
ptp_event_msg_id = MSG_DELAY_REQ;
rx_ptp_type = PTP_V2_L2_L4;
break;
default:
return -ERANGE;
}
priv->hwts_rx_en = ((config.rx_filter == HWTSTAMP_FILTER_NONE) ? 0 : 1);
priv->hwts_tx_en = config.tx_type == HWTSTAMP_TX_ON;
if (!priv->hwts_tx_en && !priv->hwts_rx_en)
priv->hwptp->config_hw_tstamping(priv, 0, 0, 0);
else {
priv->hwptp->config_hw_tstamping(priv, 1,
rx_ptp_type, ptp_event_msg_id);
/* initialize system time */
ktime_get_real_ts64(&now);
priv->hwptp->init_systime(priv, timespec64_to_ns(&now));
/* program Increment reg */
priv->hwptp->config_systime_increment(priv);
ns_ptp = priv->hwptp->get_phc_time(priv);
ktime_get_real_ts64(&now);
/* check the diff between ptp timer and system time */
if (abs(timespec64_to_ns(&now) - ns_ptp) > 5000)
priv->hwptp->init_systime(priv,
timespec64_to_ns(&now));
}
memcpy(&priv->tstamp_config, &config, sizeof(config));
return copy_to_user(ifr->ifr_data, &config,
sizeof(struct hwtstamp_config)) ? -EFAULT : 0;
}
/**
* emac_hwtstamp_get - read hardware timestamping.
* @dev: device pointer.
* @ifr: An IOCTL specific structure, that can contain a pointer to
* a proprietary structure used to pass information to the driver.
* Description:
* This function obtain the current hardware timestamping settings
as requested.
*/
static int emac_hwtstamp_get(struct net_device *dev, struct ifreq *ifr)
{
struct emac_priv *priv = netdev_priv(dev);
struct hwtstamp_config *config = &priv->tstamp_config;
if (!priv->ptp_support)
return -EOPNOTSUPP;
return copy_to_user(ifr->ifr_data, config,
sizeof(*config)) ? -EFAULT : 0;
}
/* Name emac_ioctl
* Arguments pstNetdev : pointer to net_device structure
* pstIfReq : pointer to interface request structure used.
* u32Cmd : IOCTL command number
* Return Status: 0 - Success; non-zero - Fail
* Description It is called by upper layer and
* handling various task IOCTL commands.
*/
static int emac_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
{
int ret = -EOPNOTSUPP;
if (!netif_running(ndev))
return -EINVAL;
switch (cmd) {
case SIOCGMIIPHY:
case SIOCGMIIREG:
case SIOCSMIIREG:
if (!ndev->phydev)
return -EINVAL;
ret = phy_mii_ioctl(ndev->phydev, rq, cmd);
break;
case SIOCSHWTSTAMP:
ret = emac_hwtstamp_set(ndev, rq);
break;
case SIOCGHWTSTAMP:
ret = emac_hwtstamp_get(ndev, rq);
break;
default:
break;
}
return ret;
}
static irqreturn_t emac_wakeup_handler(int irq, void *dev_id)
{
struct net_device *ndev = (struct net_device *)dev_id;
struct emac_priv *priv = netdev_priv(ndev);
u32 ctrl;
emac_set_axi_bus_clock(priv, 1);
ctrl = emac_rd(priv, MAC_GLOBAL_CONTROL);
if (!(ctrl & (MREGBIT_UNICAST_WAKEUP_MODE |
MREGBIT_MAGIC_PACKET_WAKEUP_MODE)))
return IRQ_NONE;
ctrl &= ~(MREGBIT_UNICAST_WAKEUP_MODE |
MREGBIT_MAGIC_PACKET_WAKEUP_MODE);
emac_wr(priv, MAC_GLOBAL_CONTROL, ctrl);
return IRQ_HANDLED;
}
static irqreturn_t emac_irq_tso(int irq, void *dev_id)
{
struct net_device *ndev = (struct net_device *)dev_id;
struct emac_priv *priv = netdev_priv(ndev);
u32 status;
/* handle rx */
status = emac_rd_tso(priv, TSO_AP_RX_INTR_STS);
if (status) {
emac_print("TSO_AP_RX_INTR_STS=0x%x", status);
if (status & TSO_AP_RX_INTR_ENA_CSUM_DONE) {
#ifdef CONFIG_ASR_EMAC_NAPI
if (likely(napi_schedule_prep(&priv->rx_napi))) {
unsigned long flags;
spin_lock_irqsave(&priv->intr_lock, flags);
emac_disable_interrupt(priv, 0);
spin_unlock_irqrestore(&priv->intr_lock, flags);
__napi_schedule(&priv->rx_napi);
}
#else
emac_rx_clean_desc(priv);
#endif
}
#ifdef EMAC_DEBUG
if (status & TSO_AP_RX_INTR_ENA_CSUM_ERR)
pr_err("rx checksum err irq\n");
#endif
/* clear rx status */
emac_wr_tso(priv, TSO_AP_RX_INTR_STS, status);
}
/* handle tx */
status = emac_rd_tso(priv, TSO_AP_TX_INTR_STS);
if (status) {
emac_print("TSO_AP_TX_INTR_STS=0x%x\n", status);
if (status & TSO_AP_TX_INTR_TSO_DONE) {
emac_print("TX TSO done\n");
emac_dma_start_transmit(priv);
}
if (status & TSO_AP_TX_INTR_CSUM_DONE) {
emac_print("TX checksum done\n");
emac_dma_start_transmit(priv);
}
/* clear tx status */
emac_wr_tso(priv, TSO_AP_TX_INTR_STS, status);
}
/* handle err */
status = emac_rd_tso(priv, TSO_ERR_INTR_STS);
if (status) {
pr_err("TSO: TX/RX ERR, status=0x%x\n", status);
emac_wr_tso(priv, TSO_ERR_INTR_STS, status);
}
return IRQ_HANDLED;
}
/* Name emac_interrupt_handler
* Arguments irq : irq number for which the interrupt is fired
* dev_id : pointer was passed to request_irq and same pointer is passed
* back to handler
* Return irqreturn_t : integer value
* Description Interrupt handler routine for interrupts from target for RX packets indication.
*/
static irqreturn_t emac_interrupt_handler(int irq, void *dev_id)
{
struct net_device *ndev = (struct net_device *)dev_id;
struct emac_priv *priv = netdev_priv(ndev);
u32 status;
u32 clr = 0;
/* read the status register for IRQ received */
status = emac_rd(priv, DMA_STATUS_IRQ);
/* Check if emac is up */
if (test_bit(EMAC_DOWN, &priv->state)) {
emac_wr(priv, DMA_STATUS_IRQ, status & 0x1F7);
return IRQ_HANDLED;
}
if (status & MREGBIT_TRANSMIT_TRANSFER_DONE_IRQ) {
clr |= MREGBIT_TRANSMIT_TRANSFER_DONE_IRQ;
#ifdef CONFIG_ASR_EMAC_NAPI
if (likely(napi_schedule_prep(&priv->tx_napi))) {
unsigned long flags;
spin_lock_irqsave(&priv->intr_lock, flags);
emac_disable_interrupt(priv, 1);
spin_unlock_irqrestore(&priv->intr_lock, flags);
__napi_schedule(&priv->tx_napi);
}
#else
emac_tx_clean_desc(priv);
#endif
}
if (status & MREGBIT_TRANSMIT_DES_UNAVAILABLE_IRQ)
clr |= MREGBIT_TRANSMIT_DES_UNAVAILABLE_IRQ;
if (status & MREGBIT_TRANSMIT_DMA_STOPPED_IRQ)
clr |= MREGBIT_TRANSMIT_DMA_STOPPED_IRQ;
if (status & (MREGBIT_RECEIVE_TRANSFER_DONE_IRQ |
MREGBIT_RECEIVE_MISSED_FRAME_IRQ)) {
if (status & MREGBIT_RECEIVE_TRANSFER_DONE_IRQ)
clr |= MREGBIT_RECEIVE_TRANSFER_DONE_IRQ;
if (status & MREGBIT_RECEIVE_MISSED_FRAME_IRQ)
clr |= MREGBIT_RECEIVE_MISSED_FRAME_IRQ;
if (priv->tso)
emac_wr_tso(priv, TSO_RX_POLL_DEMAND, 0xFF);
#ifdef CONFIG_ASR_EMAC_NAPI
if (likely(napi_schedule_prep(&priv->rx_napi))) {
unsigned long flags;
spin_lock_irqsave(&priv->intr_lock, flags);
emac_disable_interrupt(priv, 0);
spin_unlock_irqrestore(&priv->intr_lock, flags);
__napi_schedule(&priv->rx_napi);
}
#else
emac_rx_clean_desc(priv);
#endif
}
if (status & MREGBIT_RECEIVE_DES_UNAVAILABLE_IRQ)
clr |= MREGBIT_RECEIVE_DES_UNAVAILABLE_IRQ;
if (status & MREGBIT_RECEIVE_DMA_STOPPED_IRQ)
clr |= MREGBIT_RECEIVE_DMA_STOPPED_IRQ;
emac_wr(priv, DMA_STATUS_IRQ, clr);
return IRQ_HANDLED;
}
/* Name emac_command_options
* Arguments priv : pointer to driver private data structure
* Return none
* Description This function actually handles the command line para passed
* when the driver is loaded at the command prompt.
* It parses the parameters and validates them for valid values.
*/
void emac_command_options(struct emac_priv *priv)
{
int pages = totalram_pages();
if (pages <= (EMAC_SMALL_RING_MEM_LIMIT >> PAGE_SHIFT))
priv->rx_ring.total_cnt = EMAC_SMALL_RX_RING_SIZE;
else
priv->rx_ring.total_cnt = EMAC_RX_RING_SIZE;
priv->tx_ring.total_cnt = EMAC_TX_RING_SIZE;
pr_info("emac: rx_ring=%d, tx_ring=%d, pages=%d\n",
priv->rx_ring.total_cnt, priv->tx_ring.total_cnt, pages);
}
/* Name emac_configure_tx
* Arguments priv : pointer to driver private data structure
* Return none
* Description Configures the transmit unit of the device
*/
static void emac_configure_tx(struct emac_priv *priv)
{
u32 val;
/* set the transmit base address */
val = (u32)(priv->tx_ring.desc_dma_addr);
emac_wr(priv, DMA_TRANSMIT_BASE_ADDRESS, val);
/* Tx Inter Packet Gap value and enable the transmit */
val = emac_rd(priv, MAC_TRANSMIT_CONTROL);
val &= (~MREGBIT_IFG_LEN);
val |= MREGBIT_TRANSMIT_ENABLE;
val |= MREGBIT_TRANSMIT_AUTO_RETRY;
emac_wr(priv, MAC_TRANSMIT_CONTROL, val);
emac_wr(priv, DMA_TRANSMIT_AUTO_POLL_COUNTER, 0x00);
/* start tx dma */
val = emac_rd(priv, DMA_CONTROL);
val |= MREGBIT_START_STOP_TRANSMIT_DMA;
emac_wr(priv, DMA_CONTROL, val);
}
/* Name emac_configure_rx
* Arguments priv : pointer to driver private data structure
* Return none
* Description Configures the receive unit of the device
*/
static void emac_configure_rx(struct emac_priv *priv)
{
u32 val;
/* set the receive base address */
val = (u32)(priv->rx_ring.desc_dma_addr);
emac_wr(priv, DMA_RECEIVE_BASE_ADDRESS, val);
/* enable the receive */
val = emac_rd(priv, MAC_RECEIVE_CONTROL);
val |= MREGBIT_RECEIVE_ENABLE;
val |= MREGBIT_STORE_FORWARD;
val |= MREGBIT_ACOOUNT_VLAN;
emac_wr(priv, MAC_RECEIVE_CONTROL, val);
/* start rx dma */
val = emac_rd(priv, DMA_CONTROL);
val |= MREGBIT_START_STOP_RECEIVE_DMA;
emac_wr(priv, DMA_CONTROL, val);
}
/* Name emac_clean_tx_desc_ring
* Arguments priv : pointer to driver private data structure
* Return none
* Description Freeing the TX resources allocated earlier.
*/
static void emac_clean_tx_desc_ring(struct emac_priv *priv)
{
struct emac_desc_ring *tx_ring = &priv->tx_ring;
struct emac_desc_buffer *tx_buf;
u32 i;
/* Free all the Tx ring sk_buffs */
for (i = 0; i < tx_ring->total_cnt; i++) {
tx_buf = &tx_ring->desc_buf[i];
if (tx_buf->dma_addr) {
dma_unmap_page(&priv->pdev->dev,
tx_buf->dma_addr,
tx_buf->dma_len,
DMA_TO_DEVICE);
tx_buf->dma_addr = 0;
}
if (tx_buf->skb) {
dev_kfree_skb_any(tx_buf->skb);
tx_buf->skb = NULL;
}
}
tx_ring->nxt_use = 0;
tx_ring->nxt_clean = 0;
}
/* Name emac_clean_rx_desc_ring
* Arguments priv : pointer to driver private data structure
* Return none
* Description Freeing the RX resources allocated earlier.
*/
static void emac_clean_rx_desc_ring(struct emac_priv *priv)
{
struct emac_desc_ring *rx_ring;
struct emac_desc_buffer *rx_buf;
u32 i;
rx_ring = &priv->rx_ring;
/* Free all the Rx ring sk_buffs */
for (i = 0; i < rx_ring->total_cnt; i++) {
rx_buf = &rx_ring->desc_buf[i];
if (rx_buf->skb) {
emac_unmap_single(&priv->pdev->dev,
rx_buf->dma_addr,
rx_buf->dma_len,
DMA_FROM_DEVICE);
dev_kfree_skb(rx_buf->skb);
rx_buf->skb = NULL;
}
if (rx_buf->buff_addr) {
#ifndef CONFIG_ASR_EMAC_RX_NO_COPY
kfree(rx_buf->buff_addr);
#endif
rx_buf->buff_addr = NULL;
}
}
rx_ring->nxt_clean = 0;
rx_ring->nxt_use = 0;
}
void emac_ptp_init(struct emac_priv *priv)
{
int ret;
if (priv->ptp_support) {
ret = clk_prepare_enable(priv->ptp_clk);
if (ret < 0) {
pr_warning("ptp clock failed to enable \n");
priv->ptp_clk = NULL;
}
emac_ptp_register(priv);
if (IS_ERR_OR_NULL(priv->ptp_clock)) {
priv->ptp_support = 0;
pr_warning("disable PTP due to clock not enabled\n");
}
}
}
void emac_ptp_deinit(struct emac_priv *priv)
{
if (priv->ptp_support) {
if (priv->ptp_clk)
clk_disable_unprepare(priv->ptp_clk);
emac_ptp_unregister(priv);
}
}
static void emac_rx_timer_arm(struct emac_priv *priv)
{
u32 rx_fill_timer = EMAC_RX_FILL_TIMER_US;
if (!rx_fill_timer)
return;
if (hrtimer_is_queued(&priv->rx_timer))
return;
hrtimer_start(&priv->rx_timer,
ns_to_ktime(rx_fill_timer) * NSEC_PER_USEC,
HRTIMER_MODE_REL);
}
static enum hrtimer_restart emac_rx_timer(struct hrtimer *t)
{
struct emac_priv *priv = container_of(t, struct emac_priv, rx_timer);
struct napi_struct *napi = &priv->rx_napi;
if (likely(napi_schedule_prep(napi))) {
unsigned long flags;
spin_lock_irqsave(&priv->intr_lock, flags);
emac_disable_interrupt(priv, 0);
spin_unlock_irqrestore(&priv->intr_lock, flags);
__napi_schedule(napi);
}
return HRTIMER_NORESTART;
}
static void emac_tx_timer_arm(struct emac_priv *priv)
{
u32 tx_coal_timer = EMAC_TX_COAL_TIMER_US;
if (!tx_coal_timer)
return;
if (hrtimer_is_queued(&priv->tx_timer))
return;
hrtimer_start(&priv->tx_timer,
ns_to_ktime(tx_coal_timer) * NSEC_PER_USEC,
HRTIMER_MODE_REL);
}
static enum hrtimer_restart emac_tx_timer(struct hrtimer *t)
{
struct emac_priv *priv = container_of(t, struct emac_priv, tx_timer);
struct napi_struct *napi = &priv->tx_napi;
if (priv->tso) {
emac_dma_start_transmit(priv);
return HRTIMER_NORESTART;
}
if (likely(napi_schedule_prep(napi))) {
unsigned long flags;
spin_lock_irqsave(&priv->intr_lock, flags);
emac_disable_interrupt(priv, 1);
spin_unlock_irqrestore(&priv->intr_lock, flags);
__napi_schedule(napi);
}
return HRTIMER_NORESTART;
}
static int emac_tso_config(struct emac_priv *priv)
{
struct emac_desc_ring * tx_ring = &priv->tx_ring;
u32 val = 0;
/* reset */
emac_wr_tso(priv, TSO_CONFIG, TSO_CONFIG_RST);
mdelay(1);
emac_wr_tso(priv, TSO_CONFIG, 0x0);
emac_wr_tso(priv, TSO_DMA_CONFIG, 0x2 << 8);
/* rx */
/* set the transmit base address */
val = (u32)(priv->rx_ring.desc_dma_addr);
emac_wr_tso(priv, TSO_RX_DESC_BA, val >> 1);
emac_wr_tso(priv, TSO_RX_AUTO_POLL_CNT, 0x0);
/* tx */
val = (u32)(priv->tx_ring.desc_dma_addr);
emac_wr_tso(priv, TSO_TX_DESC_BA, val >> 1);
priv->tso_hdr = dma_alloc_coherent(&priv->pdev->dev,
tx_ring->total_cnt * 0x80,
&priv->tso_hdr_addr,
GFP_KERNEL | __GFP_ZERO);
if (!priv->tso_hdr) {
pr_err("Memory allocation failed for tso_hdr\n");
return -ENOMEM;
}
val = (u32)(priv->tso_hdr_addr);
emac_wr_tso(priv, TSO_TX_HDR_BA, val >> 1);
emac_wr_tso(priv, TSO_TX_HDR_CTR, tx_ring->total_cnt);
emac_wr_tso(priv, TSO_TX_AUTO_POLL_CNT, 0x0);
/* enable tx/rx tso/coe */
emac_wr_tso(priv, TSO_CONFIG,
TSO_CONFIG_RX_EN | TSO_CONFIG_TX_EN | TSO_CONFIG_RX_CSUM_EN);
/* enable tx/rx/err interrupt */
emac_wr_tso(priv, TSO_ERR_INTR_ENA, 0xF0007);
emac_wr_tso(priv, TSO_AP_RX_INTR_ENA,
TSO_AP_RX_INTR_ENA_CSUM_DONE | TSO_AP_RX_INTR_ENA_CSUM_ERR);
#if 1
emac_wr_tso(priv, TSO_AP_TX_INTR_ENA,
TSO_AP_TX_INTR_ENA_TSO_DONE | TSO_AP_TX_INTR_ENA_CSUM_DONE);
#else
emac_wr_tso(priv, TSO_AP_TX_INTR_ENA, 0x0);
#endif
return 0;
}
/* Name emac_up
* Arguments priv : pointer to driver private data structure
* Return Status: 0 - Success; non-zero - Fail
* Description This function is called from emac_open and
* performs the things when net interface is about to up.
* It configues the Tx and Rx unit of the device and
* registers interrupt handler.
* It also starts one watchdog timer to monitor
* the net interface link status.
*/
int emac_up(struct emac_priv *priv)
{
struct net_device *ndev = priv->ndev;
int ret, val;
#if CLOSE_AIB_POWER_DOMAIN
void __iomem *aib_emac_io;
void __iomem *apbc_asfar;
u32 tmp;
#endif
#ifdef WAN_LAN_AUTO_ADAPT
u32 phy_id;
#endif
priv->hw_stats->tx_tso_pkts = 0;
priv->hw_stats->tx_tso_bytes = 0;
ret = emac_phy_connect(ndev);
if (ret) {
pr_err("%s phy_connet failed\n", __func__);
#if CLOSE_AIB_POWER_DOMAIN
printk("===> enter emac_close_aib_power_domain\n");
aib_emac_io = ioremap(AIB_GMAC_IO_REG, 4);
apbc_asfar = ioremap(APBC_ASFAR, 8);
writel(AKEY_ASFAR, apbc_asfar);
writel(AKEY_ASSAR, apbc_asfar + 4);
writel(0x81, aib_emac_io);
writel(AKEY_ASFAR, apbc_asfar);
writel(AKEY_ASSAR, apbc_asfar + 4);
tmp = readl(aib_emac_io);
iounmap(apbc_asfar);
iounmap(aib_emac_io);
printk("===> exit emac_close_aib_power_domain = 0x%x\n", tmp);
#endif
return ret;
}
if (!priv->en_suspend)
pm_stay_awake(&priv->pdev->dev);
pm_qos_update_request(&priv->pm_qos_req, priv->pm_qos);
clk_phase_set(priv, TX_PHASE);
clk_phase_set(priv, RX_PHASE);
/* init hardware */
emac_init_hw(priv);
emac_ptp_init(priv);
emac_set_mac_addr(priv, ndev->dev_addr);
emac_set_fc_source_addr(priv, ndev->dev_addr);
/* configure transmit unit */
emac_configure_tx(priv);
/* configure rx unit */
emac_configure_rx(priv);
/* allocate buffers for receive descriptors */
emac_alloc_rx_desc_buffers(priv);
if (ndev->phydev)
phy_start(ndev->phydev);
/* allocates interrupt resources and
* enables the interrupt line and IRQ handling
*/
ret = request_irq(priv->irq, emac_interrupt_handler,
IRQF_SHARED, ndev->name, ndev);
if (ret) {
pr_err("request_irq failed, ret=%d\n", ret);
goto request_irq_failed;
}
if (priv->irq_wakeup) {
ret = request_irq(priv->irq_wakeup, emac_wakeup_handler,
IRQF_SHARED, ndev->name, ndev);
if (ret) {
pr_err("request wakeup_irq failed, ret=%d\\n", ret);
goto request_wakeup_irq_failed;
}
}
if (priv->irq_tso) {
ret = request_irq(priv->irq_tso, emac_irq_tso,
IRQF_SHARED, "emac_tso", ndev);
if (ret) {
pr_err("request tso failed, ret=%d\\n", ret);
goto request_tso_irq_failed;
}
}
if (priv->fix_link)
emac_set_speed_duplex(priv);
clear_bit(EMAC_DOWN, &priv->state);
/* enable mac interrupt */
emac_wr(priv, MAC_INTERRUPT_ENABLE, 0x0000);
/* both rx tx */
val = MREGBIT_TRANSMIT_TRANSFER_DONE_INTR_ENABLE |
MREGBIT_RECEIVE_TRANSFER_DONE_INTR_ENABLE |
MREGBIT_RECEIVE_MISSED_FRAME_INTR_ENABLE;
#if 0
val |= MREGBIT_TRANSMIT_DMA_STOPPED_INTR_ENABLE |
MREGBIT_RECEIVE_DMA_STOPPED_INTR_ENABLE |
MREGBIT_RECEIVE_DES_UNAVAILABLE_INTR_ENABLE;
#endif
emac_wr(priv, DMA_INTERRUPT_ENABLE, val);
#ifdef CONFIG_ASR_EMAC_NAPI
napi_enable(&priv->rx_napi);
napi_enable(&priv->tx_napi);
#endif
if (priv->fix_link && !netif_carrier_ok(ndev))
netif_carrier_on(ndev);
#ifdef WAN_LAN_AUTO_ADAPT
phy_id = ndev->phydev->phy_id;
if(phy_id == IP175D_PHY_ID)
emac_sig_workq(CARRIER_UP_IP175D, 0);
else
emac_sig_workq(CARRIER_UP, 0);
#endif
hrtimer_init(&priv->tx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
priv->tx_timer.function = emac_tx_timer;
hrtimer_init(&priv->rx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
priv->rx_timer.function = emac_rx_timer;
if (priv->tso)
emac_tso_config(priv);
netif_tx_start_all_queues(ndev);
return 0;
request_tso_irq_failed:
if (priv->irq_wakeup)
free_irq(priv->irq_wakeup, ndev);
request_wakeup_irq_failed:
free_irq(priv->irq, ndev);
request_irq_failed:
if (ndev->phydev) {
phy_stop(ndev->phydev);
phy_disconnect(ndev->phydev);
}
return ret;
}
/* Name emac_down
* Arguments priv : pointer to driver private data structure
* Return Status: 0 - Success; non-zero - Fail
* Description This function is called from emac_close and
* performs the things when net interface is about to down.
* It frees the irq, removes the various timers.
* It sets the net interface off and
* resets the hardware. Cleans the Tx and Rx
* ring descriptor.
*/
int emac_down(struct emac_priv *priv)
{
struct net_device *ndev = priv->ndev;
//#LYNQ_MODFIY modify for task-1618 2025/6/24 start
struct pinctrl_state *sleep_pins = pinctrl_lookup_state(priv->pinctrl, "sleep");
//#LYNQ_MODFIY modify for task-1618 2025/6/24 end
#ifdef WAN_LAN_AUTO_ADAPT
u32 phy_id;
priv->dhcp = 0;
priv->vlan_port = -1;
priv->link = 0;
phy_id = ndev->phydev->phy_id;
if(priv->dhcp_delaywork){
cancel_delayed_work(&priv->dhcp_work);
priv->dhcp_delaywork = 0;
}
#endif
set_bit(EMAC_DOWN, &priv->state);
netif_tx_disable(ndev);
hrtimer_cancel(&priv->tx_timer);
hrtimer_cancel(&priv->rx_timer);
/* Stop and disconnect the PHY */
if (ndev->phydev) {
phy_stop(ndev->phydev);
phy_disconnect(ndev->phydev);
//#LYNQ_MODFIY modify for task-1618 2025/6/24 start
if (IS_ERR(priv->rgmii_pins))
printk("could not get rgmii-pins pinstate\n");
pinctrl_select_state(priv->pinctrl, sleep_pins);
//#LYNQ_MODFIY modify for task-1618 2025/6/24 end
}
if (!priv->fix_link) {
priv->duplex = DUPLEX_UNKNOWN;
priv->speed = SPEED_UNKNOWN;
}
#ifdef CONFIG_ASR_EMAC_NAPI
napi_disable(&priv->rx_napi);
napi_disable(&priv->tx_napi);
#endif
emac_wr(priv, MAC_INTERRUPT_ENABLE, 0x0000);
emac_wr(priv, DMA_INTERRUPT_ENABLE, 0x0000);
free_irq(priv->irq, ndev);
if (priv->irq_wakeup)
free_irq(priv->irq_wakeup, ndev);
emac_ptp_deinit(priv);
emac_reset_hw(priv);
netif_carrier_off(ndev);
#ifdef WAN_LAN_AUTO_ADAPT
if(phy_id == IP175D_PHY_ID)
emac_sig_workq(CARRIER_DOWN_IP175D, 0);
else
emac_sig_workq(CARRIER_DOWN, 0);
#endif
#ifdef CONFIG_ASR_EMAC_DDR_QOS
flush_work(&priv->qos_work);
pm_qos_update_request(&priv->clk_scaling.ddr_qos, PM_QOS_DEFAULT_VALUE);
#endif
pm_qos_update_request(&priv->pm_qos_req,
PM_QOS_CPUIDLE_BLOCK_DEFAULT_VALUE);
if (!priv->en_suspend)
pm_relax(&priv->pdev->dev);
if (priv->tso) {
dma_free_coherent(&priv->pdev->dev,
priv->tx_ring.total_cnt * 0x80,
priv->tso_hdr,
priv->tso_hdr_addr);
}
return 0;
}
/* Name emac_alloc_tx_resources
* Arguments priv : pointer to driver private data structure
* Return Status: 0 - Success; non-zero - Fail
* Description Allocates TX resources and getting virtual & physical address.
*/
int emac_alloc_tx_resources(struct emac_priv *priv)
{
struct emac_desc_ring *tx_ring = &priv->tx_ring;
struct platform_device *pdev = priv->pdev;
u32 size;
size = sizeof(struct emac_desc_buffer) * tx_ring->total_cnt;
/* allocate memory */
tx_ring->desc_buf = kzalloc(size, GFP_KERNEL);
if (!tx_ring->desc_buf) {
pr_err("Memory allocation failed for the Transmit descriptor buffer\n");
return -ENOMEM;
}
memset(tx_ring->desc_buf, 0, size);
tx_ring->total_size = tx_ring->total_cnt * sizeof(struct emac_tx_desc);
EMAC_ROUNDUP(tx_ring->total_size, 1024);
if (priv->sram_pool) {
tx_ring->desc_addr =
(void *)gen_pool_dma_alloc(
priv->sram_pool, tx_ring->total_size,
&tx_ring->desc_dma_addr);
tx_ring->in_sram = true;
}
if (!tx_ring->desc_addr) {
tx_ring->desc_addr = dma_alloc_coherent(&pdev->dev,
tx_ring->total_size,
&tx_ring->desc_dma_addr,
GFP_KERNEL | __GFP_ZERO);
if (!tx_ring->desc_addr) {
pr_err("Memory allocation failed for the Transmit descriptor ring\n");
kfree(tx_ring->desc_buf);
return -ENOMEM;
}
if (priv->sram_pool) {
pr_err("sram pool left size not enough, tx fallback\n");
tx_ring->in_sram = false;
}
}
memset(tx_ring->desc_addr, 0, tx_ring->total_size);
tx_ring->nxt_use = 0;
tx_ring->nxt_clean = 0;
return 0;
}
/* Name emac_alloc_rx_resources
* Arguments priv : pointer to driver private data structure
* Return Status: 0 - Success; non-zero - Fail
* Description Allocates RX resources and getting virtual & physical address.
*/
int emac_alloc_rx_resources(struct emac_priv *priv)
{
struct emac_desc_ring *rx_ring = &priv->rx_ring;
struct platform_device *pdev = priv->pdev;
u32 buf_len;
buf_len = sizeof(struct emac_desc_buffer) * rx_ring->total_cnt;
rx_ring->desc_buf = kzalloc(buf_len, GFP_KERNEL);
if (!rx_ring->desc_buf) {
pr_err("Memory allocation failed for the Receive descriptor buffer\n");
return -ENOMEM;
}
memset(rx_ring->desc_buf, 0, buf_len);
/* round up to nearest 4K */
rx_ring->total_size = rx_ring->total_cnt * sizeof(struct emac_rx_desc);
EMAC_ROUNDUP(rx_ring->total_size, 1024);
if (priv->sram_pool) {
rx_ring->desc_addr =
(void *)gen_pool_dma_alloc(
priv->sram_pool, rx_ring->total_size,
&rx_ring->desc_dma_addr);
rx_ring->in_sram = true;
}
if (!rx_ring->desc_addr) {
rx_ring->desc_addr = dma_alloc_coherent(&pdev->dev,
rx_ring->total_size,
&rx_ring->desc_dma_addr,
GFP_KERNEL | __GFP_ZERO);
if (!rx_ring->desc_addr) {
pr_err("Memory allocation failed for the Receive descriptor ring\n");
kfree(rx_ring->desc_buf);
return -ENOMEM;
}
if (priv->sram_pool) {
pr_err("sram pool left size not enough, rx fallback\n");
rx_ring->in_sram = false;
}
}
memset(rx_ring->desc_addr, 0, rx_ring->total_size);
rx_ring->nxt_use = 0;
rx_ring->nxt_clean = 0;
return 0;
}
/* Name emac_free_tx_resources
* Arguments priv : pointer to driver private data structure
* Return none
* Description Frees the Tx resources allocated
*/
void emac_free_tx_resources(struct emac_priv *priv)
{
emac_clean_tx_desc_ring(priv);
kfree(priv->tx_ring.desc_buf);
priv->tx_ring.desc_buf = NULL;
if (priv->tx_ring.in_sram)
gen_pool_free(priv->sram_pool,
(unsigned long) priv->tx_ring.desc_addr,
priv->tx_ring.total_size);
else
dma_free_coherent(&priv->pdev->dev, priv->tx_ring.total_size,
priv->tx_ring.desc_addr,
priv->tx_ring.desc_dma_addr);
priv->tx_ring.desc_addr = NULL;
}
/* Name emac_free_rx_resources
* Arguments priv : pointer to driver private data structure
* Return none
* Description Frees the Rx resources allocated
*/
void emac_free_rx_resources(struct emac_priv *priv)
{
emac_clean_rx_desc_ring(priv);
kfree(priv->rx_ring.desc_buf);
priv->rx_ring.desc_buf = NULL;
if (priv->rx_ring.in_sram)
gen_pool_free(priv->sram_pool,
(unsigned long) priv->rx_ring.desc_addr,
priv->rx_ring.total_size);
else
dma_free_coherent(&priv->pdev->dev, priv->rx_ring.total_size,
priv->rx_ring.desc_addr,
priv->rx_ring.desc_dma_addr);
priv->rx_ring.desc_addr = NULL;
}
/* Name emac_open
* Arguments pstNetdev : pointer to net_device structure
* Return Status: 0 - Success; non-zero - Fail
* Description This function is called when net interface is made up.
* Setting up Tx and Rx
* resources and making the interface up.
*/
static int emac_open(struct net_device *ndev)
{
struct emac_priv *priv = netdev_priv(ndev);
int ret;
ret = emac_alloc_tx_resources(priv);
if (ret) {
pr_err("Error in setting up the Tx resources\n");
goto emac_alloc_tx_resource_fail;
}
ret = emac_alloc_rx_resources(priv);
if (ret) {
pr_err("Error in setting up the Rx resources\n");
goto emac_alloc_rx_resource_fail;
}
ret = emac_up(priv);
if (ret) {
pr_err("Error in making the net intrface up\n");
goto emac_up_fail;
}
return 0;
emac_up_fail:
emac_free_rx_resources(priv);
emac_alloc_rx_resource_fail:
emac_free_tx_resources(priv);
emac_alloc_tx_resource_fail:
emac_reset_hw(priv);
return ret;
}
/* Name emac_close
* Arguments pstNetdev : pointer to net_device structure
* Return Status: 0 - Success; non-zero - Fail
* Description This function is called when net interface is made down.
* It calls the appropriate functions to
* free Tx and Rx resources.
*/
static int emac_close(struct net_device *ndev)
{
struct emac_priv *priv = netdev_priv(ndev);
emac_down(priv);
emac_free_tx_resources(priv);
emac_free_rx_resources(priv);
return 0;
}
/* Name emac_tx_clean_desc
* Arguments priv : pointer to driver private data structure
* Return 1: Cleaned; 0:Failed
* Description
*/
#ifdef CONFIG_ASR_EMAC_NAPI
static int emac_tx_clean_desc(struct emac_priv *priv, int budget)
#else
static int emac_tx_clean_desc(struct emac_priv *priv)
#endif
{
struct emac_desc_ring *tx_ring;
struct emac_tx_desc *tx_desc, *end_desc;
struct emac_desc_buffer *tx_buf;
struct net_device *ndev = priv->ndev;
u32 i, u32LastIndex;
u8 u8Cleaned;
unsigned int count = 0;
tx_ring = &priv->tx_ring;
i = tx_ring->nxt_clean;
do {
if (i == tx_ring->nxt_use)
break;
u32LastIndex = tx_ring->desc_buf[i].nxt_watch;
end_desc = emac_get_tx_desc(priv, u32LastIndex);
if (end_desc->OWN == 1 ||
(priv->tso && (end_desc->tso || end_desc->coe)))
break;
u8Cleaned = false;
for ( ; !u8Cleaned; count++) {
tx_desc = emac_get_tx_desc(priv, i);
tx_buf = &tx_ring->desc_buf[i];
emac_get_tx_hwtstamp(priv, tx_buf->skb);
/* own bit will be reset to 0 by dma
* once packet is transmitted
*/
if (tx_buf->dma_addr) {
dma_unmap_page(&priv->pdev->dev,
tx_buf->dma_addr,
tx_buf->dma_len,
DMA_TO_DEVICE);
tx_buf->dma_addr = 0;
}
if (tx_buf->skb) {
dev_kfree_skb_any(tx_buf->skb);
tx_buf->skb = NULL;
}
if (tx_buf->buff_addr)
tx_buf->buff_addr = NULL;
memset(tx_desc, 0, sizeof(struct emac_tx_desc));
u8Cleaned = (i == u32LastIndex);
if (++i == tx_ring->total_cnt)
i = 0;
}
#ifdef CONFIG_ASR_EMAC_NAPI
if (count >= budget) {
count = budget;
break;
}
#endif
} while (1);
tx_ring->nxt_clean = i;
#ifndef CONFIG_ASR_EMAC_NAPI
spin_lock(&priv->spTxLock);
#endif
if (unlikely(count && netif_queue_stopped(ndev) &&
netif_carrier_ok(ndev) &&
EMAC_DESC_UNUSED(tx_ring) >= EMAC_TX_WAKE_THRESHOLD))
netif_wake_queue(ndev);
#ifndef CONFIG_ASR_EMAC_NAPI
spin_unlock(&priv->spTxLock);
#endif
return count;
}
static int emac_rx_frame_status(struct emac_priv *priv, struct emac_rx_desc *dsc)
{
/* if last descritpor isn't set, so we drop it*/
if (!dsc->LastDescriptor) {
netdev_dbg(priv->ndev, "rx LD bit isn't set, drop it.\n");
return frame_discard;
}
/*
* A Frame that is less than 64-bytes (from DA thru the FCS field)
* is considered as Runt Frame.
* Most of the Runt Frames happen because of collisions.
*/
if (dsc->ApplicationStatus & EMAC_RX_FRAME_RUNT) {
netdev_dbg(priv->ndev, "rx frame less than 64.\n");
return frame_discard;
}
/*
* When the frame fails the CRC check,
* the frame is assumed to have the CRC error
*/
if (dsc->ApplicationStatus & EMAC_RX_FRAME_CRC_ERR) {
netdev_dbg(priv->ndev, "rx frame crc error\n");
return frame_discard;
}
if (priv->tso && dsc->csum_res == EMAC_CSUM_FAIL) {
netdev_dbg(priv->ndev, "COE: rx frame checksum error\n");
return frame_discard;
}
/*
* When the length of the frame exceeds
* the Programmed Max Frame Length
*/
if (dsc->ApplicationStatus & EMAC_RX_FRAME_MAX_LEN_ERR) {
netdev_dbg(priv->ndev, "rx frame too long\n");
return frame_discard;
}
/*
* frame reception is truncated at that point and
* frame is considered to have Jabber Error
*/
if (dsc->ApplicationStatus & EMAC_RX_FRAME_JABBER_ERR) {
netdev_dbg(priv->ndev, "rx frame has been truncated\n");
return frame_discard;
}
/* this bit is only for 802.3 Type Frames */
if (dsc->ApplicationStatus & EMAC_RX_FRAME_LENGTH_ERR) {
netdev_dbg(priv->ndev, "rx frame length err for 802.3\n");
return frame_discard;
}
if (dsc->FramePacketLength <= ETHERNET_FCS_SIZE ||
dsc->FramePacketLength > EMAC_RX_BUFFER_2048) {
netdev_dbg(priv->ndev, "rx frame len too small or too long\n");
return frame_discard;
}
return frame_ok;
}
/* Name emac_rx_clean_desc
* Arguments priv : pointer to driver private data structure
* Return 1: Cleaned; 0:Failed
* Description
*/
#ifdef CONFIG_ASR_EMAC_NAPI
static int emac_rx_clean_desc(struct emac_priv *priv, int budget)
#else
static int emac_rx_clean_desc(struct emac_priv *priv)
#endif
{
struct emac_desc_ring *rx_ring;
struct emac_desc_buffer *rx_buf;
struct net_device *ndev = priv->ndev;
struct emac_rx_desc *rx_desc;
struct sk_buff *skb = NULL;
int status;
#ifdef CONFIG_ASR_EMAC_NAPI
u32 receive_packet = 0;
#endif
u32 i;
u32 u32Len;
u32 u32Size;
u8 *pu8Data;
#ifdef WAN_LAN_AUTO_ADAPT
int port = -1, vlan = -1;
struct vlan_hdr *vhdr;
struct iphdr *iph = NULL;
struct udphdr *udph = NULL;
#endif
rx_ring = &priv->rx_ring;
i = rx_ring->nxt_clean;
rx_desc = emac_get_rx_desc(priv, i);
u32Size = 0;
if (priv->pause.tx_pause && !priv->pause.fc_auto)
emac_check_ring_and_send_pause(priv);
while (rx_desc->OWN == 0) {
if (priv->tso && !rx_desc->csum_done)
break;
if (skb_queue_len(&priv->rx_skb) > priv->rx_ring.total_cnt)
break;
rx_buf = &rx_ring->desc_buf[i];
if (!rx_buf->skb)
break;
emac_unmap_single(&priv->pdev->dev, rx_buf->dma_addr,
rx_buf->dma_len, DMA_FROM_DEVICE);
status = emac_rx_frame_status(priv, rx_desc);
if (unlikely(status == frame_discard)) {
ndev->stats.rx_dropped++;
dev_kfree_skb_irq(rx_buf->skb);
rx_buf->skb = NULL;
} else {
skb = rx_buf->skb;
u32Len = rx_desc->FramePacketLength - ETHERNET_FCS_SIZE;
pu8Data = skb_put(skb, u32Len);
#ifndef CONFIG_ASR_EMAC_RX_NO_COPY
memcpy(pu8Data, (u8 *)rx_buf->buff_addr, u32Len);
#endif
skb->dev = ndev;
ndev->hard_header_len = ETH_HLEN;
emac_get_rx_hwtstamp(priv, rx_desc, skb);
skb->protocol = eth_type_trans(skb, ndev);
if (priv->tso)
skb->ip_summed = CHECKSUM_UNNECESSARY;
else
skb->ip_summed = CHECKSUM_NONE;
#ifdef WAN_LAN_AUTO_ADAPT
{/* Special tag format: DA-SA-0x81-xx-data.
Bit 7-3 Packet Information
- bit 4: Reserved
- bit 3: Reserved
- bit 2: Miss address table
- bit 1: Security violation
- bit 0: VLAN violation
Bit 2-0 Ingress Port number
- b000: Disabled
- b001: Port 0
- b010: Port 1
- b011: Port 2
- b100: Port 3
- b101: Port 4
- Other: Reserved */
if(ntohs(skb->protocol)>>8 == 0x81) {
port = ntohs(skb->protocol) & 0x7;
if(port > 0 && port <= 0x5) {
skb->protocol = htons(ETH_P_8021Q);
port = port - 1;
}
}
if (skb->protocol == htons(ETH_P_8021Q)) {
vhdr = (struct vlan_hdr *) skb->data;
vlan = ntohs(vhdr->h_vlan_TCI);
iph = (struct iphdr *)(skb->data + VLAN_HLEN);
} else if (skb->protocol == htons(ETH_P_IP))
iph = (struct iphdr *)skb->data;
if (iph && iph->protocol == IPPROTO_UDP) {
udph = (struct udphdr *)((unsigned char *)iph + (iph->ihl<<2));
if ((htons(udph->dest) == 68 && htons(udph->source) == 67)) {
u8 *udp_data = (u8 *)((u8 *)udph + sizeof(struct udphdr));
u8 dhcp_type = *(udp_data + 242);
if ((DHCP_ACK == dhcp_type || DHCP_OFFER == dhcp_type)
&& (DHCP_SEND_REQ == priv->dhcp)) {
priv->dhcp = DHCP_REC_RESP;
if (ndev->phydev->phy_id == IP175D_PHY_ID)
priv->vlan_port = port;
else
priv->vlan_port = -1;
}
}
}
}
#endif
skb_queue_tail(&priv->rx_skb, skb);
rx_buf->skb = NULL;
}
if (++i == rx_ring->total_cnt)
i = 0;
rx_desc = emac_get_rx_desc(priv, i);
/* restart RX COE */
if (priv->tso)
emac_wr_tso(priv, TSO_RX_POLL_DEMAND, 0xFF);
}
rx_ring->nxt_clean = i;
emac_alloc_rx_desc_buffers(priv);
/*
* Since netif_rx may consume too much time, put this after
* emac_alloc_rx_desc_buffers so that RX DMA desc refill ASAP,
* reduce packet loss probability.
*/
while ((skb = skb_dequeue(&priv->rx_skb))) {
ndev->stats.rx_packets++;
ndev->stats.rx_bytes += skb->len;
#ifdef CONFIG_ASR_EMAC_NAPI
napi_gro_receive(&priv->rx_napi, skb);
#else
netif_rx(skb);
#endif
#ifdef CONFIG_ASR_EMAC_NAPI
receive_packet++;
if (receive_packet >= budget)
break;
#endif
}
#ifdef CONFIG_ASR_EMAC_DDR_QOS
emac_ddr_clk_scaling(priv);
#endif
#ifdef CONFIG_ASR_EMAC_NAPI
return receive_packet;
#else
return 0;
#endif
}
/* Name emac_alloc_rx_desc_buffers
* Arguments priv : pointer to driver private data structure
* Return 1: Cleaned; 0:Failed
* Description
*/
static void emac_alloc_rx_desc_buffers(struct emac_priv *priv)
{
struct net_device *ndev = priv->ndev;
struct emac_desc_ring *rx_ring = &priv->rx_ring;
struct emac_desc_buffer *rx_buf;
struct sk_buff *skb;
struct emac_rx_desc *rx_desc;
u32 i;
#ifndef CONFIG_ASR_EMAC_RX_NO_COPY
void *buff;
#endif
u32 buff_len;
int fail_cnt = 0;
i = rx_ring->nxt_use;
rx_buf = &rx_ring->desc_buf[i];
buff_len = priv->u32RxBufferLen;
while (!rx_buf->skb) {
skb = emac_skbrb_alloc_skb(EMAC_SKBRB_SLOT_SIZE);
if (!skb) {
if (priv->rx_ring.total_cnt == EMAC_RX_RING_SIZE)
skb = dev_alloc_skb(EMAC_SKBRB_SLOT_SIZE);
if (!skb) {
fail_cnt++;
pr_warn_ratelimited("emac sk_buff allocation failed\n");
break;
}
}
/* make buffer alignment */
skb_reserve(skb, NET_IP_ALIGN + EMAC_EXTRA_ROOM);
skb->dev = ndev;
#ifdef CONFIG_ASR_EMAC_RX_NO_COPY
rx_buf->buff_addr = skb->data;
#else
if (!rx_buf->buff_addr) {
buff = kmalloc(buff_len, GFP_ATOMIC | GFP_DMA);
if (!buff) {
pr_err("kmalloc failed\n");
dev_kfree_skb(skb);
break;
}
rx_buf->buff_addr = buff;
}
#endif
rx_buf->skb = skb;
rx_buf->dma_len = buff_len;
rx_buf->dma_addr = emac_map_single(&priv->pdev->dev,
rx_buf->buff_addr,
buff_len,
DMA_FROM_DEVICE);
rx_desc = emac_get_rx_desc(priv, i);
rx_desc->BufferAddr1 = rx_buf->dma_addr;
rx_desc->BufferSize1 = rx_buf->dma_len;
rx_desc->rx_timestamp = 0;
rx_desc->ptp_pkt = 0;
rx_desc->FirstDescriptor = 0;
rx_desc->LastDescriptor = 0;
rx_desc->FramePacketLength = 0;
rx_desc->ApplicationStatus = 0;
if (++i == rx_ring->total_cnt) {
rx_desc->EndRing = 1;
i = 0;
}
wmb();
rx_desc->OWN = 1;
if (priv->tso)
rx_desc->csum_done = 0;
rx_buf = &rx_ring->desc_buf[i];
}
rx_ring->nxt_use = i;
if (fail_cnt)
priv->refill = 1;
else
priv->refill = 0;
emac_dma_start_receive(priv);
}
#ifdef CONFIG_ASR_EMAC_NAPI
static int emac_rx_poll(struct napi_struct *napi, int budget)
{
struct emac_priv *priv = container_of(napi, struct emac_priv, rx_napi);
int work_done;
work_done = emac_rx_clean_desc(priv, budget);
if (work_done < budget && napi_complete_done(napi, work_done)) {
unsigned long flags;
spin_lock_irqsave(&priv->intr_lock, flags);
emac_enable_interrupt(priv, 0);
spin_unlock_irqrestore(&priv->intr_lock, flags);
if (priv->refill)
emac_rx_timer_arm(priv);
}
return work_done;
}
static int emac_tx_poll(struct napi_struct *napi, int budget)
{
struct emac_priv *priv = container_of(napi, struct emac_priv, tx_napi);
int work_done;
work_done = emac_tx_clean_desc(priv, budget);
if (work_done < budget && napi_complete_done(napi, work_done)) {
unsigned long flags;
spin_lock_irqsave(&priv->intr_lock, flags);
emac_enable_interrupt(priv, 1);
spin_unlock_irqrestore(&priv->intr_lock, flags);
}
return work_done;
}
#endif
/* Name emac_tx_mem_map
* Arguments priv : pointer to driver private data structure
* pstSkb : pointer to sk_buff structure passed by upper layer
* max_tx_len : max data len per descriptor
* frag_num : number of fragments in the packet
* Return number of descriptors needed for transmitting packet
* Description
*/
static int emac_tx_mem_map(struct emac_priv *priv, struct sk_buff *skb,
u32 max_tx_len, u32 frag_num, int ioc)
{
struct emac_desc_ring *tx_ring;
struct emac_desc_buffer *tx_buf;
struct emac_tx_desc *tx_desc, *first_desc;
u32 skb_len;
u32 u32Offset, u32Size, i;
u32 use_desc_cnt;
u32 f;
void *pvPtr;
u32 cur_desc_addr;
u32 cur_desc_idx;
u8 do_tx_timestamp = 0;
bool use_buf2 = 0;
u32Offset = 0;
use_desc_cnt = 0;
skb_tx_timestamp(skb);
if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
priv->hwts_tx_en)) {
/* declare that device is doing timestamping */
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
do_tx_timestamp = 1;
}
tx_ring = &priv->tx_ring;
skb_len = skb->len - skb->data_len;
i = cur_desc_idx = tx_ring->nxt_use;
cur_desc_addr = emac_rd(priv, DMA_TRANSMIT_BASE_ADDRESS);
while (skb_len > 0) {
u32Size = min(skb_len, max_tx_len);
skb_len -= u32Size;
tx_buf = &tx_ring->desc_buf[i];
tx_buf->dma_len = u32Size;
pvPtr = skb->data + u32Offset;
tx_buf->dma_addr = emac_map_single(&priv->pdev->dev, pvPtr,
u32Size, DMA_TO_DEVICE);
tx_buf->buff_addr = pvPtr;
tx_buf->ulTimeStamp = jiffies;
tx_desc = emac_get_tx_desc(priv, i);
if (use_buf2) {
tx_desc->BufferAddr2 = tx_buf->dma_addr;
tx_desc->BufferSize2 = tx_buf->dma_len;
i++;
use_buf2 = 0;
} else {
memset(tx_desc, 0, sizeof(struct emac_tx_desc));
tx_desc->BufferAddr1 = tx_buf->dma_addr;
tx_desc->BufferSize1 = tx_buf->dma_len;
use_buf2 = 1;
}
if (use_desc_cnt == 0) {
first_desc = tx_desc;
tx_desc->FirstSegment = 1;
if (do_tx_timestamp)
tx_desc->tx_timestamp = 1;
}
if (skb_len == 0 && frag_num == 0) {
tx_desc->LastSegment = 1;
tx_desc->InterruptOnCompletion = ioc ? 1 : 0;
}
if (!use_buf2 && i == tx_ring->total_cnt) {
tx_desc->EndRing = 1;
i = 0;
}
/* trigger first desc OWN bit later */
use_desc_cnt++;
if (use_desc_cnt > 2)
tx_desc->OWN = 1;
u32Offset += u32Size;
}
/* if the data is fragmented */
for (f = 0; f < frag_num; f++) {
skb_frag_t *frag;
frag = &(skb_shinfo(skb)->frags[f]);
skb_len = skb_frag_size(frag);
u32Offset = skb_frag_off(frag);
while (skb_len) {
u32Size = min(skb_len, max_tx_len);
skb_len -= u32Size;
tx_buf = &tx_ring->desc_buf[i];
tx_buf->dma_len = u32Size;
tx_buf->dma_addr =
dma_map_page(&priv->pdev->dev,
skb_frag_page(frag),
u32Offset,
u32Size,
DMA_TO_DEVICE);
tx_buf->ulTimeStamp = jiffies;
tx_desc = emac_get_tx_desc(priv, i);
if (use_buf2) {
tx_desc->BufferAddr2 = tx_buf->dma_addr;
tx_desc->BufferSize2 = tx_buf->dma_len;
i++;
use_buf2 = 0;
} else {
memset(tx_desc, 0, sizeof(struct emac_tx_desc));
tx_desc->BufferAddr1 = tx_buf->dma_addr;
tx_desc->BufferSize1 = tx_buf->dma_len;
use_buf2 = 1;
}
if (skb_len == 0 && f == (frag_num - 1)) {
tx_desc->LastSegment = 1;
tx_desc->InterruptOnCompletion = ioc ? 1 : 0;
}
if (!use_buf2 && i == tx_ring->total_cnt) {
tx_desc->EndRing = 1;
i = 0;
}
/* trigger first desc OWN bit later */
use_desc_cnt++;
if (use_desc_cnt > 2)
tx_desc->OWN = 1;
u32Offset += u32Size;
}
}
if (use_buf2 && ++i == tx_ring->total_cnt) {
tx_desc->EndRing = 1;
i = 0;
}
tx_ring->desc_buf[cur_desc_idx].skb = skb;
tx_ring->desc_buf[cur_desc_idx].nxt_watch =
(i == 0 ? tx_ring->total_cnt : 0) + i - 1;
wmb();
first_desc->OWN = 1;
emac_dma_start_transmit(priv);
tx_ring->nxt_use = i;
return use_desc_cnt;
}
static int emac_prepare_tso_desc(struct emac_priv *priv, int idx,
bool tso, bool coe,
u32 addr, int payload, u8 hlen, int mss,
bool fst, bool last, bool ioc, bool ts,
u32 *cnt)
{
struct emac_desc_ring *tx_ring = &priv->tx_ring;
struct emac_tx_desc *pdesc;
pdesc = emac_get_tx_desc(priv, idx);
if (tso) {
if (fst && hlen) {
emac_set_buf1_addr_len(pdesc, addr, 0);
payload -= hlen;
addr += hlen;
}
emac_set_buf2_addr_len(pdesc, addr, payload);
} else {
emac_set_buf1_addr_len(pdesc, addr, payload);
}
if (fst) {
emac_tx_desc_set_fd(pdesc);
} else {
if (tso)
emac_tx_desc_set_offload(pdesc, 1, 1, 1);
else if (coe)
emac_tx_desc_set_offload(pdesc, 0, 1, 0);
else
emac_tx_desc_set_offload(pdesc, 1, 0, 0);
}
if (ts)
emac_tx_desc_set_ts(pdesc);
if (last) {
/* last segment */
emac_tx_desc_set_ld(pdesc);
if (ioc)
emac_tx_desc_set_ioc(pdesc);
}
print_desc((void *)pdesc, 16);
if (payload <= 0)
return idx;
do {
(*cnt)++;
if (++idx == tx_ring->total_cnt) {
emac_tx_desc_set_ring_end(pdesc);
idx = 0;
}
if (!tso)
break;
payload -= mss;
if (payload <= 0)
break;
pdesc = emac_get_tx_desc(priv, idx);
emac_tx_desc_set_offload(pdesc, 1, 1, 0);
print_desc((void *)pdesc, 16);
} while (1);
return idx;
}
static int emac_tso_xmit(struct sk_buff *skb, struct net_device *ndev,
bool tso, bool coe)
{
struct emac_priv *priv = netdev_priv(ndev);
struct emac_desc_ring *tx_ring = &priv->tx_ring;
struct emac_desc_buffer *tx_buf;
struct emac_tx_desc *pdesc;
skb_frag_t *frag;
u32 desc_cnt, frag_num, f, mss, fst;
u32 offset, i;
u8 hlen;
int skb_len, payload;
void *pbuf;
int ioc;
u8 timestamp = 0;
frag_num = skb_shinfo(skb)->nr_frags;
skb_len = skb->len - skb->data_len;
if (tso) {
hlen = skb_transport_offset(skb) + tcp_hdrlen(skb);
mss = skb_shinfo(skb)->gso_size;
desc_cnt = (skb_len / mss) + 1;
for (f = 0; f < frag_num; f++) {
frag = &skb_shinfo(skb)->frags[f];
desc_cnt += (skb_frag_size(frag) / mss) + 1;
}
} else {
hlen = 0;
mss = 0;
desc_cnt = EMAC_TXD_COUNT(skb_len, MAX_DATA_PWR_TX_DES);
for (i = 0; i < frag_num; i++) {
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
desc_cnt += EMAC_TXD_COUNT(skb_frag_size(frag),
MAX_DATA_PWR_TX_DES);
}
}
emac_print("%s: skb=0x%x, skb->len=%d skb_len=%d mss=%d frag_num=%d hlen=%d\n",
__func__, (unsigned)skb, skb->len, skb_len, mss, frag_num, hlen);
#ifdef EMAC_DEBUG
print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 32, 1, skb->data, skb_len, 0);
#endif
/* disable hard interrupt on local CPUs */
#ifndef CONFIG_ASR_EMAC_NAPI
local_irq_save(ulFlags);
#endif
if (!spin_trylock(&priv->spTxLock)) {
pr_err("Collision detected\n");
#ifndef CONFIG_ASR_EMAC_NAPI
local_irq_restore(ulFlags);
#endif
return NETDEV_TX_BUSY;
}
/* check whether sufficient free descriptors are there */
if (EMAC_DESC_UNUSED(tx_ring) < (desc_cnt + 2)) {
pr_err_ratelimited("TSO Descriptors are not free\n");
netif_stop_queue(ndev);
#ifndef CONFIG_ASR_EMAC_NAPI
spin_unlock_irqrestore(&priv->spTxLock, ulFlags);
#else
spin_unlock(&priv->spTxLock);
#endif
return NETDEV_TX_BUSY;
}
priv->tx_count_frames += desc_cnt;
if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
priv->hwts_tx_en))
ioc = 1;
else if (priv->tx_count_frames >= EMAC_TX_FRAMES)
ioc = 1;
else
ioc = 0;
if (ioc)
priv->tx_count_frames = 0;
skb_tx_timestamp(skb);
if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
priv->hwts_tx_en)) {
/* declare that device is doing timestamping */
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
timestamp = 1;
}
offset = 0;
desc_cnt = 0;
i = fst = tx_ring->nxt_use;
do {
payload = min(skb_len, TSO_MAX_SEG_SIZE);
tx_buf = &tx_ring->desc_buf[i];
tx_buf->dma_len = payload;
pbuf = skb->data + offset;
tx_buf->dma_addr = emac_map_single(&priv->pdev->dev, pbuf,
payload, DMA_TO_DEVICE);
tx_buf->buff_addr = pbuf;
tx_buf->ulTimeStamp = jiffies;
skb_len -= payload;
offset += payload;
i = emac_prepare_tso_desc(priv, i, tso, coe,
tx_buf->dma_addr, payload, hlen, mss,
(i == fst), (skb_len == 0 && frag_num == 0),
ioc, timestamp, &desc_cnt);
} while (skb_len > 0);
/* if the data is fragmented */
for (f = 0; f < frag_num; f++) {
frag = &(skb_shinfo(skb)->frags[f]);
skb_len = skb_frag_size(frag);
offset = skb_frag_off(frag);
emac_print("%s: frag %d len=%d\n", __func__, f, skb_len);
#ifdef EMAC_DEBUG
{
u8 *vaddr;
vaddr = kmap_atomic(skb_frag_page(frag));
print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET,
32, 1, vaddr + offset, skb_len, 0);
kunmap_atomic(vaddr);
}
#endif
do {
payload = min(skb_len, TSO_MAX_SEG_SIZE);
tx_buf = &tx_ring->desc_buf[i];
tx_buf->dma_len = payload;
//pbuf = skb->data + offset;
tx_buf->dma_addr = dma_map_page(&priv->pdev->dev,
skb_frag_page(frag),
offset, payload,
DMA_TO_DEVICE);
tx_buf->ulTimeStamp = jiffies;
skb_len -= payload;
offset += payload;
i = emac_prepare_tso_desc(priv, i, tso, coe,
tx_buf->dma_addr, payload, 0, mss,
(i == fst),
(skb_len == 0 && f == (frag_num - 1)),
ioc, timestamp, &desc_cnt);
} while (skb_len > 0);
}
tx_ring->desc_buf[fst].skb = skb;
tx_ring->desc_buf[fst].nxt_watch =
(i == 0 ? tx_ring->total_cnt : 0) + i - 1;
wmb();
/* set first descriptor for this packet */
pdesc = emac_get_tx_desc(priv, fst);
emac_tx_update_fst_desc(pdesc, hlen, mss, tso, coe);
print_desc((void *)pdesc, 16);
tx_ring->nxt_use = i;
ndev->stats.tx_packets++;
ndev->stats.tx_bytes += skb->len;
if (tso) {
priv->hw_stats->tx_tso_pkts++;
priv->hw_stats->tx_tso_bytes += skb->len;
}
emac_wr_tso(priv, TSO_TX_POLL_DEMAND, 0xFF);
/* Make sure there is space in the ring for the next send. */
if (EMAC_DESC_UNUSED(tx_ring) < (MAX_SKB_FRAGS + 2)) {
pr_debug_ratelimited("TSO Descriptors not enough, stop\n");
netif_stop_queue(ndev);
}
#ifndef CONFIG_ASR_EMAC_NAPI
spin_unlock_irqrestore(&priv->spTxLock, ulFlags);
#else
spin_unlock(&priv->spTxLock);
#endif
#ifdef CONFIG_ASR_EMAC_DDR_QOS
emac_ddr_clk_scaling(priv);
#endif
if (!tso && !coe)
emac_tx_timer_arm(priv);
return NETDEV_TX_OK;
}
/* Name emac_start_xmit
* Arguments pstSkb : pointer to sk_buff structure passed by upper layer
* pstNetdev : pointer to net_device structure
* Return Status: 0 - Success; non-zero - Fail
* Description This function is called by upper layer to
* handover the Tx packet to the driver
* for sending it to the device.
* Currently this is doing nothing but
* simply to simulate the tx packet handling.
*/
static int emac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct emac_priv *priv = netdev_priv(ndev);
int ioc;
u32 frag_num;
u32 skb_len;
u32 tx_des_cnt = 0;
u32 i;
#ifndef CONFIG_ASR_EMAC_NAPI
unsigned long ulFlags;
#endif
#ifdef WAN_LAN_AUTO_ADAPT
int vlan = 0;
struct iphdr *iph = NULL;
struct udphdr *udph = NULL;
struct vlan_hdr *vhdr;
{ struct ethhdr *myeth = (struct ethhdr *)skb->data;
if (myeth->h_proto == htons(ETH_P_8021Q)) {
vhdr = (struct vlan_hdr *)((u8 *)myeth + sizeof(struct ethhdr));
vlan = ntohs(vhdr->h_vlan_TCI);
iph = (struct iphdr *)((u8 *)myeth + sizeof(struct ethhdr) + VLAN_HLEN);
}
else if (myeth->h_proto == htons(ETH_P_IP))
iph = (struct iphdr *)((u8 *)myeth + sizeof(struct ethhdr));
if (iph && iph->protocol == IPPROTO_UDP) {
udph = (struct udphdr *)((unsigned char *)iph + (iph->ihl<<2));
if ((htons(udph->dest) == 67 && htons(udph->source) == 68)) {
u8 *udp_data = (u8 *)((u8 *)udph + sizeof(struct udphdr));
u8 dhcp_type = *(udp_data + 242);
if ((DHCP_DISCOVER == dhcp_type || DHCP_REQUEST == dhcp_type)
&& (0 == priv->dhcp)) {
priv->dhcp = DHCP_SEND_REQ;
if (ndev->phydev->phy_id == IP175D_PHY_ID)
priv->vlan_port = vlan;
else
priv->vlan_port = -1;
}
}
}
}
#endif
/* pstSkb->len: is the full length of the data in the packet
* pstSkb->data_len: the number of bytes in skb fragments
* u16Len: length of the first fragment
*/
skb_len = skb->len - skb->data_len;
if (skb->len <= 0) {
pr_err("Packet length is zero\n");
dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
if (priv->tso) {
bool tso = false, coe = false;
if (skb_is_gso(skb) &&
(skb_shinfo(skb)->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))) {
tso = true;
coe = true;
} else if (skb->ip_summed == CHECKSUM_PARTIAL) {
coe = true;
}
/* WR: COE need skb->data to be 2 bytes alinged */
if (coe && !IS_ALIGNED((unsigned long)skb->data, 2))
pskb_expand_head(skb, 1, 0, GFP_ATOMIC);
return emac_tso_xmit(skb, ndev, tso, coe);
}
/* increment the count if len exceeds MAX_DATA_LEN_TX_DES */
tx_des_cnt += EMAC_TXD_COUNT(skb_len, MAX_DATA_PWR_TX_DES);
frag_num = skb_shinfo(skb)->nr_frags;
for (i = 0; i < frag_num; i++) {
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
tx_des_cnt += EMAC_TXD_COUNT(skb_frag_size(frag),
MAX_DATA_PWR_TX_DES);
}
/* disable hard interrupt on local CPUs */
#ifndef CONFIG_ASR_EMAC_NAPI
local_irq_save(ulFlags);
#endif
if (!spin_trylock(&priv->spTxLock)) {
pr_err("Collision detected\n");
#ifndef CONFIG_ASR_EMAC_NAPI
local_irq_restore(ulFlags);
#endif
return NETDEV_TX_BUSY;
}
/* check whether sufficient free descriptors are there */
if (EMAC_DESC_UNUSED(&priv->tx_ring) < (tx_des_cnt + 2)) {
pr_err_ratelimited("Descriptors are not free\n");
netif_stop_queue(ndev);
#ifndef CONFIG_ASR_EMAC_NAPI
spin_unlock_irqrestore(&priv->spTxLock, ulFlags);
#else
spin_unlock(&priv->spTxLock);
#endif
return NETDEV_TX_BUSY;
}
priv->tx_count_frames += frag_num + 1;
if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
priv->hwts_tx_en))
ioc = 1;
else if (priv->tx_count_frames >= EMAC_TX_FRAMES)
ioc = 1;
else
ioc = 0;
if (ioc)
priv->tx_count_frames = 0;
tx_des_cnt = emac_tx_mem_map(priv, skb, MAX_DATA_LEN_TX_DES, frag_num, ioc);
if (tx_des_cnt == 0) {
pr_err("Could not acquire memory from pool\n");
netif_stop_queue(ndev);
#ifndef CONFIG_ASR_EMAC_NAPI
spin_unlock_irqrestore(&priv->spTxLock, ulFlags);
#else
spin_unlock(&priv->spTxLock);
#endif
return NETDEV_TX_BUSY;
}
ndev->stats.tx_packets++;
ndev->stats.tx_bytes += skb->len;
/* Make sure there is space in the ring for the next send. */
if (EMAC_DESC_UNUSED(&priv->tx_ring) < (MAX_SKB_FRAGS + 2))
netif_stop_queue(ndev);
#ifndef CONFIG_ASR_EMAC_NAPI
spin_unlock_irqrestore(&priv->spTxLock, ulFlags);
#else
spin_unlock(&priv->spTxLock);
#endif
#ifdef CONFIG_ASR_EMAC_DDR_QOS
emac_ddr_clk_scaling(priv);
#endif
emac_tx_timer_arm(priv);
return NETDEV_TX_OK;
}
u32 ReadTxStatCounters(struct emac_priv *priv, u8 cnt)
{
u32 val, tmp;
val = 0x8000 | cnt;
emac_wr(priv, MAC_TX_STATCTR_CONTROL, val);
val = emac_rd(priv, MAC_TX_STATCTR_CONTROL);
while (val & 0x8000)
val = emac_rd(priv, MAC_TX_STATCTR_CONTROL);
tmp = emac_rd(priv, MAC_TX_STATCTR_DATA_HIGH);
val = tmp << 16;
tmp = emac_rd(priv, MAC_TX_STATCTR_DATA_LOW);
val |= tmp;
return val;
}
u32 ReadRxStatCounters(struct emac_priv *priv, u8 cnt)
{
u32 val, tmp;
val = 0x8000 | cnt;
emac_wr(priv, MAC_RX_STATCTR_CONTROL, val);
val = emac_rd(priv, MAC_RX_STATCTR_CONTROL);
while (val & 0x8000)
val = emac_rd(priv, MAC_RX_STATCTR_CONTROL);
tmp = emac_rd(priv, MAC_RX_STATCTR_DATA_HIGH);
val = tmp << 16;
tmp = emac_rd(priv, MAC_RX_STATCTR_DATA_LOW);
val |= tmp;
return val;
}
/* Name emac_set_mac_address
* Arguments pstNetdev : pointer to net_device structure
* addr : pointer to addr
* Return Status: 0 - Success; non-zero - Fail
* Description It is called by upper layer to set the mac address.
*/
static int emac_set_mac_address(struct net_device *ndev, void *addr)
{
struct sockaddr *sa = addr;
struct emac_priv *priv = netdev_priv(ndev);
if (!is_valid_ether_addr(sa->sa_data))
return -EADDRNOTAVAIL;
memcpy(ndev->dev_addr, sa->sa_data, ETH_ALEN);
//#LYNQ_MODFIY modify for task-1620 2025/6/10 start
(ndev->dev_addr)[0] = 0x2;
(ndev->dev_addr)[1] = 0x0;
(ndev->dev_addr)[2] = 0x0;
(ndev->dev_addr)[3] = 0x0;
(ndev->dev_addr)[4] = 0x10;
(ndev->dev_addr)[5] = 0x1;
//#LYNQ_MODFIY modify for task-1620 2025/6/10 end
emac_set_mac_addr(priv, ndev->dev_addr);
emac_set_fc_source_addr(priv, ndev->dev_addr);
return 0;
}
/* Name emac_change_mtu
* Arguments pstNetdev : pointer to net_device structure
* u32MTU : maximum transmit unit value
* Return Status: 0 - Success; non-zero - Fail
* Description It is called by upper layer to set the MTU value.
*/
static int emac_change_mtu(struct net_device *ndev, int mtu)
{
struct emac_priv *priv = netdev_priv(ndev);
u32 frame_len;
if (netif_running(ndev)) {
pr_err("must be stopped to change its MTU\n");
return -EBUSY;
}
frame_len = mtu + ETHERNET_HEADER_SIZE + ETHERNET_FCS_SIZE;
if (frame_len < MINIMUM_ETHERNET_FRAME_SIZE ||
frame_len > EMAC_SKBRB_MAX_PAYLOAD) {
pr_err("Invalid MTU setting\n");
return -EINVAL;
}
if (frame_len <= EMAC_RX_BUFFER_1024)
priv->u32RxBufferLen = EMAC_RX_BUFFER_1024;
else
priv->u32RxBufferLen = EMAC_SKBRB_MAX_PAYLOAD;
ndev->mtu = mtu;
return 0;
}
static void emac_reset(struct emac_priv *priv)
{
if (!test_and_clear_bit(EMAC_RESET_REQUESTED, &priv->state))
return;
if (test_bit(EMAC_DOWN, &priv->state))
return;
netdev_dbg(priv->ndev, "Reset controller.\n");
rtnl_lock();
//netif_trans_update(priv->ndev);
while (test_and_set_bit(EMAC_RESETING, &priv->state))
usleep_range(1000, 2000);
dev_close(priv->ndev);
dev_open(priv->ndev, NULL);
clear_bit(EMAC_RESETING, &priv->state);
rtnl_unlock();
}
static void emac_tx_timeout_task(struct work_struct *work)
{
struct emac_priv *priv = container_of(work,
struct emac_priv, tx_timeout_task);
emac_reset(priv);
clear_bit(EMAC_TASK_SCHED, &priv->state);
}
/* Name emac_tx_timeout
* Arguments pstNetdev : pointer to net_device structure
* Return none
* Description It is called by upper layer
* for packet transmit timeout.
*/
static void emac_tx_timeout(struct net_device *ndev)
{
struct emac_priv *priv = netdev_priv(ndev);
netdev_info(ndev, "TX timeout\n");
register_dump(priv);
netif_carrier_off(priv->ndev);
set_bit(EMAC_RESET_REQUESTED, &priv->state);
if (!test_bit(EMAC_DOWN, &priv->state) &&
!test_and_set_bit(EMAC_TASK_SCHED, &priv->state))
schedule_work(&priv->tx_timeout_task);
}
static int emac_set_axi_bus_clock(struct emac_priv *priv, bool enable)
{
const struct emac_regdata *regdata = priv->regdata;
void __iomem* apmu;
u32 val;
apmu = ioremap(AXI_PHYS_BASE + 0x82800, SZ_4K);
if (apmu == NULL) {
pr_err("error to ioremap APMU base\n");
return -ENOMEM;
}
val = readl(apmu + regdata->clk_rst_ctrl_reg_offset);
if (enable) {
val |= 0x1;
} else {
val &= ~0x1;
}
writel(val, apmu + regdata->clk_rst_ctrl_reg_offset);
iounmap(apmu);
return 0;
}
static int clk_phase_rgmii_set(struct emac_priv *priv, bool is_tx)
{
const struct emac_regdata *regdata = priv->regdata;
void __iomem* apmu;
u32 val, dline;
u8 phase, tmp;
apmu = ioremap(AXI_PHYS_BASE + 0x82800, SZ_4K);
if (apmu == NULL) {
pr_err("error to ioremap APMU base\n");
return -ENOMEM;
}
val = readl(apmu + regdata->clk_rst_ctrl_reg_offset);
if (is_tx) {
if (regdata->rgmii_tx_clk_src_sel_shift > 0) {
phase = (priv->tx_clk_config >> 16) & 0x1;
val &= ~(0x1 << regdata->rgmii_tx_clk_src_sel_shift);
val |= phase << regdata->rgmii_tx_clk_src_sel_shift;
}
if (regdata->rgmii_tx_dline_reg_offset > 0) {
/* Set RGMIII TX DLINE */
dline = readl(apmu + regdata->rgmii_tx_dline_reg_offset);
/* delay code */
tmp = (priv->tx_clk_config >> 8) &
regdata->rgmii_tx_delay_code_mask;
dline &= ~(regdata->rgmii_tx_delay_code_mask <<
regdata->rgmii_tx_delay_code_shift);
dline |= tmp << regdata->rgmii_tx_delay_code_shift;
/* delay step */
tmp = priv->tx_clk_config &
regdata->rgmii_tx_delay_step_mask;
dline &= ~(regdata->rgmii_tx_delay_step_mask <<
regdata->rgmii_tx_delay_step_shift);
dline |= tmp << regdata->rgmii_tx_delay_step_shift;
/* delay line enable */
dline |= 1 << regdata->rgmii_tx_delay_enable_shift;
writel(dline, apmu + regdata->rgmii_tx_dline_reg_offset);
pr_info("===> emac set tx dline 0x%x 0x%x", dline,
readl(apmu + regdata->rgmii_tx_dline_reg_offset));
}
} else {
if (regdata->rgmii_rx_clk_src_sel_shift > 0) {
phase = (priv->rx_clk_config >> 16) & 0x1;
val &= ~(0x1 << regdata->rgmii_rx_clk_src_sel_shift);
val |= phase << regdata->rgmii_rx_clk_src_sel_shift;
}
/* Set RGMIII RX DLINE */
if (regdata->rgmii_rx_dline_reg_offset > 0) {
dline = readl(apmu + regdata->rgmii_rx_dline_reg_offset);
/* delay code */
tmp = (priv->rx_clk_config >> 8) &
regdata->rgmii_rx_delay_code_mask;
dline &= ~(regdata->rgmii_rx_delay_code_mask <<
regdata->rgmii_rx_delay_code_shift);
dline |= tmp << regdata->rgmii_rx_delay_code_shift;
/* delay step */
tmp = priv->rx_clk_config &
regdata->rgmii_rx_delay_step_mask;
dline &= ~(regdata->rgmii_rx_delay_step_mask <<
regdata->rgmii_rx_delay_step_shift);
dline |= tmp << regdata->rgmii_rx_delay_step_shift;
/* delay line enable */
dline |= 1 << regdata->rgmii_rx_delay_enable_shift;
writel(dline, apmu + regdata->rgmii_rx_dline_reg_offset);
pr_info("===> emac set rx dline 0x%x 0x%x", dline,
readl(apmu + regdata->rgmii_rx_dline_reg_offset));
}
}
writel(val, apmu + regdata->clk_rst_ctrl_reg_offset);
pr_info("%s phase:%d direction:%s 0x%x 0x%x\n", __func__, phase,
is_tx ? "tx": "rx", val,
readl(apmu + regdata->clk_rst_ctrl_reg_offset));
iounmap(apmu);
return 0;
}
static int clk_phase_rmii_set(struct emac_priv *priv, bool is_tx)
{
const struct emac_regdata *regdata = priv->regdata;
void __iomem* apmu;
u32 val;
u8 phase, tmp;
apmu = ioremap(AXI_PHYS_BASE + 0x82800, SZ_4K);
if (apmu == NULL) {
pr_err("error to ioremap APMU base\n");
return -ENOMEM;
}
val = readl(apmu + regdata->clk_rst_ctrl_reg_offset);
if (is_tx) {
/* rmii tx clock select */
if (regdata->rmii_tx_clk_sel_shift > 0) {
tmp = (priv->tx_clk_config >> 16) & 0x1;
val &= ~(0x1 << regdata->rmii_tx_clk_sel_shift);
val |= tmp << regdata->rmii_tx_clk_sel_shift;
}
/* rmii ref clock selct, 1 - from soc, 0 - from phy */
if (regdata->rmii_rx_clk_sel_shift) {
tmp = (priv->tx_clk_config >> 24) & 0x1;
val &= ~(0x1 << regdata->rmii_ref_clk_sel_shift);
val |= tmp << regdata->rmii_ref_clk_sel_shift;
}
} else {
/* rmii rx clock select */
if (regdata->rmii_rx_clk_sel_shift > 0) {
tmp = (priv->rx_clk_config >> 16) & 0x1;
val &= ~(0x1 << regdata->rmii_rx_clk_sel_shift);
val |= tmp << regdata->rmii_rx_clk_sel_shift;
}
/* rmii ref clock selct, 1 - from soc, 0 - from phy */
if (regdata->rmii_rx_clk_sel_shift) {
tmp = (priv->tx_clk_config >> 24) & 0x1;
val &= ~(0x1 << regdata->rmii_ref_clk_sel_shift);
val |= tmp << regdata->rmii_ref_clk_sel_shift;
}
}
writel(val, apmu + regdata->clk_rst_ctrl_reg_offset);
pr_debug("%s phase:%d direction:%s\n", __func__, phase,
is_tx ? "tx": "rx");
iounmap(apmu);
return 0;
}
static int clk_phase_set(struct emac_priv *priv, bool is_tx)
{
if (emac_is_rmii_interface(priv)) {
clk_phase_rmii_set(priv, is_tx);
} else {
clk_phase_rgmii_set(priv, is_tx);
}
return 0;
}
#ifdef CONFIG_DEBUG_FS
static int clk_phase_show(struct seq_file *s, void *data)
{
struct emac_priv *priv = s->private;
bool rmii_intf;
rmii_intf = emac_is_rmii_interface(priv);
seq_printf(s, "Emac MII Interface : %s\n", rmii_intf ? "RMII" : "RGMII");
seq_printf(s, "Current rx clk config : %d\n", priv->rx_clk_config);
seq_printf(s, "Current tx clk config : %d\n", priv->tx_clk_config);
return 0;
}
static ssize_t clk_tuning_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct emac_priv *priv =
((struct seq_file *)(file->private_data))->private;
int err;
int clk_phase;
char buff[TUNING_CMD_LEN] = { 0 };
char mode_str[20];
if (count > TUNING_CMD_LEN) {
pr_err("count must be less than 50.\n");
return count;
}
err = copy_from_user(buff, user_buf, count);
if (err)
return err;
err = sscanf(buff, "%s %d", (char *)&mode_str, &clk_phase);
if (err != 2) {
pr_err("debugfs para count error\n");
return count;
}
pr_info("input:%s %d\n", mode_str, clk_phase);
if (strcmp(mode_str, "tx") == 0) {
priv->tx_clk_config = clk_phase;
clk_phase_set(priv, TX_PHASE);
} else if (strcmp(mode_str, "rx") == 0) {
priv->rx_clk_config = clk_phase;
clk_phase_set(priv, RX_PHASE);
} else {
pr_err("command error\n");
pr_err("eg: echo rx 1 > clk_tuning\n");
return count;
}
return count;
}
static int clk_tuning_open(struct inode *inode, struct file *file)
{
return single_open(file, clk_phase_show, inode->i_private);
}
const struct file_operations clk_tuning_fops = {
.open = clk_tuning_open,
.write = clk_tuning_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
#endif
static int emac_power_down(struct emac_priv *priv)
{
if (priv->rst_gpio >= 0)
gpio_direction_output(priv->rst_gpio,
priv->low_active_rst ? 0 : 1);
if (priv->ldo_gpio >= 0)
gpio_direction_output(priv->ldo_gpio,
priv->low_active_ldo ? 0 : 1);
return 0;
}
static int emac_power_up(struct emac_priv *priv)
{
u32 *delays_ldo = priv->delays_ldo;
u32 *delays_rst = priv->delays_rst;
int rst_gpio = priv->rst_gpio;
int low_active_rst = priv->low_active_rst;
int ldo_gpio = priv->ldo_gpio;
int low_active_ldo = priv->low_active_ldo;
if (rst_gpio >= 0) {
gpio_direction_output(rst_gpio, low_active_rst ? 0 : 1);
}
if (ldo_gpio >= 0) {
gpio_direction_output(ldo_gpio, low_active_ldo ? 0 : 1);
if (delays_ldo[0]) {
gpio_set_value(ldo_gpio, low_active_ldo ? 1 : 0);
msleep(DIV_ROUND_UP(delays_ldo[0], 1000));
}
gpio_set_value(ldo_gpio, low_active_ldo ? 0 : 1);
if (delays_ldo[1])
msleep(DIV_ROUND_UP(delays_ldo[1], 1000));
gpio_set_value(ldo_gpio, low_active_ldo ? 1 : 0);
if (delays_ldo[2])
msleep(DIV_ROUND_UP(delays_ldo[2], 1000));
}
if (rst_gpio >= 0) {
if (delays_rst[0]) {
gpio_set_value(rst_gpio, low_active_rst ? 1 : 0);
msleep(DIV_ROUND_UP(delays_rst[0], 1000));
}
gpio_set_value(rst_gpio, low_active_rst ? 0 : 1);
if (delays_rst[1])
msleep(DIV_ROUND_UP(delays_rst[1], 1000));
gpio_set_value(rst_gpio, low_active_rst ? 1 : 0);
if (delays_rst[2])
msleep(DIV_ROUND_UP(delays_rst[2], 1000));
}
return 0;
}
static int emac_mii_reset(struct mii_bus *bus)
{
struct emac_priv *priv = bus->priv;
struct device *dev = &priv->pdev->dev;
struct device_node *np = dev->of_node;
int rst_gpio, ldo_gpio;
int low_active_ldo, low_active_rst;
u32 *delays_ldo = priv->delays_ldo;
u32 *delays_rst = priv->delays_rst;
priv->rst_gpio = -1;
priv->ldo_gpio = -1;
if (!np)
return 0;
rst_gpio = of_get_named_gpio(np, "reset-gpio", 0);
if (rst_gpio >= 0) {
low_active_rst = of_property_read_bool(np, "reset-active-low");
of_property_read_u32_array(np, "reset-delays-us", delays_rst, 3);
if (gpio_request(rst_gpio, "mdio-reset")) {
printk("emac: reset-gpio=%d request failed\n",
rst_gpio);
return 0;
}
priv->rst_gpio = rst_gpio;
priv->low_active_rst = low_active_rst;
}
ldo_gpio = of_get_named_gpio(np, "ldo-gpio", 0);
if (ldo_gpio >= 0) {
low_active_ldo = of_property_read_bool(np, "ldo-active-low");
of_property_read_u32_array(np, "ldo-delays-us", delays_ldo, 3);
if (gpio_request(ldo_gpio, "mdio-ldo"))
return 0;
priv->ldo_gpio = ldo_gpio;
priv->low_active_ldo = low_active_ldo;
}
/*
* Some device not allow MDC/MDIO operation during power on/reset,
* disable AXI clock to shutdown mdio clock.
*/
clk_disable_unprepare(priv->clk);
emac_power_up(priv);
clk_prepare_enable(priv->clk);
emac_reset_hw(priv);
return 0;
}
static int emac_mii_read(struct mii_bus *bus, int phy_addr, int regnum)
{
struct emac_priv *priv = bus->priv;
u32 cmd = 0;
u32 val;
if (!__clk_is_enabled(priv->clk))
return -EBUSY;
mutex_lock(&priv->mii_mutex);
cmd |= phy_addr & 0x1F;
cmd |= (regnum & 0x1F) << 5;
cmd |= MREGBIT_START_MDIO_TRANS | MREGBIT_MDIO_READ_WRITE;
/*
* MDC/MDIO clock is from AXI, add qos to avoid MDC frequency
* change during MDIO read/write
*/
#ifdef CONFIG_DDR_DEVFREQ
pm_qos_update_request(&priv->pm_ddr_qos, INT_MAX);
#endif
emac_wr(priv, MAC_MDIO_DATA, 0x0);
emac_wr(priv, MAC_MDIO_CONTROL, cmd);
if (readl_poll_timeout(priv->iobase + MAC_MDIO_CONTROL, val,
!(val & MREGBIT_START_MDIO_TRANS), 100, 100000))
return -EBUSY;
val = emac_rd(priv, MAC_MDIO_DATA);
#ifdef CONFIG_DDR_DEVFREQ
pm_qos_update_request(&priv->pm_ddr_qos, PM_QOS_DEFAULT_VALUE);
#endif
mutex_unlock(&priv->mii_mutex);
return val;
}
static int emac_mii_write(struct mii_bus *bus, int phy_addr, int regnum,
u16 value)
{
struct emac_priv *priv = bus->priv;
u32 cmd = 0;
u32 val;
if (!__clk_is_enabled(priv->clk))
return -EBUSY;
mutex_lock(&priv->mii_mutex);
emac_wr(priv, MAC_MDIO_DATA, value);
cmd |= phy_addr & 0x1F;
cmd |= (regnum & 0x1F) << 5;
cmd |= MREGBIT_START_MDIO_TRANS;
/*
* MDC/MDIO clock is from AXI, add qos to avoid MDC frequency
* change during MDIO read/write
*/
#ifdef CONFIG_DDR_DEVFREQ
pm_qos_update_request(&priv->pm_ddr_qos, INT_MAX);
#endif
emac_wr(priv, MAC_MDIO_CONTROL, cmd);
if (readl_poll_timeout(priv->iobase + MAC_MDIO_CONTROL, val,
!(val & MREGBIT_START_MDIO_TRANS), 100, 100000))
return -EBUSY;
#ifdef CONFIG_DDR_DEVFREQ
pm_qos_update_request(&priv->pm_ddr_qos, PM_QOS_DEFAULT_VALUE);
#endif
mutex_unlock(&priv->mii_mutex);
return 0;
}
static void emac_adjust_link(struct net_device *dev)
{
struct phy_device *phydev = dev->phydev;
struct emac_priv *priv = netdev_priv(dev);
u32 ctrl;
#ifdef WAN_LAN_AUTO_ADAPT
int status_change = 0;
int addr = 0;
int i = 0;
#endif
if (!phydev || priv->fix_link)
return;
if (phydev->link) {
ctrl = emac_rd(priv, MAC_GLOBAL_CONTROL);
/* Now we make sure that we can be in full duplex mode
* If not, we operate in half-duplex mode.
*/
if (phydev->duplex != priv->duplex) {
if (!phydev->duplex)
ctrl &= ~MREGBIT_FULL_DUPLEX_MODE;
else
ctrl |= MREGBIT_FULL_DUPLEX_MODE;
priv->duplex = phydev->duplex;
}
if (phydev->speed != priv->speed) {
ctrl &= ~MREGBIT_SPEED;
switch (phydev->speed) {
case SPEED_1000:
ctrl |= MREGBIT_SPEED_1000M;
break;
case SPEED_100:
ctrl |= MREGBIT_SPEED_100M;
break;
case SPEED_10:
ctrl |= MREGBIT_SPEED_10M;
break;
default:
pr_err("broken speed: %d\n", phydev->speed);
phydev->speed = SPEED_UNKNOWN;
break;
}
if (phydev->speed != SPEED_UNKNOWN) {
priv->speed = phydev->speed;
}
}
emac_wr(priv, MAC_GLOBAL_CONTROL, ctrl);
pr_info("%s link:%d speed:%dM duplex:%s\n", __func__,
phydev->link, phydev->speed,
phydev->duplex ? "Full": "Half");
}
#ifdef WAN_LAN_AUTO_ADAPT
if(phydev->phy_id == IP175D_PHY_ID) {
if (phydev->link != priv->link) {
for (i=0; i<16; i++) {
if((priv->link & (1<<i)) != (phydev->link & (1<<i))) {
addr = i;
if (phydev->link & (1<<i)) {
/* link up */
printk("eth0 port%d link up\n", addr);
priv->dhcp = 0;
emac_sig_workq(CARRIER_UP_IP175D, addr);
if(priv->dhcp_delaywork)
cancel_delayed_work(&priv->dhcp_work);
priv->dhcp_delaywork = 1;
schedule_delayed_work(&priv->dhcp_work, 25*HZ);
} else {
/* link down */
printk("eth0 port%d link down\n", addr);
priv->dhcp = 0;
if(priv->dhcp_delaywork)
cancel_delayed_work(&priv->dhcp_work);
priv->dhcp_delaywork = 0;
emac_sig_workq(CARRIER_DOWN_IP175D, addr);
}
}
}
priv->link = phydev->link;
}
} else {
if (phydev->link != priv->link) {
priv->link = phydev->link;
status_change = 1;
}
if (status_change) {
if (phydev->link) {
/* link up */
priv->dhcp = 0;
emac_sig_workq(CARRIER_UP, 0);
if(priv->dhcp_delaywork)
cancel_delayed_work(&priv->dhcp_work);
priv->dhcp_delaywork = 1;
schedule_delayed_work(&priv->dhcp_work, 25*HZ);
} else {
/* link down */
priv->dhcp = 0;
if(priv->dhcp_delaywork)
cancel_delayed_work(&priv->dhcp_work);
priv->dhcp_delaywork = 0;
emac_sig_workq(CARRIER_DOWN, 0);
}
}
}
#endif
}
static int emac_phy_connect(struct net_device *dev)
{
struct phy_device *phydev;
int phy_interface;
struct device_node *np;
struct emac_priv *priv = netdev_priv(dev);
np = of_parse_phandle(priv->pdev->dev.of_node, "phy-handle", 0);
if (!np) {
if (priv->fix_link) {
emac_phy_interface_config(priv, priv->interface);
if (priv->interface == PHY_INTERFACE_MODE_RGMII)
pinctrl_select_state(priv->pinctrl,
priv->rgmii_pins);
emac_config_phy_interrupt(priv, 0);
return 0;
}
return -ENODEV;
}
printk("%s: %s\n",__func__, np->full_name);
phy_interface = of_get_phy_mode(np);
emac_phy_interface_config(priv, phy_interface);
if (phy_interface != PHY_INTERFACE_MODE_RMII)
pinctrl_select_state(priv->pinctrl, priv->rgmii_pins);
phydev = phy_find_first(priv->mii);
if (!phydev) {
printk("%s: no PHY found\n", dev->name);
return -ENODEV;
}
phy_connect_direct(dev, phydev, emac_adjust_link, phy_interface); /* phy_start_machine */
//phydev = of_phy_connect(dev, np,&emac_adjust_link, 0, phy_interface);
if (IS_ERR_OR_NULL(phydev)) {
pr_err("Could not attach to PHY\n");
emac_power_down(priv);
if (!phydev)
return -ENODEV;
return PTR_ERR(phydev);
}
if (!phydev->phy_id || phydev->phy_id == 0xffffffff) {
pr_err("Not valid phy_id=0x%x\n", phydev->phy_id);
emac_power_down(priv);
return -ENODEV;
}
if(phy_interrupt_is_valid(phydev))
emac_config_phy_interrupt(priv, 1);
else
emac_config_phy_interrupt(priv, 0);
//phydev->supported &= ~(SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full);
pr_info("%s: %s: attached to PHY (UID 0x%x)"
" Link = %d irq=%d\n", __func__,
dev->name, phydev->phy_id, phydev->link, phydev->irq);
dev->phydev = phydev;
#ifdef WAN_LAN_AUTO_ADAPT
if(phydev->phy_id == IP175D_PHY_ID)
emac_sig_workq(PHY_IP175D_CONNECT, 0);
#endif
return 0;
}
static int emac_mdio_init(struct emac_priv *priv)
{
struct device_node *mii_np;
struct device *dev = &priv->pdev->dev;
int ret;
//#LYNQ_MODFIY modify for task-1618 2025/6/24 start
struct device_node *phy_np;
int phy_power_en_gpio;
int phy_rst_gpio;
//#LYNQ_MODFIY modify for task-1618 2025/6/24 end
mii_np = of_get_child_by_name(dev->of_node, "mdio-bus");
if (!mii_np) {
dev_err(dev, "no %s child node found", "mdio-bus");
return -ENODEV;
}
if (!of_device_is_available(mii_np)) {
ret = -ENODEV;
goto err_put_node;
}
priv->mii = mdiobus_alloc();//devm_mdiobus_alloc(dev);
if (!priv->mii) {
ret = -ENOMEM;
goto err_put_node;
}
priv->mii->priv = priv;
//priv->mii->irq = priv->mdio_irqs;
priv->mii->name = "emac mii";
priv->mii->reset = emac_mii_reset;
priv->mii->read = emac_mii_read;
priv->mii->write = emac_mii_write;
snprintf(priv->mii->id, MII_BUS_ID_SIZE, "%pOFn",
mii_np);
priv->mii->parent = dev;
priv->mii->phy_mask = 0xffffffff;
//#LYNQ_MODFIY modify for task-1618 2025/6/19 start
phy_np = of_parse_phandle(dev->of_node, "phy-handle", 0);
if (!phy_np) {
dev_err(dev, "Failed to find PHY node via phy-handle\n");
return -ENODEV;
}
phy_power_en_gpio = of_get_named_gpio(phy_np, "power-en-gpio", 0);
if (phy_power_en_gpio < 0) {
dev_err(dev, "Failed to get power_en gpio: %d\n", phy_power_en_gpio);
return phy_power_en_gpio;
}
gpio_request(phy_power_en_gpio, "phy_power_en");
gpio_direction_output(phy_power_en_gpio, 1);
msleep(10);
gpio_free(phy_power_en_gpio);
//#LYNQ_MODFIY modify for task-1618 2025/6/19 end
//#LYNQ_MODFIY modify for task-1618 2025/6/24 start
phy_rst_gpio = of_get_named_gpio(phy_np, "rst-gpio", 0);
if (phy_rst_gpio < 0) {
dev_err(dev, "Failed to get phy_rst gpio: %d\n", phy_rst_gpio);
return phy_rst_gpio;
}
gpio_request(phy_rst_gpio, "phy_reset");
gpio_direction_output(phy_rst_gpio, 0);
msleep(10);
gpio_set_value(phy_rst_gpio, 1);
gpio_free(phy_rst_gpio);
//#LYNQ_MODFIY modify for task-1618 2025/6/24 end
ret = of_mdiobus_register(priv->mii, mii_np);
err_put_node:
of_node_put(mii_np);
return ret;
}
static int emac_mdio_deinit(struct emac_priv *priv)
{
if (!priv->mii)
return 0;
mdiobus_unregister(priv->mii);
return 0;
}
static int emac_get_ts_info(struct net_device *dev,
struct ethtool_ts_info *info)
{
struct emac_priv *priv = netdev_priv(dev);
if (priv->ptp_support) {
info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
SOF_TIMESTAMPING_TX_HARDWARE |
SOF_TIMESTAMPING_RX_SOFTWARE |
SOF_TIMESTAMPING_RX_HARDWARE |
SOF_TIMESTAMPING_SOFTWARE |
SOF_TIMESTAMPING_RAW_HARDWARE;
if (priv->ptp_clock)
info->phc_index = ptp_clock_index(priv->ptp_clock);
info->tx_types = (1 << HWTSTAMP_TX_OFF) | (1 << HWTSTAMP_TX_ON);
info->rx_filters = ((1 << HWTSTAMP_FILTER_NONE) |
(1 << HWTSTAMP_FILTER_PTP_V1_L4_SYNC) |
(1 << HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ) |
(1 << HWTSTAMP_FILTER_PTP_V2_L4_SYNC) |
(1 << HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ) |
(1 << HWTSTAMP_FILTER_PTP_V2_SYNC) |
(1 << HWTSTAMP_FILTER_PTP_V2_DELAY_REQ) |
(1 << HWTSTAMP_FILTER_ALL));
if (priv->regdata->ptp_rx_ts_all_events) {
info->rx_filters |=
(1 << HWTSTAMP_FILTER_PTP_V1_L4_EVENT) |
(1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT);
}
return 0;
} else
return ethtool_op_get_ts_info(dev, info);
}
static void emac_get_strings(struct net_device *dev, u32 stringset, u8 *data)
{
int i;
switch (stringset) {
case ETH_SS_STATS:
for (i = 0; i < ARRAY_SIZE(emac_ethtool_stats); i++) {
memcpy(data, emac_ethtool_stats[i].str, ETH_GSTRING_LEN);
data += ETH_GSTRING_LEN;
}
break;
}
}
static int emac_get_sset_count(struct net_device *dev, int sset)
{
switch (sset) {
case ETH_SS_STATS:
return ARRAY_SIZE(emac_ethtool_stats);
default:
return -EOPNOTSUPP;
}
}
static void emac_stats_update(struct emac_priv *priv)
{
struct emac_hw_stats *hwstats = priv->hw_stats;
int i;
u32 *p;
p = (u32 *)(hwstats);
for (i = 0; i < MAX_TX_STATS_NUM; i++)
*(p + i) = ReadTxStatCounters(priv, i);
p = (u32 *)hwstats + MAX_TX_STATS_NUM;
for (i = 0; i < MAX_RX_STATS_NUM; i++)
*(p + i) = ReadRxStatCounters(priv, i);
*(p + i++) = emac_rd(priv, DMA_MISSED_FRAME_COUNTER);
*(p + i++) = hwstats->tx_tso_pkts;
*(p + i++) = hwstats->tx_tso_bytes;
}
static void emac_get_ethtool_stats(struct net_device *dev,
struct ethtool_stats *stats, u64 *data)
{
struct emac_priv *priv = netdev_priv(dev);
struct emac_hw_stats *hwstats = priv->hw_stats;
u32 *data_src;
u64 *data_dst;
int i;
if (netif_running(dev) && netif_device_present(dev)) {
if (spin_trylock_bh(&hwstats->stats_lock)) {
emac_stats_update(priv);
spin_unlock_bh(&hwstats->stats_lock);
}
}
data_dst = data;
for (i = 0; i < ARRAY_SIZE(emac_ethtool_stats); i++) {
data_src = (u32 *)hwstats + emac_ethtool_stats[i].offset;
*data_dst++ = (u64)(*data_src);
}
}
static int emac_ethtool_get_regs_len(struct net_device *dev)
{
return EMAC_REG_SPACE_SIZE;
}
static void emac_ethtool_get_regs(struct net_device *dev,
struct ethtool_regs *regs, void *space)
{
struct emac_priv *priv = netdev_priv(dev);
u32 *reg_space = (u32 *) space;
void __iomem *base = priv->iobase;
int i;
regs->version = 1;
memset(reg_space, 0x0, EMAC_REG_SPACE_SIZE);
for (i = 0; i < EMAC_DMA_REG_CNT; i++)
reg_space[i] = readl(base + DMA_CONFIGURATION + i * 4);
for (i = 0; i < EMAC_MAC_REG_CNT; i++)
reg_space[i + MAC_GLOBAL_CONTROL / 4] = readl(base + MAC_GLOBAL_CONTROL + i * 4);
}
static int emac_get_link_ksettings(struct net_device *ndev,
struct ethtool_link_ksettings *cmd)
{
if (!ndev->phydev)
return -ENODEV;
phy_ethtool_ksettings_get(ndev->phydev, cmd);
return 0;
}
static int emac_set_link_ksettings(struct net_device *ndev,
const struct ethtool_link_ksettings *cmd)
{
if (!ndev->phydev)
return -ENODEV;
return phy_ethtool_ksettings_set(ndev->phydev, cmd);
}
static void emac_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver));
info->n_stats = ARRAY_SIZE(emac_ethtool_stats);
}
static void emac_get_pauseparam(struct net_device *ndev,
struct ethtool_pauseparam *param)
{
struct emac_priv *priv = netdev_priv(ndev);
int val = emac_mii_read(priv->mii, 0, 0);
param->autoneg = (val & BIT(12)) ? 1 : 0;
param->rx_pause = priv->pause.rx_pause;
param->tx_pause = priv->pause.tx_pause;
return;
}
static int emac_set_pauseparam(struct net_device *ndev,
struct ethtool_pauseparam *param)
{
struct emac_priv *priv = netdev_priv(ndev);
struct device *dev = &priv->pdev->dev;
struct device_node *np = dev->of_node;
int val;
int phyval;
u32 threshold[2];
static int init_flag = 1;
val = readl(priv->iobase + MAC_FC_CONTROL);
phyval = emac_mii_read(priv->mii, 0, 0);
if (param->rx_pause)
val |= MREGBIT_FC_DECODE_ENABLE;
else
val &= ~MREGBIT_FC_DECODE_ENABLE;
if (param->tx_pause)
val |= MREGBIT_FC_GENERATION_ENABLE;
else
val &= ~MREGBIT_FC_GENERATION_ENABLE;
if (init_flag && (param->rx_pause | param->tx_pause)) {
val |= MREGBIT_MULTICAST_MODE;
priv->pause.pause_time_max = 0;
if (0 != of_property_read_u32_array(np, "flow-control-threshold", threshold, 2)) {
threshold[0] = 60;
threshold[1] = 90;
}
threshold[0] = clamp(threshold[0], 0U, 99U);
threshold[1] = clamp(threshold[1], 1U, 100U);
if (cpu_is_asr18xx() || cpu_is_asr1903_z1()) {
priv->pause.low_water = priv->rx_ring.total_cnt * threshold[0] / 100;
priv->pause.high_water = priv->rx_ring.total_cnt * threshold[1] / 100 - 1;
priv->pause.fc_auto = 0;
} else {
priv->pause.low_water = 0;
priv->pause.high_water = 0;
priv->pause.fc_auto = 1;
val |= MREGBIT_AUTO_FC_GENERATION_ENABLE;
threshold[0] = 1024 * threshold[0] / 100;
threshold[1] = 1024 * threshold[1] / 100;
emac_wr(priv, MAC_FC_AUTO_HIGH_THRESHOLD, threshold[1]);
emac_wr(priv, MAC_FC_AUTO_LOW_THRESHOLD, threshold[0]);
emac_wr(priv, MAC_FC_AUTO_HIGH_PAUSE_TIME_VALUE, 0xffff);
emac_wr(priv, MAC_FC_AUTO_LOW_PAUSE_TIME_VALUE, 0);
}
init_flag = 0;
}
emac_wr(priv, MAC_FC_CONTROL, val);
if (param->autoneg)
phyval |= BIT(12);
else
phyval &= ~BIT(12);
(void)emac_mii_write(priv->mii, 0, 0, (u16)phyval);
priv->pause.rx_pause = param->rx_pause;
priv->pause.tx_pause = param->tx_pause;
return 0;
}
static void emac_get_wol(struct net_device *dev,
struct ethtool_wolinfo *wol)
{
struct emac_priv *priv = netdev_priv(dev);
struct device *device = &priv->pdev->dev;
if (device_can_wakeup(device)) {
wol->supported = WAKE_MAGIC | WAKE_UCAST;
wol->wolopts = priv->wolopts;
}
}
static int emac_set_wol(struct net_device *dev,
struct ethtool_wolinfo *wol)
{
struct emac_priv *priv = netdev_priv(dev);
struct device *device = &priv->pdev->dev;
u32 support = WAKE_MAGIC | WAKE_UCAST;
if (!device_can_wakeup(device) || !priv->en_suspend)
return -ENOTSUPP;
if (wol->wolopts & ~support)
return -EINVAL;
priv->wolopts = wol->wolopts;
if (wol->wolopts) {
device_set_wakeup_enable(device, 1);
enable_irq_wake(priv->irq_wakeup);
} else {
device_set_wakeup_enable(device, 0);
disable_irq_wake(priv->irq_wakeup);
}
return 0;
}
static const struct ethtool_ops emac_ethtool_ops = {
.get_link_ksettings = emac_get_link_ksettings,
.set_link_ksettings = emac_set_link_ksettings,
.get_drvinfo = emac_get_drvinfo,
.nway_reset = phy_ethtool_nway_reset,
.get_link = ethtool_op_get_link,
.get_pauseparam = emac_get_pauseparam,
.set_pauseparam = emac_set_pauseparam,
.get_strings = emac_get_strings,
.get_sset_count = emac_get_sset_count,
.get_ethtool_stats = emac_get_ethtool_stats,
.get_regs = emac_ethtool_get_regs,
.get_regs_len = emac_ethtool_get_regs_len,
.get_ts_info = emac_get_ts_info,
.get_wol = emac_get_wol,
.set_wol = emac_set_wol,
};
static const struct net_device_ops emac_netdev_ops = {
.ndo_open = emac_open,
.ndo_stop = emac_close,
.ndo_start_xmit = emac_start_xmit,
.ndo_set_mac_address = emac_set_mac_address,
.ndo_do_ioctl = emac_ioctl,
.ndo_change_mtu = emac_change_mtu,
.ndo_tx_timeout = emac_tx_timeout,
};
#ifdef WAN_LAN_AUTO_ADAPT
#define EMAC_SKB_SIZE 2048
static int emac_event_add_var(struct emac_event *event, int argv,
const char *format, ...)
{
static char buf[128];
char *s;
va_list args;
int len;
if (argv)
return 0;
va_start(args, format);
len = vsnprintf(buf, sizeof(buf), format, args);
va_end(args);
if (len >= sizeof(buf)) {
printk("buffer size too small\n");
WARN_ON(1);
return -ENOMEM;
}
s = skb_put(event->skb, len + 1);
strcpy(s, buf);
return 0;
}
static int emac_hotplug_fill_event(struct emac_event *event)
{
int ret;
ret = emac_event_add_var(event, 0, "HOME=%s", "/");
if (ret)
return ret;
ret = emac_event_add_var(event, 0, "PATH=%s",
"/sbin:/bin:/usr/sbin:/usr/bin");
if (ret)
return ret;
ret = emac_event_add_var(event, 0, "SUBSYSTEM=%s", "ethernet");
if (ret)
return ret;
ret = emac_event_add_var(event, 0, "ACTION=%s", event->action);
if (ret)
return ret;
ret = emac_event_add_var(event, 0, "ETH=%s", event->name);
if (ret)
return ret;
ret = emac_event_add_var(event, 0, "PORT=%d", event->port);
if (ret)
return ret;
ret = emac_event_add_var(event, 0, "SEQNUM=%llu", uevent_next_seqnum());
return ret;
}
static void emac_hotplug_work(struct work_struct *work)
{
struct emac_event *event = container_of(work, struct emac_event, work);
int ret = 0;
event->skb = alloc_skb(EMAC_SKB_SIZE, GFP_KERNEL);
if (!event->skb)
goto out_free_event;
ret = emac_event_add_var(event, 0, "%s@", event->action);
if (ret)
goto out_free_skb;
ret = emac_hotplug_fill_event(event);
if (ret)
goto out_free_skb;
NETLINK_CB(event->skb).dst_group = 1;
broadcast_uevent(event->skb, 0, 1, GFP_KERNEL);
out_free_skb:
if (ret) {
printk("work error %d\n", ret);
kfree_skb(event->skb);
}
out_free_event:
kfree(event);
}
static int emac_sig_workq(int event, int port)
{
struct emac_event *u_event = NULL;
u_event = kzalloc(sizeof(*u_event), GFP_KERNEL);
if (!u_event)
return -ENOMEM;
u_event->name = DRIVER_NAME;
if(event == CARRIER_UP)
u_event->action = "LINKUP";
else if(event == CARRIER_DOWN)
u_event->action = "LINKDW";
else if(event == CARRIER_DOWN_IP175D)
u_event->action = "IP175D_LINKDW";
else if(event == CARRIER_UP_IP175D)
u_event->action = "IP175D_LINKUP";
else if(event == DHCP_EVENT_CLIENT)
u_event->action = "DHCPCLIENT";
else if(event == DHCP_EVENT_SERVER)
u_event->action = "DHCPSERVER";
else if(event == PHY_IP175D_CONNECT)
u_event->action = "PHY_CONNECT";
u_event->port = port;
INIT_WORK(&u_event->work, (void *)emac_hotplug_work);
schedule_work(&u_event->work);
return 0;
}
static inline void __emac_dhcp_work_func(struct emac_priv *priv)
{
if (priv->dhcp == DHCP_REC_RESP) {
emac_sig_workq(DHCP_EVENT_CLIENT, priv->vlan_port);
} else if (priv->dhcp == DHCP_SEND_REQ || priv->dhcp == 0) {
emac_sig_workq(DHCP_EVENT_SERVER, priv->vlan_port);
}
priv->dhcp = 0;
if(priv->dhcp_delaywork){
cancel_delayed_work(&priv->dhcp_work);
priv->dhcp_delaywork = 0;
}
}
static void emac_dhcp_work_func_t(struct work_struct *work)
{
struct emac_priv *priv = container_of(work, struct emac_priv, dhcp_work.work);
__emac_dhcp_work_func(priv);
}
#endif
long g_PhyVersionNumber = 0;
static ssize_t phy_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int len = 0;
len = sprintf(buf, "phy_version = 0x%x\n", g_PhyVersionNumber);
return (ssize_t)len;
}
static ssize_t phy_version_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int reg, val, devad = 0;
struct emac_priv *priv = dev_get_drvdata(dev);
sscanf(buf, "%d", &val);
if(val == 1)
{
devad = 0x1f;
reg = 0x113;
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, devad);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xe, reg);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, 0x4000 | devad);
val = mdiobus_read(priv->mii, priv->ndev->phydev->mdio.addr, 0x0e);
}
g_PhyVersionNumber = val;
return size;
}
static ssize_t lpsd_sleep_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int len = 0;
int reg, val, devad = 0;
struct emac_priv *priv = dev_get_drvdata(dev);
devad = 0x3;
reg = 0x8700;
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, devad);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xe, reg);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, 0x4000 | devad);
val = mdiobus_read(priv->mii, priv->ndev->phydev->mdio.addr, 0x0e);
len = sprintf(buf, "phy_version = 0x%x\n", val);
return (ssize_t)len;
}
static ssize_t lpsd_sleep_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int reg, val, devad = 0;
struct emac_priv *priv = dev_get_drvdata(dev);
sscanf(buf, "%d", &val);
if(val == 1) //enter lpsd sleep mode
{
devad = 0x3;
reg = 0x8700;
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, devad);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xe, reg);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, 0x4000 | devad);
val = mdiobus_read(priv->mii, priv->ndev->phydev->mdio.addr, 0x0e);
printk("lpsd sleep mode : reg3.8700 = 0x%x", val);
msleep(200);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, devad);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xe, reg);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, 0x4000 | devad);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0x0e, (val | BIT(0)));
}else
{
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, devad);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xe, reg);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, 0x4000 | devad);
val = mdiobus_read(priv->mii, priv->ndev->phydev->mdio.addr, 0x0e);
printk("lpsd sleep mode : reg3.8700 = 0x%x", val);
msleep(200);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, devad);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xe, reg);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, 0x4000 | devad);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0x0e, (val | ~BIT(0)));
}
return size;
}
static int mode_type = -1;
static int enter_only_one = 0;
static ssize_t gmac_master_or_slave_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int val = 0;
int reg = 0;
int devad = 0;
int ret = 0;
struct emac_priv *priv = dev_get_drvdata(dev);
//read mode_type
ret = sscanf(buf, "%d", &mode_type);
if(ret < 1)
{
printk(KERN_ERR "Please enter the number 0-3 to enable the corresponding mode \n"
"Enter values in the non-0-3 range to get pattern description \n");
return size;
}
//Judgment model
if (mode_type < 0 || mode_type > 3) {
printk(KERN_DEBUG "Please enter the number range 0-3\n"
"0: Set the slave mode \n"
"1: Set the main mode \n"
"2: indicates setting SQI value view mode \n"
"3: Set the VCT value view mode \n"
"After the mode is set, the corresponding value can be obtained\n");
return ret ? ret : size;
}
//Set the Ethernet slave mode
if (mode_type == 0)
{
devad = 0x1;
reg = 0x834;
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, devad);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xe, reg);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, 0x4000 | devad);
val = mdiobus_read(priv->mii, priv->ndev->phydev->mdio.addr, 0x0e);
msleep(200);
val &= ~BIT(14);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, devad);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xe, reg);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, 0x4000 | devad);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xe, val );
}
//Set the Ethernet master mode
else if (mode_type == 1)
{
devad = 0x1;
reg = 0x834;
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, devad);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xe, reg);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, 0x4000 | devad);
val = mdiobus_read(priv->mii, priv->ndev->phydev->mdio.addr, 0x0e);
msleep(200);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, devad);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xe, reg);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, 0x4000 | devad);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xe, val | BIT(14));
}
return size;
}
static ssize_t gmac_master_or_slave_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int len = 0;
int val = 0;
int reg = 0;
int devad = 0;
int ret = 0;
struct emac_priv *priv = dev_get_drvdata(dev);
if(enter_only_one == 1)
{
return 0;
}
enter_only_one = 1;
//Read the network master/slave
if (mode_type == 0 || mode_type == 1)
{
devad = 0x1;
reg = 0x834;
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, devad);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xe, reg);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, 0x4000 | devad);
val = mdiobus_read(priv->mii, priv->ndev->phydev->mdio.addr, 0x0e) & BIT(14);
if(val)
memcpy(buf, "Master\n",7);
else
memcpy(buf, "Slave\n", 6);
printk(KERN_DEBUG "mode_type %d - gmac_master_or_slave is %s\n", mode_type, buf);
}
//Obtain the cable quality SQI value
else if(mode_type == 2)
{
devad = 0x1;
reg = 0x8B10;
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0x0d, devad);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0x0e, reg);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0x0d, 0x4000 | devad);
val = mdiobus_read(priv->mii, priv->ndev->phydev->mdio.addr, 0x0e);
sprintf(buf, "0x%x\n", val);
sprintf(buf, "SQI : 0x%x\n", val);
printk(KERN_DEBUG "mode_type %d - SQI is 0x%x", mode_type, val);
}
//Obtain short circuit, open circuit and normal connection of VCT
else if(mode_type == 3)
{
//--TDR Enable
devad = 0x1;
reg = 0x8B00;
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, devad);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xe, reg);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, 0x4000 | devad);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xe, BIT(14));
msleep(200);
//--TDR Start
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, devad);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xe, reg);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, 0x4000 | devad);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xe, BIT(12) | BIT(14));
msleep(20);
//--Read VCT
devad = 0x1;
reg = 0x8B02;
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, devad);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xe, reg);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, 0x4000 | devad);
val = mdiobus_read(priv->mii, priv->ndev->phydev->mdio.addr, 0xe);
printk(KERN_DEBUG "Open status: %s - Short status: %s\n",
(val & BIT(1)) ? "Open" : "Normal", (val & BIT(0)) ? "Short" : "Normal");
sprintf(buf, "Open status: %s\nShort status: %s\n",
(val & BIT(1)) ? "Open" : "Normal", (val & BIT(0)) ? "Short" : "Normal");
reg = 0x8B01;
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, devad);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xe, reg);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, 0x4000 | devad);
val = mdiobus_read(priv->mii, priv->ndev->phydev->mdio.addr, 0xe);
sprintf(buf, "%sDistance status: 0x%x\n", buf, val);
printk(KERN_DEBUG "mode_type %d - Distance status is 0x%x\n", mode_type, val);
//--TDR Disable
devad = 0x1;
reg = 0x8B00;
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, devad);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xe, reg);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xd, 0x4000 | devad);
mdiobus_write(priv->mii, priv->ndev->phydev->mdio.addr, 0xe, 0x0);
}
else{
sprintf(buf, "Please enter the number range 0-3\n"
"0: Set the slave mode \n"
"1: Set the main mode \n"
"2: indicates setting SQI value view mode \n"
"3: Set the VCT value view mode \n"
"After the mode is set, the corresponding value can be obtained\n");
printk(KERN_DEBUG "Please enter the number range 0-3\n"
"0: Set the slave mode \n"
"1: Set the main mode \n"
"2: indicates setting SQI value view mode \n"
"3: Set the VCT value view mode \n"
"After the mode is set, the corresponding value can be obtained\n");
}
enter_only_one = 0;
return strlen(buf);
}
static DEVICE_ATTR(lpsd_sleep, S_IRUGO | S_IWUSR, lpsd_sleep_show, lpsd_sleep_store);
static DEVICE_ATTR(phy_version, S_IRUGO | S_IWUSR, phy_version_show, phy_version_store);
static DEVICE_ATTR(gmac_master_or_slave, S_IRUGO | S_IWUSR, gmac_master_or_slave_show, gmac_master_or_slave_store);
static struct attribute *ethrnet_opera_attrs[] = {
&dev_attr_lpsd_sleep.attr,
&dev_attr_phy_version.attr,
&dev_attr_gmac_master_or_slave.attr,
NULL,
};
static const struct attribute_group demo_attr_grp = {
.attrs = ethrnet_opera_attrs,
};
static int emac_probe(struct platform_device *pdev)
{
struct emac_priv *priv;
struct net_device *ndev = NULL;
struct resource *res;
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
const unsigned char *mac_addr = NULL;
const struct of_device_id *match;
#ifdef CONFIG_DEBUG_FS
struct dentry *emac_fs_dir = NULL;
struct dentry *emac_clk_tuning;
#endif
int ret;
struct regulator *vcc3v3_gmac;
ndev = alloc_etherdev(sizeof(struct emac_priv));
if (!ndev) {
ret = -ENOMEM;
return ret;
}
priv = netdev_priv(ndev);
priv->ndev = ndev;
priv->pdev = pdev;
#ifdef WAN_LAN_AUTO_ADAPT
priv->dhcp = -1;
priv->vlan_port = -1;
priv->dhcp_delaywork = 0;
#endif
platform_set_drvdata(pdev, priv);
match = of_match_device(of_match_ptr(emac_of_match), &pdev->dev);
if (match) {
priv->regdata = match->data;
} else {
pr_info("===> not match valid device\n");
}
emac_command_options(priv);
emac_skbrb_init(EMAC_SKBRB_SLOT_SIZE, priv->rx_ring.total_cnt * 2);
priv->hw_stats = devm_kzalloc(&pdev->dev,
sizeof(*priv->hw_stats),
GFP_KERNEL);
if (!priv->hw_stats) {
dev_err(&pdev->dev, "failed to allocate counter memory\n");
ret = -ENOMEM;
goto err_netdev;
}
spin_lock_init(&priv->hw_stats->stats_lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->iobase = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(priv->iobase)) {
ret = -ENOMEM;
goto err_netdev;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
priv->tso_base = devm_ioremap_resource(&pdev->dev, res);
if (!IS_ERR(priv->tso_base)) {
dev_info(&pdev->dev, "tso base=0x%x\n", (unsigned)priv->tso_base);
}
priv->irq = irq_of_parse_and_map(np, 0);
if (!priv->irq) {
ret = -ENXIO;
goto err_netdev;
}
priv->irq_wakeup = irq_of_parse_and_map(np, 1);
if (!priv->irq_wakeup)
dev_err(&pdev->dev, "wake_up irq not found\n");
priv->tso = of_property_read_bool(np, "tso-support");
if (cpu_is_asr1903_a0() || cpu_is_asr1903_z1())
priv->tso = false;
if (priv->tso) {
priv->irq_tso = irq_of_parse_and_map(np, 3);
if (!priv->irq_tso) {
dev_err(&pdev->dev, "tso irq not found\n");
priv->tso = false;
}
}
priv->sram_pool = of_gen_pool_get(dev->of_node, "eth,sram", 0);
if (priv->sram_pool) {
dev_notice(&pdev->dev, "use sram as tx desc\n");
}
ret = of_property_read_u32(np, "lpm-qos", &priv->pm_qos);
if (ret)
return ret;
ret = of_property_read_u32(np, "3v3-enable", &priv->power_domain);
if (ret)
priv->power_domain = 0;
ret = of_property_read_u32(np, "mdio-clk-div", &priv->mdio_clk_div);
if (ret)
priv->mdio_clk_div = 0xfe;
if (of_property_read_bool(np, "enable-suspend"))
priv->en_suspend = 1;
else
priv->en_suspend = 0;
priv->wolopts = 0;
if (of_property_read_bool(np, "magic-packet-wakeup"))
priv->wolopts |= WAKE_MAGIC;
if (of_property_read_bool(np, "unicast-packet-wakeup"))
priv->wolopts |= WAKE_UCAST;
priv->dev_flags = 0;
if (of_property_read_bool(np, "suspend-not-keep-power")) {
priv->dev_flags |= EMAC_SUSPEND_POWER_DOWN_PHY;
priv->wolopts = 0;
}
vcc3v3_gmac = devm_regulator_get(dev, "vmmc");
if (!IS_ERR(vcc3v3_gmac))
{
if( regulator_set_voltage(vcc3v3_gmac, 1800000,1800000))
pr_err("fail to set regulator vcc3v3_gmac to 1.8v\n");
if (!regulator_is_enabled(vcc3v3_gmac) && regulator_enable(vcc3v3_gmac))
pr_err("fail to enable regulator vcc3v3_gmac\n");
}
g_vcc3v3_gmac = vcc3v3_gmac;
priv->pinctrl = devm_pinctrl_get(dev);
if (IS_ERR(priv->pinctrl))
dev_err(dev, "could not get pinctrl handle\n");
priv->rgmii_pins = pinctrl_lookup_state(priv->pinctrl, "rgmii-pins");
if (IS_ERR(priv->rgmii_pins))
dev_err(dev, "could not get rgmii-pins pinstate\n");
emac_set_aib_power_domain(priv);
device_init_wakeup(&pdev->dev, 1);
priv->pm_qos_req.name = pdev->name;
pm_qos_add_request(&priv->pm_qos_req, PM_QOS_CPUIDLE_BLOCK,
PM_QOS_CPUIDLE_BLOCK_DEFAULT_VALUE);
#ifdef CONFIG_DDR_DEVFREQ
pm_qos_add_request(&priv->pm_ddr_qos, PM_QOS_DDR_DEVFREQ_MIN,
PM_QOS_DEFAULT_VALUE);
priv->clk_scaling.polling_delay_ms = 1000; /* 1s window */
priv->clk_scaling.tx_up_threshold = 120; /* 120Mbps */
priv->clk_scaling.tx_down_threshold = 60;
priv->clk_scaling.rx_up_threshold = 60; /* 60Mbps */
priv->clk_scaling.rx_down_threshold = 20;
priv->clk_scaling.window_time = jiffies;
pm_qos_add_request(&priv->clk_scaling.ddr_qos, PM_QOS_DDR_DEVFREQ_MIN,
PM_QOS_DEFAULT_VALUE);
INIT_WORK(&priv->qos_work, emac_ddr_qos_work);
#endif
skb_queue_head_init(&priv->rx_skb);
ndev->watchdog_timeo = 5 * HZ;
ndev->base_addr = (unsigned long)priv->iobase;
ndev->irq = priv->irq;
/* set hw features */
ndev->features = NETIF_F_SG | NETIF_F_SOFT_FEATURES;
if (priv->tso) {
ndev->features |= NETIF_F_RXCSUM;
ndev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
ndev->features |= NETIF_F_TSO | NETIF_F_TSO6;
dev_info(&pdev->dev, "TSO feature enabled\n");
}
ndev->hw_features = ndev->features;
ndev->vlan_features = ndev->features;
ndev->ethtool_ops = &emac_ethtool_ops;
ndev->netdev_ops = &emac_netdev_ops;
if (pdev->dev.of_node)
mac_addr = of_get_mac_address(np);
if (!IS_ERR_OR_NULL(mac_addr)) {
//ether_addr_copy(ndev->dev_addr, mac_addr);
memcpy(ndev->dev_addr, mac_addr, ETH_ALEN);
if (!is_valid_ether_addr(ndev->dev_addr)) {
dev_info(&pdev->dev, "Using random mac address\n");
eth_hw_addr_random(ndev);
}
} else {
dev_info(&pdev->dev, "Using random mac address\n");
eth_hw_addr_random(ndev);
}
priv->hw_adj = of_property_read_bool(np, "hw-increment");
priv->ptp_support = of_property_read_bool(np, "ptp-support");
if (priv->ptp_support) {
pr_info("EMAC support IEEE1588 PTP Protocol\n");
if (of_property_read_u32(np, "ptp-clk-rate",
&priv->ptp_clk_rate)) {
priv->ptp_clk_rate = 20000000;
pr_info("%s ptp_clk rate using default value:%d may inaccurate!!1\n",
__func__, priv->ptp_clk_rate);
}
priv->ptp_clk = devm_clk_get(&pdev->dev, "ptp-clk");
if (IS_ERR(priv->ptp_clk)) {
dev_err(&pdev->dev, "ptp clock not found.\n");
ret = PTR_ERR(priv->ptp_clk);
goto err_netdev;
}
clk_set_rate(priv->ptp_clk, priv->ptp_clk_rate);
}
priv->pps_info.enable_pps = 0;
#ifdef CONFIG_PPS
ret = of_property_read_u32(np, "pps_source", &priv->pps_info.pps_source);
if (!ret) {
priv->irq_pps = irq_of_parse_and_map(np, 2);
if (priv->pps_info.pps_source < EMAC_PPS_MAX)
priv->pps_info.enable_pps = 1;
else
dev_err(&pdev->dev, "wrong PPS source!\n");
}
#endif
priv->clk = devm_clk_get(&pdev->dev, "emac-clk");
if (IS_ERR(priv->clk)) {
dev_err(&pdev->dev, "emac clock not found.\n");
ret = PTR_ERR(priv->clk);
goto err_netdev;
}
ret = clk_prepare_enable(priv->clk);
if (ret < 0) {
dev_err(&pdev->dev, "failed to enable emac clock: %d\n",
ret);
goto clk_disable;
}
emac_sw_init(priv);
ret = emac_mdio_init(priv);
if (ret)
goto clk_disable;
INIT_WORK(&priv->tx_timeout_task, emac_tx_timeout_task);
#ifdef WAN_LAN_AUTO_ADAPT
INIT_DELAYED_WORK(&priv->dhcp_work, emac_dhcp_work_func_t);
#endif
if (of_phy_is_fixed_link(np)) {
if ((emac_set_fixed_link(np, priv) < 0)) {
ret = -ENODEV;
goto clk_disable;
}
dev_info(&pdev->dev, "find fixed link\n");
priv->fix_link = 1;
}
INIT_DELAYED_WORK(&priv->emac_pause_work, emac_pause_generate_work_fuc);
SET_NETDEV_DEV(ndev, &pdev->dev);
strcpy(ndev->name, "eth%d");
ret = register_netdev(ndev);
if (ret) {
pr_err("register_netdev failed\n");
goto err_mdio_deinit;
}
dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
#ifdef CONFIG_ASR_EMAC_NAPI
netif_napi_add(ndev, &priv->rx_napi, emac_rx_poll, 32);
netif_tx_napi_add(ndev, &priv->tx_napi, emac_tx_poll, 32);
#endif
priv->tx_clk_config = TXCLK_PHASE_DEFAULT;
priv->rx_clk_config = RXCLK_PHASE_DEFAULT;
priv->clk_tuning_enable = of_property_read_bool(np, "clk-tuning-enable");
if (priv->clk_tuning_enable) {
ret = of_property_read_u32(np, "tx-clk-config",
&priv->tx_clk_config);
if (ret)
priv->tx_clk_config = TXCLK_PHASE_DEFAULT;
ret = of_property_read_u32(np, "rx-clk-config",
&priv->rx_clk_config);
if (ret)
priv->rx_clk_config = RXCLK_PHASE_DEFAULT;
#ifdef CONFIG_DEBUG_FS
if (!emac_fs_dir) {
emac_fs_dir = debugfs_create_dir(DRIVER_NAME, NULL);
if (!emac_fs_dir || IS_ERR(emac_fs_dir)) {
pr_err("emac debugfs create directory failed\n");
}else {
emac_clk_tuning = debugfs_create_file("clk_tuning", 0664,
emac_fs_dir, priv, &clk_tuning_fops);
if (!emac_clk_tuning) {
pr_err("emac debugfs create file failed\n");
}
}
}
#endif
}
sysfs_create_group(&pdev->dev.kobj,&demo_attr_grp);
//device_create_file(&pdev->dev, &dev_attr_cable_sqi_value);
return 0;
err_mdio_deinit:
emac_mdio_deinit(priv);
clk_disable:
clk_disable_unprepare(priv->clk);
err_netdev:
free_netdev(ndev);
emac_skbrb_release();
return ret;
}
static int emac_remove(struct platform_device *pdev)
{
struct emac_priv *priv = platform_get_drvdata(pdev);
device_init_wakeup(&pdev->dev, 0);
unregister_netdev(priv->ndev);
emac_reset_hw(priv);
free_netdev(priv->ndev);
emac_mdio_deinit(priv);
clk_disable_unprepare(priv->clk);
pm_qos_remove_request(&priv->pm_qos_req);
cancel_delayed_work_sync(&priv->emac_pause_work);
#ifdef CONFIG_DDR_DEVFREQ
pm_qos_remove_request(&priv->pm_ddr_qos);
pm_qos_remove_request(&priv->clk_scaling.ddr_qos);
#endif
emac_skbrb_release();
return 0;
}
static void emac_shutdown(struct platform_device *pdev)
{
}
#ifdef CONFIG_PM_SLEEP
static int emac_resume(struct device *dev)
{
struct emac_priv *priv = dev_get_drvdata(dev);
struct net_device *ndev = priv->ndev;
u32 ctrl, wake_mode = 0;
if (!priv->en_suspend)
return 0;
if (priv->wolopts) {
if (netif_running(ndev)) {
netif_device_attach(ndev);
#ifdef CONFIG_ASR_EMAC_NAPI
napi_enable(&priv->rx_napi);
napi_enable(&priv->tx_napi);
#endif
}
if (priv->wolopts & WAKE_MAGIC)
wake_mode |= MREGBIT_UNICAST_WAKEUP_MODE;
if (priv->wolopts & WAKE_UCAST)
wake_mode |= MREGBIT_MAGIC_PACKET_WAKEUP_MODE;
disable_irq_wake(priv->irq_wakeup);
ctrl = emac_rd(priv, MAC_GLOBAL_CONTROL);
ctrl &= ~wake_mode;
emac_wr(priv, MAC_GLOBAL_CONTROL, ctrl);
} else {
clk_prepare_enable(priv->clk);
if (priv->dev_flags & EMAC_SUSPEND_POWER_DOWN_PHY)
emac_power_up(priv);
rtnl_lock();
dev_open(ndev, NULL);
rtnl_unlock();
}
return 0;
}
static int emac_suspend(struct device *dev)
{
struct emac_priv *priv = dev_get_drvdata(dev);
struct net_device *ndev = priv->ndev;
u32 ctrl, wake_mode = 0;
if (!priv->en_suspend)
return 0;
if (priv->wolopts) {
if (netif_running(ndev)) {
netif_device_detach(ndev);
#ifdef CONFIG_ASR_EMAC_NAPI
napi_disable(&priv->rx_napi);
napi_disable(&priv->tx_napi);
#endif
}
if (priv->wolopts & WAKE_MAGIC)
wake_mode |= MREGBIT_UNICAST_WAKEUP_MODE;
if (priv->wolopts & WAKE_UCAST)
wake_mode |= MREGBIT_MAGIC_PACKET_WAKEUP_MODE;
ctrl = emac_rd(priv, MAC_GLOBAL_CONTROL);
ctrl |= wake_mode;
emac_wr(priv, MAC_GLOBAL_CONTROL, ctrl);
enable_irq_wake(priv->irq_wakeup);
} else {
rtnl_lock();
dev_close(ndev);
rtnl_unlock();
if (priv->dev_flags & EMAC_SUSPEND_POWER_DOWN_PHY)
emac_power_down(priv);
clk_disable_unprepare(priv->clk);
}
return 0;
}
static int emac_suspend_noirq(struct device *dev)
{
struct emac_priv *priv = dev_get_drvdata(dev);
struct net_device *ndev = priv->ndev;
if (!ndev->phydev && !priv->fix_link)
return 0;
pr_pm_debug("==> enter emac_suspend_noirq\n");
pm_qos_update_request(&priv->pm_qos_req,
PM_QOS_CPUIDLE_BLOCK_DEFAULT_VALUE);
return 0;
}
static int emac_resume_noirq(struct device *dev)
{
struct emac_priv *priv = dev_get_drvdata(dev);
struct net_device *ndev = priv->ndev;
if (!ndev->phydev && !priv->fix_link)
return 0;
pr_pm_debug("==> enter emac_resume_noirq\n");
pm_qos_update_request(&priv->pm_qos_req, priv->pm_qos);
return 0;
}
static const struct dev_pm_ops emac_pm_ops = {
.suspend = emac_suspend,
.resume = emac_resume,
.suspend_noirq = emac_suspend_noirq,
.resume_noirq = emac_resume_noirq,
};
#define ASR_EMAC_PM_OPS (&emac_pm_ops)
#else
#define ASR_EMAC_PM_OPS NULL
#endif
static struct platform_driver emac_driver = {
.probe = emac_probe,
.remove = emac_remove,
.shutdown = emac_shutdown,
.driver = {
.name = DRIVER_NAME,
.of_match_table = of_match_ptr(emac_of_match),
.pm = ASR_EMAC_PM_OPS,
},
};
module_platform_driver(emac_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Ethernet driver for ASR Emac");
MODULE_ALIAS("platform:asr_eth");