Commit e466af66 authored by David S. Miller's avatar David S. Miller

Merge branch 'hix5hd2_gmac-txsg-reset-clock-control'

Dongpo Li says:

====================
net: hix5hd2_gmac: add tx sg feature and reset/clock control signals

The "hix5hd2" is SoC name, add the generic ethernet driver compatible string.
The "hisi-gemac-v1" is the basic version and "hisi-gemac-v2" adds
the SG/TXCSUM/TSO/UFO features.
This patch set only adds the SG(scatter-gather) driver for transmitting,
the drivers of other features will be submitted later.

Add the MAC reset control signals and clock signals.
We make these signals optional to be backward compatible with
the hix5hd2 SoC.

Changes in v2:
- Make the compatible string changes be a separate patch and
the most specific string come first than the generic string
as advised by Rob.
- Make the MAC reset control signals and clock signals optional
to be backward compatible with the hix5hd2 SoC.
- Change the compatible string and give the clock a specific name
in hix5hd2 dts file.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 2bfcfcd3 0855950b
Hisilicon hix5hd2 gmac controller
Required properties:
- compatible: should be "hisilicon,hix5hd2-gmac".
- compatible: should contain one of the following SoC strings:
* "hisilicon,hix5hd2-gemac"
* "hisilicon,hi3798cv200-gemac"
and one of the following version string:
* "hisilicon,hisi-gemac-v1"
* "hisilicon,hisi-gemac-v2"
- reg: specifies base physical address(s) and size of the device registers.
The first region is the MAC register base and size.
The second region is external interface control register.
......@@ -12,6 +17,16 @@ Required properties:
- phy-handle: see ethernet.txt [1].
- mac-address: see ethernet.txt [1].
- clocks: clock phandle and specifier pair.
- clock-names: contain the clock name "mac_core"(required) and "mac_ifc"(optional).
- resets: should contain the phandle to the MAC core reset signal(optional),
the MAC interface reset signal(optional)
and the PHY reset signal(optional).
- reset-names: contain the reset signal name "mac_core"(optional),
"mac_ifc"(optional) and "phy"(optional).
- hisilicon,phy-reset-delays-us: triplet of delays if PHY reset signal given.
The 1st cell is reset pre-delay in micro seconds.
The 2nd cell is reset pulse in micro seconds.
The 3rd cell is reset post-delay in micro seconds.
- PHY subnode: inherits from phy binding [2]
......@@ -20,15 +35,19 @@ Required properties:
Example:
gmac0: ethernet@f9840000 {
compatible = "hisilicon,hix5hd2-gmac";
compatible = "hisilicon,hi3798cv200-gemac", "hisilicon,hisi-gemac-v2";
reg = <0xf9840000 0x1000>,<0xf984300c 0x4>;
interrupts = <0 71 4>;
#address-cells = <1>;
#size-cells = <0>;
phy-mode = "mii";
phy-mode = "rgmii";
phy-handle = <&phy2>;
mac-address = [00 00 00 00 00 00];
clocks = <&clock HIX5HD2_MAC0_CLK>;
clocks = <&crg HISTB_ETH0_MAC_CLK>, <&crg HISTB_ETH0_MACIF_CLK>;
clock-names = "mac_core", "mac_ifc";
resets = <&crg 0xcc 8>, <&crg 0xcc 10>, <&crg 0xcc 12>;
reset-names = "mac_core", "mac_ifc", "phy";
hisilicon,phy-reset-delays-us = <10000 10000 30000>;
phy2: ethernet-phy@2 {
reg = <2>;
......
......@@ -436,18 +436,20 @@ sd: mmc@1820000 {
};
gmac0: ethernet@1840000 {
compatible = "hisilicon,hix5hd2-gmac";
compatible = "hisilicon,hix5hd2-gemac", "hisilicon,hisi-gemac-v1";
reg = <0x1840000 0x1000>,<0x184300c 0x4>;
interrupts = <0 71 4>;
clocks = <&clock HIX5HD2_MAC0_CLK>;
clock-names = "mac_core";
status = "disabled";
};
gmac1: ethernet@1841000 {
compatible = "hisilicon,hix5hd2-gmac";
compatible = "hisilicon,hix5hd2-gemac", "hisilicon,hisi-gemac-v1";
reg = <0x1841000 0x1000>,<0x1843010 0x4>;
interrupts = <0 72 4>;
clocks = <&clock HIX5HD2_MAC1_CLK>;
clock-names = "mac_core";
status = "disabled";
};
......
......@@ -11,8 +11,10 @@
#include <linux/interrupt.h>
#include <linux/etherdevice.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/of_net.h>
#include <linux/of_mdio.h>
#include <linux/reset.h>
#include <linux/clk.h>
#include <linux/circ_buf.h>
......@@ -183,12 +185,28 @@
#define DESC_DATA_LEN_OFF 16
#define DESC_BUFF_LEN_OFF 0
#define DESC_DATA_MASK 0x7ff
#define DESC_SG BIT(30)
#define DESC_FRAGS_NUM_OFF 11
/* DMA descriptor ring helpers */
#define dma_ring_incr(n, s) (((n) + 1) & ((s) - 1))
#define dma_cnt(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 {
__le32 buff_addr;
__le32 cmd;
......@@ -201,6 +219,27 @@ struct hix5hd2_desc_sw {
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
struct hix5hd2_priv {
struct hix5hd2_desc_sw pool[QUEUE_NUMS];
......@@ -208,6 +247,7 @@ struct hix5hd2_priv {
#define rx_bq pool[1]
#define tx_bq pool[2]
#define tx_rq pool[3]
struct hix5hd2_sg_desc_ring tx_ring;
void __iomem *base;
void __iomem *ctrl_base;
......@@ -221,15 +261,30 @@ struct hix5hd2_priv {
struct device_node *phy_node;
phy_interface_t phy_mode;
unsigned long hw_cap;
unsigned int speed;
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 napi_struct napi;
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)
{
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)
val |= GMAC_FULL_DUPLEX;
writel_relaxed(val, priv->ctrl_base);
hix5hd2_mac_interface_reset(priv);
writel_relaxed(BIT_MODE_CHANGE_EN, priv->base + MODE_CHANGE_EN);
if (speed == SPEED_1000)
......@@ -511,6 +567,27 @@ static int hix5hd2_rx(struct net_device *dev, int limit)
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)
{
struct sk_buff *skb;
......@@ -538,8 +615,15 @@ static void hix5hd2_xmit_reclaim(struct net_device *dev)
pkts_compl++;
bytes_compl += skb->len;
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;
dev_consume_skb_any(skb);
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;
}
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)
{
struct hix5hd2_priv *priv = netdev_priv(dev);
struct hix5hd2_desc *desc;
dma_addr_t addr;
u32 pos;
u32 cmd;
int ret;
/* software write pointer */
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;
}
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;
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);
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 */
wmb();
......@@ -681,16 +832,26 @@ static int hix5hd2_net_open(struct net_device *dev)
struct phy_device *phy;
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) {
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;
}
phy = of_phy_connect(dev, priv->phy_node,
&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;
}
phy_start(phy);
hix5hd2_hw_init(priv);
......@@ -721,7 +882,8 @@ static int hix5hd2_net_close(struct net_device *dev)
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;
}
......@@ -862,10 +1024,82 @@ static int hix5hd2_init_hw_desc_queue(struct hix5hd2_priv *priv)
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)
{
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
const struct of_device_id *of_id = NULL;
struct net_device *ndev;
struct hix5hd2_priv *priv;
struct resource *res;
......@@ -883,6 +1117,13 @@ static int hix5hd2_dev_probe(struct platform_device *pdev)
priv->dev = dev;
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);
priv->base = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->base)) {
......@@ -897,23 +1138,55 @@ static int hix5hd2_dev_probe(struct platform_device *pdev)
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;
goto out_free_netdev;
}
ret = clk_prepare_enable(priv->clk);
ret = clk_prepare_enable(priv->mac_core_clk);
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;
}
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();
if (bus == NULL) {
ret = -ENOMEM;
goto out_free_netdev;
goto out_disable_clk;
}
bus->priv = priv;
......@@ -972,22 +1245,38 @@ static int hix5hd2_dev_probe(struct platform_device *pdev)
ndev->ethtool_ops = &hix5hd2_ethtools_ops;
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);
if (ret)
goto out_phy_node;
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);
if (ret) {
netdev_err(ndev, "register_netdev failed!");
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;
out_destroy_queue:
if (HAS_CAP_TSO(priv->hw_cap))
hix5hd2_destroy_sg_desc_queue(priv);
netif_napi_del(&priv->napi);
hix5hd2_destroy_hw_desc_queue(priv);
out_phy_node:
......@@ -996,6 +1285,10 @@ static int hix5hd2_dev_probe(struct platform_device *pdev)
mdiobus_unregister(bus);
err_free_mdio:
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:
free_netdev(ndev);
......@@ -1012,6 +1305,8 @@ static int hix5hd2_dev_remove(struct platform_device *pdev)
mdiobus_unregister(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);
of_node_put(priv->phy_node);
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[] = {
{.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 = {
.driver = {
.name = "hix5hd2-gmac",
.name = "hisi-gemac",
.of_match_table = hix5hd2_of_match,
},
.probe = hix5hd2_dev_probe,
......@@ -1038,6 +1336,6 @@ static struct 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_ALIAS("platform:hix5hd2-gmac");
MODULE_ALIAS("platform:hisi-gemac");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment