From 3353739262c89c2e24fb3a285a0ed2a3f01ce351 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 13 Jun 2013 01:17:37 +0000 Subject: [PATCH] [armhf/armmp] Add EMAC ethernet driver found on Allwinner A10 SoCs (Closes: #711998) - Add MDIO bus driver for the Allwinner EMAC - Add EMAC controller node to sun4i DTSI - cubieboard: Enable ethernet (EMAC) support in dts - Add EMAC Controller to Hackberry dt All cherry-picked by Niall Walsh svn path=/dists/trunk/linux/; revision=20250 --- debian/changelog | 11 + debian/config/armhf/config.armmp | 10 + ...ernet-driver-found-on-Allwinner-A10-.patch | 1221 +++++++++++++++++ ...IO-bus-driver-for-the-Allwinner-EMAC.patch | 286 ++++ ...xing-options-for-the-ethernet-contro.patch | 42 + ...d-EMAC-controller-node-to-sun4i-DTSI.patch | 43 + ...-Enable-ethernet-EMAC-support-in-dts.patch | 43 + ...-Add-EMAC-Controller-to-Hackberry-dt.patch | 75 + ...-sun4i-emac-fix-a-typo-in-emac_probe.patch | 31 + ...n4i-emac-remove-erroneous-assignment.patch | 43 + ...emove-redundant-platform_set_drvdata.patch | 35 + ...t-sun4i-emac-Staticize-local-symbols.patch | 41 + debian/patches/series | 10 + 13 files changed, 1891 insertions(+) create mode 100644 debian/patches/features/arm/sunxi-emac/0001-net-Add-EMAC-ethernet-driver-found-on-Allwinner-A10-.patch create mode 100644 debian/patches/features/arm/sunxi-emac/0002-net-Add-MDIO-bus-driver-for-the-Allwinner-EMAC.patch create mode 100644 debian/patches/features/arm/sunxi-emac/0003-ARM-sun4i-Add-muxing-options-for-the-ethernet-contro.patch create mode 100644 debian/patches/features/arm/sunxi-emac/0004-ARM-sunxi-Add-EMAC-controller-node-to-sun4i-DTSI.patch create mode 100644 debian/patches/features/arm/sunxi-emac/0005-ARM-cubieboard-Enable-ethernet-EMAC-support-in-dts.patch create mode 100644 debian/patches/features/arm/sunxi-emac/0006-ARM-sunxi-Add-EMAC-Controller-to-Hackberry-dt.patch create mode 100644 debian/patches/features/arm/sunxi-emac/0007-net-sun4i-emac-fix-a-typo-in-emac_probe.patch create mode 100644 debian/patches/features/arm/sunxi-emac/0008-net-sun4i-emac-remove-erroneous-assignment.patch create mode 100644 debian/patches/features/arm/sunxi-emac/0009-net-sun4i-emac-Remove-redundant-platform_set_drvdata.patch create mode 100644 debian/patches/features/arm/sunxi-emac/0010-net-sun4i-emac-Staticize-local-symbols.patch diff --git a/debian/changelog b/debian/changelog index aa60811dd..61173f78b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,14 @@ +linux (3.10~rc5-1~exp2) UNRELEASED; urgency=low + + * [armhf/armmp] Add EMAC ethernet driver found on Allwinner A10 SoCs, + thanks to Niall Walsh (Closes: #711998) + - Add MDIO bus driver for the Allwinner EMAC + - Add EMAC controller node to sun4i DTSI + - cubieboard: Enable ethernet (EMAC) support in dts + - Add EMAC Controller to Hackberry dt + + -- Ben Hutchings Thu, 13 Jun 2013 01:50:52 +0100 + linux (3.10~rc5-1~exp1) experimental; urgency=low * New upstream release candidate diff --git a/debian/config/armhf/config.armmp b/debian/config/armhf/config.armmp index dc871a330..31ad66539 100644 --- a/debian/config/armhf/config.armmp +++ b/debian/config/armhf/config.armmp @@ -332,6 +332,11 @@ CONFIG_MTD_NAND_MXC=m CONFIG_MTD_ONENAND=y CONFIG_MTD_ONENAND_OMAP2=m +## +## file: drivers/net/ethernet/allwinner/Kconfig +## +CONFIG_SUN4I_EMAC=m + ## ## file: drivers/net/ethernet/marvell/Kconfig ## @@ -353,6 +358,11 @@ CONFIG_TI_DAVINCI_MDIO=m CONFIG_TI_DAVINCI_CPDMA=m CONFIG_TI_CPSW=m +## +## file: drivers/net/phy/Kconfig +## +CONFIG_MDIO_SUN4I=y + ## ## file: drivers/net/wireless/ti/wl1251/Kconfig ## diff --git a/debian/patches/features/arm/sunxi-emac/0001-net-Add-EMAC-ethernet-driver-found-on-Allwinner-A10-.patch b/debian/patches/features/arm/sunxi-emac/0001-net-Add-EMAC-ethernet-driver-found-on-Allwinner-A10-.patch new file mode 100644 index 000000000..40bae3e1e --- /dev/null +++ b/debian/patches/features/arm/sunxi-emac/0001-net-Add-EMAC-ethernet-driver-found-on-Allwinner-A10-.patch @@ -0,0 +1,1221 @@ +From: Stefan Roese +Date: Thu, 30 May 2013 03:49:20 +0000 +Subject: [PATCH 01/10] net: Add EMAC ethernet driver found on Allwinner A10 + SoC's +Bug-Debian: http://bugs.debian.org/711998 + +The Allwinner A10 has an ethernet controller that seem to be developped +internally by them. + +The exact feature set of this controller is unknown, since there is no +public documentation for this IP, and this driver is mostly the one +published by Allwinner that has been heavily cleaned up. + +Signed-off-by: Stefan Roese +Signed-off-by: Maxime Ripard +Tested-by: Richard Genoud +Signed-off-by: David S. Miller +(cherry picked from commit 492205050d77bcc4f85f6dc0da6b6fdbca1d6ff7) +--- + .../bindings/net/allwinner,sun4i-emac.txt | 22 + + drivers/net/ethernet/Kconfig | 1 + + drivers/net/ethernet/Makefile | 1 + + drivers/net/ethernet/allwinner/Kconfig | 36 + + drivers/net/ethernet/allwinner/Makefile | 5 + + drivers/net/ethernet/allwinner/sun4i-emac.c | 960 ++++++++++++++++++++ + drivers/net/ethernet/allwinner/sun4i-emac.h | 108 +++ + 7 files changed, 1133 insertions(+) + create mode 100644 Documentation/devicetree/bindings/net/allwinner,sun4i-emac.txt + create mode 100644 drivers/net/ethernet/allwinner/Kconfig + create mode 100644 drivers/net/ethernet/allwinner/Makefile + create mode 100644 drivers/net/ethernet/allwinner/sun4i-emac.c + create mode 100644 drivers/net/ethernet/allwinner/sun4i-emac.h + +diff --git a/Documentation/devicetree/bindings/net/allwinner,sun4i-emac.txt b/Documentation/devicetree/bindings/net/allwinner,sun4i-emac.txt +new file mode 100644 +index 0000000..b90bfcd +--- /dev/null ++++ b/Documentation/devicetree/bindings/net/allwinner,sun4i-emac.txt +@@ -0,0 +1,22 @@ ++* Allwinner EMAC ethernet controller ++ ++Required properties: ++- compatible: should be "allwinner,sun4i-emac". ++- reg: address and length of the register set for the device. ++- interrupts: interrupt for the device ++- phy: A phandle to a phy node defining the PHY address (as the reg ++ property, a single integer). ++- clocks: A phandle to the reference clock for this device ++ ++Optional properties: ++- (local-)mac-address: mac address to be used by this driver ++ ++Example: ++ ++emac: ethernet@01c0b000 { ++ compatible = "allwinner,sun4i-emac"; ++ reg = <0x01c0b000 0x1000>; ++ interrupts = <55>; ++ clocks = <&ahb_gates 17>; ++ phy = <&phy0>; ++}; +diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig +index ed956e0..18fd6fb 100644 +--- a/drivers/net/ethernet/Kconfig ++++ b/drivers/net/ethernet/Kconfig +@@ -20,6 +20,7 @@ config SUNGEM_PHY + source "drivers/net/ethernet/3com/Kconfig" + source "drivers/net/ethernet/adaptec/Kconfig" + source "drivers/net/ethernet/aeroflex/Kconfig" ++source "drivers/net/ethernet/allwinner/Kconfig" + source "drivers/net/ethernet/alteon/Kconfig" + source "drivers/net/ethernet/amd/Kconfig" + source "drivers/net/ethernet/apple/Kconfig" +diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile +index 8268d85..009da27 100644 +--- a/drivers/net/ethernet/Makefile ++++ b/drivers/net/ethernet/Makefile +@@ -6,6 +6,7 @@ obj-$(CONFIG_NET_VENDOR_3COM) += 3com/ + obj-$(CONFIG_NET_VENDOR_8390) += 8390/ + obj-$(CONFIG_NET_VENDOR_ADAPTEC) += adaptec/ + obj-$(CONFIG_GRETH) += aeroflex/ ++obj-$(CONFIG_NET_VENDOR_ALLWINNER) += allwinner/ + obj-$(CONFIG_NET_VENDOR_ALTEON) += alteon/ + obj-$(CONFIG_NET_VENDOR_AMD) += amd/ + obj-$(CONFIG_NET_VENDOR_APPLE) += apple/ +diff --git a/drivers/net/ethernet/allwinner/Kconfig b/drivers/net/ethernet/allwinner/Kconfig +new file mode 100644 +index 0000000..66d3532 +--- /dev/null ++++ b/drivers/net/ethernet/allwinner/Kconfig +@@ -0,0 +1,36 @@ ++# ++# Allwinner device configuration ++# ++ ++config NET_VENDOR_ALLWINNER ++ bool "Allwinner devices" ++ default y ++ depends on ARCH_SUNXI ++ ---help--- ++ If you have a network (Ethernet) card belonging to this ++ class, say Y and read the Ethernet-HOWTO, available from ++ . ++ ++ Note that the answer to this question doesn't directly ++ affect the kernel: saying N will just cause the configurator ++ to skip all the questions about Allwinner cards. If you say Y, ++ you will be asked for your specific card in the following ++ questions. ++ ++if NET_VENDOR_ALLWINNER ++ ++config SUN4I_EMAC ++ tristate "Allwinner A10 EMAC support" ++ depends on ARCH_SUNXI ++ depends on OF ++ select CRC32 ++ select NET_CORE ++ select MII ++ select PHYLIB ++ ---help--- ++ Support for Allwinner A10 EMAC ethernet driver. ++ ++ To compile this driver as a module, choose M here. The module ++ will be called sun4i-emac. ++ ++endif # NET_VENDOR_ALLWINNER +diff --git a/drivers/net/ethernet/allwinner/Makefile b/drivers/net/ethernet/allwinner/Makefile +new file mode 100644 +index 0000000..03129f7 +--- /dev/null ++++ b/drivers/net/ethernet/allwinner/Makefile +@@ -0,0 +1,5 @@ ++# ++# Makefile for the Allwinner device drivers. ++# ++ ++obj-$(CONFIG_SUN4I_EMAC) += sun4i-emac.o +diff --git a/drivers/net/ethernet/allwinner/sun4i-emac.c b/drivers/net/ethernet/allwinner/sun4i-emac.c +new file mode 100644 +index 0000000..b411344 +--- /dev/null ++++ b/drivers/net/ethernet/allwinner/sun4i-emac.c +@@ -0,0 +1,960 @@ ++/* ++ * Allwinner EMAC Fast Ethernet driver for Linux. ++ * ++ * Copyright 2012-2013 Stefan Roese ++ * Copyright 2013 Maxime Ripard ++ * ++ * Based on the Linux driver provided by Allwinner: ++ * Copyright (C) 1997 Sten Wang ++ * ++ * This file is licensed under the terms of the GNU General Public ++ * License version 2. This program is licensed "as is" without any ++ * warranty of any kind, whether express or implied. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "sun4i-emac.h" ++ ++#define DRV_NAME "sun4i-emac" ++#define DRV_VERSION "1.02" ++ ++#define EMAC_MAX_FRAME_LEN 0x0600 ++ ++/* Transmit timeout, default 5 seconds. */ ++static int watchdog = 5000; ++module_param(watchdog, int, 0400); ++MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds"); ++ ++/* EMAC register address locking. ++ * ++ * The EMAC uses an address register to control where data written ++ * to the data register goes. This means that the address register ++ * must be preserved over interrupts or similar calls. ++ * ++ * During interrupt and other critical calls, a spinlock is used to ++ * protect the system, but the calls themselves save the address ++ * in the address register in case they are interrupting another ++ * access to the device. ++ * ++ * For general accesses a lock is provided so that calls which are ++ * allowed to sleep are serialised so that the address register does ++ * not need to be saved. This lock also serves to serialise access ++ * to the EEPROM and PHY access registers which are shared between ++ * these two devices. ++ */ ++ ++/* The driver supports the original EMACE, and now the two newer ++ * devices, EMACA and EMACB. ++ */ ++ ++struct emac_board_info { ++ struct clk *clk; ++ struct device *dev; ++ struct platform_device *pdev; ++ spinlock_t lock; ++ void __iomem *membase; ++ u32 msg_enable; ++ struct net_device *ndev; ++ struct sk_buff *skb_last; ++ u16 tx_fifo_stat; ++ ++ int emacrx_completed_flag; ++ ++ struct phy_device *phy_dev; ++ struct device_node *phy_node; ++ unsigned int link; ++ unsigned int speed; ++ unsigned int duplex; ++ ++ phy_interface_t phy_interface; ++}; ++ ++static void emac_update_speed(struct net_device *dev) ++{ ++ struct emac_board_info *db = netdev_priv(dev); ++ unsigned int reg_val; ++ ++ /* set EMAC SPEED, depend on PHY */ ++ reg_val = readl(db->membase + EMAC_MAC_SUPP_REG); ++ reg_val &= ~(0x1 << 8); ++ if (db->speed == SPEED_100) ++ reg_val |= 1 << 8; ++ writel(reg_val, db->membase + EMAC_MAC_SUPP_REG); ++} ++ ++static void emac_update_duplex(struct net_device *dev) ++{ ++ struct emac_board_info *db = netdev_priv(dev); ++ unsigned int reg_val; ++ ++ /* set duplex depend on phy */ ++ reg_val = readl(db->membase + EMAC_MAC_CTL1_REG); ++ reg_val &= ~EMAC_MAC_CTL1_DUPLEX_EN; ++ if (db->duplex) ++ reg_val |= EMAC_MAC_CTL1_DUPLEX_EN; ++ writel(reg_val, db->membase + EMAC_MAC_CTL1_REG); ++} ++ ++static void emac_handle_link_change(struct net_device *dev) ++{ ++ struct emac_board_info *db = netdev_priv(dev); ++ struct phy_device *phydev = db->phy_dev; ++ unsigned long flags; ++ int status_change = 0; ++ ++ if (phydev->link) { ++ if (db->speed != phydev->speed) { ++ spin_lock_irqsave(&db->lock, flags); ++ db->speed = phydev->speed; ++ emac_update_speed(dev); ++ spin_unlock_irqrestore(&db->lock, flags); ++ status_change = 1; ++ } ++ ++ if (db->duplex != phydev->duplex) { ++ spin_lock_irqsave(&db->lock, flags); ++ db->duplex = phydev->duplex; ++ emac_update_duplex(dev); ++ spin_unlock_irqrestore(&db->lock, flags); ++ status_change = 1; ++ } ++ } ++ ++ if (phydev->link != db->link) { ++ if (!phydev->link) { ++ db->speed = 0; ++ db->duplex = -1; ++ } ++ db->link = phydev->link; ++ ++ status_change = 1; ++ } ++ ++ if (status_change) ++ phy_print_status(phydev); ++} ++ ++static int emac_mdio_probe(struct net_device *dev) ++{ ++ struct emac_board_info *db = netdev_priv(dev); ++ ++ /* to-do: PHY interrupts are currently not supported */ ++ ++ /* attach the mac to the phy */ ++ db->phy_dev = of_phy_connect(db->ndev, db->phy_node, ++ &emac_handle_link_change, 0, ++ db->phy_interface); ++ if (!db->phy_dev) { ++ netdev_err(db->ndev, "could not find the PHY\n"); ++ return -ENODEV; ++ } ++ ++ /* mask with MAC supported features */ ++ db->phy_dev->supported &= PHY_BASIC_FEATURES; ++ db->phy_dev->advertising = db->phy_dev->supported; ++ ++ db->link = 0; ++ db->speed = 0; ++ db->duplex = -1; ++ ++ return 0; ++} ++ ++static void emac_mdio_remove(struct net_device *dev) ++{ ++ struct emac_board_info *db = netdev_priv(dev); ++ ++ phy_disconnect(db->phy_dev); ++ db->phy_dev = NULL; ++} ++ ++static void emac_reset(struct emac_board_info *db) ++{ ++ dev_dbg(db->dev, "resetting device\n"); ++ ++ /* RESET device */ ++ writel(0, db->membase + EMAC_CTL_REG); ++ udelay(200); ++ writel(EMAC_CTL_RESET, db->membase + EMAC_CTL_REG); ++ udelay(200); ++} ++ ++static void emac_outblk_32bit(void __iomem *reg, void *data, int count) ++{ ++ writesl(reg, data, round_up(count, 4) / 4); ++} ++ ++static void emac_inblk_32bit(void __iomem *reg, void *data, int count) ++{ ++ readsl(reg, data, round_up(count, 4) / 4); ++} ++ ++static int emac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) ++{ ++ struct emac_board_info *dm = netdev_priv(dev); ++ struct phy_device *phydev = dm->phy_dev; ++ ++ if (!netif_running(dev)) ++ return -EINVAL; ++ ++ if (!phydev) ++ return -ENODEV; ++ ++ return phy_mii_ioctl(phydev, rq, cmd); ++} ++ ++/* ethtool ops */ ++static void emac_get_drvinfo(struct net_device *dev, ++ struct ethtool_drvinfo *info) ++{ ++ strlcpy(info->driver, DRV_NAME, sizeof(DRV_NAME)); ++ strlcpy(info->version, DRV_VERSION, sizeof(DRV_VERSION)); ++ strlcpy(info->bus_info, dev_name(&dev->dev), sizeof(info->bus_info)); ++} ++ ++static int emac_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) ++{ ++ struct emac_board_info *dm = netdev_priv(dev); ++ struct phy_device *phydev = dm->phy_dev; ++ ++ if (!phydev) ++ return -ENODEV; ++ ++ return phy_ethtool_gset(phydev, cmd); ++} ++ ++static int emac_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) ++{ ++ struct emac_board_info *dm = netdev_priv(dev); ++ struct phy_device *phydev = dm->phy_dev; ++ ++ if (!phydev) ++ return -ENODEV; ++ ++ return phy_ethtool_sset(phydev, cmd); ++} ++ ++static const struct ethtool_ops emac_ethtool_ops = { ++ .get_drvinfo = emac_get_drvinfo, ++ .get_settings = emac_get_settings, ++ .set_settings = emac_set_settings, ++ .get_link = ethtool_op_get_link, ++}; ++ ++unsigned int emac_setup(struct net_device *ndev) ++{ ++ struct emac_board_info *db = netdev_priv(ndev); ++ unsigned int reg_val; ++ ++ /* set up TX */ ++ reg_val = readl(db->membase + EMAC_TX_MODE_REG); ++ ++ writel(reg_val | EMAC_TX_MODE_ABORTED_FRAME_EN, ++ db->membase + EMAC_TX_MODE_REG); ++ ++ /* set up RX */ ++ reg_val = readl(db->membase + EMAC_RX_CTL_REG); ++ ++ writel(reg_val | EMAC_RX_CTL_PASS_LEN_OOR_EN | ++ EMAC_RX_CTL_ACCEPT_UNICAST_EN | EMAC_RX_CTL_DA_FILTER_EN | ++ EMAC_RX_CTL_ACCEPT_MULTICAST_EN | ++ EMAC_RX_CTL_ACCEPT_BROADCAST_EN, ++ db->membase + EMAC_RX_CTL_REG); ++ ++ /* set MAC */ ++ /* set MAC CTL0 */ ++ reg_val = readl(db->membase + EMAC_MAC_CTL0_REG); ++ writel(reg_val | EMAC_MAC_CTL0_RX_FLOW_CTL_EN | ++ EMAC_MAC_CTL0_TX_FLOW_CTL_EN, ++ db->membase + EMAC_MAC_CTL0_REG); ++ ++ /* set MAC CTL1 */ ++ reg_val = readl(db->membase + EMAC_MAC_CTL1_REG); ++ reg_val |= EMAC_MAC_CTL1_LEN_CHECK_EN; ++ reg_val |= EMAC_MAC_CTL1_CRC_EN; ++ reg_val |= EMAC_MAC_CTL1_PAD_EN; ++ writel(reg_val, db->membase + EMAC_MAC_CTL1_REG); ++ ++ /* set up IPGT */ ++ writel(EMAC_MAC_IPGT_FULL_DUPLEX, db->membase + EMAC_MAC_IPGT_REG); ++ ++ /* set up IPGR */ ++ writel((EMAC_MAC_IPGR_IPG1 << 8) | EMAC_MAC_IPGR_IPG2, ++ db->membase + EMAC_MAC_IPGR_REG); ++ ++ /* set up Collison window */ ++ writel((EMAC_MAC_CLRT_COLLISION_WINDOW << 8) | EMAC_MAC_CLRT_RM, ++ db->membase + EMAC_MAC_CLRT_REG); ++ ++ /* set up Max Frame Length */ ++ writel(EMAC_MAX_FRAME_LEN, ++ db->membase + EMAC_MAC_MAXF_REG); ++ ++ return 0; ++} ++ ++unsigned int emac_powerup(struct net_device *ndev) ++{ ++ struct emac_board_info *db = netdev_priv(ndev); ++ unsigned int reg_val; ++ ++ /* initial EMAC */ ++ /* flush RX FIFO */ ++ reg_val = readl(db->membase + EMAC_RX_CTL_REG); ++ reg_val |= 0x8; ++ writel(reg_val, db->membase + EMAC_RX_CTL_REG); ++ udelay(1); ++ ++ /* initial MAC */ ++ /* soft reset MAC */ ++ reg_val = readl(db->membase + EMAC_MAC_CTL0_REG); ++ reg_val &= ~EMAC_MAC_CTL0_SOFT_RESET; ++ writel(reg_val, db->membase + EMAC_MAC_CTL0_REG); ++ ++ /* set MII clock */ ++ reg_val = readl(db->membase + EMAC_MAC_MCFG_REG); ++ reg_val &= (~(0xf << 2)); ++ reg_val |= (0xD << 2); ++ writel(reg_val, db->membase + EMAC_MAC_MCFG_REG); ++ ++ /* clear RX counter */ ++ writel(0x0, db->membase + EMAC_RX_FBC_REG); ++ ++ /* disable all interrupt and clear interrupt status */ ++ writel(0, db->membase + EMAC_INT_CTL_REG); ++ reg_val = readl(db->membase + EMAC_INT_STA_REG); ++ writel(reg_val, db->membase + EMAC_INT_STA_REG); ++ ++ udelay(1); ++ ++ /* set up EMAC */ ++ emac_setup(ndev); ++ ++ /* set mac_address to chip */ ++ writel(ndev->dev_addr[0] << 16 | ndev->dev_addr[1] << 8 | ndev-> ++ dev_addr[2], db->membase + EMAC_MAC_A1_REG); ++ writel(ndev->dev_addr[3] << 16 | ndev->dev_addr[4] << 8 | ndev-> ++ dev_addr[5], db->membase + EMAC_MAC_A0_REG); ++ ++ mdelay(1); ++ ++ return 0; ++} ++ ++static int emac_set_mac_address(struct net_device *dev, void *p) ++{ ++ struct sockaddr *addr = p; ++ struct emac_board_info *db = netdev_priv(dev); ++ ++ if (netif_running(dev)) ++ return -EBUSY; ++ ++ memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); ++ ++ writel(dev->dev_addr[0] << 16 | dev->dev_addr[1] << 8 | dev-> ++ dev_addr[2], db->membase + EMAC_MAC_A1_REG); ++ writel(dev->dev_addr[3] << 16 | dev->dev_addr[4] << 8 | dev-> ++ dev_addr[5], db->membase + EMAC_MAC_A0_REG); ++ ++ return 0; ++} ++ ++/* Initialize emac board */ ++static void emac_init_device(struct net_device *dev) ++{ ++ struct emac_board_info *db = netdev_priv(dev); ++ unsigned long flags; ++ unsigned int reg_val; ++ ++ spin_lock_irqsave(&db->lock, flags); ++ ++ emac_update_speed(dev); ++ emac_update_duplex(dev); ++ ++ /* enable RX/TX */ ++ reg_val = readl(db->membase + EMAC_CTL_REG); ++ writel(reg_val | EMAC_CTL_RESET | EMAC_CTL_TX_EN | EMAC_CTL_RX_EN, ++ db->membase + EMAC_CTL_REG); ++ ++ /* enable RX/TX0/RX Hlevel interrup */ ++ reg_val = readl(db->membase + EMAC_INT_CTL_REG); ++ reg_val |= (0xf << 0) | (0x01 << 8); ++ writel(reg_val, db->membase + EMAC_INT_CTL_REG); ++ ++ spin_unlock_irqrestore(&db->lock, flags); ++} ++ ++/* Our watchdog timed out. Called by the networking layer */ ++static void emac_timeout(struct net_device *dev) ++{ ++ struct emac_board_info *db = netdev_priv(dev); ++ unsigned long flags; ++ ++ if (netif_msg_timer(db)) ++ dev_err(db->dev, "tx time out.\n"); ++ ++ /* Save previous register address */ ++ spin_lock_irqsave(&db->lock, flags); ++ ++ netif_stop_queue(dev); ++ emac_reset(db); ++ emac_init_device(dev); ++ /* We can accept TX packets again */ ++ dev->trans_start = jiffies; ++ netif_wake_queue(dev); ++ ++ /* Restore previous register address */ ++ spin_unlock_irqrestore(&db->lock, flags); ++} ++ ++/* Hardware start transmission. ++ * Send a packet to media from the upper layer. ++ */ ++static int emac_start_xmit(struct sk_buff *skb, struct net_device *dev) ++{ ++ struct emac_board_info *db = netdev_priv(dev); ++ unsigned long channel; ++ unsigned long flags; ++ ++ channel = db->tx_fifo_stat & 3; ++ if (channel == 3) ++ return 1; ++ ++ channel = (channel == 1 ? 1 : 0); ++ ++ spin_lock_irqsave(&db->lock, flags); ++ ++ writel(channel, db->membase + EMAC_TX_INS_REG); ++ ++ emac_outblk_32bit(db->membase + EMAC_TX_IO_DATA_REG, ++ skb->data, skb->len); ++ dev->stats.tx_bytes += skb->len; ++ ++ db->tx_fifo_stat |= 1 << channel; ++ /* TX control: First packet immediately send, second packet queue */ ++ if (channel == 0) { ++ /* set TX len */ ++ writel(skb->len, db->membase + EMAC_TX_PL0_REG); ++ /* start translate from fifo to phy */ ++ writel(readl(db->membase + EMAC_TX_CTL0_REG) | 1, ++ db->membase + EMAC_TX_CTL0_REG); ++ ++ /* save the time stamp */ ++ dev->trans_start = jiffies; ++ } else if (channel == 1) { ++ /* set TX len */ ++ writel(skb->len, db->membase + EMAC_TX_PL1_REG); ++ /* start translate from fifo to phy */ ++ writel(readl(db->membase + EMAC_TX_CTL1_REG) | 1, ++ db->membase + EMAC_TX_CTL1_REG); ++ ++ /* save the time stamp */ ++ dev->trans_start = jiffies; ++ } ++ ++ if ((db->tx_fifo_stat & 3) == 3) { ++ /* Second packet */ ++ netif_stop_queue(dev); ++ } ++ ++ spin_unlock_irqrestore(&db->lock, flags); ++ ++ /* free this SKB */ ++ dev_kfree_skb(skb); ++ ++ return NETDEV_TX_OK; ++} ++ ++/* EMAC interrupt handler ++ * receive the packet to upper layer, free the transmitted packet ++ */ ++static void emac_tx_done(struct net_device *dev, struct emac_board_info *db, ++ unsigned int tx_status) ++{ ++ /* One packet sent complete */ ++ db->tx_fifo_stat &= ~(tx_status & 3); ++ if (3 == (tx_status & 3)) ++ dev->stats.tx_packets += 2; ++ else ++ dev->stats.tx_packets++; ++ ++ if (netif_msg_tx_done(db)) ++ dev_dbg(db->dev, "tx done, NSR %02x\n", tx_status); ++ ++ netif_wake_queue(dev); ++} ++ ++/* Received a packet and pass to upper layer ++ */ ++static void emac_rx(struct net_device *dev) ++{ ++ struct emac_board_info *db = netdev_priv(dev); ++ struct sk_buff *skb; ++ u8 *rdptr; ++ bool good_packet; ++ static int rxlen_last; ++ unsigned int reg_val; ++ u32 rxhdr, rxstatus, rxcount, rxlen; ++ ++ /* Check packet ready or not */ ++ while (1) { ++ /* race warning: the first packet might arrive with ++ * the interrupts disabled, but the second will fix ++ * it ++ */ ++ rxcount = readl(db->membase + EMAC_RX_FBC_REG); ++ ++ if (netif_msg_rx_status(db)) ++ dev_dbg(db->dev, "RXCount: %x\n", rxcount); ++ ++ if ((db->skb_last != NULL) && (rxlen_last > 0)) { ++ dev->stats.rx_bytes += rxlen_last; ++ ++ /* Pass to upper layer */ ++ db->skb_last->protocol = eth_type_trans(db->skb_last, ++ dev); ++ netif_rx(db->skb_last); ++ dev->stats.rx_packets++; ++ db->skb_last = NULL; ++ rxlen_last = 0; ++ ++ reg_val = readl(db->membase + EMAC_RX_CTL_REG); ++ reg_val &= ~EMAC_RX_CTL_DMA_EN; ++ writel(reg_val, db->membase + EMAC_RX_CTL_REG); ++ } ++ ++ if (!rxcount) { ++ db->emacrx_completed_flag = 1; ++ reg_val = readl(db->membase + EMAC_INT_CTL_REG); ++ reg_val |= (0xf << 0) | (0x01 << 8); ++ writel(reg_val, db->membase + EMAC_INT_CTL_REG); ++ ++ /* had one stuck? */ ++ rxcount = readl(db->membase + EMAC_RX_FBC_REG); ++ if (!rxcount) ++ return; ++ } ++ ++ reg_val = readl(db->membase + EMAC_RX_IO_DATA_REG); ++ if (netif_msg_rx_status(db)) ++ dev_dbg(db->dev, "receive header: %x\n", reg_val); ++ if (reg_val != EMAC_UNDOCUMENTED_MAGIC) { ++ /* disable RX */ ++ reg_val = readl(db->membase + EMAC_CTL_REG); ++ writel(reg_val & ~EMAC_CTL_RX_EN, ++ db->membase + EMAC_CTL_REG); ++ ++ /* Flush RX FIFO */ ++ reg_val = readl(db->membase + EMAC_RX_CTL_REG); ++ writel(reg_val | (1 << 3), ++ db->membase + EMAC_RX_CTL_REG); ++ ++ do { ++ reg_val = readl(db->membase + EMAC_RX_CTL_REG); ++ } while (reg_val & (1 << 3)); ++ ++ /* enable RX */ ++ reg_val = readl(db->membase + EMAC_CTL_REG); ++ writel(reg_val | EMAC_CTL_RX_EN, ++ db->membase + EMAC_CTL_REG); ++ reg_val = readl(db->membase + EMAC_INT_CTL_REG); ++ reg_val |= (0xf << 0) | (0x01 << 8); ++ writel(reg_val, db->membase + EMAC_INT_CTL_REG); ++ ++ db->emacrx_completed_flag = 1; ++ ++ return; ++ } ++ ++ /* A packet ready now & Get status/length */ ++ good_packet = true; ++ ++ emac_inblk_32bit(db->membase + EMAC_RX_IO_DATA_REG, ++ &rxhdr, sizeof(rxhdr)); ++ ++ if (netif_msg_rx_status(db)) ++ dev_dbg(db->dev, "rxhdr: %x\n", *((int *)(&rxhdr))); ++ ++ rxlen = EMAC_RX_IO_DATA_LEN(rxhdr); ++ rxstatus = EMAC_RX_IO_DATA_STATUS(rxhdr); ++ ++ if (netif_msg_rx_status(db)) ++ dev_dbg(db->dev, "RX: status %02x, length %04x\n", ++ rxstatus, rxlen); ++ ++ /* Packet Status check */ ++ if (rxlen < 0x40) { ++ good_packet = false; ++ if (netif_msg_rx_err(db)) ++ dev_dbg(db->dev, "RX: Bad Packet (runt)\n"); ++ } ++ ++ if (unlikely(!(rxstatus & EMAC_RX_IO_DATA_STATUS_OK))) { ++ good_packet = false; ++ ++ if (rxstatus & EMAC_RX_IO_DATA_STATUS_CRC_ERR) { ++ if (netif_msg_rx_err(db)) ++ dev_dbg(db->dev, "crc error\n"); ++ dev->stats.rx_crc_errors++; ++ } ++ ++ if (rxstatus & EMAC_RX_IO_DATA_STATUS_LEN_ERR) { ++ if (netif_msg_rx_err(db)) ++ dev_dbg(db->dev, "length error\n"); ++ dev->stats.rx_length_errors++; ++ } ++ } ++ ++ /* Move data from EMAC */ ++ skb = dev_alloc_skb(rxlen + 4); ++ if (good_packet && skb) { ++ skb_reserve(skb, 2); ++ rdptr = (u8 *) skb_put(skb, rxlen - 4); ++ ++ /* Read received packet from RX SRAM */ ++ if (netif_msg_rx_status(db)) ++ dev_dbg(db->dev, "RxLen %x\n", rxlen); ++ ++ emac_inblk_32bit(db->membase + EMAC_RX_IO_DATA_REG, ++ rdptr, rxlen); ++ dev->stats.rx_bytes += rxlen; ++ ++ /* Pass to upper layer */ ++ skb->protocol = eth_type_trans(skb, dev); ++ netif_rx(skb); ++ dev->stats.rx_packets++; ++ } ++ } ++} ++ ++static irqreturn_t emac_interrupt(int irq, void *dev_id) ++{ ++ struct net_device *dev = dev_id; ++ struct emac_board_info *db = netdev_priv(dev); ++ int int_status; ++ unsigned long flags; ++ unsigned int reg_val; ++ ++ /* A real interrupt coming */ ++ ++ /* holders of db->lock must always block IRQs */ ++ spin_lock_irqsave(&db->lock, flags); ++ ++ /* Disable all interrupts */ ++ writel(0, db->membase + EMAC_INT_CTL_REG); ++ ++ /* Got EMAC interrupt status */ ++ /* Got ISR */ ++ int_status = readl(db->membase + EMAC_INT_STA_REG); ++ /* Clear ISR status */ ++ writel(int_status, db->membase + EMAC_INT_STA_REG); ++ ++ if (netif_msg_intr(db)) ++ dev_dbg(db->dev, "emac interrupt %02x\n", int_status); ++ ++ /* Received the coming packet */ ++ if ((int_status & 0x100) && (db->emacrx_completed_flag == 1)) { ++ /* carrier lost */ ++ db->emacrx_completed_flag = 0; ++ emac_rx(dev); ++ } ++ ++ /* Transmit Interrupt check */ ++ if (int_status & (0x01 | 0x02)) ++ emac_tx_done(dev, db, int_status); ++ ++ if (int_status & (0x04 | 0x08)) ++ netdev_info(dev, " ab : %x\n", int_status); ++ ++ /* Re-enable interrupt mask */ ++ if (db->emacrx_completed_flag == 1) { ++ reg_val = readl(db->membase + EMAC_INT_CTL_REG); ++ reg_val |= (0xf << 0) | (0x01 << 8); ++ writel(reg_val, db->membase + EMAC_INT_CTL_REG); ++ } ++ spin_unlock_irqrestore(&db->lock, flags); ++ ++ return IRQ_HANDLED; ++} ++ ++#ifdef CONFIG_NET_POLL_CONTROLLER ++/* ++ * Used by netconsole ++ */ ++static void emac_poll_controller(struct net_device *dev) ++{ ++ disable_irq(dev->irq); ++ emac_interrupt(dev->irq, dev); ++ enable_irq(dev->irq); ++} ++#endif ++ ++/* Open the interface. ++ * The interface is opened whenever "ifconfig" actives it. ++ */ ++static int emac_open(struct net_device *dev) ++{ ++ struct emac_board_info *db = netdev_priv(dev); ++ int ret; ++ ++ if (netif_msg_ifup(db)) ++ dev_dbg(db->dev, "enabling %s\n", dev->name); ++ ++ if (devm_request_irq(db->dev, dev->irq, &emac_interrupt, ++ 0, dev->name, dev)) ++ return -EAGAIN; ++ ++ /* Initialize EMAC board */ ++ emac_reset(db); ++ emac_init_device(dev); ++ ++ ret = emac_mdio_probe(dev); ++ if (ret < 0) { ++ netdev_err(dev, "cannot probe MDIO bus\n"); ++ return ret; ++ } ++ ++ phy_start(db->phy_dev); ++ netif_start_queue(dev); ++ ++ return 0; ++} ++ ++static void emac_shutdown(struct net_device *dev) ++{ ++ unsigned int reg_val; ++ struct emac_board_info *db = netdev_priv(dev); ++ ++ /* Disable all interrupt */ ++ writel(0, db->membase + EMAC_INT_CTL_REG); ++ ++ /* clear interupt status */ ++ reg_val = readl(db->membase + EMAC_INT_STA_REG); ++ writel(reg_val, db->membase + EMAC_INT_STA_REG); ++ ++ /* Disable RX/TX */ ++ reg_val = readl(db->membase + EMAC_CTL_REG); ++ reg_val &= ~(EMAC_CTL_TX_EN | EMAC_CTL_RX_EN | EMAC_CTL_RESET); ++ writel(reg_val, db->membase + EMAC_CTL_REG); ++} ++ ++/* Stop the interface. ++ * The interface is stopped when it is brought. ++ */ ++static int emac_stop(struct net_device *ndev) ++{ ++ struct emac_board_info *db = netdev_priv(ndev); ++ ++ if (netif_msg_ifdown(db)) ++ dev_dbg(db->dev, "shutting down %s\n", ndev->name); ++ ++ netif_stop_queue(ndev); ++ netif_carrier_off(ndev); ++ ++ phy_stop(db->phy_dev); ++ ++ emac_mdio_remove(ndev); ++ ++ emac_shutdown(ndev); ++ ++ return 0; ++} ++ ++static const struct net_device_ops emac_netdev_ops = { ++ .ndo_open = emac_open, ++ .ndo_stop = emac_stop, ++ .ndo_start_xmit = emac_start_xmit, ++ .ndo_tx_timeout = emac_timeout, ++ .ndo_do_ioctl = emac_ioctl, ++ .ndo_change_mtu = eth_change_mtu, ++ .ndo_validate_addr = eth_validate_addr, ++ .ndo_set_mac_address = emac_set_mac_address, ++#ifdef CONFIG_NET_POLL_CONTROLLER ++ .ndo_poll_controller = emac_poll_controller, ++#endif ++}; ++ ++/* Search EMAC board, allocate space and register it ++ */ ++static int emac_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct emac_board_info *db; ++ struct net_device *ndev; ++ int ret = 0; ++ const char *mac_addr; ++ ++ ndev = alloc_etherdev(sizeof(struct emac_board_info)); ++ if (!ndev) { ++ dev_err(&pdev->dev, "could not allocate device.\n"); ++ return -ENOMEM; ++ } ++ ++ SET_NETDEV_DEV(ndev, &pdev->dev); ++ ++ db = netdev_priv(ndev); ++ memset(db, 0, sizeof(*db)); ++ ++ db->dev = &pdev->dev; ++ db->ndev = ndev; ++ db->pdev = pdev; ++ ++ spin_lock_init(&db->lock); ++ ++ db->membase = of_iomap(np, 0); ++ if (!db->membase) { ++ dev_err(&pdev->dev, "failed to remap registers\n"); ++ return -ENOMEM; ++ goto out; ++ } ++ ++ /* fill in parameters for net-dev structure */ ++ ndev->base_addr = (unsigned long)db->membase; ++ ndev->irq = irq_of_parse_and_map(np, 0); ++ if (ndev->irq == -ENXIO) { ++ netdev_err(ndev, "No irq resource\n"); ++ ret = ndev->irq; ++ goto out; ++ } ++ ++ db->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(db->clk)) ++ goto out; ++ ++ clk_prepare_enable(db->clk); ++ ++ db->phy_node = of_parse_phandle(np, "phy", 0); ++ if (!db->phy_node) { ++ dev_err(&pdev->dev, "no associated PHY\n"); ++ ret = -ENODEV; ++ goto out; ++ } ++ ++ /* Read MAC-address from DT */ ++ mac_addr = of_get_mac_address(np); ++ if (mac_addr) ++ memcpy(ndev->dev_addr, mac_addr, ETH_ALEN); ++ ++ /* Check if the MAC address is valid, if not get a random one */ ++ if (!is_valid_ether_addr(ndev->dev_addr)) { ++ eth_hw_addr_random(ndev); ++ dev_warn(&pdev->dev, "using random MAC address %pM\n", ++ ndev->dev_addr); ++ } ++ ++ db->emacrx_completed_flag = 1; ++ emac_powerup(ndev); ++ emac_reset(db); ++ ++ ether_setup(ndev); ++ ++ ndev->netdev_ops = &emac_netdev_ops; ++ ndev->watchdog_timeo = msecs_to_jiffies(watchdog); ++ ndev->ethtool_ops = &emac_ethtool_ops; ++ ++#ifdef CONFIG_NET_POLL_CONTROLLER ++ ndev->poll_controller = &emac_poll_controller; ++#endif ++ ++ platform_set_drvdata(pdev, ndev); ++ ++ /* Carrier starts down, phylib will bring it up */ ++ netif_carrier_off(ndev); ++ ++ ret = register_netdev(ndev); ++ if (ret) { ++ dev_err(&pdev->dev, "Registering netdev failed!\n"); ++ ret = -ENODEV; ++ goto out; ++ } ++ ++ dev_info(&pdev->dev, "%s: at %p, IRQ %d MAC: %pM\n", ++ ndev->name, db->membase, ndev->irq, ndev->dev_addr); ++ ++ return 0; ++ ++out: ++ dev_err(db->dev, "not found (%d).\n", ret); ++ ++ free_netdev(ndev); ++ ++ return ret; ++} ++ ++static int emac_remove(struct platform_device *pdev) ++{ ++ struct net_device *ndev = platform_get_drvdata(pdev); ++ ++ platform_set_drvdata(pdev, NULL); ++ ++ unregister_netdev(ndev); ++ free_netdev(ndev); ++ ++ dev_dbg(&pdev->dev, "released and freed device\n"); ++ return 0; ++} ++ ++static int emac_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ struct net_device *ndev = platform_get_drvdata(dev); ++ ++ netif_carrier_off(ndev); ++ netif_device_detach(ndev); ++ emac_shutdown(ndev); ++ ++ return 0; ++} ++ ++static int emac_resume(struct platform_device *dev) ++{ ++ struct net_device *ndev = platform_get_drvdata(dev); ++ struct emac_board_info *db = netdev_priv(ndev); ++ ++ emac_reset(db); ++ emac_init_device(ndev); ++ netif_device_attach(ndev); ++ ++ return 0; ++} ++ ++static const struct of_device_id emac_of_match[] = { ++ {.compatible = "allwinner,sun4i-emac",}, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(of, emac_of_match); ++ ++static struct platform_driver emac_driver = { ++ .driver = { ++ .name = "sun4i-emac", ++ .of_match_table = emac_of_match, ++ }, ++ .probe = emac_probe, ++ .remove = emac_remove, ++ .suspend = emac_suspend, ++ .resume = emac_resume, ++}; ++ ++module_platform_driver(emac_driver); ++ ++MODULE_AUTHOR("Stefan Roese "); ++MODULE_AUTHOR("Maxime Ripard "); ++MODULE_DESCRIPTION("Allwinner A10 emac network driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/net/ethernet/allwinner/sun4i-emac.h b/drivers/net/ethernet/allwinner/sun4i-emac.h +new file mode 100644 +index 0000000..38c72d9 +--- /dev/null ++++ b/drivers/net/ethernet/allwinner/sun4i-emac.h +@@ -0,0 +1,108 @@ ++/* ++ * Allwinner EMAC Fast Ethernet driver for Linux. ++ * ++ * Copyright 2012 Stefan Roese ++ * Copyright 2013 Maxime Ripard ++ * ++ * Based on the Linux driver provided by Allwinner: ++ * Copyright (C) 1997 Sten Wang ++ * ++ * This file is licensed under the terms of the GNU General Public ++ * License version 2. This program is licensed "as is" without any ++ * warranty of any kind, whether express or implied. ++ */ ++ ++#ifndef _SUN4I_EMAC_H_ ++#define _SUN4I_EMAC_H_ ++ ++#define EMAC_CTL_REG (0x00) ++#define EMAC_CTL_RESET (1 << 0) ++#define EMAC_CTL_TX_EN (1 << 1) ++#define EMAC_CTL_RX_EN (1 << 2) ++#define EMAC_TX_MODE_REG (0x04) ++#define EMAC_TX_MODE_ABORTED_FRAME_EN (1 << 0) ++#define EMAC_TX_MODE_DMA_EN (1 << 1) ++#define EMAC_TX_FLOW_REG (0x08) ++#define EMAC_TX_CTL0_REG (0x0c) ++#define EMAC_TX_CTL1_REG (0x10) ++#define EMAC_TX_INS_REG (0x14) ++#define EMAC_TX_PL0_REG (0x18) ++#define EMAC_TX_PL1_REG (0x1c) ++#define EMAC_TX_STA_REG (0x20) ++#define EMAC_TX_IO_DATA_REG (0x24) ++#define EMAC_TX_IO_DATA1_REG (0x28) ++#define EMAC_TX_TSVL0_REG (0x2c) ++#define EMAC_TX_TSVH0_REG (0x30) ++#define EMAC_TX_TSVL1_REG (0x34) ++#define EMAC_TX_TSVH1_REG (0x38) ++#define EMAC_RX_CTL_REG (0x3c) ++#define EMAC_RX_CTL_AUTO_DRQ_EN (1 << 1) ++#define EMAC_RX_CTL_DMA_EN (1 << 2) ++#define EMAC_RX_CTL_PASS_ALL_EN (1 << 4) ++#define EMAC_RX_CTL_PASS_CTL_EN (1 << 5) ++#define EMAC_RX_CTL_PASS_CRC_ERR_EN (1 << 6) ++#define EMAC_RX_CTL_PASS_LEN_ERR_EN (1 << 7) ++#define EMAC_RX_CTL_PASS_LEN_OOR_EN (1 << 8) ++#define EMAC_RX_CTL_ACCEPT_UNICAST_EN (1 << 16) ++#define EMAC_RX_CTL_DA_FILTER_EN (1 << 17) ++#define EMAC_RX_CTL_ACCEPT_MULTICAST_EN (1 << 20) ++#define EMAC_RX_CTL_HASH_FILTER_EN (1 << 21) ++#define EMAC_RX_CTL_ACCEPT_BROADCAST_EN (1 << 22) ++#define EMAC_RX_CTL_SA_FILTER_EN (1 << 24) ++#define EMAC_RX_CTL_SA_FILTER_INVERT_EN (1 << 25) ++#define EMAC_RX_HASH0_REG (0x40) ++#define EMAC_RX_HASH1_REG (0x44) ++#define EMAC_RX_STA_REG (0x48) ++#define EMAC_RX_IO_DATA_REG (0x4c) ++#define EMAC_RX_IO_DATA_LEN(x) (x & 0xffff) ++#define EMAC_RX_IO_DATA_STATUS(x) ((x >> 16) & 0xffff) ++#define EMAC_RX_IO_DATA_STATUS_CRC_ERR (1 << 4) ++#define EMAC_RX_IO_DATA_STATUS_LEN_ERR (3 << 5) ++#define EMAC_RX_IO_DATA_STATUS_OK (1 << 7) ++#define EMAC_RX_FBC_REG (0x50) ++#define EMAC_INT_CTL_REG (0x54) ++#define EMAC_INT_STA_REG (0x58) ++#define EMAC_MAC_CTL0_REG (0x5c) ++#define EMAC_MAC_CTL0_RX_FLOW_CTL_EN (1 << 2) ++#define EMAC_MAC_CTL0_TX_FLOW_CTL_EN (1 << 3) ++#define EMAC_MAC_CTL0_SOFT_RESET (1 << 15) ++#define EMAC_MAC_CTL1_REG (0x60) ++#define EMAC_MAC_CTL1_DUPLEX_EN (1 << 0) ++#define EMAC_MAC_CTL1_LEN_CHECK_EN (1 << 1) ++#define EMAC_MAC_CTL1_HUGE_FRAME_EN (1 << 2) ++#define EMAC_MAC_CTL1_DELAYED_CRC_EN (1 << 3) ++#define EMAC_MAC_CTL1_CRC_EN (1 << 4) ++#define EMAC_MAC_CTL1_PAD_EN (1 << 5) ++#define EMAC_MAC_CTL1_PAD_CRC_EN (1 << 6) ++#define EMAC_MAC_CTL1_AD_SHORT_FRAME_EN (1 << 7) ++#define EMAC_MAC_CTL1_BACKOFF_DIS (1 << 12) ++#define EMAC_MAC_IPGT_REG (0x64) ++#define EMAC_MAC_IPGT_HALF_DUPLEX (0x12) ++#define EMAC_MAC_IPGT_FULL_DUPLEX (0x15) ++#define EMAC_MAC_IPGR_REG (0x68) ++#define EMAC_MAC_IPGR_IPG1 (0x0c) ++#define EMAC_MAC_IPGR_IPG2 (0x12) ++#define EMAC_MAC_CLRT_REG (0x6c) ++#define EMAC_MAC_CLRT_COLLISION_WINDOW (0x37) ++#define EMAC_MAC_CLRT_RM (0x0f) ++#define EMAC_MAC_MAXF_REG (0x70) ++#define EMAC_MAC_SUPP_REG (0x74) ++#define EMAC_MAC_TEST_REG (0x78) ++#define EMAC_MAC_MCFG_REG (0x7c) ++#define EMAC_MAC_A0_REG (0x98) ++#define EMAC_MAC_A1_REG (0x9c) ++#define EMAC_MAC_A2_REG (0xa0) ++#define EMAC_SAFX_L_REG0 (0xa4) ++#define EMAC_SAFX_H_REG0 (0xa8) ++#define EMAC_SAFX_L_REG1 (0xac) ++#define EMAC_SAFX_H_REG1 (0xb0) ++#define EMAC_SAFX_L_REG2 (0xb4) ++#define EMAC_SAFX_H_REG2 (0xb8) ++#define EMAC_SAFX_L_REG3 (0xbc) ++#define EMAC_SAFX_H_REG3 (0xc0) ++ ++#define EMAC_PHY_DUPLEX (1 << 8) ++ ++#define EMAC_EEPROM_MAGIC (0x444d394b) ++#define EMAC_UNDOCUMENTED_MAGIC (0x0143414d) ++#endif /* _SUN4I_EMAC_H_ */ +-- +1.7.10.4 + diff --git a/debian/patches/features/arm/sunxi-emac/0002-net-Add-MDIO-bus-driver-for-the-Allwinner-EMAC.patch b/debian/patches/features/arm/sunxi-emac/0002-net-Add-MDIO-bus-driver-for-the-Allwinner-EMAC.patch new file mode 100644 index 000000000..41dcbee89 --- /dev/null +++ b/debian/patches/features/arm/sunxi-emac/0002-net-Add-MDIO-bus-driver-for-the-Allwinner-EMAC.patch @@ -0,0 +1,286 @@ +From: Maxime Ripard +Date: Thu, 30 May 2013 03:49:21 +0000 +Subject: [PATCH 02/10] net: Add MDIO bus driver for the Allwinner EMAC +Bug-Debian: http://bugs.debian.org/711998 + +This patch adds a separate driver for the MDIO interface of the +Allwinner ethernet controllers. + +Signed-off-by: Maxime Ripard +Tested-by: Richard Genoud +Signed-off-by: David S. Miller +(cherry picked from commit 4bdcb1dd9feb03608e12cfa46aba385035af8ea5) +--- + .../bindings/net/allwinner,sun4i-mdio.txt | 26 +++ + drivers/net/phy/Kconfig | 10 + + drivers/net/phy/Makefile | 1 + + drivers/net/phy/mdio-sun4i.c | 194 ++++++++++++++++++++ + 4 files changed, 231 insertions(+) + create mode 100644 Documentation/devicetree/bindings/net/allwinner,sun4i-mdio.txt + create mode 100644 drivers/net/phy/mdio-sun4i.c + +diff --git a/Documentation/devicetree/bindings/net/allwinner,sun4i-mdio.txt b/Documentation/devicetree/bindings/net/allwinner,sun4i-mdio.txt +new file mode 100644 +index 0000000..00b9f9a +--- /dev/null ++++ b/Documentation/devicetree/bindings/net/allwinner,sun4i-mdio.txt +@@ -0,0 +1,26 @@ ++* Allwinner A10 MDIO Ethernet Controller interface ++ ++Required properties: ++- compatible: should be "allwinner,sun4i-mdio". ++- reg: address and length of the register set for the device. ++ ++Optional properties: ++- phy-supply: phandle to a regulator if the PHY needs one ++ ++Example at the SoC level: ++mdio@01c0b080 { ++ compatible = "allwinner,sun4i-mdio"; ++ reg = <0x01c0b080 0x14>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++}; ++ ++And at the board level: ++ ++mdio@01c0b080 { ++ phy-supply = <®_emac_3v3>; ++ ++ phy0: ethernet-phy@0 { ++ reg = <0>; ++ }; ++}; +diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig +index 1e11f2b..3a316b3 100644 +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -144,6 +144,16 @@ config MDIO_OCTEON + + If in doubt, say Y. + ++config MDIO_SUN4I ++ tristate "Allwinner sun4i MDIO interface support" ++ depends on ARCH_SUNXI ++ select REGULATOR ++ select REGULATOR_FIXED_VOLTAGE ++ help ++ This driver supports the MDIO interface found in the network ++ interface units of the Allwinner SoC that have an EMAC (A10, ++ A12, A10s, etc.) ++ + config MDIO_BUS_MUX + tristate + depends on OF_MDIO +diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile +index 9645e38..23a2ab2 100644 +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -30,3 +30,4 @@ obj-$(CONFIG_AMD_PHY) += amd.o + obj-$(CONFIG_MDIO_BUS_MUX) += mdio-mux.o + obj-$(CONFIG_MDIO_BUS_MUX_GPIO) += mdio-mux-gpio.o + obj-$(CONFIG_MDIO_BUS_MUX_MMIOREG) += mdio-mux-mmioreg.o ++obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o +diff --git a/drivers/net/phy/mdio-sun4i.c b/drivers/net/phy/mdio-sun4i.c +new file mode 100644 +index 0000000..61d3f4e +--- /dev/null ++++ b/drivers/net/phy/mdio-sun4i.c +@@ -0,0 +1,194 @@ ++/* ++ * Allwinner EMAC MDIO interface driver ++ * ++ * Copyright 2012-2013 Stefan Roese ++ * Copyright 2013 Maxime Ripard ++ * ++ * Based on the Linux driver provided by Allwinner: ++ * Copyright (C) 1997 Sten Wang ++ * ++ * This file is licensed under the terms of the GNU General Public ++ * License version 2. This program is licensed "as is" without any ++ * warranty of any kind, whether express or implied. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define EMAC_MAC_MCMD_REG (0x00) ++#define EMAC_MAC_MADR_REG (0x04) ++#define EMAC_MAC_MWTD_REG (0x08) ++#define EMAC_MAC_MRDD_REG (0x0c) ++#define EMAC_MAC_MIND_REG (0x10) ++#define EMAC_MAC_SSRR_REG (0x14) ++ ++#define MDIO_TIMEOUT (msecs_to_jiffies(100)) ++ ++struct sun4i_mdio_data { ++ void __iomem *membase; ++ struct regulator *regulator; ++}; ++ ++static int sun4i_mdio_read(struct mii_bus *bus, int mii_id, int regnum) ++{ ++ struct sun4i_mdio_data *data = bus->priv; ++ unsigned long start_jiffies; ++ int value; ++ ++ /* issue the phy address and reg */ ++ writel((mii_id << 8) | regnum, data->membase + EMAC_MAC_MADR_REG); ++ /* pull up the phy io line */ ++ writel(0x1, data->membase + EMAC_MAC_MCMD_REG); ++ ++ /* Wait read complete */ ++ start_jiffies = jiffies; ++ while (readl(data->membase + EMAC_MAC_MIND_REG) & 0x1) { ++ if (time_after(start_jiffies, ++ start_jiffies + MDIO_TIMEOUT)) ++ return -ETIMEDOUT; ++ msleep(1); ++ } ++ ++ /* push down the phy io line */ ++ writel(0x0, data->membase + EMAC_MAC_MCMD_REG); ++ /* and read data */ ++ value = readl(data->membase + EMAC_MAC_MRDD_REG); ++ ++ return value; ++} ++ ++static int sun4i_mdio_write(struct mii_bus *bus, int mii_id, int regnum, ++ u16 value) ++{ ++ struct sun4i_mdio_data *data = bus->priv; ++ unsigned long start_jiffies; ++ ++ /* issue the phy address and reg */ ++ writel((mii_id << 8) | regnum, data->membase + EMAC_MAC_MADR_REG); ++ /* pull up the phy io line */ ++ writel(0x1, data->membase + EMAC_MAC_MCMD_REG); ++ ++ /* Wait read complete */ ++ start_jiffies = jiffies; ++ while (readl(data->membase + EMAC_MAC_MIND_REG) & 0x1) { ++ if (time_after(start_jiffies, ++ start_jiffies + MDIO_TIMEOUT)) ++ return -ETIMEDOUT; ++ msleep(1); ++ } ++ ++ /* push down the phy io line */ ++ writel(0x0, data->membase + EMAC_MAC_MCMD_REG); ++ /* and write data */ ++ writel(value, data->membase + EMAC_MAC_MWTD_REG); ++ ++ return 0; ++} ++ ++static int sun4i_mdio_reset(struct mii_bus *bus) ++{ ++ return 0; ++} ++ ++static int sun4i_mdio_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct mii_bus *bus; ++ struct sun4i_mdio_data *data; ++ int ret, i; ++ ++ bus = mdiobus_alloc_size(sizeof(*data)); ++ if (!bus) ++ return -ENOMEM; ++ ++ bus->name = "sun4i_mii_bus"; ++ bus->read = &sun4i_mdio_read; ++ bus->write = &sun4i_mdio_write; ++ bus->reset = &sun4i_mdio_reset; ++ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev)); ++ bus->parent = &pdev->dev; ++ ++ bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); ++ if (!bus->irq) { ++ ret = -ENOMEM; ++ goto err_out_free_mdiobus; ++ } ++ ++ for (i = 0; i < PHY_MAX_ADDR; i++) ++ bus->irq[i] = PHY_POLL; ++ ++ data = bus->priv; ++ data->membase = of_iomap(np, 0); ++ if (!data->membase) { ++ ret = -ENOMEM; ++ goto err_out_free_mdio_irq; ++ } ++ ++ data->regulator = devm_regulator_get(&pdev->dev, "phy"); ++ if (IS_ERR(data->regulator)) { ++ if (PTR_ERR(data->regulator) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ ++ dev_info(&pdev->dev, "no regulator found\n"); ++ } else { ++ ret = regulator_enable(data->regulator); ++ if (ret) ++ goto err_out_free_mdio_irq; ++ } ++ ++ ret = of_mdiobus_register(bus, np); ++ if (ret < 0) ++ goto err_out_disable_regulator; ++ ++ platform_set_drvdata(pdev, bus); ++ ++ return 0; ++ ++err_out_disable_regulator: ++ regulator_disable(data->regulator); ++err_out_free_mdio_irq: ++ kfree(bus->irq); ++err_out_free_mdiobus: ++ mdiobus_free(bus); ++ return ret; ++} ++ ++static int sun4i_mdio_remove(struct platform_device *pdev) ++{ ++ struct mii_bus *bus = platform_get_drvdata(pdev); ++ ++ mdiobus_unregister(bus); ++ kfree(bus->irq); ++ mdiobus_free(bus); ++ ++ return 0; ++} ++ ++static const struct of_device_id sun4i_mdio_dt_ids[] = { ++ { .compatible = "allwinner,sun4i-mdio" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, sun4i_mdio_dt_ids); ++ ++static struct platform_driver sun4i_mdio_driver = { ++ .probe = sun4i_mdio_probe, ++ .remove = sun4i_mdio_remove, ++ .driver = { ++ .name = "sun4i-mdio", ++ .of_match_table = sun4i_mdio_dt_ids, ++ }, ++}; ++ ++module_platform_driver(sun4i_mdio_driver); ++ ++MODULE_DESCRIPTION("Allwinner EMAC MDIO interface driver"); ++MODULE_AUTHOR("Maxime Ripard "); ++MODULE_LICENSE("GPL"); +-- +1.7.10.4 + diff --git a/debian/patches/features/arm/sunxi-emac/0003-ARM-sun4i-Add-muxing-options-for-the-ethernet-contro.patch b/debian/patches/features/arm/sunxi-emac/0003-ARM-sun4i-Add-muxing-options-for-the-ethernet-contro.patch new file mode 100644 index 000000000..d29e56e0e --- /dev/null +++ b/debian/patches/features/arm/sunxi-emac/0003-ARM-sun4i-Add-muxing-options-for-the-ethernet-contro.patch @@ -0,0 +1,42 @@ +From: Maxime Ripard +Date: Thu, 30 May 2013 03:49:22 +0000 +Subject: [PATCH 03/10] ARM: sun4i: Add muxing options for the ethernet + controller +Bug-Debian: http://bugs.debian.org/711998 + +The EMAC only has one pinset available for muxing, so hopefully, we +cover all cases. + +Signed-off-by: Maxime Ripard +Tested-by: Richard Genoud +Signed-off-by: David S. Miller +(cherry picked from commit b21da664120b1c8332fc5059a1e7c1462dd26433) +--- + arch/arm/boot/dts/sun4i-a10.dtsi | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi +index e7ef619..ff1f41f 100644 +--- a/arch/arm/boot/dts/sun4i-a10.dtsi ++++ b/arch/arm/boot/dts/sun4i-a10.dtsi +@@ -199,6 +199,17 @@ + allwinner,drive = <0>; + allwinner,pull = <0>; + }; ++ ++ emac_pins_a: emac0@0 { ++ allwinner,pins = "PA0", "PA1", "PA2", ++ "PA3", "PA4", "PA5", "PA6", ++ "PA7", "PA8", "PA9", "PA10", ++ "PA11", "PA12", "PA13", "PA14", ++ "PA15", "PA16"; ++ allwinner,function = "emac"; ++ allwinner,drive = <0>; ++ allwinner,pull = <0>; ++ }; + }; + + timer@01c20c00 { +-- +1.7.10.4 + diff --git a/debian/patches/features/arm/sunxi-emac/0004-ARM-sunxi-Add-EMAC-controller-node-to-sun4i-DTSI.patch b/debian/patches/features/arm/sunxi-emac/0004-ARM-sunxi-Add-EMAC-controller-node-to-sun4i-DTSI.patch new file mode 100644 index 000000000..6eaf5377d --- /dev/null +++ b/debian/patches/features/arm/sunxi-emac/0004-ARM-sunxi-Add-EMAC-controller-node-to-sun4i-DTSI.patch @@ -0,0 +1,43 @@ +From: Maxime Ripard +Date: Thu, 30 May 2013 03:49:23 +0000 +Subject: [PATCH 04/10] ARM: sunxi: Add EMAC controller node to sun4i DTSI +Bug-Debian: http://bugs.debian.org/711998 + +Signed-off-by: Maxime Ripard +Tested-by: Richard Genoud +Signed-off-by: David S. Miller +(cherry picked from commit e38afcb33ff4b472254c8f6f140699e002c44d84) +--- + arch/arm/boot/dts/sun4i-a10.dtsi | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi +index ff1f41f..983da33 100644 +--- a/arch/arm/boot/dts/sun4i-a10.dtsi ++++ b/arch/arm/boot/dts/sun4i-a10.dtsi +@@ -163,6 +163,22 @@ + reg = <0x01c20000 0x300000>; + ranges; + ++ emac: ethernet@01c0b000 { ++ compatible = "allwinner,sun4i-emac"; ++ reg = <0x01c0b000 0x1000>; ++ interrupts = <55>; ++ clocks = <&ahb_gates 17>; ++ status = "disabled"; ++ }; ++ ++ mdio@01c0b080 { ++ compatible = "allwinner,sun4i-mdio"; ++ reg = <0x01c0b080 0x14>; ++ status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ + intc: interrupt-controller@01c20400 { + compatible = "allwinner,sun4i-ic"; + reg = <0x01c20400 0x400>; +-- +1.7.10.4 + diff --git a/debian/patches/features/arm/sunxi-emac/0005-ARM-cubieboard-Enable-ethernet-EMAC-support-in-dts.patch b/debian/patches/features/arm/sunxi-emac/0005-ARM-cubieboard-Enable-ethernet-EMAC-support-in-dts.patch new file mode 100644 index 000000000..3a064f7fe --- /dev/null +++ b/debian/patches/features/arm/sunxi-emac/0005-ARM-cubieboard-Enable-ethernet-EMAC-support-in-dts.patch @@ -0,0 +1,43 @@ +From: Stefan Roese +Date: Thu, 30 May 2013 03:49:24 +0000 +Subject: [PATCH 05/10] ARM: cubieboard: Enable ethernet (EMAC) support in dts +Bug-Debian: http://bugs.debian.org/711998 + +Signed-off-by: Stefan Roese +Signed-off-by: Maxime Ripard +Tested-by: Richard Genoud +Signed-off-by: David S. Miller +(cherry picked from commit e5a84bac5d6797c64541f87e7a567d56699bbb74) +--- + arch/arm/boot/dts/sun4i-a10-cubieboard.dts | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/arch/arm/boot/dts/sun4i-a10-cubieboard.dts b/arch/arm/boot/dts/sun4i-a10-cubieboard.dts +index b70fe0d..e752b57 100644 +--- a/arch/arm/boot/dts/sun4i-a10-cubieboard.dts ++++ b/arch/arm/boot/dts/sun4i-a10-cubieboard.dts +@@ -27,6 +27,21 @@ + }; + + soc@01c20000 { ++ emac: ethernet@01c0b000 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&emac_pins_a>; ++ phy = <&phy1>; ++ status = "okay"; ++ }; ++ ++ mdio@01c0b080 { ++ status = "okay"; ++ ++ phy1: ethernet-phy@1 { ++ reg = <1>; ++ }; ++ }; ++ + pinctrl@01c20800 { + led_pins_cubieboard: led_pins@0 { + allwinner,pins = "PH20", "PH21"; +-- +1.7.10.4 + diff --git a/debian/patches/features/arm/sunxi-emac/0006-ARM-sunxi-Add-EMAC-Controller-to-Hackberry-dt.patch b/debian/patches/features/arm/sunxi-emac/0006-ARM-sunxi-Add-EMAC-Controller-to-Hackberry-dt.patch new file mode 100644 index 000000000..dc53b9477 --- /dev/null +++ b/debian/patches/features/arm/sunxi-emac/0006-ARM-sunxi-Add-EMAC-Controller-to-Hackberry-dt.patch @@ -0,0 +1,75 @@ +From: Maxime Ripard +Date: Thu, 30 May 2013 03:49:25 +0000 +Subject: [PATCH 06/10] ARM: sunxi: Add EMAC Controller to Hackberry dt +Bug-Debian: http://bugs.debian.org/711998 + +The Hackberry has a PHY that needs to be powered up through a GPIO, so +we need to use a fixed regulator here. + +Signed-off-by: Maxime Ripard +Tested-by: Richard Genoud +Signed-off-by: David S. Miller +(cherry picked from commit d97a079a79a7dcbc2470572b41cd1f07a16d8b7f) +--- + arch/arm/boot/dts/sun4i-a10-hackberry.dts | 41 +++++++++++++++++++++++++++++ + 1 file changed, 41 insertions(+) + +diff --git a/arch/arm/boot/dts/sun4i-a10-hackberry.dts b/arch/arm/boot/dts/sun4i-a10-hackberry.dts +index b9efac1..3514b37 100644 +--- a/arch/arm/boot/dts/sun4i-a10-hackberry.dts ++++ b/arch/arm/boot/dts/sun4i-a10-hackberry.dts +@@ -23,10 +23,51 @@ + }; + + soc@01c20000 { ++ emac: ethernet@01c0b000 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&emac_pins_a>; ++ phy = <&phy0>; ++ status = "okay"; ++ }; ++ ++ mdio@01c0b080 { ++ phy-supply = <®_emac_3v3>; ++ status = "okay"; ++ ++ phy0: ethernet-phy@0 { ++ reg = <0>; ++ }; ++ }; ++ ++ pio: pinctrl@01c20800 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&hackberry_hogs>; ++ ++ hackberry_hogs: hogs@0 { ++ allwinner,pins = "PH19"; ++ allwinner,function = "gpio_out"; ++ allwinner,drive = <0>; ++ allwinner,pull = <0>; ++ }; ++ }; ++ + uart0: serial@01c28000 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pins_a>; + status = "okay"; + }; + }; ++ ++ regulators { ++ compatible = "simple-bus"; ++ ++ reg_emac_3v3: emac-3v3 { ++ compatible = "regulator-fixed"; ++ regulator-name = "emac-3v3"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ enable-active-high; ++ gpio = <&pio 7 19 0>; ++ }; ++ }; + }; +-- +1.7.10.4 + diff --git a/debian/patches/features/arm/sunxi-emac/0007-net-sun4i-emac-fix-a-typo-in-emac_probe.patch b/debian/patches/features/arm/sunxi-emac/0007-net-sun4i-emac-fix-a-typo-in-emac_probe.patch new file mode 100644 index 000000000..a24093e3a --- /dev/null +++ b/debian/patches/features/arm/sunxi-emac/0007-net-sun4i-emac-fix-a-typo-in-emac_probe.patch @@ -0,0 +1,31 @@ +From: Wei Yongjun +Date: Mon, 3 Jun 2013 03:36:52 +0000 +Subject: [PATCH 07/10] net: sun4i-emac: fix a typo in emac_probe() +Bug-Debian: http://bugs.debian.org/711998 + +Just fixed a typo in emac_probe(). + +Signed-off-by: Wei Yongjun +Acked-by: Maxime Ripard +Signed-off-by: David S. Miller +(cherry picked from commit 93baf4c615584fa02c21aa78f305428fc7060656) +--- + drivers/net/ethernet/allwinner/sun4i-emac.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/allwinner/sun4i-emac.c b/drivers/net/ethernet/allwinner/sun4i-emac.c +index b411344..26083cd 100644 +--- a/drivers/net/ethernet/allwinner/sun4i-emac.c ++++ b/drivers/net/ethernet/allwinner/sun4i-emac.c +@@ -821,7 +821,7 @@ static int emac_probe(struct platform_device *pdev) + db->membase = of_iomap(np, 0); + if (!db->membase) { + dev_err(&pdev->dev, "failed to remap registers\n"); +- return -ENOMEM; ++ ret = -ENOMEM; + goto out; + } + +-- +1.7.10.4 + diff --git a/debian/patches/features/arm/sunxi-emac/0008-net-sun4i-emac-remove-erroneous-assignment.patch b/debian/patches/features/arm/sunxi-emac/0008-net-sun4i-emac-remove-erroneous-assignment.patch new file mode 100644 index 000000000..fc95b33ea --- /dev/null +++ b/debian/patches/features/arm/sunxi-emac/0008-net-sun4i-emac-remove-erroneous-assignment.patch @@ -0,0 +1,43 @@ +From: Arnd Bergmann +Date: Mon, 3 Jun 2013 11:36:50 +0000 +Subject: [PATCH 08/10] net: sun4i-emac: remove erroneous assignment +Bug-Debian: http://bugs.debian.org/711998 + +The newly added sun4i-emac driver causes a build error when +CONFIG_NET_POLL_CONTROLLER is set, because it attempts to +assign a pointer to netdev->poll_controller, which has +been replaced with ops->ndo_poll_controller in 2.6.31! + +The correct assignment is present as well, so we just need +to remove the wrong one. + +Signed-off-by: Arnd Bergmann +Cc: Stefan Roese +Cc: Maxime Ripard +Cc: Richard Genoud +Acked-by: Stefan Roese +Acked-by: Maxime Ripard +Signed-off-by: David S. Miller +(cherry picked from commit 10e179e364beafc23d837e81cf98d99720f42551) +--- + drivers/net/ethernet/allwinner/sun4i-emac.c | 4 ---- + 1 file changed, 4 deletions(-) + +diff --git a/drivers/net/ethernet/allwinner/sun4i-emac.c b/drivers/net/ethernet/allwinner/sun4i-emac.c +index 26083cd..0bb2f4a 100644 +--- a/drivers/net/ethernet/allwinner/sun4i-emac.c ++++ b/drivers/net/ethernet/allwinner/sun4i-emac.c +@@ -869,10 +869,6 @@ static int emac_probe(struct platform_device *pdev) + ndev->watchdog_timeo = msecs_to_jiffies(watchdog); + ndev->ethtool_ops = &emac_ethtool_ops; + +-#ifdef CONFIG_NET_POLL_CONTROLLER +- ndev->poll_controller = &emac_poll_controller; +-#endif +- + platform_set_drvdata(pdev, ndev); + + /* Carrier starts down, phylib will bring it up */ +-- +1.7.10.4 + diff --git a/debian/patches/features/arm/sunxi-emac/0009-net-sun4i-emac-Remove-redundant-platform_set_drvdata.patch b/debian/patches/features/arm/sunxi-emac/0009-net-sun4i-emac-Remove-redundant-platform_set_drvdata.patch new file mode 100644 index 000000000..3b5c09c29 --- /dev/null +++ b/debian/patches/features/arm/sunxi-emac/0009-net-sun4i-emac-Remove-redundant-platform_set_drvdata.patch @@ -0,0 +1,35 @@ +From: Sachin Kamat +Date: Tue, 4 Jun 2013 00:31:19 +0000 +Subject: [PATCH 09/10] net: sun4i-emac: Remove redundant + platform_set_drvdata() +Bug-Debian: http://bugs.debian.org/711998 + +Commit 0998d06310 (device-core: Ensure drvdata = NULL when no +driver is bound) removes the need to set driver data field to +NULL. + +Signed-off-by: Sachin Kamat +Cc: Stefan Roese +Cc: Maxime Ripard +Signed-off-by: David S. Miller +(cherry picked from commit aff5c3557cf72fcad913d8b4b44b055a4e034bb5) +--- + drivers/net/ethernet/allwinner/sun4i-emac.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/drivers/net/ethernet/allwinner/sun4i-emac.c b/drivers/net/ethernet/allwinner/sun4i-emac.c +index 0bb2f4a..e9c8dbe 100644 +--- a/drivers/net/ethernet/allwinner/sun4i-emac.c ++++ b/drivers/net/ethernet/allwinner/sun4i-emac.c +@@ -898,8 +898,6 @@ static int emac_remove(struct platform_device *pdev) + { + struct net_device *ndev = platform_get_drvdata(pdev); + +- platform_set_drvdata(pdev, NULL); +- + unregister_netdev(ndev); + free_netdev(ndev); + +-- +1.7.10.4 + diff --git a/debian/patches/features/arm/sunxi-emac/0010-net-sun4i-emac-Staticize-local-symbols.patch b/debian/patches/features/arm/sunxi-emac/0010-net-sun4i-emac-Staticize-local-symbols.patch new file mode 100644 index 000000000..3480d19cc --- /dev/null +++ b/debian/patches/features/arm/sunxi-emac/0010-net-sun4i-emac-Staticize-local-symbols.patch @@ -0,0 +1,41 @@ +From: Sachin Kamat +Date: Tue, 4 Jun 2013 00:31:20 +0000 +Subject: [PATCH 10/10] net: sun4i-emac: Staticize local symbols +Bug-Debian: http://bugs.debian.org/711998 + +Some symbols referenced only in this file are made static. + +Signed-off-by: Sachin Kamat +Cc: Stefan Roese +Cc: Maxime Ripard +Signed-off-by: David S. Miller +(cherry picked from commit 11a164a04382d735230b01f4cc46ad78a7c4abf6) +--- + drivers/net/ethernet/allwinner/sun4i-emac.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/allwinner/sun4i-emac.c b/drivers/net/ethernet/allwinner/sun4i-emac.c +index e9c8dbe..50b853a 100644 +--- a/drivers/net/ethernet/allwinner/sun4i-emac.c ++++ b/drivers/net/ethernet/allwinner/sun4i-emac.c +@@ -258,7 +258,7 @@ static const struct ethtool_ops emac_ethtool_ops = { + .get_link = ethtool_op_get_link, + }; + +-unsigned int emac_setup(struct net_device *ndev) ++static unsigned int emac_setup(struct net_device *ndev) + { + struct emac_board_info *db = netdev_priv(ndev); + unsigned int reg_val; +@@ -310,7 +310,7 @@ unsigned int emac_setup(struct net_device *ndev) + return 0; + } + +-unsigned int emac_powerup(struct net_device *ndev) ++static unsigned int emac_powerup(struct net_device *ndev) + { + struct emac_board_info *db = netdev_priv(ndev); + unsigned int reg_val; +-- +1.7.10.4 + diff --git a/debian/patches/series b/debian/patches/series index 5cef71891..2c06a9ddf 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -88,3 +88,13 @@ features/arm/imx53-qsb-usb-power.patch bugfix/all/ath9k-Disable-PowerSave-by-default.patch bugfix/x86/viafb-autoload-on-olpc-xo1.5-only.patch bugfix/all/iwlegacy-use-consistent-condition-for-il_pm_ops.patch +features/arm/sunxi-emac/0001-net-Add-EMAC-ethernet-driver-found-on-Allwinner-A10-.patch +features/arm/sunxi-emac/0002-net-Add-MDIO-bus-driver-for-the-Allwinner-EMAC.patch +features/arm/sunxi-emac/0003-ARM-sun4i-Add-muxing-options-for-the-ethernet-contro.patch +features/arm/sunxi-emac/0004-ARM-sunxi-Add-EMAC-controller-node-to-sun4i-DTSI.patch +features/arm/sunxi-emac/0005-ARM-cubieboard-Enable-ethernet-EMAC-support-in-dts.patch +features/arm/sunxi-emac/0006-ARM-sunxi-Add-EMAC-Controller-to-Hackberry-dt.patch +features/arm/sunxi-emac/0007-net-sun4i-emac-fix-a-typo-in-emac_probe.patch +features/arm/sunxi-emac/0008-net-sun4i-emac-remove-erroneous-assignment.patch +features/arm/sunxi-emac/0009-net-sun4i-emac-Remove-redundant-platform_set_drvdata.patch +features/arm/sunxi-emac/0010-net-sun4i-emac-Staticize-local-symbols.patch