|
@@ -11,8 +11,10 @@
|
|
#include <linux/interrupt.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/platform_device.h>
|
|
|
|
+#include <linux/of_device.h>
|
|
#include <linux/of_net.h>
|
|
#include <linux/of_net.h>
|
|
#include <linux/of_mdio.h>
|
|
#include <linux/of_mdio.h>
|
|
|
|
+#include <linux/reset.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/circ_buf.h>
|
|
#include <linux/circ_buf.h>
|
|
|
|
|
|
@@ -183,12 +185,28 @@
|
|
#define DESC_DATA_LEN_OFF 16
|
|
#define DESC_DATA_LEN_OFF 16
|
|
#define DESC_BUFF_LEN_OFF 0
|
|
#define DESC_BUFF_LEN_OFF 0
|
|
#define DESC_DATA_MASK 0x7ff
|
|
#define DESC_DATA_MASK 0x7ff
|
|
|
|
+#define DESC_SG BIT(30)
|
|
|
|
+#define DESC_FRAGS_NUM_OFF 11
|
|
|
|
|
|
/* DMA descriptor ring helpers */
|
|
/* DMA descriptor ring helpers */
|
|
#define dma_ring_incr(n, s) (((n) + 1) & ((s) - 1))
|
|
#define dma_ring_incr(n, s) (((n) + 1) & ((s) - 1))
|
|
#define dma_cnt(n) ((n) >> 5)
|
|
#define dma_cnt(n) ((n) >> 5)
|
|
#define dma_byte(n) ((n) << 5)
|
|
#define dma_byte(n) ((n) << 5)
|
|
|
|
|
|
|
|
+#define HW_CAP_TSO BIT(0)
|
|
|
|
+#define GEMAC_V1 0
|
|
|
|
+#define GEMAC_V2 (GEMAC_V1 | HW_CAP_TSO)
|
|
|
|
+#define HAS_CAP_TSO(hw_cap) ((hw_cap) & HW_CAP_TSO)
|
|
|
|
+
|
|
|
|
+#define PHY_RESET_DELAYS_PROPERTY "hisilicon,phy-reset-delays-us"
|
|
|
|
+
|
|
|
|
+enum phy_reset_delays {
|
|
|
|
+ PRE_DELAY,
|
|
|
|
+ PULSE,
|
|
|
|
+ POST_DELAY,
|
|
|
|
+ DELAYS_NUM,
|
|
|
|
+};
|
|
|
|
+
|
|
struct hix5hd2_desc {
|
|
struct hix5hd2_desc {
|
|
__le32 buff_addr;
|
|
__le32 buff_addr;
|
|
__le32 cmd;
|
|
__le32 cmd;
|
|
@@ -201,6 +219,27 @@ struct hix5hd2_desc_sw {
|
|
unsigned int size;
|
|
unsigned int size;
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+struct hix5hd2_sg_desc_ring {
|
|
|
|
+ struct sg_desc *desc;
|
|
|
|
+ dma_addr_t phys_addr;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+struct frags_info {
|
|
|
|
+ __le32 addr;
|
|
|
|
+ __le32 size;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/* hardware supported max skb frags num */
|
|
|
|
+#define SG_MAX_SKB_FRAGS 17
|
|
|
|
+struct sg_desc {
|
|
|
|
+ __le32 total_len;
|
|
|
|
+ __le32 resvd0;
|
|
|
|
+ __le32 linear_addr;
|
|
|
|
+ __le32 linear_len;
|
|
|
|
+ /* reserve one more frags for memory alignment */
|
|
|
|
+ struct frags_info frags[SG_MAX_SKB_FRAGS + 1];
|
|
|
|
+};
|
|
|
|
+
|
|
#define QUEUE_NUMS 4
|
|
#define QUEUE_NUMS 4
|
|
struct hix5hd2_priv {
|
|
struct hix5hd2_priv {
|
|
struct hix5hd2_desc_sw pool[QUEUE_NUMS];
|
|
struct hix5hd2_desc_sw pool[QUEUE_NUMS];
|
|
@@ -208,6 +247,7 @@ struct hix5hd2_priv {
|
|
#define rx_bq pool[1]
|
|
#define rx_bq pool[1]
|
|
#define tx_bq pool[2]
|
|
#define tx_bq pool[2]
|
|
#define tx_rq pool[3]
|
|
#define tx_rq pool[3]
|
|
|
|
+ struct hix5hd2_sg_desc_ring tx_ring;
|
|
|
|
|
|
void __iomem *base;
|
|
void __iomem *base;
|
|
void __iomem *ctrl_base;
|
|
void __iomem *ctrl_base;
|
|
@@ -221,15 +261,30 @@ struct hix5hd2_priv {
|
|
struct device_node *phy_node;
|
|
struct device_node *phy_node;
|
|
phy_interface_t phy_mode;
|
|
phy_interface_t phy_mode;
|
|
|
|
|
|
|
|
+ unsigned long hw_cap;
|
|
unsigned int speed;
|
|
unsigned int speed;
|
|
unsigned int duplex;
|
|
unsigned int duplex;
|
|
|
|
|
|
- struct clk *clk;
|
|
|
|
|
|
+ struct clk *mac_core_clk;
|
|
|
|
+ struct clk *mac_ifc_clk;
|
|
|
|
+ struct reset_control *mac_core_rst;
|
|
|
|
+ struct reset_control *mac_ifc_rst;
|
|
|
|
+ struct reset_control *phy_rst;
|
|
|
|
+ u32 phy_reset_delays[DELAYS_NUM];
|
|
struct mii_bus *bus;
|
|
struct mii_bus *bus;
|
|
struct napi_struct napi;
|
|
struct napi_struct napi;
|
|
struct work_struct tx_timeout_task;
|
|
struct work_struct tx_timeout_task;
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+static inline void hix5hd2_mac_interface_reset(struct hix5hd2_priv *priv)
|
|
|
|
+{
|
|
|
|
+ if (!priv->mac_ifc_rst)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ reset_control_assert(priv->mac_ifc_rst);
|
|
|
|
+ reset_control_deassert(priv->mac_ifc_rst);
|
|
|
|
+}
|
|
|
|
+
|
|
static void hix5hd2_config_port(struct net_device *dev, u32 speed, u32 duplex)
|
|
static void hix5hd2_config_port(struct net_device *dev, u32 speed, u32 duplex)
|
|
{
|
|
{
|
|
struct hix5hd2_priv *priv = netdev_priv(dev);
|
|
struct hix5hd2_priv *priv = netdev_priv(dev);
|
|
@@ -262,6 +317,7 @@ static void hix5hd2_config_port(struct net_device *dev, u32 speed, u32 duplex)
|
|
if (duplex)
|
|
if (duplex)
|
|
val |= GMAC_FULL_DUPLEX;
|
|
val |= GMAC_FULL_DUPLEX;
|
|
writel_relaxed(val, priv->ctrl_base);
|
|
writel_relaxed(val, priv->ctrl_base);
|
|
|
|
+ hix5hd2_mac_interface_reset(priv);
|
|
|
|
|
|
writel_relaxed(BIT_MODE_CHANGE_EN, priv->base + MODE_CHANGE_EN);
|
|
writel_relaxed(BIT_MODE_CHANGE_EN, priv->base + MODE_CHANGE_EN);
|
|
if (speed == SPEED_1000)
|
|
if (speed == SPEED_1000)
|
|
@@ -511,6 +567,27 @@ next:
|
|
return num;
|
|
return num;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void hix5hd2_clean_sg_desc(struct hix5hd2_priv *priv,
|
|
|
|
+ struct sk_buff *skb, u32 pos)
|
|
|
|
+{
|
|
|
|
+ struct sg_desc *desc;
|
|
|
|
+ dma_addr_t addr;
|
|
|
|
+ u32 len;
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ desc = priv->tx_ring.desc + pos;
|
|
|
|
+
|
|
|
|
+ addr = le32_to_cpu(desc->linear_addr);
|
|
|
|
+ len = le32_to_cpu(desc->linear_len);
|
|
|
|
+ dma_unmap_single(priv->dev, addr, len, DMA_TO_DEVICE);
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
|
|
|
|
+ addr = le32_to_cpu(desc->frags[i].addr);
|
|
|
|
+ len = le32_to_cpu(desc->frags[i].size);
|
|
|
|
+ dma_unmap_page(priv->dev, addr, len, DMA_TO_DEVICE);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
static void hix5hd2_xmit_reclaim(struct net_device *dev)
|
|
static void hix5hd2_xmit_reclaim(struct net_device *dev)
|
|
{
|
|
{
|
|
struct sk_buff *skb;
|
|
struct sk_buff *skb;
|
|
@@ -538,8 +615,15 @@ static void hix5hd2_xmit_reclaim(struct net_device *dev)
|
|
pkts_compl++;
|
|
pkts_compl++;
|
|
bytes_compl += skb->len;
|
|
bytes_compl += skb->len;
|
|
desc = priv->tx_rq.desc + pos;
|
|
desc = priv->tx_rq.desc + pos;
|
|
- addr = le32_to_cpu(desc->buff_addr);
|
|
|
|
- dma_unmap_single(priv->dev, addr, skb->len, DMA_TO_DEVICE);
|
|
|
|
|
|
+
|
|
|
|
+ if (skb_shinfo(skb)->nr_frags) {
|
|
|
|
+ hix5hd2_clean_sg_desc(priv, skb, pos);
|
|
|
|
+ } else {
|
|
|
|
+ addr = le32_to_cpu(desc->buff_addr);
|
|
|
|
+ dma_unmap_single(priv->dev, addr, skb->len,
|
|
|
|
+ DMA_TO_DEVICE);
|
|
|
|
+ }
|
|
|
|
+
|
|
priv->tx_skb[pos] = NULL;
|
|
priv->tx_skb[pos] = NULL;
|
|
dev_consume_skb_any(skb);
|
|
dev_consume_skb_any(skb);
|
|
pos = dma_ring_incr(pos, TX_DESC_NUM);
|
|
pos = dma_ring_incr(pos, TX_DESC_NUM);
|
|
@@ -600,12 +684,66 @@ static irqreturn_t hix5hd2_interrupt(int irq, void *dev_id)
|
|
return IRQ_HANDLED;
|
|
return IRQ_HANDLED;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static u32 hix5hd2_get_desc_cmd(struct sk_buff *skb, unsigned long hw_cap)
|
|
|
|
+{
|
|
|
|
+ u32 cmd = 0;
|
|
|
|
+
|
|
|
|
+ if (HAS_CAP_TSO(hw_cap)) {
|
|
|
|
+ if (skb_shinfo(skb)->nr_frags)
|
|
|
|
+ cmd |= DESC_SG;
|
|
|
|
+ cmd |= skb_shinfo(skb)->nr_frags << DESC_FRAGS_NUM_OFF;
|
|
|
|
+ } else {
|
|
|
|
+ cmd |= DESC_FL_FULL |
|
|
|
|
+ ((skb->len & DESC_DATA_MASK) << DESC_BUFF_LEN_OFF);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ cmd |= (skb->len & DESC_DATA_MASK) << DESC_DATA_LEN_OFF;
|
|
|
|
+ cmd |= DESC_VLD_BUSY;
|
|
|
|
+
|
|
|
|
+ return cmd;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int hix5hd2_fill_sg_desc(struct hix5hd2_priv *priv,
|
|
|
|
+ struct sk_buff *skb, u32 pos)
|
|
|
|
+{
|
|
|
|
+ struct sg_desc *desc;
|
|
|
|
+ dma_addr_t addr;
|
|
|
|
+ int ret;
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ desc = priv->tx_ring.desc + pos;
|
|
|
|
+
|
|
|
|
+ desc->total_len = cpu_to_le32(skb->len);
|
|
|
|
+ addr = dma_map_single(priv->dev, skb->data, skb_headlen(skb),
|
|
|
|
+ DMA_TO_DEVICE);
|
|
|
|
+ if (unlikely(dma_mapping_error(priv->dev, addr)))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ desc->linear_addr = cpu_to_le32(addr);
|
|
|
|
+ desc->linear_len = cpu_to_le32(skb_headlen(skb));
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
|
|
|
|
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
|
|
|
|
+ int len = frag->size;
|
|
|
|
+
|
|
|
|
+ addr = skb_frag_dma_map(priv->dev, frag, 0, len, DMA_TO_DEVICE);
|
|
|
|
+ ret = dma_mapping_error(priv->dev, addr);
|
|
|
|
+ if (unlikely(ret))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ desc->frags[i].addr = cpu_to_le32(addr);
|
|
|
|
+ desc->frags[i].size = cpu_to_le32(len);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
static int hix5hd2_net_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
static int hix5hd2_net_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
{
|
|
{
|
|
struct hix5hd2_priv *priv = netdev_priv(dev);
|
|
struct hix5hd2_priv *priv = netdev_priv(dev);
|
|
struct hix5hd2_desc *desc;
|
|
struct hix5hd2_desc *desc;
|
|
dma_addr_t addr;
|
|
dma_addr_t addr;
|
|
u32 pos;
|
|
u32 pos;
|
|
|
|
+ u32 cmd;
|
|
|
|
+ int ret;
|
|
|
|
|
|
/* software write pointer */
|
|
/* software write pointer */
|
|
pos = dma_cnt(readl_relaxed(priv->base + TX_BQ_WR_ADDR));
|
|
pos = dma_cnt(readl_relaxed(priv->base + TX_BQ_WR_ADDR));
|
|
@@ -616,18 +754,31 @@ static int hix5hd2_net_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
return NETDEV_TX_BUSY;
|
|
return NETDEV_TX_BUSY;
|
|
}
|
|
}
|
|
|
|
|
|
- addr = dma_map_single(priv->dev, skb->data, skb->len, DMA_TO_DEVICE);
|
|
|
|
- if (dma_mapping_error(priv->dev, addr)) {
|
|
|
|
- dev_kfree_skb_any(skb);
|
|
|
|
- return NETDEV_TX_OK;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
desc = priv->tx_bq.desc + pos;
|
|
desc = priv->tx_bq.desc + pos;
|
|
|
|
+
|
|
|
|
+ cmd = hix5hd2_get_desc_cmd(skb, priv->hw_cap);
|
|
|
|
+ desc->cmd = cpu_to_le32(cmd);
|
|
|
|
+
|
|
|
|
+ if (skb_shinfo(skb)->nr_frags) {
|
|
|
|
+ ret = hix5hd2_fill_sg_desc(priv, skb, pos);
|
|
|
|
+ if (unlikely(ret)) {
|
|
|
|
+ dev_kfree_skb_any(skb);
|
|
|
|
+ dev->stats.tx_dropped++;
|
|
|
|
+ return NETDEV_TX_OK;
|
|
|
|
+ }
|
|
|
|
+ addr = priv->tx_ring.phys_addr + pos * sizeof(struct sg_desc);
|
|
|
|
+ } else {
|
|
|
|
+ addr = dma_map_single(priv->dev, skb->data, skb->len,
|
|
|
|
+ DMA_TO_DEVICE);
|
|
|
|
+ if (unlikely(dma_mapping_error(priv->dev, addr))) {
|
|
|
|
+ dev_kfree_skb_any(skb);
|
|
|
|
+ dev->stats.tx_dropped++;
|
|
|
|
+ return NETDEV_TX_OK;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
desc->buff_addr = cpu_to_le32(addr);
|
|
desc->buff_addr = cpu_to_le32(addr);
|
|
|
|
+
|
|
priv->tx_skb[pos] = skb;
|
|
priv->tx_skb[pos] = skb;
|
|
- desc->cmd = cpu_to_le32(DESC_VLD_BUSY | DESC_FL_FULL |
|
|
|
|
- (skb->len & DESC_DATA_MASK) << DESC_DATA_LEN_OFF |
|
|
|
|
- (skb->len & DESC_DATA_MASK) << DESC_BUFF_LEN_OFF);
|
|
|
|
|
|
|
|
/* ensure desc updated */
|
|
/* ensure desc updated */
|
|
wmb();
|
|
wmb();
|
|
@@ -681,16 +832,26 @@ static int hix5hd2_net_open(struct net_device *dev)
|
|
struct phy_device *phy;
|
|
struct phy_device *phy;
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
- ret = clk_prepare_enable(priv->clk);
|
|
|
|
|
|
+ ret = clk_prepare_enable(priv->mac_core_clk);
|
|
|
|
+ if (ret < 0) {
|
|
|
|
+ netdev_err(dev, "failed to enable mac core clk %d\n", ret);
|
|
|
|
+ return ret;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ret = clk_prepare_enable(priv->mac_ifc_clk);
|
|
if (ret < 0) {
|
|
if (ret < 0) {
|
|
- netdev_err(dev, "failed to enable clk %d\n", ret);
|
|
|
|
|
|
+ clk_disable_unprepare(priv->mac_core_clk);
|
|
|
|
+ netdev_err(dev, "failed to enable mac ifc clk %d\n", ret);
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
phy = of_phy_connect(dev, priv->phy_node,
|
|
phy = of_phy_connect(dev, priv->phy_node,
|
|
&hix5hd2_adjust_link, 0, priv->phy_mode);
|
|
&hix5hd2_adjust_link, 0, priv->phy_mode);
|
|
- if (!phy)
|
|
|
|
|
|
+ if (!phy) {
|
|
|
|
+ clk_disable_unprepare(priv->mac_ifc_clk);
|
|
|
|
+ clk_disable_unprepare(priv->mac_core_clk);
|
|
return -ENODEV;
|
|
return -ENODEV;
|
|
|
|
+ }
|
|
|
|
|
|
phy_start(phy);
|
|
phy_start(phy);
|
|
hix5hd2_hw_init(priv);
|
|
hix5hd2_hw_init(priv);
|
|
@@ -721,7 +882,8 @@ static int hix5hd2_net_close(struct net_device *dev)
|
|
phy_disconnect(dev->phydev);
|
|
phy_disconnect(dev->phydev);
|
|
}
|
|
}
|
|
|
|
|
|
- clk_disable_unprepare(priv->clk);
|
|
|
|
|
|
+ clk_disable_unprepare(priv->mac_ifc_clk);
|
|
|
|
+ clk_disable_unprepare(priv->mac_core_clk);
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -862,10 +1024,82 @@ error_free_pool:
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int hix5hd2_init_sg_desc_queue(struct hix5hd2_priv *priv)
|
|
|
|
+{
|
|
|
|
+ struct sg_desc *desc;
|
|
|
|
+ dma_addr_t phys_addr;
|
|
|
|
+
|
|
|
|
+ desc = (struct sg_desc *)dma_alloc_coherent(priv->dev,
|
|
|
|
+ TX_DESC_NUM * sizeof(struct sg_desc),
|
|
|
|
+ &phys_addr, GFP_KERNEL);
|
|
|
|
+ if (!desc)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ priv->tx_ring.desc = desc;
|
|
|
|
+ priv->tx_ring.phys_addr = phys_addr;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void hix5hd2_destroy_sg_desc_queue(struct hix5hd2_priv *priv)
|
|
|
|
+{
|
|
|
|
+ if (priv->tx_ring.desc) {
|
|
|
|
+ dma_free_coherent(priv->dev,
|
|
|
|
+ TX_DESC_NUM * sizeof(struct sg_desc),
|
|
|
|
+ priv->tx_ring.desc, priv->tx_ring.phys_addr);
|
|
|
|
+ priv->tx_ring.desc = NULL;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static inline void hix5hd2_mac_core_reset(struct hix5hd2_priv *priv)
|
|
|
|
+{
|
|
|
|
+ if (!priv->mac_core_rst)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ reset_control_assert(priv->mac_core_rst);
|
|
|
|
+ reset_control_deassert(priv->mac_core_rst);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void hix5hd2_sleep_us(u32 time_us)
|
|
|
|
+{
|
|
|
|
+ u32 time_ms;
|
|
|
|
+
|
|
|
|
+ if (!time_us)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ time_ms = DIV_ROUND_UP(time_us, 1000);
|
|
|
|
+ if (time_ms < 20)
|
|
|
|
+ usleep_range(time_us, time_us + 500);
|
|
|
|
+ else
|
|
|
|
+ msleep(time_ms);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void hix5hd2_phy_reset(struct hix5hd2_priv *priv)
|
|
|
|
+{
|
|
|
|
+ /* To make sure PHY hardware reset success,
|
|
|
|
+ * we must keep PHY in deassert state first and
|
|
|
|
+ * then complete the hardware reset operation
|
|
|
|
+ */
|
|
|
|
+ reset_control_deassert(priv->phy_rst);
|
|
|
|
+ hix5hd2_sleep_us(priv->phy_reset_delays[PRE_DELAY]);
|
|
|
|
+
|
|
|
|
+ reset_control_assert(priv->phy_rst);
|
|
|
|
+ /* delay some time to ensure reset ok,
|
|
|
|
+ * this depends on PHY hardware feature
|
|
|
|
+ */
|
|
|
|
+ hix5hd2_sleep_us(priv->phy_reset_delays[PULSE]);
|
|
|
|
+ reset_control_deassert(priv->phy_rst);
|
|
|
|
+ /* delay some time to ensure later MDIO access */
|
|
|
|
+ hix5hd2_sleep_us(priv->phy_reset_delays[POST_DELAY]);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static const struct of_device_id hix5hd2_of_match[];
|
|
|
|
+
|
|
static int hix5hd2_dev_probe(struct platform_device *pdev)
|
|
static int hix5hd2_dev_probe(struct platform_device *pdev)
|
|
{
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct device *dev = &pdev->dev;
|
|
struct device_node *node = dev->of_node;
|
|
struct device_node *node = dev->of_node;
|
|
|
|
+ const struct of_device_id *of_id = NULL;
|
|
struct net_device *ndev;
|
|
struct net_device *ndev;
|
|
struct hix5hd2_priv *priv;
|
|
struct hix5hd2_priv *priv;
|
|
struct resource *res;
|
|
struct resource *res;
|
|
@@ -883,6 +1117,13 @@ static int hix5hd2_dev_probe(struct platform_device *pdev)
|
|
priv->dev = dev;
|
|
priv->dev = dev;
|
|
priv->netdev = ndev;
|
|
priv->netdev = ndev;
|
|
|
|
|
|
|
|
+ of_id = of_match_device(hix5hd2_of_match, dev);
|
|
|
|
+ if (!of_id) {
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ goto out_free_netdev;
|
|
|
|
+ }
|
|
|
|
+ priv->hw_cap = (unsigned long)of_id->data;
|
|
|
|
+
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
priv->base = devm_ioremap_resource(dev, res);
|
|
priv->base = devm_ioremap_resource(dev, res);
|
|
if (IS_ERR(priv->base)) {
|
|
if (IS_ERR(priv->base)) {
|
|
@@ -897,23 +1138,55 @@ static int hix5hd2_dev_probe(struct platform_device *pdev)
|
|
goto out_free_netdev;
|
|
goto out_free_netdev;
|
|
}
|
|
}
|
|
|
|
|
|
- priv->clk = devm_clk_get(&pdev->dev, NULL);
|
|
|
|
- if (IS_ERR(priv->clk)) {
|
|
|
|
- netdev_err(ndev, "failed to get clk\n");
|
|
|
|
|
|
+ priv->mac_core_clk = devm_clk_get(&pdev->dev, "mac_core");
|
|
|
|
+ if (IS_ERR(priv->mac_core_clk)) {
|
|
|
|
+ netdev_err(ndev, "failed to get mac core clk\n");
|
|
ret = -ENODEV;
|
|
ret = -ENODEV;
|
|
goto out_free_netdev;
|
|
goto out_free_netdev;
|
|
}
|
|
}
|
|
|
|
|
|
- ret = clk_prepare_enable(priv->clk);
|
|
|
|
|
|
+ ret = clk_prepare_enable(priv->mac_core_clk);
|
|
if (ret < 0) {
|
|
if (ret < 0) {
|
|
- netdev_err(ndev, "failed to enable clk %d\n", ret);
|
|
|
|
|
|
+ netdev_err(ndev, "failed to enable mac core clk %d\n", ret);
|
|
goto out_free_netdev;
|
|
goto out_free_netdev;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ priv->mac_ifc_clk = devm_clk_get(&pdev->dev, "mac_ifc");
|
|
|
|
+ if (IS_ERR(priv->mac_ifc_clk))
|
|
|
|
+ priv->mac_ifc_clk = NULL;
|
|
|
|
+
|
|
|
|
+ ret = clk_prepare_enable(priv->mac_ifc_clk);
|
|
|
|
+ if (ret < 0) {
|
|
|
|
+ netdev_err(ndev, "failed to enable mac ifc clk %d\n", ret);
|
|
|
|
+ goto out_disable_mac_core_clk;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ priv->mac_core_rst = devm_reset_control_get(dev, "mac_core");
|
|
|
|
+ if (IS_ERR(priv->mac_core_rst))
|
|
|
|
+ priv->mac_core_rst = NULL;
|
|
|
|
+ hix5hd2_mac_core_reset(priv);
|
|
|
|
+
|
|
|
|
+ priv->mac_ifc_rst = devm_reset_control_get(dev, "mac_ifc");
|
|
|
|
+ if (IS_ERR(priv->mac_ifc_rst))
|
|
|
|
+ priv->mac_ifc_rst = NULL;
|
|
|
|
+
|
|
|
|
+ priv->phy_rst = devm_reset_control_get(dev, "phy");
|
|
|
|
+ if (IS_ERR(priv->phy_rst)) {
|
|
|
|
+ priv->phy_rst = NULL;
|
|
|
|
+ } else {
|
|
|
|
+ ret = of_property_read_u32_array(node,
|
|
|
|
+ PHY_RESET_DELAYS_PROPERTY,
|
|
|
|
+ priv->phy_reset_delays,
|
|
|
|
+ DELAYS_NUM);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto out_disable_clk;
|
|
|
|
+ hix5hd2_phy_reset(priv);
|
|
|
|
+ }
|
|
|
|
+
|
|
bus = mdiobus_alloc();
|
|
bus = mdiobus_alloc();
|
|
if (bus == NULL) {
|
|
if (bus == NULL) {
|
|
ret = -ENOMEM;
|
|
ret = -ENOMEM;
|
|
- goto out_free_netdev;
|
|
|
|
|
|
+ goto out_disable_clk;
|
|
}
|
|
}
|
|
|
|
|
|
bus->priv = priv;
|
|
bus->priv = priv;
|
|
@@ -972,22 +1245,38 @@ static int hix5hd2_dev_probe(struct platform_device *pdev)
|
|
ndev->ethtool_ops = &hix5hd2_ethtools_ops;
|
|
ndev->ethtool_ops = &hix5hd2_ethtools_ops;
|
|
SET_NETDEV_DEV(ndev, dev);
|
|
SET_NETDEV_DEV(ndev, dev);
|
|
|
|
|
|
|
|
+ if (HAS_CAP_TSO(priv->hw_cap))
|
|
|
|
+ ndev->hw_features |= NETIF_F_SG;
|
|
|
|
+
|
|
|
|
+ ndev->features |= ndev->hw_features | NETIF_F_HIGHDMA;
|
|
|
|
+ ndev->vlan_features |= ndev->features;
|
|
|
|
+
|
|
ret = hix5hd2_init_hw_desc_queue(priv);
|
|
ret = hix5hd2_init_hw_desc_queue(priv);
|
|
if (ret)
|
|
if (ret)
|
|
goto out_phy_node;
|
|
goto out_phy_node;
|
|
|
|
|
|
netif_napi_add(ndev, &priv->napi, hix5hd2_poll, NAPI_POLL_WEIGHT);
|
|
netif_napi_add(ndev, &priv->napi, hix5hd2_poll, NAPI_POLL_WEIGHT);
|
|
|
|
+
|
|
|
|
+ if (HAS_CAP_TSO(priv->hw_cap)) {
|
|
|
|
+ ret = hix5hd2_init_sg_desc_queue(priv);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto out_destroy_queue;
|
|
|
|
+ }
|
|
|
|
+
|
|
ret = register_netdev(priv->netdev);
|
|
ret = register_netdev(priv->netdev);
|
|
if (ret) {
|
|
if (ret) {
|
|
netdev_err(ndev, "register_netdev failed!");
|
|
netdev_err(ndev, "register_netdev failed!");
|
|
goto out_destroy_queue;
|
|
goto out_destroy_queue;
|
|
}
|
|
}
|
|
|
|
|
|
- clk_disable_unprepare(priv->clk);
|
|
|
|
|
|
+ clk_disable_unprepare(priv->mac_ifc_clk);
|
|
|
|
+ clk_disable_unprepare(priv->mac_core_clk);
|
|
|
|
|
|
return ret;
|
|
return ret;
|
|
|
|
|
|
out_destroy_queue:
|
|
out_destroy_queue:
|
|
|
|
+ if (HAS_CAP_TSO(priv->hw_cap))
|
|
|
|
+ hix5hd2_destroy_sg_desc_queue(priv);
|
|
netif_napi_del(&priv->napi);
|
|
netif_napi_del(&priv->napi);
|
|
hix5hd2_destroy_hw_desc_queue(priv);
|
|
hix5hd2_destroy_hw_desc_queue(priv);
|
|
out_phy_node:
|
|
out_phy_node:
|
|
@@ -996,6 +1285,10 @@ err_mdiobus:
|
|
mdiobus_unregister(bus);
|
|
mdiobus_unregister(bus);
|
|
err_free_mdio:
|
|
err_free_mdio:
|
|
mdiobus_free(bus);
|
|
mdiobus_free(bus);
|
|
|
|
+out_disable_clk:
|
|
|
|
+ clk_disable_unprepare(priv->mac_ifc_clk);
|
|
|
|
+out_disable_mac_core_clk:
|
|
|
|
+ clk_disable_unprepare(priv->mac_core_clk);
|
|
out_free_netdev:
|
|
out_free_netdev:
|
|
free_netdev(ndev);
|
|
free_netdev(ndev);
|
|
|
|
|
|
@@ -1012,6 +1305,8 @@ static int hix5hd2_dev_remove(struct platform_device *pdev)
|
|
mdiobus_unregister(priv->bus);
|
|
mdiobus_unregister(priv->bus);
|
|
mdiobus_free(priv->bus);
|
|
mdiobus_free(priv->bus);
|
|
|
|
|
|
|
|
+ if (HAS_CAP_TSO(priv->hw_cap))
|
|
|
|
+ hix5hd2_destroy_sg_desc_queue(priv);
|
|
hix5hd2_destroy_hw_desc_queue(priv);
|
|
hix5hd2_destroy_hw_desc_queue(priv);
|
|
of_node_put(priv->phy_node);
|
|
of_node_put(priv->phy_node);
|
|
cancel_work_sync(&priv->tx_timeout_task);
|
|
cancel_work_sync(&priv->tx_timeout_task);
|
|
@@ -1021,7 +1316,10 @@ static int hix5hd2_dev_remove(struct platform_device *pdev)
|
|
}
|
|
}
|
|
|
|
|
|
static const struct of_device_id hix5hd2_of_match[] = {
|
|
static const struct of_device_id hix5hd2_of_match[] = {
|
|
- {.compatible = "hisilicon,hix5hd2-gmac",},
|
|
|
|
|
|
+ { .compatible = "hisilicon,hisi-gemac-v1", .data = (void *)GEMAC_V1 },
|
|
|
|
+ { .compatible = "hisilicon,hisi-gemac-v2", .data = (void *)GEMAC_V2 },
|
|
|
|
+ { .compatible = "hisilicon,hix5hd2-gemac", .data = (void *)GEMAC_V1 },
|
|
|
|
+ { .compatible = "hisilicon,hi3798cv200-gemac", .data = (void *)GEMAC_V2 },
|
|
{},
|
|
{},
|
|
};
|
|
};
|
|
|
|
|
|
@@ -1029,7 +1327,7 @@ MODULE_DEVICE_TABLE(of, hix5hd2_of_match);
|
|
|
|
|
|
static struct platform_driver hix5hd2_dev_driver = {
|
|
static struct platform_driver hix5hd2_dev_driver = {
|
|
.driver = {
|
|
.driver = {
|
|
- .name = "hix5hd2-gmac",
|
|
|
|
|
|
+ .name = "hisi-gemac",
|
|
.of_match_table = hix5hd2_of_match,
|
|
.of_match_table = hix5hd2_of_match,
|
|
},
|
|
},
|
|
.probe = hix5hd2_dev_probe,
|
|
.probe = hix5hd2_dev_probe,
|
|
@@ -1038,6 +1336,6 @@ static struct platform_driver hix5hd2_dev_driver = {
|
|
|
|
|
|
module_platform_driver(hix5hd2_dev_driver);
|
|
module_platform_driver(hix5hd2_dev_driver);
|
|
|
|
|
|
-MODULE_DESCRIPTION("HISILICON HIX5HD2 Ethernet driver");
|
|
|
|
|
|
+MODULE_DESCRIPTION("HISILICON Gigabit Ethernet MAC driver");
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_LICENSE("GPL v2");
|
|
-MODULE_ALIAS("platform:hix5hd2-gmac");
|
|
|
|
|
|
+MODULE_ALIAS("platform:hisi-gemac");
|