diff options
-rw-r--r-- | AllWinner-net-emac.patch | 2669 | ||||
-rw-r--r-- | bcm283x-vc4-fixes.patch | 43 | ||||
-rw-r--r-- | config-arm64 | 1 | ||||
-rw-r--r-- | config-armv7-generic | 1 | ||||
-rw-r--r-- | kernel.spec | 7 |
5 files changed, 2721 insertions, 0 deletions
diff --git a/AllWinner-net-emac.patch b/AllWinner-net-emac.patch new file mode 100644 index 000000000..42dadabab --- /dev/null +++ b/AllWinner-net-emac.patch @@ -0,0 +1,2669 @@ +From fb909e29d6c073f4c5777a0db75df72b726e4314 Mon Sep 17 00:00:00 2001 +From: Corentin LABBE <clabbe.montjoie@gmail.com> +Date: Fri, 7 Oct 2016 10:25:48 +0200 +Subject: [PATCH 1/8] ethernet: add sun8i-emac driver + +This patch add support for sun8i-emac ethernet MAC hardware. +It could be found in Allwinner H3/A83T/A64 SoCs. + +It supports 10/100/1000 Mbit/s speed with half/full duplex. +It can use an internal PHY (MII 10/100) or an external PHY +via RGMII/RMII. + +Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com> +--- + drivers/net/ethernet/allwinner/Kconfig | 13 + + drivers/net/ethernet/allwinner/Makefile | 1 + + drivers/net/ethernet/allwinner/sun8i-emac.c | 2266 +++++++++++++++++++++++++++ + 3 files changed, 2280 insertions(+) + create mode 100644 drivers/net/ethernet/allwinner/sun8i-emac.c + +diff --git a/drivers/net/ethernet/allwinner/Kconfig b/drivers/net/ethernet/allwinner/Kconfig +index 47da7e7..060569c 100644 +--- a/drivers/net/ethernet/allwinner/Kconfig ++++ b/drivers/net/ethernet/allwinner/Kconfig +@@ -33,4 +33,17 @@ config SUN4I_EMAC + To compile this driver as a module, choose M here. The module + will be called sun4i-emac. + ++config SUN8I_EMAC ++ tristate "Allwinner sun8i EMAC support" ++ depends on ARCH_SUNXI || COMPILE_TEST ++ depends on OF ++ select MII ++ select PHYLIB ++ ---help--- ++ This driver support the sun8i EMAC ethernet driver present on ++ H3/A83T/A64 Allwinner SoCs. ++ ++ To compile this driver as a module, choose M here. The module ++ will be called sun8i-emac. ++ + endif # NET_VENDOR_ALLWINNER +diff --git a/drivers/net/ethernet/allwinner/Makefile b/drivers/net/ethernet/allwinner/Makefile +index 03129f7..8bd1693c 100644 +--- a/drivers/net/ethernet/allwinner/Makefile ++++ b/drivers/net/ethernet/allwinner/Makefile +@@ -3,3 +3,4 @@ + # + + obj-$(CONFIG_SUN4I_EMAC) += sun4i-emac.o ++obj-$(CONFIG_SUN8I_EMAC) += sun8i-emac.o +diff --git a/drivers/net/ethernet/allwinner/sun8i-emac.c b/drivers/net/ethernet/allwinner/sun8i-emac.c +new file mode 100644 +index 0000000..bc74467 +--- /dev/null ++++ b/drivers/net/ethernet/allwinner/sun8i-emac.c +@@ -0,0 +1,2266 @@ ++/* ++ * sun8i-emac driver ++ * ++ * Copyright (C) 2015-2016 Corentin LABBE <clabbe.montjoie@gmail.com> ++ * ++ * This is the driver for Allwinner Ethernet MAC found in H3/A83T/A64 SoC ++ * ++ * TODO: ++ * - MAC filtering ++ * - Jumbo frame ++ * - features rx-all (NETIF_F_RXALL_BIT) ++ * - PM runtime ++ */ ++#include <linux/bitops.h> ++#include <linux/clk.h> ++#include <linux/dma-mapping.h> ++#include <linux/etherdevice.h> ++#include <linux/interrupt.h> ++#include <linux/iopoll.h> ++#include <linux/mii.h> ++#include <linux/module.h> ++#include <linux/netdevice.h> ++#include <linux/of_device.h> ++#include <linux/of_mdio.h> ++#include <linux/of_net.h> ++#include <linux/phy.h> ++#include <linux/pinctrl/consumer.h> ++#include <linux/pinctrl/pinctrl.h> ++#include <linux/platform_device.h> ++#include <linux/reset.h> ++#include <linux/scatterlist.h> ++#include <linux/skbuff.h> ++#include <linux/mfd/syscon.h> ++#include <linux/regmap.h> ++ ++#define EMAC_BASIC_CTL0 0x00 ++#define EMAC_BASIC_CTL1 0x04 ++#define EMAC_INT_STA 0x08 ++#define EMAC_INT_EN 0x0C ++#define EMAC_TX_CTL0 0x10 ++#define EMAC_TX_CTL1 0x14 ++#define EMAC_TX_FLOW_CTL 0x1C ++#define EMAC_RX_CTL0 0x24 ++#define EMAC_RX_CTL1 0x28 ++#define EMAC_RX_FRM_FLT 0x38 ++#define EMAC_MDIO_CMD 0x48 ++#define EMAC_MDIO_DATA 0x4C ++#define EMAC_TX_DMA_STA 0xB0 ++#define EMAC_TX_CUR_DESC 0xB4 ++#define EMAC_TX_CUR_BUF 0xB8 ++#define EMAC_RX_DMA_STA 0xC0 ++ ++#define MDIO_CMD_MII_BUSY BIT(0) ++#define MDIO_CMD_MII_WRITE BIT(1) ++#define MDIO_CMD_MII_PHY_REG_ADDR_MASK GENMASK(8, 4) ++#define MDIO_CMD_MII_PHY_REG_ADDR_SHIFT 4 ++#define MDIO_CMD_MII_PHY_ADDR_MASK GENMASK(16, 12) ++#define MDIO_CMD_MII_PHY_ADDR_SHIFT 12 ++ ++#define EMAC_MACADDR_HI 0x50 ++#define EMAC_MACADDR_LO 0x54 ++ ++#define EMAC_RX_DESC_LIST 0x34 ++#define EMAC_TX_DESC_LIST 0x20 ++ ++#define EMAC_RX_DO_CRC BIT(27) ++#define EMAC_RX_STRIP_FCS BIT(28) ++ ++#define LE32_BIT(x) (cpu_to_le32(BIT(x))) ++ ++#define EMAC_COULD_BE_USED_BY_DMA LE32_BIT(31) ++ ++/* Used in RX_CTL1*/ ++#define EMAC_RX_DMA_EN BIT(30) ++#define EMAC_RX_DMA_START BIT(31) ++/* Used in TX_CTL1*/ ++#define EMAC_TX_DMA_EN BIT(30) ++#define EMAC_TX_DMA_START BIT(31) ++ ++/* Used in RX_CTL0 */ ++#define EMAC_RX_RECEIVER_EN BIT(31) ++/* Used in TX_CTL0 */ ++#define EMAC_TX_TRANSMITTER_EN BIT(31) ++ ++/* Basic CTL0 */ ++#define EMAC_BCTL0_FD BIT(0) ++#define EMAC_BCTL0_SPEED_10 2 ++#define EMAC_BCTL0_SPEED_100 3 ++#define EMAC_BCTL0_SPEED_MASK GENMASK(3, 2) ++#define EMAC_BCTL0_SPEED_SHIFT 2 ++ ++#define EMAC_FLOW_RX 1 ++#define EMAC_FLOW_TX 2 ++ ++#define EMAC_TX_INT BIT(0) ++#define EMAC_TX_DMA_STOP_INT BIT(1) ++#define EMAC_TX_BUF_UA_INT BIT(2) ++#define EMAC_TX_TIMEOUT_INT BIT(3) ++#define EMAC_TX_UNDERFLOW_INT BIT(4) ++#define EMAC_TX_EARLY_INT BIT(5) ++#define EMAC_RX_INT BIT(8) ++#define EMAC_RX_BUF_UA_INT BIT(9) ++#define EMAC_RX_DMA_STOP_INT BIT(10) ++#define EMAC_RX_TIMEOUT_INT BIT(11) ++#define EMAC_RX_OVERFLOW_INT BIT(12) ++#define EMAC_RX_EARLY_INT BIT(13) ++#define EMAC_RGMII_STA_INT BIT(16) ++ ++/* Bits used in frame RX status */ ++#define EMAC_DSC_RX_FIRST BIT(9) ++#define EMAC_DSC_RX_LAST BIT(8) ++ ++/* Bits used in frame TX ctl */ ++#define EMAC_MAGIC_TX_BIT LE32_BIT(24) ++#define EMAC_TX_DO_CRC (LE32_BIT(27) | LE32_BIT(28)) ++#define EMAC_DSC_TX_FIRST LE32_BIT(29) ++#define EMAC_DSC_TX_LAST LE32_BIT(30) ++#define EMAC_WANT_INT LE32_BIT(31) ++ ++/* struct emac_variant - Describe an emac variant of sun8i-emac ++ * @default_syscon_value: Default value of the syscon EMAC register ++ * The default_syscon_value is also used for powering down the PHY ++ * @internal_phy: which PHY type is internal ++ * @support_mii: Does the SoC support MII ++ * @support_rmii: Does the SoC support RMII ++ * @support_rgmii: Does the SoC support RGMII ++ */ ++struct emac_variant { ++ u32 default_syscon_value; ++ int internal_phy; ++ bool support_mii; ++ bool support_rmii; ++ bool support_rgmii; ++}; ++ ++static const struct emac_variant emac_variant_h3 = { ++ .default_syscon_value = 0x58000, ++ .internal_phy = PHY_INTERFACE_MODE_MII, ++ .support_mii = true, ++ .support_rmii = true, ++ .support_rgmii = true ++}; ++ ++static const struct emac_variant emac_variant_a83t = { ++ .default_syscon_value = 0, ++ .internal_phy = 0, ++ .support_mii = true, ++ .support_rgmii = true ++}; ++ ++static const struct emac_variant emac_variant_a64 = { ++ .default_syscon_value = 0, ++ .internal_phy = 0, ++ .support_mii = true, ++ .support_rmii = true, ++ .support_rgmii = true ++}; ++ ++static const char const estats_str[][ETH_GSTRING_LEN] = { ++ /* errors */ ++ "rx_payload_error", ++ "rx_CRC_error", ++ "rx_phy_error", ++ "rx_length_error", ++ "rx_col_error", ++ "rx_header_error", ++ "rx_overflow_error", ++ "rx_saf_error", ++ "rx_daf_error", ++ "rx_buf_error", ++ "rx_invalid_error", ++ "tx_timeout", ++ /* misc infos */ ++ "tx_stop_queue", ++ "rx_dma_ua", ++ "rx_dma_stop", ++ "tx_dma_ua", ++ "tx_dma_stop", ++ "rx_hw_csum", ++ "tx_hw_csum", ++ /* interrupts */ ++ "rx_int", ++ "tx_int", ++ "tx_early_int", ++ "tx_underflow_int", ++ "tx_timeout_int", ++ "rx_early_int", ++ "rx_overflow_int", ++ "rx_timeout_int", ++ "rgmii_state_int", ++ /* debug */ ++ "tx_used_desc", ++ "napi_schedule", ++ "napi_underflow", ++}; ++ ++struct sun8i_emac_stats { ++ u64 rx_payload_error; ++ u64 rx_crc_error; ++ u64 rx_phy_error; ++ u64 rx_length_error; ++ u64 rx_col_error; ++ u64 rx_header_error; ++ u64 rx_overflow_error; ++ u64 rx_saf_fail; ++ u64 rx_daf_fail; ++ u64 rx_buf_error; ++ u64 rx_invalid_error; ++ u64 tx_timeout; ++ ++ u64 tx_stop_queue; ++ u64 rx_dma_ua; ++ u64 rx_dma_stop; ++ u64 tx_dma_ua; ++ u64 tx_dma_stop; ++ u64 rx_hw_csum; ++ u64 tx_hw_csum; ++ ++ u64 rx_int; ++ u64 tx_int; ++ u64 tx_early_int; ++ u64 tx_underflow_int; ++ u64 tx_timeout_int; ++ u64 rx_early_int; ++ u64 rx_overflow_int; ++ u64 rx_timeout_int; ++ u64 rgmii_state_int; ++ ++ u64 tx_used_desc; ++ u64 napi_schedule; ++ u64 napi_underflow; ++}; ++ ++/* The datasheet said that each descriptor can transfers up to 4096bytes ++ * But latter, a register documentation reduce that value to 2048 ++ * Anyway using 2048 cause strange behaviours and even BSP driver use 2047 ++ */ ++#define DESC_BUF_MAX 2044 ++ ++/* MAGIC value for knowing if a descriptor is available or not */ ++#define DCLEAN cpu_to_le32(BIT(16) | BIT(14) | BIT(12) | BIT(10) | BIT(9)) ++ ++/* struct dma_desc - Structure of DMA descriptor used by the hardware ++ * @status: Status of the frame written by HW, so RO for the ++ * driver (except for BIT(31) which is R/W) ++ * @ctl: Information on the frame written by the driver (INT, len,...) ++ * @buf_addr: physical address of the frame data ++ * @next: physical address of next dma_desc ++ */ ++struct dma_desc { ++ __le32 status; ++ __le32 ctl; ++ __le32 buf_addr; ++ __le32 next; ++}; ++ ++/* Describe how data from skb are DMA mapped (used in txinfo map member) */ ++#define MAP_SINGLE 1 ++#define MAP_PAGE 2 ++ ++/* Structure for storing information about data in TX ring buffer */ ++struct txinfo { ++ struct sk_buff *skb; ++ int map; ++}; ++ ++struct sun8i_emac_priv { ++ void __iomem *base; ++ struct regmap *regmap; ++ int irq; ++ struct device *dev; ++ struct net_device *ndev; ++ struct mii_bus *mdio; ++ struct napi_struct napi; ++ spinlock_t tx_lock;/* control the access of transmit descriptors */ ++ int duplex; ++ int speed; ++ int link; ++ int phy_interface; ++ const struct emac_variant *variant; ++ struct device_node *phy_node; ++ struct device_node *mdio_node; ++ struct clk *ahb_clk; ++ struct clk *ephy_clk; ++ bool use_internal_phy; ++ ++ struct reset_control *rst_mac; ++ struct reset_control *rst_ephy; ++ ++ struct dma_desc *dd_rx; ++ dma_addr_t dd_rx_phy; ++ struct dma_desc *dd_tx; ++ dma_addr_t dd_tx_phy; ++ struct sk_buff **rx_skb; ++ struct txinfo *txl; ++ ++ int nbdesc_tx; ++ int nbdesc_rx; ++ int tx_slot; ++ int tx_dirty; ++ int rx_dirty; ++ struct sun8i_emac_stats estats; ++ u32 msg_enable; ++ int flow_ctrl; ++ int pause; ++}; ++ ++static irqreturn_t sun8i_emac_dma_interrupt(int irq, void *dev_id); ++ ++static void rb_inc(int *p, const int max) ++{ ++ (*p)++; ++ (*p) %= max; ++} ++ ++/* Locking strategy: ++ * RX queue does not need any lock since only sun8i_emac_poll() access it. ++ * (All other RX modifiers (ringparam/ndo_stop) disable NAPI and so ++ * sun8i_emac_poll()) ++ * TX queue is handled by sun8i_emac_xmit(), sun8i_emac_complete_xmit() and ++ * sun8i_emac_tx_timeout() ++ * (All other RX modifiers (ringparam/ndo_stop) disable NAPI and stop queue) ++ * ++ * sun8i_emac_xmit() could fire only once (netif_tx_lock) ++ * sun8i_emac_complete_xmit() could fire only once (called from NAPI) ++ * sun8i_emac_tx_timeout() could fire only once (netif_tx_lock) and could not ++ * race with sun8i_emac_xmit (due to netif_tx_lock) and with ++ * sun8i_emac_complete_xmit which disable NAPI. ++ * ++ * So only sun8i_emac_xmit and sun8i_emac_complete_xmit could fire at the same ++ * time. ++ * But they never could modify the same descriptors: ++ * - sun8i_emac_complete_xmit() will modify only descriptors with empty status ++ * - sun8i_emac_xmit() will modify only descriptors set to DCLEAN ++ * Proper memory barriers ensure that descriptor set to DCLEAN could not be ++ * modified latter by sun8i_emac_complete_xmit(). ++ */ ++ ++/* Return the number of contiguous free descriptors ++ * starting from tx_slot ++ */ ++static int rb_tx_numfreedesc(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ ++ if (priv->tx_slot < priv->tx_dirty) ++ return priv->tx_dirty - priv->tx_slot; ++ ++ return (priv->nbdesc_tx - priv->tx_slot) + priv->tx_dirty; ++} ++ ++/* sun8i_emac_rx_skb - Allocate a skb in a DMA descriptor ++ * ++ * @ndev: The net_device for this interface ++ * @i: index of slot to fill ++ * ++ * Refill a DMA descriptor with a fresh skb and map it for DMA. ++*/ ++static int sun8i_emac_rx_skb(struct net_device *ndev, int i) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ struct dma_desc *ddesc; ++ struct sk_buff *skb; ++ ++ ddesc = priv->dd_rx + i; ++ ++ ddesc->ctl = 0; ++ ++ skb = netdev_alloc_skb_ip_align(ndev, DESC_BUF_MAX); ++ if (!skb) ++ return -ENOMEM; ++ ++ /* should not happen */ ++ if (unlikely(priv->rx_skb[i])) ++ dev_warn(priv->dev, "BUG: Leaking a skbuff\n"); ++ ++ priv->rx_skb[i] = skb; ++ ++ ddesc->buf_addr = dma_map_single(priv->dev, skb->data, ++ DESC_BUF_MAX, DMA_FROM_DEVICE); ++ if (dma_mapping_error(priv->dev, ddesc->buf_addr)) { ++ dev_err(priv->dev, "ERROR: Cannot map RX buffer for DMA\n"); ++ dev_kfree_skb(skb); ++ return -EFAULT; ++ } ++ /* We cannot direcly use cpu_to_le32() after dma_map_single ++ * since dma_mapping_error use it ++ */ ++ ddesc->buf_addr = cpu_to_le32(ddesc->buf_addr); ++ ddesc->ctl |= cpu_to_le32(DESC_BUF_MAX); ++ /* EMAC_COULD_BE_USED_BY_DMA must be the last value written */ ++ wmb(); ++ ddesc->status = EMAC_COULD_BE_USED_BY_DMA; ++ ++ return 0; ++} ++ ++static void sun8i_emac_stop_tx(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ u32 v; ++ ++ netif_stop_queue(ndev); ++ ++ v = readl(priv->base + EMAC_TX_CTL0); ++ /* Disable transmitter after current reception */ ++ v &= ~EMAC_TX_TRANSMITTER_EN; ++ writel(v, priv->base + EMAC_TX_CTL0); ++ ++ v = readl(priv->base + EMAC_TX_CTL1); ++ /* Stop TX DMA */ ++ v &= ~EMAC_TX_DMA_EN; ++ writel(v, priv->base + EMAC_TX_CTL1); ++ ++ /* We must be sure that all is stopped before leaving this function */ ++ wmb(); ++} ++ ++static void sun8i_emac_stop_rx(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ u32 v; ++ ++ v = readl(priv->base + EMAC_RX_CTL0); ++ /* Disable receiver after current reception */ ++ v &= ~EMAC_RX_RECEIVER_EN; ++ writel(v, priv->base + EMAC_RX_CTL0); ++ ++ v = readl(priv->base + EMAC_RX_CTL1); ++ /* Stop RX DMA */ ++ v &= ~EMAC_RX_DMA_EN; ++ writel(v, priv->base + EMAC_RX_CTL1); ++ ++ /* We must be sure that all is stopped before leaving this function */ ++ wmb(); ++} ++ ++static void sun8i_emac_start_rx(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ u32 v; ++ ++ v = readl(priv->base + EMAC_RX_CTL0); ++ /* Enable receiver */ ++ v |= EMAC_RX_RECEIVER_EN; ++ writel(v, priv->base + EMAC_RX_CTL0); ++ ++ v = readl(priv->base + EMAC_RX_CTL1); ++ v |= EMAC_RX_DMA_START; ++ v |= EMAC_RX_DMA_EN; ++ writel(v, priv->base + EMAC_RX_CTL1); ++} ++ ++static void sun8i_emac_start_tx(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ u32 v; ++ ++ v = readl(priv->base + EMAC_TX_CTL0); ++ v |= EMAC_TX_TRANSMITTER_EN; ++ writel(v, priv->base + EMAC_TX_CTL0); ++ ++ v = readl(priv->base + EMAC_TX_CTL1); ++ v |= EMAC_TX_DMA_START; ++ v |= EMAC_TX_DMA_EN; ++ writel(v, priv->base + EMAC_TX_CTL1); ++} ++ ++/* sun8i_emac_set_macaddr - Set MAC address for slot index ++ * ++ * @addr: the MAC address to set ++ * @index: The index of slot where to set address. ++ * ++ * The slot 0 is the main MAC address ++ */ ++static void sun8i_emac_set_macaddr(struct sun8i_emac_priv *priv, ++ const u8 *addr, int index) ++{ ++ u32 v; ++ ++ dev_info(priv->dev, "device MAC address slot %d %pM", index, addr); ++ ++ v = (addr[5] << 8) | addr[4]; ++ writel(v, priv->base + EMAC_MACADDR_HI + index * 8); ++ ++ v = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0]; ++ writel(v, priv->base + EMAC_MACADDR_LO + index * 8); ++} ++ ++static void sun8i_emac_set_link_mode(struct sun8i_emac_priv *priv) ++{ ++ u32 v; ++ ++ v = readl(priv->base + EMAC_BASIC_CTL0); ++ ++ if (priv->duplex) ++ v |= EMAC_BCTL0_FD; ++ else ++ v &= ~EMAC_BCTL0_FD; ++ ++ v &= ~EMAC_BCTL0_SPEED_MASK; ++ ++ switch (priv->speed) { ++ case 1000: ++ break; ++ case 100: ++ v |= EMAC_BCTL0_SPEED_100 << EMAC_BCTL0_SPEED_SHIFT; ++ break; ++ case 10: ++ v |= EMAC_BCTL0_SPEED_10 << EMAC_BCTL0_SPEED_SHIFT; ++ break; ++ default: ++ dev_err(priv->dev, "Unsupported speed %d\n", priv->speed); ++ return; ++ } ++ ++ writel(v, priv->base + EMAC_BASIC_CTL0); ++} ++ ++static void sun8i_emac_flow_ctrl(struct sun8i_emac_priv *priv, int duplex, ++ int fc) ++{ ++ u32 flow = 0; ++ ++ flow = readl(priv->base + EMAC_RX_CTL0); ++ if (fc & EMAC_FLOW_RX) ++ flow |= BIT(16); ++ else ++ flow &= ~BIT(16); ++ writel(flow, priv->base + EMAC_RX_CTL0); ++ ++ flow = readl(priv->base + EMAC_TX_FLOW_CTL); ++ if (fc & EMAC_FLOW_TX) ++ flow |= BIT(0); ++ else ++ flow &= ~BIT(0); ++ writel(flow, priv->base + EMAC_TX_FLOW_CTL); ++} ++ ++/* Grab a frame into a skb from descriptor number i */ ++static int sun8i_emac_rx_from_ddesc(struct net_device *ndev, int i) ++{ ++ struct sk_buff *skb; ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ struct dma_desc *ddesc = priv->dd_rx + i; ++ int frame_len; ++ int rxcsum_done = 0; ++ u32 dstatus = le32_to_cpu(ddesc->status); ++ ++ if (ndev->features & NETIF_F_RXCSUM) ++ rxcsum_done = 1; ++ ++ /* bit0/bit7 work only on IPv4/IPv6 TCP traffic, ++ * (not on ARP for example) so we do not raise rx_errors/discard frame ++ */ ++ /* the checksum or length of received frame's payload is wrong*/ ++ if (dstatus & BIT(0)) { ++ priv->estats.rx_payload_error++; ++ rxcsum_done = 0; ++ } ++ ++ /* RX_CRC_ERR */ ++ if (dstatus & BIT(1)) { ++ priv->ndev->stats.rx_errors++; ++ priv->ndev->stats.rx_crc_errors++; ++ priv->estats.rx_crc_error++; ++ goto discard_frame; ++ } ++ ++ /* RX_PHY_ERR */ ++ if ((dstatus & BIT(3))) { ++ priv->ndev->stats.rx_errors++; ++ priv->estats.rx_phy_error++; ++ goto discard_frame; ++ } ++ ++ /* RX_LENGTH_ERR */ ++ if ((dstatus & BIT(4))) { ++ priv->ndev->stats.rx_errors++; ++ priv->ndev->stats.rx_length_errors++; ++ priv->estats.rx_length_error++; ++ goto discard_frame; ++ } ++ ++ /* RX_COL_ERR */ ++ if ((dstatus & BIT(6))) { ++ priv->ndev->stats.rx_errors++; ++ priv->estats.rx_col_error++; ++ goto discard_frame; ++ } ++ ++ /* RX_HEADER_ERR */ ++ if ((dstatus & BIT(7))) { ++ priv->estats.rx_header_error++; ++ rxcsum_done = 0; ++ } ++ ++ /* RX_OVERFLOW_ERR */ ++ if ((dstatus & BIT(11))) { ++ priv->ndev->stats.rx_over_errors++; ++ priv->estats.rx_overflow_error++; ++ goto discard_frame; ++ } ++ ++ /* RX_NO_ENOUGTH_BUF_ERR */ ++ if ((dstatus & BIT(14))) { ++ priv->ndev->stats.rx_errors++; ++ priv->estats.rx_buf_error++; ++ goto discard_frame; ++ } ++ ++ /* BIT(9) is for the first frame, not having it is bad since we do not ++ * handle Jumbo frame ++ */ ++ if ((dstatus & EMAC_DSC_RX_FIRST) == 0) { ++ priv->ndev->stats.rx_errors++; ++ priv->estats.rx_invalid_error++; ++ goto discard_frame; ++ } ++ ++ /* this frame is not the last */ ++ if ((dstatus & EMAC_DSC_RX_LAST) == 0) { ++ priv->ndev->stats.rx_errors++; ++ priv->estats.rx_invalid_error++; ++ goto discard_frame; ++ } ++ ++ frame_len = (dstatus >> 16) & 0x3FFF; ++ if (!(ndev->features & NETIF_F_RXFCS)) ++ frame_len -= ETH_FCS_LEN; ++ ++ skb = priv->rx_skb[i]; ++ ++ netif_dbg(priv, rx_status, priv->ndev, ++ "%s from %02d %pad len=%d status=%x st=%x\n", ++ __func__, i, &ddesc, frame_len, dstatus, ++ cpu_to_le32(ddesc->ctl)); ++ ++ skb_put(skb, frame_len); ++ ++ dma_unmap_single(priv->dev, le32_to_cpu(ddesc->buf_addr), DESC_BUF_MAX, ++ DMA_FROM_DEVICE); ++ skb->protocol = eth_type_trans(skb, priv->ndev); ++ if (rxcsum_done) { ++ skb->ip_summed = CHECKSUM_UNNECESSARY; ++ priv->estats.rx_hw_csum++; ++ } else { ++ skb->ip_summed = CHECKSUM_PARTIAL; ++ } ++ ++ priv->ndev->stats.rx_packets++; ++ priv->ndev->stats.rx_bytes += frame_len; ++ priv->rx_skb[i] = NULL; ++ ++ sun8i_emac_rx_skb(ndev, i); ++ napi_gro_receive(&priv->napi, skb); ++ ++ return 0; ++ /* If the frame need to be dropped, we simply reuse the buffer */ ++discard_frame: ++ ddesc->ctl = cpu_to_le32(DESC_BUF_MAX); ++ /* EMAC_COULD_BE_USED_BY_DMA must be the last value written */ ++ wmb(); ++ ddesc->status = EMAC_COULD_BE_USED_BY_DMA; ++ return 0; ++} ++ ++/* Iterate over dma_desc for finding completed xmit. ++ * ++ * The problem is: how to know that a descriptor is sent and not just in ++ * preparation. ++ * Need to have status=0 and st set but this is the state of first frame just ++ * before setting the own-by-DMA bit. ++ * The solution is to used the artificial value DCLEAN. ++ */ ++static int sun8i_emac_complete_xmit(struct net_device *ndev, int budget) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ struct dma_desc *ddesc; ++ int frame_len; ++ int work = 0; ++ unsigned int bytes_compl = 0, pkts_compl = 0; ++ u32 dstatus; ++ ++ do { ++ ddesc = priv->dd_tx + priv->tx_dirty; ++ ++ if (ddesc->status & EMAC_COULD_BE_USED_BY_DMA) ++ goto xmit_end; ++ ++ if (ddesc->status == DCLEAN) ++ goto xmit_end; ++ ++ dstatus = cpu_to_le32(ddesc->status); ++ ++ if (ddesc->status == 0 && !ddesc->ctl) { ++ dev_err(priv->dev, "BUG: reached the void %d %d\n", ++ priv->tx_dirty, priv->tx_slot); ++ goto xmit_end; ++ } ++ ++ /* TX_UNDERFLOW_ERR */ ++ if (dstatus & BIT(1)) ++ priv->ndev->stats.tx_errors++; ++ /* TX_DEFER_ERR */ ++ if (dstatus & BIT(2)) ++ priv->ndev->stats.tx_errors++; ++ /* BIT 6:3 numbers of collisions */ ++ if (dstatus & 0x78) ++ priv->ndev->stats.collisions += ++ (dstatus & 0x78) >> 3; ++ /* TX_COL_ERR_1 */ ++ if (dstatus & BIT(8)) ++ priv->ndev->stats.tx_errors++; ++ /* TX_COL_ERR_0 */ ++ if (dstatus & BIT(9)) ++ priv->ndev->stats.tx_errors++; ++ /* TX_CRS_ERR */ ++ if (dstatus & BIT(10)) ++ priv->ndev->stats.tx_carrier_errors++; ++ /* TX_PAYLOAD_ERR */ ++ if (dstatus & BIT(12)) ++ priv->ndev->stats.tx_errors++; ++ /* TX_LENGTH_ERR */ ++ if (dstatus & BIT(14)) ++ priv->ndev->stats.tx_errors++; ++ /* TX_HEADER_ERR */ ++ if (dstatus & BIT(16)) ++ priv->ndev->stats.tx_errors++; ++ ++ frame_len = le32_to_cpu(ddesc->ctl) & 0x3FFF; ++ bytes_compl += frame_len; ++ ++ if (priv->txl[priv->tx_dirty].map == MAP_SINGLE) ++ dma_unmap_single(priv->dev, ++ le32_to_cpu(ddesc->buf_addr), ++ frame_len, DMA_TO_DEVICE); ++ else ++ dma_unmap_page(priv->dev, ++ le32_to_cpu(ddesc->buf_addr), ++ frame_len, DMA_TO_DEVICE); ++ /* we can free skb only on last frame */ ++ if (priv->txl[priv->tx_dirty].skb && ++ (ddesc->ctl & EMAC_DSC_TX_LAST)) { ++ dev_kfree_skb_irq(priv->txl[priv->tx_dirty].skb); ++ pkts_compl++; ++ } ++ ++ priv->txl[priv->tx_dirty].skb = NULL; ++ priv->txl[priv->tx_dirty].map = 0; ++ ddesc->ctl = 0; ++ /* setting status to DCLEAN is the last value to be set */ ++ wmb(); ++ ddesc->status = DCLEAN; ++ work++; ++ ++ rb_inc(&priv->tx_dirty, priv->nbdesc_tx); ++ ddesc = priv->dd_tx + priv->tx_dirty; ++ } while (ddesc->ctl && ++ !(ddesc->status & EMAC_COULD_BE_USED_BY_DMA) && ++ work < budget); ++ ++xmit_end: ++ netdev_completed_queue(ndev, pkts_compl, bytes_compl); ++ ++ /* if we don't have handled all packets */ ++ if (work < budget) ++ work = 0; ++ ++ if (netif_queue_stopped(ndev) && ++ rb_tx_numfreedesc(ndev) > MAX_SKB_FRAGS + 1) ++ netif_wake_queue(ndev); ++ return work; ++} ++ ++static int sun8i_emac_poll(struct napi_struct *napi, int budget) ++{ ++ struct sun8i_emac_priv *priv = ++ container_of(napi, struct sun8i_emac_priv, napi); ++ struct net_device *ndev = priv->ndev; ++ int worked; ++ struct dma_desc *ddesc; ++ ++ priv->estats.napi_schedule++; ++ worked = sun8i_emac_complete_xmit(ndev, budget); ++ ++ ddesc = priv->dd_rx + priv->rx_dirty; ++ while (!(ddesc->status & EMAC_COULD_BE_USED_BY_DMA) && ++ worked < budget) { ++ sun8i_emac_rx_from_ddesc(ndev, priv->rx_dirty); ++ worked++; ++ rb_inc(&priv->rx_dirty, priv->nbdesc_rx); ++ ddesc = priv->dd_rx + priv->rx_dirty; ++ }; ++ if (worked < budget) { ++ priv->estats.napi_underflow++; ++ napi_complete(&priv->napi); ++ writel(EMAC_RX_INT | EMAC_TX_INT, priv->base + EMAC_INT_EN); ++ } ++ return worked; ++} ++ ++static int sun8i_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg) ++{ ++ struct net_device *ndev = bus->priv; ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ int err; ++ u32 reg; ++ ++ err = readl_poll_timeout(priv->base + EMAC_MDIO_CMD, reg, ++ !(reg & MDIO_CMD_MII_BUSY), 100, 10000); ++ if (err) { ++ dev_err(priv->dev, "%s timeout %x\n", __func__, reg); ++ return err; ++ } ++ ++ reg &= ~MDIO_CMD_MII_WRITE; ++ reg &= ~MDIO_CMD_MII_PHY_REG_ADDR_MASK; ++ reg |= (phy_reg << MDIO_CMD_MII_PHY_REG_ADDR_SHIFT) & ++ MDIO_CMD_MII_PHY_REG_ADDR_MASK; ++ ++ reg &= ~MDIO_CMD_MII_PHY_ADDR_MASK; ++ ++ reg |= (phy_addr << MDIO_CMD_MII_PHY_ADDR_SHIFT) & ++ MDIO_CMD_MII_PHY_ADDR_MASK; ++ ++ reg |= MDIO_CMD_MII_BUSY; ++ ++ writel(reg, priv->base + EMAC_MDIO_CMD); ++ ++ err = readl_poll_timeout(priv->base + EMAC_MDIO_CMD, reg, ++ !(reg & MDIO_CMD_MII_BUSY), 100, 10000); ++ ++ if (err) { ++ dev_err(priv->dev, "%s timeout %x\n", __func__, reg); ++ return err; ++ } ++ ++ return readl(priv->base + EMAC_MDIO_DATA); ++} ++ ++static int sun8i_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, ++ u16 data) ++{ ++ struct net_device *ndev = bus->priv; ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ u32 reg; ++ int err; ++ ++ err = readl_poll_timeout(priv->base + EMAC_MDIO_CMD, reg, ++ !(reg & MDIO_CMD_MII_BUSY), 100, 10000); ++ if (err) { ++ dev_err(priv->dev, "%s timeout %x\n", __func__, reg); ++ return err; ++ } ++ ++ reg &= ~MDIO_CMD_MII_PHY_REG_ADDR_MASK; ++ reg |= (phy_reg << MDIO_CMD_MII_PHY_REG_ADDR_SHIFT) & ++ MDIO_CMD_MII_PHY_REG_ADDR_MASK; ++ ++ reg &= ~MDIO_CMD_MII_PHY_ADDR_MASK; ++ reg |= (phy_addr << MDIO_CMD_MII_PHY_ADDR_SHIFT) & ++ MDIO_CMD_MII_PHY_ADDR_MASK; ++ ++ reg |= MDIO_CMD_MII_WRITE; ++ reg |= MDIO_CMD_MII_BUSY; ++ ++ writel(reg, priv->base + EMAC_MDIO_CMD); ++ writel(data, priv->base + EMAC_MDIO_DATA); ++ ++ err = readl_poll_timeout(priv->base + EMAC_MDIO_CMD, reg, ++ !(reg & MDIO_CMD_MII_BUSY), 100, 10000); ++ if (err) { ++ dev_err(priv->dev, "%s timeout %x\n", __func__, reg); ++ return err; ++ } ++ ++ return 0; ++} ++ ++static int sun8i_emac_mdio_register(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ struct mii_bus *bus; ++ int ret; ++ ++ bus = mdiobus_alloc(); ++ if (!bus) { ++ netdev_err(ndev, "Failed to allocate a new mdio bus\n"); ++ return -ENOMEM; ++ } ++ ++ bus->name = dev_name(priv->dev); ++ bus->read = &sun8i_mdio_read; ++ bus->write = &sun8i_mdio_write; ++ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%x", bus->name, priv->dev->id); ++ ++ bus->parent = priv->dev; ++ bus->priv = ndev; ++ ++ ret = of_mdiobus_register(bus, priv->mdio_node); ++ if (ret) { ++ netdev_err(ndev, "Could not register a MDIO bus: %d\n", ret); ++ mdiobus_free(bus); ++ return ret; ++ } ++ ++ priv->mdio = bus; ++ ++ return 0; ++} ++ ++static void sun8i_emac_mdio_unregister(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ ++ mdiobus_unregister(priv->mdio); ++ mdiobus_free(priv->mdio); ++} ++ ++/* Run within phydev->lock */ ++static void sun8i_emac_adjust_link(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ struct phy_device *phydev = ndev->phydev; ++ int new_state = 0; ++ ++ netif_dbg(priv, link, priv->ndev, ++ "%s link=%x duplex=%x speed=%x\n", __func__, ++ phydev->link, phydev->duplex, phydev->speed); ++ if (!phydev) ++ return; ++ ++ if (phydev->link) { ++ if (phydev->duplex != priv->duplex) { ++ new_state = 1; ++ priv->duplex = phydev->duplex; ++ } ++ if (phydev->pause) ++ sun8i_emac_flow_ctrl(priv, phydev->duplex, ++ priv->flow_ctrl); ++ ++ if (phydev->speed != priv->speed) { ++ new_state = 1; ++ priv->speed = phydev->speed; ++ } ++ ++ if (priv->link == 0) { ++ new_state = 1; ++ priv->link = phydev->link; ++ } ++ ++ netif_dbg(priv, link, priv->ndev, ++ "%s new=%d link=%d pause=%d\n", ++ __func__, new_state, priv->link, phydev->pause); ++ if (new_state) ++ sun8i_emac_set_link_mode(priv); ++ } else if (priv->link != phydev->link) { ++ new_state = 1; ++ priv->link = 0; ++ priv->speed = 0; ++ priv->duplex = -1; ++ } ++ ++ if (new_state) ++ phy_print_status(phydev); ++} ++ ++/* H3 specific bits for EPHY */ ++#define H3_EPHY_ADDR_SHIFT 20 ++#define H3_EPHY_LED_POL BIT(17) /* 1: active low, 0: active high */ ++#define H3_EPHY_SHUTDOWN BIT(16) /* 1: shutdown, 0: power up */ ++#define H3_EPHY_SELECT BIT(15) /* 1: internal PHY, 0: external PHY */ ++ ++/* H3/A64 specific bits */ ++#define SYSCON_RMII_EN BIT(13) /* 1: enable RMII (overrides EPIT) */ ++ ++/* Generic system control EMAC_CLK bits */ ++#define SYSCON_ETXDC_MASK GENMASK(2, 0) ++#define SYSCON_ETXDC_SHIFT 10 ++#define SYSCON_ERXDC_MASK GENMASK(4, 0) ++#define SYSCON_ERXDC_SHIFT 5 ++/* EMAC PHY Interface Type */ ++#define SYSCON_EPIT BIT(2) /* 1: RGMII, 0: MII */ ++#define SYSCON_ETCS_MASK GENMASK(1, 0) ++#define SYSCON_ETCS_MII 0x0 ++#define SYSCON_ETCS_EXT_GMII 0x1 ++#define SYSCON_ETCS_INT_GMII 0x2 ++#define SYSCON_EMAC_REG 0x30 ++ ++static int sun8i_emac_set_syscon(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ struct device_node *node = priv->dev->of_node; ++ int ret; ++ u32 reg, val; ++ ++ reg = priv->variant->default_syscon_value; ++ ++ if (priv->variant->internal_phy) { ++ if (!priv->use_internal_phy) { ++ /* switch to external PHY interface */ ++ reg &= ~H3_EPHY_SELECT; ++ } else { ++ reg |= H3_EPHY_SELECT; ++ reg &= ~H3_EPHY_SHUTDOWN; ++ ++ if (of_property_read_bool(priv->phy_node, ++ "allwinner,leds-active-low")) ++ reg |= H3_EPHY_LED_POL; ++ ++ ret = of_mdio_parse_addr(priv->dev, priv->phy_node); ++ if (ret < 0) { ++ netdev_err(ndev, "Could not parse MDIO addr\n"); ++ return ret; ++ } ++ /* of_mdio_parse_addr returns a valid (0 ~ 31) PHY ++ * address. No need to mask it again. ++ */ ++ reg |= ret << H3_EPHY_ADDR_SHIFT; ++ } ++ } ++ ++ if (!of_property_read_u32(node, "allwinner,tx-delay", &val)) { ++ if (val <= SYSCON_ETXDC_MASK) { ++ reg &= ~(SYSCON_ETXDC_MASK << SYSCON_ETXDC_SHIFT); ++ reg |= (val << SYSCON_ETXDC_SHIFT); ++ } else { ++ netdev_warn(ndev, "Invalid TX clock delay: %d\n", val); ++ } ++ } ++ ++ if (!of_property_read_u32(node, "allwinner,rx-delay", &val)) { ++ if (val <= SYSCON_ERXDC_MASK) { ++ reg &= ~(SYSCON_ERXDC_MASK << SYSCON_ERXDC_SHIFT); ++ reg |= (val << SYSCON_ERXDC_SHIFT); ++ } else { ++ netdev_warn(ndev, "Invalid RX clock delay: %d\n", val); ++ } ++ } ++ ++ /* Clear interface mode bits */ ++ reg &= ~(SYSCON_ETCS_MASK | SYSCON_EPIT); ++ if (priv->variant->support_rmii) ++ reg &= ~SYSCON_RMII_EN; ++ ++ switch (priv->phy_interface) { ++ case PHY_INTERFACE_MODE_MII: ++ /* default */ ++ break; ++ case PHY_INTERFACE_MODE_RGMII: ++ reg |= SYSCON_EPIT | SYSCON_ETCS_INT_GMII; ++ break; ++ case PHY_INTERFACE_MODE_RMII: ++ reg |= SYSCON_RMII_EN | SYSCON_ETCS_EXT_GMII; ++ break; ++ default: ++ netdev_err(ndev, "Unsupported interface mode: %s", ++ phy_modes(priv->phy_interface)); ++ return -EINVAL; ++ } ++ ++ regmap_write(priv->regmap, SYSCON_EMAC_REG, reg); ++ ++ return 0; ++} ++ ++static void sun8i_emac_unset_syscon(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ u32 reg = priv->variant->default_syscon_value; ++ ++ regmap_write(priv->regmap, SYSCON_EMAC_REG, reg); ++} ++ ++/* Set Management Data Clock, must be call after device reset */ ++static void sun8i_emac_set_mdc(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ unsigned long rate; ++ u32 reg; ++ ++ rate = clk_get_rate(priv->ahb_clk); ++ if (rate > 160000000) ++ reg = 0x3 << 20; /* AHB / 128 */ ++ else if (rate > 80000000) ++ reg = 0x2 << 20; /* AHB / 64 */ ++ else if (rate > 40000000) ++ reg = 0x1 << 20; /* AHB / 32 */ ++ else ++ reg = 0x0 << 20; /* AHB / 16 */ ++ netif_dbg(priv, link, ndev, "MDC auto : %x\n", reg); ++ writel(reg, priv->base + EMAC_MDIO_CMD); ++} ++ ++/* "power" the device, by enabling clk/reset/regulators */ ++static int sun8i_emac_power(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ int ret; ++ ++ ret = clk_prepare_enable(priv->ahb_clk); ++ if (ret) { ++ netdev_err(ndev, "Could not enable AHB clock\n"); ++ return ret; ++ } ++ ++ if (priv->rst_mac) { ++ ret = reset_control_deassert(priv->rst_mac); ++ if (ret) { ++ netdev_err(ndev, "Could not deassert reset\n"); ++ goto err_reset; ++ } ++ } ++ ++ if (priv->ephy_clk) { ++ ret = clk_prepare_enable(priv->ephy_clk); ++ if (ret) { ++ netdev_err(ndev, "Could not enable EPHY clock\n"); ++ goto err_ephy_clk; ++ } ++ } ++ ++ if (priv->rst_ephy) { ++ ret = reset_control_deassert(priv->rst_ephy); ++ if (ret) { ++ netdev_err(ndev, "Could not deassert EPHY reset\n"); ++ goto err_ephy_reset; ++ } ++ } ++ ++ return 0; ++ ++err_ephy_reset: ++ if (priv->ephy_clk) ++ clk_disable_unprepare(priv->ephy_clk); ++err_ephy_clk: ++ if (priv->rst_mac) ++ reset_control_assert(priv->rst_mac); ++err_reset: ++ clk_disable_unprepare(priv->ahb_clk); ++ return ret; ++} ++ ++/* "Unpower" the device, disabling clocks and regulators, asserting reset */ ++static void sun8i_emac_unpower(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ ++ if (priv->rst_ephy) ++ reset_control_assert(priv->rst_ephy); ++ ++ if (priv->ephy_clk) ++ clk_disable_unprepare(priv->ephy_clk); ++ ++ if (priv->rst_mac) ++ reset_control_assert(priv->rst_mac); ++ ++ clk_disable_unprepare(priv->ahb_clk); ++} ++ ++static int sun8i_emac_init(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ struct device_node *node = priv->dev->of_node; ++ const u8 *addr; ++ ++ /* Try to get MAC address from DT, or assign a random one */ ++ addr = of_get_mac_address(node); ++ if (addr) ++ ether_addr_copy(ndev->dev_addr, addr); ++ else ++ eth_hw_addr_random(ndev); ++ ++ return 0; ++} ++ ++static int sun8i_emac_mdio_probe(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ struct phy_device *phydev = NULL; ++ ++ phydev = of_phy_connect(ndev, priv->phy_node, &sun8i_emac_adjust_link, ++ 0, priv->phy_interface); ++ ++ if (!phydev) { ++ netdev_err(ndev, "Could not attach to PHY\n"); ++ return -ENODEV; ++ } ++ ++ phy_attached_info(phydev); ++ ++ /* mask with MAC supported features */ ++ phydev->supported &= PHY_GBIT_FEATURES; ++ phydev->advertising = phydev->supported; ++ ++ priv->link = 0; ++ priv->speed = 0; ++ priv->duplex = -1; ++ ++ return 0; ++} ++ ++/* Allocate both RX and TX ring buffer and init them ++ * This function also write the startbase of thoses ring in the device. ++ * All structures that help managing thoses rings are also handled ++ * by this functions (rx_skb/txl) ++ */ ++static int sun8i_emac_alloc_rings(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ struct dma_desc *ddesc; ++ int err, i; ++ ++ priv->rx_skb = kcalloc(priv->nbdesc_rx, sizeof(struct sk_buff *), ++ GFP_KERNEL); ++ if (!priv->rx_skb) { ++ err = -ENOMEM; ++ goto rx_skb_error; ++ } ++ priv->txl = kcalloc(priv->nbdesc_tx, sizeof(struct txinfo), GFP_KERNEL); ++ if (!priv->txl) { ++ err = -ENOMEM; ++ goto tx_error; ++ } ++ ++ /* allocate/init RX ring */ ++ priv->dd_rx = dma_zalloc_coherent(priv->dev, ++ priv->nbdesc_rx * sizeof(struct dma_desc), ++ &priv->dd_rx_phy, GFP_KERNEL); ++ if (!priv->dd_rx) { ++ dev_err(priv->dev, "ERROR: cannot allocate DMA RX buffer"); ++ err = -ENOMEM; ++ goto dma_rx_error; ++ } ++ ddesc = priv->dd_rx; ++ for (i = 0; i < priv->nbdesc_rx; i++) { ++ sun8i_emac_rx_skb(ndev, i); ++ ddesc->next = cpu_to_le32(priv->dd_rx_phy + (i + 1) ++ * sizeof(struct dma_desc)); ++ ddesc++; ++ } ++ /* last descriptor point back to first one */ ++ ddesc--; ++ ddesc->next = cpu_to_le32(priv->dd_rx_phy); ++ ++ /* allocate/init TX ring */ ++ priv->dd_tx = dma_zalloc_coherent(priv->dev, ++ priv->nbdesc_tx * sizeof(struct dma_desc), ++ &priv->dd_tx_phy, GFP_KERNEL); ++ if (!priv->dd_tx) { ++ dev_err(priv->dev, "ERROR: cannot allocate DMA TX buffer"); ++ err = -ENOMEM; ++ goto dma_tx_error; ++ } ++ ddesc = priv->dd_tx; ++ for (i = 0; i < priv->nbdesc_tx; i++) { ++ ddesc->status = DCLEAN; ++ ddesc->ctl = 0; ++ ddesc->next = cpu_to_le32(priv->dd_tx_phy + (i + 1) ++ * sizeof(struct dma_desc)); ++ ddesc++; ++ } ++ /* last descriptor point back to first one */ ++ ddesc--; ++ ddesc->next = cpu_to_le32(priv->dd_tx_phy); ++ i--; ++ ++ priv->tx_slot = 0; ++ priv->tx_dirty = 0; ++ priv->rx_dirty = 0; ++ ++ /* write start of RX ring descriptor */ ++ writel(priv->dd_rx_phy, priv->base + EMAC_RX_DESC_LIST); ++ /* write start of TX ring descriptor */ ++ writel(priv->dd_tx_phy, priv->base + EMAC_TX_DESC_LIST); ++ ++ return 0; ++dma_tx_error: ++ dma_free_coherent(priv->dev, priv->nbdesc_rx * sizeof(struct dma_desc), ++ priv->dd_rx, priv->dd_rx_phy); ++dma_rx_error: ++ kfree(priv->txl); ++tx_error: ++ kfree(priv->rx_skb); ++rx_skb_error: ++ return err; ++} ++ ++static int sun8i_emac_open(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ int err; ++ u32 v; ++ ++ err = sun8i_emac_power(ndev); ++ if (err) ++ return err; ++ ++ err = request_irq(priv->irq, sun8i_emac_dma_interrupt, 0, ++ dev_name(priv->dev), ndev); ++ if (err) { ++ dev_err(priv->dev, "Cannot request IRQ: %d\n", err); ++ goto err_power; ++ } ++ ++ /* Set interface mode (and configure internal PHY on H3) */ ++ err = sun8i_emac_set_syscon(ndev); ++ if (err) ++ goto err_irq; ++ ++ /* Do SOFT RST */ ++ v = readl(priv->base + EMAC_BASIC_CTL1); ++ writel(v | 0x01, priv->base + EMAC_BASIC_CTL1); ++ ++ err = readl_poll_timeout(priv->base + EMAC_BASIC_CTL1, v, ++ !(v & 0x01), 100, 10000); ++ if (err) { ++ dev_err(priv->dev, "EMAC reset timeout\n"); ++ err = -EFAULT; ++ goto err_syscon; ++ } ++ ++ sun8i_emac_set_mdc(ndev); ++ ++ err = sun8i_emac_mdio_register(ndev); ++ if (err) ++ goto err_syscon; ++ ++ err = sun8i_emac_mdio_probe(ndev); ++ if (err) ++ goto err_syscon; ++ ++ /* DMA */ ++ v = (8 << 24);/* burst len */ ++ writel(v, priv->base + EMAC_BASIC_CTL1); ++ ++ writel(EMAC_RX_INT | EMAC_TX_INT, priv->base + EMAC_INT_EN); ++ ++ v = readl(priv->base + EMAC_RX_CTL0); ++ /* CHECK_CRC */ ++ if (ndev->features & NETIF_F_RXCSUM) ++ v |= EMAC_RX_DO_CRC; ++ else ++ v &= ~EMAC_RX_DO_CRC; ++ /* STRIP_FCS */ ++ if (ndev->features & NETIF_F_RXFCS) ++ v &= ~EMAC_RX_STRIP_FCS; ++ else ++ v |= EMAC_RX_STRIP_FCS; ++ writel(v, priv->base + EMAC_RX_CTL0); ++ ++ v = readl(priv->base + EMAC_TX_CTL1); ++ /* TX_MD Transmission starts after a full frame located in TX DMA FIFO*/ ++ v |= BIT(1); ++ /* Undocumented bit (called TX_NEXT_FRM in BSP), the original comment is ++ * "Operating on second frame increase the performance ++ * especially when transmit store-and-forward is used." ++ */ ++ v |= BIT(2); ++ writel(v, priv->base + EMAC_TX_CTL1); ++ ++ v = readl(priv->base + EMAC_RX_CTL1); ++ /* RX_MD RX DMA reads data from RX DMA FIFO to host memory after a ++ * complete frame has been written to RX DMA FIFO ++ */ ++ v |= BIT(1); ++ writel(v, priv->base + EMAC_RX_CTL1); ++ ++ sun8i_emac_set_macaddr(priv, ndev->dev_addr, 0); ++ ++ err = sun8i_emac_alloc_rings(ndev); ++ if (err) { ++ netdev_err(ndev, "Fail to allocate rings\n"); ++ goto err_mdio; ++ } ++ ++ phy_start(ndev->phydev); ++ ++ sun8i_emac_start_rx(ndev); ++ sun8i_emac_start_tx(ndev); ++ ++ netif_napi_add(ndev, &priv->napi, sun8i_emac_poll, 64); ++ napi_enable(&priv->napi); ++ netif_start_queue(ndev); ++ ++ return 0; ++err_mdio: ++ phy_disconnect(ndev->phydev); ++err_syscon: ++ sun8i_emac_unset_syscon(ndev); ++err_irq: ++ free_irq(priv->irq, ndev); ++err_power: ++ sun8i_emac_unpower(ndev); ++ return err; ++} ++ ++/* Clean the TX ring of any accepted skb for xmit */ ++static void sun8i_emac_tx_clean(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ int i; ++ struct dma_desc *ddesc; ++ int frame_len; ++ ++ for (i = 0; i < priv->nbdesc_tx; i++) { ++ if (priv->txl[i].skb) { ++ ddesc = priv->dd_tx + i; ++ frame_len = le32_to_cpu(ddesc->ctl) & 0x3FFF; ++ switch (priv->txl[i].map) { ++ case MAP_SINGLE: ++ dma_unmap_single(priv->dev, ++ le32_to_cpu(ddesc->buf_addr), ++ frame_len, DMA_TO_DEVICE); ++ break; ++ case MAP_PAGE: ++ dma_unmap_page(priv->dev, ++ le32_to_cpu(ddesc->buf_addr), ++ frame_len, DMA_TO_DEVICE); ++ break; ++ default: ++ dev_err(priv->dev, "Trying to free an empty slot\n"); ++ continue; ++ } ++ dev_kfree_skb_any(priv->txl[i].skb); ++ priv->txl[i].skb = NULL; ++ ddesc->ctl = 0; ++ ddesc->status = DCLEAN; ++ } ++ } ++ priv->tx_slot = 0; ++ priv->tx_dirty = 0; ++} ++ ++/* Clean the RX ring */ ++static void sun8i_emac_rx_clean(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ int i; ++ struct dma_desc *ddesc; ++ ++ /* clean RX ring */ ++ for (i = 0; i < priv->nbdesc_rx; i++) ++ if (priv->rx_skb[i]) { ++ ddesc = priv->dd_rx + i; ++ dma_unmap_single(priv->dev, ++ le32_to_cpu(ddesc->buf_addr), ++ DESC_BUF_MAX, DMA_FROM_DEVICE); ++ dev_kfree_skb_any(priv->rx_skb[i]); ++ priv->rx_skb[i] = NULL; ++ } ++} ++ ++static int sun8i_emac_stop(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ ++ napi_disable(&priv->napi); ++ ++ sun8i_emac_stop_tx(ndev); ++ sun8i_emac_stop_rx(ndev); ++ ++ phy_stop(ndev->phydev); ++ phy_disconnect(ndev->phydev); ++ ++ sun8i_emac_mdio_unregister(ndev); ++ ++ sun8i_emac_unset_syscon(ndev); ++ ++ free_irq(priv->irq, ndev); ++ ++ sun8i_emac_rx_clean(ndev); ++ sun8i_emac_tx_clean(ndev); ++ ++ kfree(priv->rx_skb); ++ kfree(priv->txl); ++ ++ dma_free_coherent(priv->dev, priv->nbdesc_rx * sizeof(struct dma_desc), ++ priv->dd_rx, priv->dd_rx_phy); ++ dma_free_coherent(priv->dev, priv->nbdesc_tx * sizeof(struct dma_desc), ++ priv->dd_tx, priv->dd_tx_phy); ++ ++ sun8i_emac_unpower(ndev); ++ ++ return 0; ++} ++ ++static netdev_tx_t sun8i_emac_xmit(struct sk_buff *skb, struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ struct dma_desc *ddesc; ++ struct dma_desc *first; ++ int i = 0, rbd_first; ++ unsigned int len, fraglen, tlen; ++ u32 v; ++ int n; ++ int nf; ++ const skb_frag_t *frag; ++ int do_csum = 0; ++ ++ if (skb_put_padto(skb, ETH_ZLEN)) ++ return NETDEV_TX_OK; ++ len = skb_headlen(skb); ++ ++ n = skb_shinfo(skb)->nr_frags; ++ ++ if (skb->ip_summed == CHECKSUM_PARTIAL) { ++ do_csum = 1; ++ priv->estats.tx_hw_csum++; ++ } ++ netif_dbg(priv, tx_queued, ndev, "%s len=%u skblen=%u %x\n", __func__, ++ len, skb->len, ++ (skb->ip_summed == CHECKSUM_PARTIAL)); ++ ++ /* check for contigous space ++ * We need at least 1(skb->data) + n(numfrags) + 1(one clean slot) ++ */ ++ if (rb_tx_numfreedesc(ndev) < n + 2) { ++ dev_err_ratelimited(priv->dev, "BUG!: TX is full %d %d\n", ++ priv->tx_dirty, priv->tx_slot); ++ netif_stop_queue(ndev); ++ return NETDEV_TX_BUSY; ++ } ++ i = priv->tx_slot; ++ ++ ddesc = priv->dd_tx + i; ++ first = priv->dd_tx + i; ++ rbd_first = i; ++ ++ ddesc->buf_addr = dma_map_single(priv->dev, skb->data, len, ++ DMA_TO_DEVICE); ++ if (dma_mapping_error(priv->dev, ddesc->buf_addr)) { ++ dev_err(priv->dev, "ERROR: Cannot map buffer for DMA\n"); ++ goto xmit_error; ++ } ++ /* We cannot direcly use cpu_to_le32() after dma_map_single ++ * since dma_mapping_error use it ++ */ ++ ddesc->buf_addr = cpu_to_le32(ddesc->buf_addr); ++ priv->txl[i].map = MAP_SINGLE; ++ priv->txl[i].skb = skb; ++ ++ tlen = len; ++ ddesc->ctl = le32_to_cpu(len); ++ /* Undocumented bit that make it works ++ * Without it, packets never be sent on H3 SoC ++ */ ++ ddesc->ctl |= EMAC_MAGIC_TX_BIT; ++ if (do_csum) ++ ddesc->ctl |= EMAC_TX_DO_CRC; ++ ++ /* handle fragmented skb, one descriptor per fragment */ ++ for (nf = 0; nf < n; nf++) { ++ frag = &skb_shinfo(skb)->frags[nf]; ++ rb_inc(&i, priv->nbdesc_tx); ++ priv->txl[i].skb = skb; ++ ddesc = priv->dd_tx + i; ++ fraglen = skb_frag_size(frag); ++ ddesc->ctl = le32_to_cpu(fraglen); ++ tlen += fraglen, ++ ddesc->ctl |= EMAC_MAGIC_TX_BIT; ++ if (do_csum) ++ ddesc->ctl |= EMAC_TX_DO_CRC; ++ ++ ddesc->buf_addr = skb_frag_dma_map(priv->dev, frag, 0, ++ fraglen, DMA_TO_DEVICE); ++ if (dma_mapping_error(priv->dev, ddesc->buf_addr)) { ++ dev_err(priv->dev, "Cannot map buffer for DMA\n"); ++ goto xmit_error; ++ } ++ /* Cannot directly use cpu_to_le32() after skb_frag_dma_map ++ * since dma_mapping_error use it ++ */ ++ ddesc->buf_addr = cpu_to_le32(ddesc->buf_addr); ++ priv->txl[i].map = MAP_PAGE; ++ ddesc->status = EMAC_COULD_BE_USED_BY_DMA; ++ } ++ ++ /* frame end */ ++ ddesc->ctl |= EMAC_DSC_TX_LAST; ++ /* We want an interrupt after transmission */ ++ ddesc->ctl |= EMAC_WANT_INT; ++ ++ rb_inc(&i, priv->nbdesc_tx); ++ ++ /* This line was previously after DMA start, but with that we hit a ++ * small race with complete_xmit() where we complete more data than ++ * sent. ++ * The packet is sent just after EMAC_COULD_BE_USED_BY_DMA flag set and ++ * complete_xmit fire just after before netdev_sent_queue(). ++ * This race could be observed only when overflowing a gigabit line. ++ */ ++ netdev_sent_queue(ndev, skb->len); ++ ++ /* frame begin */ ++ first->ctl |= EMAC_DSC_TX_FIRST; ++ wmb();/* EMAC_COULD_BE_USED_BY_DMA must be the last value written */ ++ first->status = EMAC_COULD_BE_USED_BY_DMA; ++ priv->tx_slot = i; ++ ++ /* Trying to optimize this (recording DMA start/stop) seems ++ * to lead to errors. So we always start DMA. ++ */ ++ v = readl(priv->base + EMAC_TX_CTL1); ++ v |= EMAC_TX_DMA_START; ++ v |= EMAC_TX_DMA_EN; ++ writel_relaxed(v, priv->base + EMAC_TX_CTL1); ++ ++ if (rb_tx_numfreedesc(ndev) < MAX_SKB_FRAGS + 1) { ++ netif_stop_queue(ndev); ++ priv->estats.tx_stop_queue++; ++ } ++ priv->estats.tx_used_desc = rb_tx_numfreedesc(ndev); ++ priv->ndev->stats.tx_packets++; ++ priv->ndev->stats.tx_bytes += tlen; ++ ++ return NETDEV_TX_OK; ++ ++xmit_error: ++ /* destroy skb and return TX OK Documentation/DMA-API-HOWTO.txt */ ++ /* clean descritors from rbd_first to i */ ++ ddesc->ctl = 0; ++ /* setting status to DCLEAN is the last value to be set */ ++ wmb(); ++ ddesc->status = DCLEAN; ++ do { ++ ddesc = priv->dd_tx + rbd_first; ++ ddesc->ctl = 0; ++ /* setting status to DCLEAN is the last value to be set */ ++ wmb(); ++ ddesc->status = DCLEAN; ++ rb_inc(&rbd_first, priv->nbdesc_tx); ++ } while (rbd_first != i); ++ dev_kfree_skb_any(skb); ++ return NETDEV_TX_OK; ++} ++ ++static int sun8i_emac_change_mtu(struct net_device *ndev, int new_mtu) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ int max_mtu; ++ ++ dev_info(priv->dev, "%s set MTU to %d\n", __func__, new_mtu); ++ ++ if (netif_running(ndev)) { ++ dev_err(priv->dev, "%s: must be stopped to change its MTU\n", ++ ndev->name); ++ return -EBUSY; ++ } ++ ++ max_mtu = SKB_MAX_HEAD(NET_SKB_PAD + NET_IP_ALIGN); ++ ++ if ((new_mtu < 68) || (new_mtu > max_mtu)) { ++ dev_err(priv->dev, "%s: invalid MTU, max MTU is: %d\n", ++ ndev->name, max_mtu); ++ return -EINVAL; ++ } ++ ++ ndev->mtu = new_mtu; ++ netdev_update_features(ndev); ++ return 0; ++} ++ ++static int sun8i_emac_set_features(struct net_device *ndev, ++ netdev_features_t features) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ u32 v; ++ ++ v = readl(priv->base + EMAC_BASIC_CTL0); ++ if (features & NETIF_F_LOOPBACK && netif_running(ndev)) { ++ netif_info(priv, hw, ndev, "Set loopback features"); ++ v |= BIT(1); ++ } else { ++ netif_info(priv, hw, ndev, "Unset loopback features"); ++ v &= ~BIT(1); ++ } ++ writel(v, priv->base + EMAC_BASIC_CTL0); ++ ++ v = readl(priv->base + EMAC_RX_CTL0); ++ if (features & NETIF_F_RXCSUM) { ++ v |= EMAC_RX_DO_CRC; ++ netif_info(priv, hw, ndev, "Doing RX CRC check by hardware"); ++ } else { ++ v &= ~EMAC_RX_DO_CRC; ++ netif_info(priv, hw, ndev, "No RX CRC check by hardware"); ++ } ++ if (features & NETIF_F_RXFCS) { ++ v &= ~EMAC_RX_STRIP_FCS; ++ netif_info(priv, hw, ndev, "Keep FCS"); ++ } else { ++ v |= EMAC_RX_STRIP_FCS; ++ netif_info(priv, hw, ndev, "Strip FCS"); ++ } ++ writel(v, priv->base + EMAC_RX_CTL0); ++ ++ netif_dbg(priv, drv, ndev, "%s %llx %x\n", __func__, features, v); ++ ++ return 0; ++} ++ ++static void sun8i_emac_set_rx_mode(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ u32 v = 0; ++ int i = 0; ++ struct netdev_hw_addr *ha; ++ ++ /* Receive all multicast frames */ ++ v |= BIT(16); ++ /* Receive all control frames */ ++ v |= BIT(13); ++ if (ndev->flags & IFF_PROMISC) ++ v |= BIT(1); ++ if (netdev_uc_count(ndev) > 7) { ++ v |= BIT(1); ++ } else { ++ netdev_for_each_uc_addr(ha, ndev) { ++ i++; ++ sun8i_emac_set_macaddr(priv, ha->addr, i); ++ } ++ } ++ writel(v, priv->base + EMAC_RX_FRM_FLT); ++} ++ ++static void sun8i_emac_tx_timeout(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ ++ netdev_err(ndev, "%s\n", __func__); ++ ++ sun8i_emac_stop_tx(ndev); ++ ++ sun8i_emac_tx_clean(ndev); ++ ++ /* write start of the new TX ring descriptor */ ++ writel(priv->dd_tx_phy, priv->base + EMAC_TX_DESC_LIST); ++ ++ sun8i_emac_start_tx(ndev); ++ ++ netdev_reset_queue(ndev); ++ ++ priv->estats.tx_timeout++; ++ ndev->stats.tx_errors++; ++ netif_wake_queue(ndev); ++} ++ ++static int sun8i_emac_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) ++{ ++ struct phy_device *phydev = ndev->phydev; ++ ++ if (!netif_running(ndev)) ++ return -EINVAL; ++ ++ if (!phydev) ++ return -ENODEV; ++ ++ return phy_mii_ioctl(phydev, rq, cmd); ++} ++ ++static int sun8i_emac_check_if_running(struct net_device *ndev) ++{ ++ if (!netif_running(ndev)) ++ return -EINVAL; ++ return 0; ++} ++ ++static int sun8i_emac_get_sset_count(struct net_device *ndev, int sset) ++{ ++ switch (sset) { ++ case ETH_SS_STATS: ++ return ARRAY_SIZE(estats_str); ++ } ++ return -EOPNOTSUPP; ++} ++ ++static int sun8i_emac_ethtool_get_settings(struct net_device *ndev, ++ struct ethtool_cmd *cmd) ++{ ++ struct phy_device *phy = ndev->phydev; ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ ++ if (!phy) { ++ netdev_err(ndev, "%s: %s: PHY is not registered\n", ++ __func__, ndev->name); ++ return -ENODEV; ++ } ++ ++ if (!netif_running(ndev)) { ++ dev_err(priv->dev, "interface disabled: we cannot track link speed / duplex setting\n"); ++ return -EBUSY; ++ } ++ ++ return phy_ethtool_gset(phy, cmd); ++} ++ ++static int sun8i_emac_ethtool_set_settings(struct net_device *ndev, ++ struct ethtool_cmd *cmd) ++{ ++ struct phy_device *phy = ndev->phydev; ++ ++ return phy_ethtool_sset(phy, cmd); ++} ++ ++static void sun8i_emac_ethtool_getdrvinfo(struct net_device *ndev, ++ struct ethtool_drvinfo *info) ++{ ++ strlcpy(info->driver, "sun8i_emac", sizeof(info->driver)); ++ strcpy(info->version, "00"); ++ info->fw_version[0] = '\0'; ++} ++ ++static void sun8i_emac_ethtool_stats(struct net_device *ndev, ++ struct ethtool_stats *dummy, u64 *data) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ ++ memcpy(data, &priv->estats, ++ sun8i_emac_get_sset_count(ndev, ETH_SS_STATS) * sizeof(u64)); ++} ++ ++static void sun8i_emac_ethtool_strings(struct net_device *dev, u32 stringset, ++ u8 *buffer) ++{ ++ switch (stringset) { ++ case ETH_SS_STATS: ++ memcpy(buffer, &estats_str, sizeof(estats_str)); ++ break; ++ } ++} ++ ++static u32 sun8i_emac_ethtool_getmsglevel(struct net_device *ndev) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ ++ return priv->msg_enable; ++} ++ ++static void sun8i_emac_ethtool_setmsglevel(struct net_device *ndev, u32 level) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ ++ priv->msg_enable = level; ++} ++ ++static void sun8i_emac_get_pauseparam(struct net_device *ndev, ++ struct ethtool_pauseparam *pause) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ ++ pause->rx_pause = 0; ++ pause->tx_pause = 0; ++ pause->autoneg = ndev->phydev->autoneg; ++ ++ if (priv->flow_ctrl & EMAC_FLOW_RX) ++ pause->rx_pause = 1; ++ if (priv->flow_ctrl & EMAC_FLOW_TX) ++ pause->tx_pause = 1; ++} ++ ++static int sun8i_emac_set_pauseparam(struct net_device *ndev, ++ struct ethtool_pauseparam *pause) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ struct phy_device *phy = ndev->phydev; ++ int new_pause = 0; ++ int ret = 0; ++ ++ if (pause->rx_pause) ++ new_pause |= EMAC_FLOW_RX; ++ if (pause->tx_pause) ++ new_pause |= EMAC_FLOW_TX; ++ ++ priv->flow_ctrl = new_pause; ++ phy->autoneg = pause->autoneg; ++ ++ if (phy->autoneg) { ++ if (netif_running(ndev)) ++ ret = phy_start_aneg(phy); ++ } else { ++ sun8i_emac_flow_ctrl(priv, phy->duplex, priv->flow_ctrl); ++ } ++ return ret; ++} ++ ++static void sun8i_emac_ethtool_get_ringparam(struct net_device *ndev, ++ struct ethtool_ringparam *ring) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ ++ ring->rx_pending = priv->nbdesc_rx; ++ ring->tx_pending = priv->nbdesc_tx; ++} ++ ++static int sun8i_emac_ethtool_set_ringparam(struct net_device *ndev, ++ struct ethtool_ringparam *ring) ++{ ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ int err; ++ ++ if (ring->rx_max_pending || ring->rx_mini_max_pending || ++ ring->rx_jumbo_max_pending || ring->rx_mini_pending || ++ ring->rx_jumbo_pending || ring->tx_max_pending) ++ return -EINVAL; ++ ++ if (ring->tx_pending < MAX_SKB_FRAGS + 1) { ++ netdev_err(ndev, "The number of TX descriptors is too low"); ++ return -EINVAL; ++ } ++ ++ sun8i_emac_stop_tx(ndev); ++ sun8i_emac_stop_rx(ndev); ++ ++ sun8i_emac_rx_clean(ndev); ++ sun8i_emac_tx_clean(ndev); ++ ++ kfree(priv->rx_skb); ++ kfree(priv->txl); ++ ++ dma_free_coherent(priv->dev, priv->nbdesc_rx * sizeof(struct dma_desc), ++ priv->dd_rx, priv->dd_rx_phy); ++ dma_free_coherent(priv->dev, priv->nbdesc_tx * sizeof(struct dma_desc), ++ priv->dd_tx, priv->dd_tx_phy); ++ ++ priv->nbdesc_rx = ring->rx_pending; ++ priv->nbdesc_tx = ring->tx_pending; ++ err = sun8i_emac_alloc_rings(ndev); ++ if (err) { ++ /* Fatal error, we cannot re start */ ++ netdev_err(ndev, "Fail to allocate rings\n"); ++ return -EFAULT; ++ } ++ ++ sun8i_emac_start_rx(ndev); ++ sun8i_emac_start_tx(ndev); ++ ++ netif_start_queue(ndev); ++ ++ netdev_info(ndev, "Ring Param settings: rx: %d, tx %d\n", ++ ring->rx_pending, ring->tx_pending); ++ return 0; ++} ++ ++static const struct ethtool_ops sun8i_emac_ethtool_ops = { ++ .begin = sun8i_emac_check_if_running, ++ .get_settings = sun8i_emac_ethtool_get_settings, ++ .set_settings = sun8i_emac_ethtool_set_settings, ++ .get_link = ethtool_op_get_link, ++ .get_pauseparam = sun8i_emac_get_pauseparam, ++ .set_pauseparam = sun8i_emac_set_pauseparam, ++ .get_ethtool_stats = sun8i_emac_ethtool_stats, ++ .get_strings = sun8i_emac_ethtool_strings, ++ .get_sset_count = sun8i_emac_get_sset_count, ++ .get_drvinfo = sun8i_emac_ethtool_getdrvinfo, ++ .get_msglevel = sun8i_emac_ethtool_getmsglevel, ++ .set_msglevel = sun8i_emac_ethtool_setmsglevel, ++ .get_ringparam = sun8i_emac_ethtool_get_ringparam, ++ .set_ringparam = sun8i_emac_ethtool_set_ringparam, ++}; ++ ++static const struct net_device_ops sun8i_emac_netdev_ops = { ++ .ndo_init = sun8i_emac_init, ++ .ndo_open = sun8i_emac_open, ++ .ndo_start_xmit = sun8i_emac_xmit, ++ .ndo_stop = sun8i_emac_stop, ++ .ndo_change_mtu = sun8i_emac_change_mtu, ++ .ndo_set_features = sun8i_emac_set_features, ++ .ndo_set_rx_mode = sun8i_emac_set_rx_mode, ++ .ndo_tx_timeout = sun8i_emac_tx_timeout, ++ .ndo_do_ioctl = sun8i_emac_ioctl, ++ .ndo_set_mac_address = eth_mac_addr, ++}; ++ ++/* No locking in this function since it is guaranteed to be run once and ++ * do actions only done here: ++ * - Scheduling NAPI ++ * - Stopping interrupts ++ * - Updating stats ++ * ++ * Even when not enabled via EMAC_INT_EN, some interrupt could fire, so we need ++ * to handle all of them. ++ * Interrupts know to fire when not enabled are: ++ * - EMAC_TX_DMA_STOP_INT ++ * - EMAC_TX_BUF_UA_INT ++ * - EMAC_TX_EARLY_INT ++ * - EMAC_RX_BUF_UA_INT ++ * - EMAC_RX_EARLY_INT ++ */ ++static irqreturn_t sun8i_emac_dma_interrupt(int irq, void *dev_id) ++{ ++ struct net_device *ndev = dev_id; ++ struct sun8i_emac_priv *priv = netdev_priv(ndev); ++ u32 v, u; ++ ++ v = readl(priv->base + EMAC_INT_STA); ++ ++ /* When this bit is asserted, a frame transmission is completed. */ ++ if (v & EMAC_TX_INT) { ++ priv->estats.tx_int++; ++ writel(0, priv->base + EMAC_INT_EN); ++ napi_schedule(&priv->napi); ++ } ++ ++ /* When this bit is asserted, the TX DMA FSM is stopped. ++ * For the moment only a call to tx_timeout cause this interrupt ++ * to fire. ++ */ ++ if (v & EMAC_TX_DMA_STOP_INT) ++ priv->estats.tx_dma_stop++; ++ ++ /* When this asserted, the TX DMA can not acquire next TX descriptor ++ * and TX DMA FSM is suspended. ++ */ ++ if (v & EMAC_TX_BUF_UA_INT) { ++ priv->estats.tx_dma_ua++; ++ writel(0, priv->base + EMAC_INT_EN); ++ napi_schedule(&priv->napi); ++ } ++ ++ if (v & EMAC_TX_TIMEOUT_INT) ++ priv->estats.tx_timeout_int++; ++ ++ if (v & EMAC_TX_UNDERFLOW_INT) ++ priv->estats.tx_underflow_int++; ++ ++ /* When this bit asserted , the frame is transmitted to FIFO totally. */ ++ if (v & EMAC_TX_EARLY_INT) ++ priv->estats.tx_early_int++; ++ ++ /* When this bit is asserted, a frame reception is completed */ ++ if (v & EMAC_RX_INT) { ++ priv->estats.rx_int++; ++ writel(0, priv->base + EMAC_INT_EN); ++ napi_schedule(&priv->napi); ++ } ++ ++ /* When this asserted, the RX DMA can not acquire next RX descriptor ++ * and RX DMA FSM is suspended. ++ */ ++ if (v & EMAC_RX_BUF_UA_INT) { ++ u = readl(priv->base + EMAC_RX_CTL1); ++ writel(u | EMAC_RX_DMA_START, priv->base + EMAC_RX_CTL1); ++ priv->estats.rx_dma_ua++; ++ } ++ ++ /* Same as TX DMA STOP, but never hit it */ ++ if (v & EMAC_RX_DMA_STOP_INT) ++ priv->estats.rx_dma_stop++; ++ ++ if (v & EMAC_RX_TIMEOUT_INT) ++ priv->estats.rx_timeout_int++; ++ ++ if (v & EMAC_RX_OVERFLOW_INT) ++ priv->estats.rx_overflow_int++; ++ ++ if (v & EMAC_RX_EARLY_INT) ++ priv->estats.rx_early_int++; ++ ++ if (v & EMAC_RGMII_STA_INT) ++ priv->estats.rgmii_state_int++; ++ ++ /* the datasheet state those register as read-only ++ * but nothing work(freeze) without writing to it ++ */ ++ writel(v, priv->base + EMAC_INT_STA); ++ ++ return IRQ_HANDLED; ++} ++ ++static int sun8i_emac_probe(struct platform_device *pdev) ++{ ++ struct device_node *node = pdev->dev.of_node; ++ struct sun8i_emac_priv *priv; ++ struct net_device *ndev; ++ struct resource *res; ++ int ret; ++ ++ ndev = alloc_etherdev(sizeof(*priv)); ++ if (!ndev) ++ return -ENOMEM; ++ ++ SET_NETDEV_DEV(ndev, &pdev->dev); ++ priv = netdev_priv(ndev); ++ platform_set_drvdata(pdev, ndev); ++ ++ priv->variant = of_device_get_match_data(&pdev->dev); ++ if (!priv->variant) { ++ dev_err(&pdev->dev, "Missing sun8i-emac variant\n"); ++ return -EINVAL; ++ } ++ ++ priv->mdio_node = of_get_child_by_name(node, "mdio"); ++ if (!priv->mdio_node) { ++ netdev_err(ndev, "Could not find a MDIO node\n"); ++ return -EINVAL; ++ } ++ ++ priv->phy_node = of_parse_phandle(node, "phy-handle", 0); ++ if (!priv->phy_node) { ++ netdev_err(ndev, "No associated PHY\n"); ++ return -EINVAL; ++ } ++ ++ priv->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, ++ "syscon"); ++ if (IS_ERR(priv->regmap)) { ++ ret = PTR_ERR(priv->regmap); ++ dev_err(&pdev->dev, "unable to map SYSCON:%d\n", ret); ++ return ret; ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ priv->base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(priv->base)) { ++ ret = PTR_ERR(priv->base); ++ dev_err(&pdev->dev, "Cannot request MMIO: %d\n", ret); ++ return ret; ++ } ++ ++ priv->ahb_clk = devm_clk_get(&pdev->dev, "ahb"); ++ if (IS_ERR(priv->ahb_clk)) { ++ ret = PTR_ERR(priv->ahb_clk); ++ dev_err(&pdev->dev, "Cannot get AHB clock err=%d\n", ret); ++ goto probe_err; ++ } ++ ++ priv->rst_mac = devm_reset_control_get_optional(&pdev->dev, "ahb"); ++ if (IS_ERR(priv->rst_mac)) { ++ ret = PTR_ERR(priv->rst_mac); ++ if (ret == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ dev_info(&pdev->dev, "No MAC reset control found %d\n", ret); ++ priv->rst_mac = NULL; ++ } ++ ++ priv->phy_interface = of_get_phy_mode(node); ++ if (priv->phy_interface < 0) { ++ netdev_err(ndev, "PHY interface mode node unspecified\n"); ++ return priv->phy_interface; ++ } ++ ++ switch (priv->phy_interface) { ++ case PHY_INTERFACE_MODE_MII: ++ if (!priv->variant->support_mii) ++ return -EINVAL; ++ break; ++ case PHY_INTERFACE_MODE_RMII: ++ if (!priv->variant->support_rmii) ++ return -EINVAL; ++ break; ++ case PHY_INTERFACE_MODE_RGMII: ++ if (!priv->variant->support_rgmii) ++ return -EINVAL; ++ break; ++ default: ++ netdev_err(ndev, "Unsupported interface mode: %s", ++ phy_modes(priv->phy_interface)); ++ return -EINVAL; ++ } ++ ++ if (priv->phy_interface == priv->variant->internal_phy) ++ priv->use_internal_phy = true; ++ ++ if (priv->use_internal_phy) { ++ priv->ephy_clk = of_clk_get(priv->phy_node, 0); ++ if (IS_ERR(priv->ephy_clk)) { ++ ret = PTR_ERR(priv->ephy_clk); ++ dev_err(&pdev->dev, "Cannot get EPHY clock err=%d\n", ++ ret); ++ goto probe_err; ++ } ++ ++ priv->rst_ephy = of_reset_control_get(priv->phy_node, 0); ++ if (IS_ERR(priv->rst_ephy)) { ++ ret = PTR_ERR(priv->rst_ephy); ++ if (ret == -EPROBE_DEFER) ++ goto probe_err; ++ dev_info(&pdev->dev, ++ "No EPHY reset control found %d\n", ret); ++ priv->rst_ephy = NULL; ++ } ++ } ++ ++ priv->irq = platform_get_irq(pdev, 0); ++ if (priv->irq < 0) { ++ ret = priv->irq; ++ dev_err(&pdev->dev, "Cannot claim IRQ: %d\n", ret); ++ goto probe_err; ++ } ++ ++ spin_lock_init(&priv->tx_lock); ++ ++ ndev->netdev_ops = &sun8i_emac_netdev_ops; ++ ndev->ethtool_ops = &sun8i_emac_ethtool_ops; ++ ++ priv->ndev = ndev; ++ priv->dev = &pdev->dev; ++ ++ ndev->hw_features = NETIF_F_SG | NETIF_F_HIGHDMA; ++ ndev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | ++ NETIF_F_RXCSUM; ++ ndev->features |= ndev->hw_features; ++ ndev->hw_features |= NETIF_F_RXFCS; ++ ndev->hw_features |= NETIF_F_RXALL; ++ ndev->hw_features |= NETIF_F_LOOPBACK; ++ ndev->priv_flags |= IFF_UNICAST_FLT; ++ ++ ndev->watchdog_timeo = msecs_to_jiffies(5000); ++ netif_carrier_off(ndev); ++ ++ /* Benched on OPIPC with 100M, setting more than 256 does not give any ++ * perf boost ++ */ ++ priv->nbdesc_rx = 128; ++ priv->nbdesc_tx = 256; ++ ++ ret = register_netdev(ndev); ++ if (ret) { ++ dev_err(&pdev->dev, "ERROR: Register %s failed\n", ndev->name); ++ goto probe_err; ++ } ++ ++ return 0; ++ ++probe_err: ++ free_netdev(ndev); ++ return ret; ++} ++ ++static int sun8i_emac_remove(struct platform_device *pdev) ++{ ++ struct net_device *ndev = platform_get_drvdata(pdev); ++ ++ unregister_netdev(ndev); ++ platform_set_drvdata(pdev, NULL); ++ free_netdev(ndev); ++ ++ return 0; ++} ++ ++static const struct of_device_id sun8i_emac_of_match_table[] = { ++ { .compatible = "allwinner,sun8i-a83t-emac", ++ .data = &emac_variant_a83t }, ++ { .compatible = "allwinner,sun8i-h3-emac", ++ .data = &emac_variant_h3 }, ++ { .compatible = "allwinner,sun50i-a64-emac", ++ .data = &emac_variant_a64 }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, sun8i_emac_of_match_table); ++ ++static struct platform_driver sun8i_emac_driver = { ++ .probe = sun8i_emac_probe, ++ .remove = sun8i_emac_remove, ++ .driver = { ++ .name = "sun8i-emac", ++ .of_match_table = sun8i_emac_of_match_table, ++ }, ++}; ++ ++module_platform_driver(sun8i_emac_driver); ++ ++MODULE_DESCRIPTION("sun8i Ethernet driver"); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Corentin Labbe <clabbe.montjoie@gmail.com"); +-- +2.9.3 + +From 1d9ea1f4df27f3a2e2ed7094852a23a4dd45e2be Mon Sep 17 00:00:00 2001 +From: Corentin LABBE <clabbe.montjoie@gmail.com> +Date: Fri, 7 Oct 2016 10:25:49 +0200 +Subject: [PATCH 2/8] MAINTAINERS: Add myself as maintainer of sun8i-emac + +This patch add myself as maintainer of the sun8i-emac driver. + +Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com> +--- + MAINTAINERS | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/MAINTAINERS b/MAINTAINERS +index 411e3b8..e99a087 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -590,6 +590,12 @@ S: Maintained + F: Documentation/i2c/busses/i2c-ali1563 + F: drivers/i2c/busses/i2c-ali1563.c + ++ALLWINNER SUN8I-EMAC ETHERNET DRIVER ++M: Corentin Labbe <clabbe.montjoie@gmail.com> ++L: netdev@vger.kernel.org ++S: Maintained ++F: drivers/net/ethernet/allwinner/sun8i-emac.c ++ + ALLWINNER SECURITY SYSTEM + M: Corentin Labbe <clabbe.montjoie@gmail.com> + L: linux-crypto@vger.kernel.org +-- +2.9.3 + +From 6b3093e31b877f39475454b5af3b820dbb3a4f73 Mon Sep 17 00:00:00 2001 +From: Corentin LABBE <clabbe.montjoie@gmail.com> +Date: Fri, 7 Oct 2016 10:25:50 +0200 +Subject: [PATCH 3/8] ARM: sun8i: dt: Add DT bindings documentation for + Allwinner sun8i-emac + +This patch adds documentation for Device-Tree bindings for the +Allwinner sun8i-emac driver. + +Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com> +--- + .../bindings/net/allwinner,sun8i-emac.txt | 70 ++++++++++++++++++++++ + 1 file changed, 70 insertions(+) + create mode 100644 Documentation/devicetree/bindings/net/allwinner,sun8i-emac.txt + +diff --git a/Documentation/devicetree/bindings/net/allwinner,sun8i-emac.txt b/Documentation/devicetree/bindings/net/allwinner,sun8i-emac.txt +new file mode 100644 +index 0000000..92e4ef3b +--- /dev/null ++++ b/Documentation/devicetree/bindings/net/allwinner,sun8i-emac.txt +@@ -0,0 +1,70 @@ ++* Allwinner sun8i EMAC ethernet controller ++ ++Required properties: ++- compatible: should be one of the following string: ++ "allwinner,sun8i-a83t-emac" ++ "allwinner,sun8i-h3-emac" ++ "allwinner,sun50i-a64-emac" ++- reg: address and length of the register for the device. ++- syscon: A phandle to the syscon of the SoC ++- interrupts: interrupt for the device ++- clocks: A phandle to the reference clock for this device ++- clock-names: should be "ahb" ++- resets: A phandle to the reset control for this device ++- reset-names: should be "ahb" ++- phy-mode: See ethernet.txt ++- phy-handle: See ethernet.txt ++- #address-cells: shall be 1 ++- #size-cells: shall be 0 ++ ++Optional properties: ++- allwinner,tx-delay: TX clock delay chain value. Range value is 0-0x07. Default is 0) ++- allwinner,rx-delay: RX clock delay chain value. Range value is 0-0x1F. Default is 0) ++Both delay properties does not have units, there are arbitrary value. ++The TX/RX clock delay chain settings are board specific and could be found ++in vendor FEX files. ++ ++Optional properties for "allwinner,sun8i-h3-emac": ++- allwinner,leds-active-low: EPHY LEDs are active low ++ ++Required child node of emac: ++- mdio bus node: should be named mdio ++ ++Required properties of the mdio node: ++- #address-cells: shall be 1 ++- #size-cells: shall be 0 ++ ++The device node referenced by "phy" or "phy-handle" should be a child node ++of the mdio node. See phy.txt for the generic PHY bindings. ++ ++Required properties of the phy node with "allwinner,sun8i-h3-emac": ++- clocks: an extra phandle to the reference clock for the EPHY ++- resets: an extra phandle to the reset control for the EPHY ++ ++Example: ++ ++emac: ethernet@01c0b000 { ++ compatible = "allwinner,sun8i-h3-emac"; ++ syscon = <&syscon>; ++ reg = <0x01c0b000 0x104>; ++ interrupts = <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>; ++ resets = <&ccu RST_BUS_EMAC>; ++ reset-names = "ahb"; ++ clocks = <&ccu CLK_BUS_EMAC>; ++ clock-names = "ahb"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ phy = <&int_mii_phy>; ++ phy-mode = "mii"; ++ allwinner,leds-active-low; ++ mdio: mdio { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ int_mii_phy: ethernet-phy@1 { ++ reg = <1>; ++ clocks = <&ccu CLK_BUS_EPHY>; ++ resets = <&ccu RST_BUS_EPHY>; ++ }; ++ }; ++}; +-- +2.9.3 + +From aa634aa1b0454befe54a14d03b9a3ba6f750dcfd Mon Sep 17 00:00:00 2001 +From: Corentin LABBE <clabbe.montjoie@gmail.com> +Date: Fri, 7 Oct 2016 10:25:51 +0200 +Subject: [PATCH 4/8] ARM: dts: sun8i-h3: Add dt node for the syscon control + module + +This patch add the dt node for the syscon register present on the +Allwinner H3. + +Only two register are present in this syscon and the only one useful is +the one dedicated to EMAC clock. + +Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com> +--- + arch/arm/boot/dts/sun8i-h3.dtsi | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/arch/arm/boot/dts/sun8i-h3.dtsi b/arch/arm/boot/dts/sun8i-h3.dtsi +index 75a8654..eac7d00 100644 +--- a/arch/arm/boot/dts/sun8i-h3.dtsi ++++ b/arch/arm/boot/dts/sun8i-h3.dtsi +@@ -140,6 +140,11 @@ + #size-cells = <1>; + ranges; + ++ syscon: syscon@01c00000 { ++ compatible = "syscon"; ++ reg = <0x01c00000 0x1000>; ++ }; ++ + dma: dma-controller@01c02000 { + compatible = "allwinner,sun8i-h3-dma"; + reg = <0x01c02000 0x1000>; +-- +2.9.3 + +From 976cca2d4eb5bf85c26dfaad03ea70bff88981fd Mon Sep 17 00:00:00 2001 +From: Peter Robinson <pbrobinson@gmail.com> +Date: Tue, 8 Nov 2016 10:23:08 +0000 +Subject: [PATCH 5/8] ARM: dts: sun8i-h3: add sun8i-emac ethernet driver + +The sun8i-emac is an ethernet MAC hardware that support 10/100/1000 +speed. + +This patch enable the sun8i-emac on the Allwinner H3 SoC Device-tree. +The SoC H3 have an internal PHY, so optionals syscon and ephy are set. + +Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com> +--- + arch/arm/boot/dts/sun8i-h3.dtsi | 24 ++++++++++++++++++++++++ + 1 file changed, 24 insertions(+) + +diff --git a/arch/arm/boot/dts/sun8i-h3.dtsi b/arch/arm/boot/dts/sun8i-h3.dtsi +index eac7d00..86a8665 100644 +--- a/arch/arm/boot/dts/sun8i-h3.dtsi ++++ b/arch/arm/boot/dts/sun8i-h3.dtsi +@@ -535,6 +535,30 @@ + #size-cells = <0>; + }; + ++ emac: ethernet@1c30000 { ++ compatible = "allwinner,sun8i-h3-emac"; ++ syscon = <&syscon>; ++ reg = <0x01c30000 0x104>; ++ interrupts = <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>; ++ resets = <&ccu RST_BUS_EMAC>; ++ reset-names = "ahb"; ++ clocks = <&ccu CLK_BUS_EMAC>; ++ clock-names = "ahb"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ ++ mdio: mdio { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ int_mii_phy: ethernet-phy@1 { ++ reg = <1>; ++ clocks = <&ccu CLK_BUS_EPHY>; ++ resets = <&ccu RST_BUS_EPHY>; ++ }; ++ }; ++ }; ++ + gic: interrupt-controller@01c81000 { + compatible = "arm,cortex-a7-gic", "arm,cortex-a15-gic"; + reg = <0x01c81000 0x1000>, +-- +2.9.3 + +From 6e483713dda9e0d5e8e5f37d8b0367ff216c53db Mon Sep 17 00:00:00 2001 +From: Corentin LABBE <clabbe.montjoie@gmail.com> +Date: Fri, 7 Oct 2016 10:25:53 +0200 +Subject: [PATCH 6/8] ARM: dts: sun8i: Enable sun8i-emac on the Orange PI PC + +The sun8i-emac hardware is present on the Orange PI PC. +It uses the internal PHY. + +This patch create the needed emac node. + +Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com> +--- + arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts +index 3ec9712..1b8dbc0 100644 +--- a/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts ++++ b/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts +@@ -54,6 +54,7 @@ + + aliases { + serial0 = &uart0; ++ ethernet0 = &emac; + }; + + chosen { +@@ -183,3 +184,10 @@ + /* USB VBUS is always on */ + status = "okay"; + }; ++ ++&emac { ++ phy-handle = <&int_mii_phy>; ++ phy-mode = "mii"; ++ allwinner,leds-active-low; ++ status = "okay"; ++}; +-- +2.9.3 + +From 05cf4e2c77424ad5c9dc8495a65d6695d2831b0a Mon Sep 17 00:00:00 2001 +From: Hans de Goede <hdegoede@redhat.com> +Date: Fri, 7 Oct 2016 10:25:54 +0200 +Subject: [PATCH 7/8] ARM: dts: sun8i: Enable sun8i-emac on the Orange PI One + +The sun8i-emac hardware is present on the Orange PI One. +It uses the internal PHY. + +This patch create the needed emac node. + +Signed-off-by: Hans de Goede <hdegoede@redhat.com> +Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com> +--- + arch/arm/boot/dts/sun8i-h3-orangepi-one.dts | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts +index 5c9b5bf..fa398cc 100644 +--- a/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts ++++ b/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts +@@ -54,6 +54,7 @@ + + aliases { + serial0 = &uart0; ++ ethernet0 = &emac; + }; + + chosen { +@@ -94,6 +95,13 @@ + status = "okay"; + }; + ++&emac { ++ phy-handle = <&int_mii_phy>; ++ phy-mode = "mii"; ++ allwinner,leds-active-low; ++ status = "okay"; ++}; ++ + &mmc0 { + pinctrl-names = "default"; + pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin>; +-- +2.9.3 + +From 52c300b614fcb29e81067aa7ed8aafa489b590b7 Mon Sep 17 00:00:00 2001 +From: Peter Robinson <pbrobinson@gmail.com> +Date: Tue, 8 Nov 2016 10:24:42 +0000 +Subject: [PATCH 8/8] ARM: dts: sun8i: Enable sun8i-emac on the Orange Pi 2 + +The sun8i-emac hardware is present on the Orange PI 2. +It uses the internal PHY. + +This patch create the needed emac node. + +Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com> +--- + arch/arm/boot/dts/sun8i-h3-orangepi-2.dts | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts +index e5bcaba..ad66b61 100644 +--- a/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts ++++ b/arch/arm/boot/dts/sun8i-h3-orangepi-2.dts +@@ -54,6 +54,7 @@ + + aliases { + serial0 = &uart0; ++ ethernet0 = &emac; + /* ethernet0 is the H3 emac, defined in sun8i-h3.dtsi */ + ethernet1 = &rtl8189; + }; +@@ -212,3 +213,10 @@ + usb1_vbus-supply = <®_usb1_vbus>; + status = "okay"; + }; ++ ++&emac { ++ phy-handle = <&int_mii_phy>; ++ phy-mode = "mii"; ++ allwinner,leds-active-low; ++ status = "okay"; ++}; +-- +2.9.3 + diff --git a/bcm283x-vc4-fixes.patch b/bcm283x-vc4-fixes.patch new file mode 100644 index 000000000..d42ceb62a --- /dev/null +++ b/bcm283x-vc4-fixes.patch @@ -0,0 +1,43 @@ +From 30772942cc1095c3129eecfa182e2c568e566b9d Mon Sep 17 00:00:00 2001 +From: Dan Carpenter <dan.carpenter@oracle.com> +Date: Thu, 13 Oct 2016 11:54:31 +0300 +Subject: [PATCH] drm/vc4: Fix a couple error codes in vc4_cl_lookup_bos() + +If the allocation fails the current code returns success. If +copy_from_user() fails it returns the number of bytes remaining instead +of -EFAULT. + +Fixes: d5b1a78a772f ("drm/vc4: Add support for drawing 3D frames.") +Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com> +Reviewed-by: Eric Anholt <eric@anholt.net> +--- + drivers/gpu/drm/vc4/vc4_gem.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c +index ae1609e..4050540 100644 +--- a/drivers/gpu/drm/vc4/vc4_gem.c ++++ b/drivers/gpu/drm/vc4/vc4_gem.c +@@ -548,14 +548,15 @@ vc4_cl_lookup_bos(struct drm_device *dev, + + handles = drm_malloc_ab(exec->bo_count, sizeof(uint32_t)); + if (!handles) { ++ ret = -ENOMEM; + DRM_ERROR("Failed to allocate incoming GEM handles\n"); + goto fail; + } + +- ret = copy_from_user(handles, +- (void __user *)(uintptr_t)args->bo_handles, +- exec->bo_count * sizeof(uint32_t)); +- if (ret) { ++ if (copy_from_user(handles, ++ (void __user *)(uintptr_t)args->bo_handles, ++ exec->bo_count * sizeof(uint32_t))) { ++ ret = -EFAULT; + DRM_ERROR("Failed to copy in GEM handles\n"); + goto fail; + } +-- +2.9.3 + diff --git a/config-arm64 b/config-arm64 index 983eafb89..c1a4bce6a 100644 --- a/config-arm64 +++ b/config-arm64 @@ -198,6 +198,7 @@ CONFIG_TEGRA_ACONNECT=m CONFIG_SUNXI_RSB=m CONFIG_AHCI_SUNXI=m CONFIG_NET_VENDOR_ALLWINNER=y +CONFIG_SUN8I_EMAC=m # CONFIG_SUN4I_EMAC is not set # CONFIG_MDIO_SUN4I is not set # CONFIG_KEYBOARD_SUN4I_LRADC is not set diff --git a/config-armv7-generic b/config-armv7-generic index 5d1006372..db6496b2d 100644 --- a/config-armv7-generic +++ b/config-armv7-generic @@ -225,6 +225,7 @@ CONFIG_IR_SUNXI=m CONFIG_MDIO_SUN4I=m CONFIG_DWMAC_SUNXI=m CONFIG_SUN4I_EMAC=m +CONFIG_SUN8I_EMAC=m CONFIG_RTC_DRV_SUN6I=m CONFIG_MTD_NAND_SUNXI=m CONFIG_SERIO_SUN4I_PS2=m diff --git a/kernel.spec b/kernel.spec index 8256b8085..025b07d75 100644 --- a/kernel.spec +++ b/kernel.spec @@ -523,6 +523,10 @@ Patch430: ARM-tegra-usb-no-reset.patch Patch431: bcm2837-initial-support.patch +Patch432: bcm283x-vc4-fixes.patch + +Patch433: AllWinner-net-emac.patch + Patch460: lib-cpumask-Make-CPUMASK_OFFSTACK-usable-without-deb.patch Patch466: input-kill-stupid-messages.patch @@ -2154,6 +2158,9 @@ fi # # %changelog +* Tue Nov 8 2016 Peter Robinson <pbrobinson@fedoraproject.org> +- Sync some ARM patches from F-25 branch + * Mon Nov 07 2016 Laura Abbott <labbott@redhat.com> - Enable CONFIG_EXT4_ENCRYPTION (rhbz 1389509) - Enable CONFIG_NFSD_FLEXFILELAYOUT |