Hi, just some comments I have on the usage of the internal PHY.
On 22/02/16 15:45, LABBE Corentin wrote: > This patch add support for sun8i-emac ethernet MAC hardware. > It could be found in Allwinner H3/A83T/A64 SoCs. > > Signed-off-by: LABBE Corentin <[email protected]> > --- > drivers/net/ethernet/allwinner/Kconfig | 14 + > drivers/net/ethernet/allwinner/Makefile | 1 + > drivers/net/ethernet/allwinner/sun8i-emac.c | 1493 > +++++++++++++++++++++++++++ > 3 files changed, 1508 insertions(+) > create mode 100644 drivers/net/ethernet/allwinner/sun8i-emac.c > > diff --git a/drivers/net/ethernet/allwinner/sun8i-emac.c > b/drivers/net/ethernet/allwinner/sun8i-emac.c > new file mode 100644 > index 0000000..a4e52cf > --- /dev/null > +++ b/drivers/net/ethernet/allwinner/sun8i-emac.c > @@ -0,0 +1,1493 @@ > +/* > + * sun8i-h3-emac driver > + * > + * Copyright (C) 2015-2016 Corentin LABBE <[email protected]> > + * > + * This is the driver for Allwinner Ethernet MAC found in H3/A83T/A64 SoC > + * > + * This is a mono block driver that need to be splited: > + * - A classic ethernet MAC driver > + * - A PHY driver > + * - A clk driver > + */ > +#include <linux/clk.h> > +#include <linux/mii.h> > +#include <linux/gpio.h> > +#include <linux/crc32.h> > +#include <linux/skbuff.h> > +#include <linux/module.h> > +#include <linux/kernel.h> > +#include <linux/interrupt.h> > +#include <linux/dma-mapping.h> > +#include <linux/platform_device.h> > +#include <linux/pinctrl/consumer.h> > +#include <linux/pinctrl/pinctrl.h> > +#include <linux/crypto.h> > +#include <linux/err.h> > +#include <linux/scatterlist.h> > +#include <linux/netdevice.h> > +#include <linux/etherdevice.h> > +#include <linux/reset.h> > +#include <linux/of_net.h> > +/* for A83T */ > +#include <linux/regulator/consumer.h> > + > +#define SUN8I_EMAC_BASIC_CTL0 0x00 > +#define SUN8I_EMAC_BASIC_CTL1 0x04 > + > +#define SUN8I_EMAC_MDIO_CMD 0x48 > +#define SUN8I_EMAC_MDIO_DATA 0x4C > + > +#define SUN8I_EMAC_RX_CTL0 0x24 > +#define SUN8I_EMAC_RX_CTL1 0x28 > + > +#define SUN8I_EMAC_TX_CTL0 0x10 > +#define SUN8I_EMAC_TX_CTL1 0x14 > + > +#define SUN8I_EMAC_TX_FLOW_CTL 0x1C > + > +#define SUN8I_EMAC_INT_STA 0x08 > +#define SUN8I_EMAC_INT_EN 0x0C > +#define SUN8I_EMAC_RGMII_STA 0xD0 > + > +#define SUN8I_EMAC_TX_DMA_STA 0xB0 > +#define SUN8I_EMAC_TX_CUR_DDESC 0xB4 > +#define SUN8I_EMAC_RX_DMA_STA 0xC0 > + > +#define MII_BUSY BIT(0) > +#define MII_WRITE BIT(1) > + > +#define SUN8I_EMAC_MACADDR_HI 0x50 > +#define SUN8I_EMAC_MACADDR_LO 0x54 > + > +#define SUN8I_EMAC_RX_DESC_LIST 0x34 > +#define SUN8I_EMAC_TX_DESC_LIST 0x20 > + > +#define SUN8I_COULD_BE_USED_BY_DMA BIT(31) > + > +struct dma_desc { > + u32 status; > + u32 st; > + u32 buf_addr; > + u32 next; > +} __attribute__((packed, aligned(4))); > + > +static int flow_ctrl; > +static int pause = 0x400; > + > +static int nbdesc = 4; > +struct sun8i_emac_priv { > + void __iomem *base; > + int irq; > + struct device *dev; > + struct net_device *ndev; > + struct mii_bus *mii; > + spinlock_t lock; > + spinlock_t tx_lock; > + int duplex; > + int speed; > + int link; > + u32 phy_interface; > + struct clk *busclk; > + struct clk *miiclk; > + u32 mdc; > + > + struct reset_control *rst_phy; > + struct reset_control *rst_mac; > + > + struct dma_desc *dd_rx ____cacheline_aligned; > + dma_addr_t dd_rx_phy ____cacheline_aligned; > + struct dma_desc *dd_tx; > + dma_addr_t dd_tx_phy; > + struct sk_buff **rx_sk; > + struct sk_buff **tx_sk; > + > + struct regulator *power; > +}; > + > +/* allocate a sk in a dma descriptor > +*/ > +static int sun8i_emac_rx_sk(struct net_device *ndev, int i) > +{ > + struct sun8i_emac_priv *priv = netdev_priv(ndev); > + struct dma_desc *ddesc; > + struct sk_buff *sk; > + > + ddesc = priv->dd_rx + i; > + > + ddesc->st = 0; > + > + sk = netdev_alloc_skb_ip_align(ndev, ndev->mtu); > + if (!sk) > + return -ENOMEM; > + > + if (priv->rx_sk[i]) { > + dev_warn(priv->dev, "WARN: Leaking a skbuff\n"); > + /* TODO should not happen */ > + } > + > + priv->rx_sk[i] = sk; > + > + ddesc->buf_addr = dma_map_single(priv->dev, sk->data, > + ndev->mtu, DMA_FROM_DEVICE); > + if (dma_mapping_error(priv->dev, ddesc->buf_addr)) { > + dev_err(priv->dev, "ERROR: Cannot dma_map RX\n"); > + dev_kfree_skb(sk); > + return -EINVAL; > + } > + ddesc->st |= ndev->mtu; > + ddesc->status = BIT(31); > + > + dev_info(priv->dev, "Init ddesc %02d at %pad buff=%p %x status=(%x > %x)\n", > + i, &ddesc, &sk->data, ddesc->buf_addr, ddesc->status, > ddesc->st); > + > + return 0; > +} > + > +/* Set MAC address for slot index > + * */ > +void sun8i_emac_set_macaddr(struct sun8i_emac_priv *priv, unsigned char > *addr, > + int index) > +{ > + u32 v; > + > + if (!is_valid_ether_addr(addr)) { > + random_ether_addr(priv->ndev->dev_addr); > + addr = priv->ndev->dev_addr; > + } > + dev_info(priv->dev, "%s %x %x %x %x %x %x\n", __func__, > + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]), > + > + v = (addr[5] << 8) | addr[4]; > + writel(v, priv->base + SUN8I_EMAC_MACADDR_HI + index * 8); > + dev_info(priv->dev, "Set macaddr %x\n", v); > + v = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0]; > + writel(v, priv->base + SUN8I_EMAC_MACADDR_HI + index * 8); > + dev_info(priv->dev, "Set macaddr %x\n", v); > +} > + > +void sun8i_emac_set_link_mode(struct sun8i_emac_priv *priv) > +{ > + u32 v; > + > + dev_info(priv->dev, "%s duplex=%x\n", __func__, priv->duplex); > + > + switch (priv->phy_interface) { > + case PHY_INTERFACE_MODE_MII: > + v = readl(priv->base + SUN8I_EMAC_BASIC_CTL0); > + > + if (!priv->duplex) > + v &= ~BIT(0); > + else > + v |= BIT(0); > + > + v &= ~0x0C; > + switch (priv->speed) { > + case 1000: > + break; > + case 100: > + v |= BIT(2); > + v |= BIT(3); > + break; > + case 10: > + v |= BIT(3); > + break; > + } > + > + writel(v, priv->base + SUN8I_EMAC_BASIC_CTL0); > + break; > + case PHY_INTERFACE_MODE_RGMII: > + v = readl(priv->base + SUN8I_EMAC_RGMII_STA); > + > + if (!priv->duplex) > + v &= ~BIT(0); > + else > + v |= BIT(0); > + > + v &= ~0x06; > + switch (priv->speed) { > + case 1000: > + v |= BIT(2); > + break; > + case 100: > + v |= BIT(1); > + break; > + case 10: > + break; > + } > + > + writel(v, priv->base + SUN8I_EMAC_RGMII_STA); > + break; > + default: > + dev_err(priv->dev, "Unknown PHY type %d\n", > priv->phy_interface); > + return; > + } > + > + dev_info(priv->dev, "%s set %x\n", __func__, v); > +} > + > +void sun8i_emac_flow_ctrl(struct sun8i_emac_priv *priv, int duplex, int fc, > + int pause) > +{ > + u32 flow = 0; > + > + dev_info(priv->dev, "%s %d %d %d\n", __func__, duplex, fc, pause); > + > + if (fc & BIT(0)) { > + flow = readl(priv->base + SUN8I_EMAC_RX_CTL0); > + flow |= 0x10000; > + /*flow |= BIT(16);*/ > + writel(flow, priv->base + SUN8I_EMAC_RX_CTL0); > + } > + > + if (fc & BIT(1)) { > + flow = readl(priv->base + SUN8I_EMAC_TX_FLOW_CTL); > + flow |= BIT(0); > + writel(flow, priv->base + SUN8I_EMAC_TX_FLOW_CTL); > + } > + > + if (duplex) { > + flow = readl(priv->base + SUN8I_EMAC_TX_FLOW_CTL); > + flow |= (pause << 4); > + /* pause & BIT(4)*/ > + writel(flow, priv->base + SUN8I_EMAC_TX_FLOW_CTL); > + } > +} > + > +/* > + * Grab a frame into a skb > +*/ > +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; > + int frame_len; > + > + ddesc = priv->dd_rx + i; > + > + frame_len = (ddesc->status >> 16) & 0x3FFF; > + skb = priv->rx_sk[i]; > + > + dev_info(priv->dev, "%s from %02d %pad len=%d st=%x\n", > + __func__, i, &ddesc, frame_len, ddesc->st); > + > + skb_put(skb, frame_len); > + > + dma_unmap_single(priv->dev, ddesc->buf_addr, ndev->mtu, > DMA_FROM_DEVICE); > + skb->protocol = eth_type_trans(skb, priv->ndev); > + skb->ip_summed = CHECKSUM_UNNECESSARY; > + skb->dev = priv->ndev; > + > + priv->ndev->stats.rx_packets++; > + priv->ndev->stats.rx_bytes += frame_len; > + > + netif_rx(skb); > + > + priv->rx_sk[i] = NULL; > + > + sun8i_emac_rx_sk(ndev, i); > + > + return 0; > +} > + > + > +static int sun8i_emac_receive_all(struct net_device *ndev) > +{ > + struct sun8i_emac_priv *priv = netdev_priv(ndev); > + struct dma_desc *ddesc; > + int i; > + > + for (i = 0; i < nbdesc; i++) { > + ddesc = priv->dd_rx + i; > + if (!(ddesc->status & BIT(31))) { > + sun8i_emac_rx_from_ddesc(ndev, i); > + } > + } > + return 0; > +} > + > +/* iterate over dma desc for finding completed xmit */ > +static int sun8i_emac_complete_xmit(struct net_device *ndev) > +{ > + struct sun8i_emac_priv *priv = netdev_priv(ndev); > + struct dma_desc *ddesc; > + int i, frame_len; > + > + dev_info(priv->dev, "%s\n", __func__); > + > + for (i = 0; i < nbdesc; i++) { > + ddesc = priv->dd_tx + i; > + if (ddesc->status & BIT(31)) > + continue; > + if (ddesc->status != 0 || ddesc->st) { > + frame_len = ddesc->st & 0x3FF; > + dev_info(priv->dev, "%s found slot to clean %d at %pad > %x %x\n", > + __func__, i, &ddesc, ddesc->status, ddesc->st); > + dma_unmap_single(priv->dev, ddesc->buf_addr, > + frame_len, DMA_TO_DEVICE); > + priv->tx_sk[i] = NULL; > + ddesc->status = 0; > + ddesc->st = 0; > + } > + } > + > + return 0; > +} > + > +static int debug_printall_desc(struct net_device *ndev) > +{ > + struct sun8i_emac_priv *priv = netdev_priv(ndev); > + struct dma_desc *ddesc; > + int i; > + int len; > +/* > + ddesc = priv->dd_rx; > + if (ddesc == NULL) > + return 0; > + > + for (i = 0; i < nbdesc; i++) { > + ddesc = priv->dd_rx + i; > + len = (ddesc->status >> 16) & 0x3FFF; > + if (!(ddesc->status & BIT(31))) { > + ptr = (char *)priv->rx_sk[i]->data; > + dev_info(priv->dev, "%x %x %x %x %x %x %x %x %x %x %x > %x %x %x %x %x\n", > + ptr[0], > + ptr[1], > + ptr[2], > + ptr[3], > + ptr[4], > + ptr[5], > + ptr[6], > + ptr[7], > + ptr[8], > + ptr[9], > + ptr[10], > + ptr[11], > + ptr[12], > + ptr[13], > + ptr[14], > + ptr[15]); > + rx_from_ddesc(ndev, i); > + } > + ddesc++; > + } > +*/ > + ddesc = priv->dd_tx; > + if (ddesc == NULL) > + return 0; > + for (i = 0; i < nbdesc; i++) { > + ddesc = priv->dd_tx + i; > + len = ddesc->st & 0x7FF; > + /*dev_info(priv->dev, "tx%02d status=%x %x d=%x len=%d n=%x", > i, ddesc->status, ddesc->st, ddesc->buf_addr, len, ddesc->next);*/ > + } > + return 0; > +} > + > +static int sun8i_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg) > +{ > + u32 v, c, t, dt, dr, x = 0; > + struct net_device *ndev = bus->priv; > + struct sun8i_emac_priv *priv = netdev_priv(ndev); > + struct dma_desc *ddesc; > + u32 cur_rd, cur_td; > + > + dt = readl(priv->base + SUN8I_EMAC_TX_DMA_STA); > + dr = readl(priv->base + SUN8I_EMAC_RX_DMA_STA); > + debug_printall_desc(ndev); > + > + ddesc = priv->dd_rx; > + if (ddesc != NULL) > + x = ddesc->status; > + > + do { > + v = readl(priv->base + SUN8I_EMAC_MDIO_CMD); > + } while ((v & MII_BUSY) == 1); > + /* TODO timeout */ > + > + c = 0; > + c |= ((priv->mdc & 0x07) << 20); > + c |= (((phyaddr << 12) & (0x0001F000)) | > + ((phyreg << 4) & (0x000007F0)) | MII_BUSY); > + writel(c, priv->base + SUN8I_EMAC_MDIO_CMD); > + t = 0; > + do { > + v = readl(priv->base + SUN8I_EMAC_MDIO_CMD); > + } while ((v & MII_BUSY) == 1 && t++ < 1500); > + /* TODO timeout */ > + > + v = readl(priv->base + SUN8I_EMAC_MDIO_DATA); > + cur_rd = readl(priv->base + 0xC4); > + cur_td = readl(priv->base + SUN8I_EMAC_TX_CUR_DDESC); > + > + /*dev_info(priv->dev, "%s %d %d %x %x t=%u dmar=%x dmat=%x %x cur=%x > %x\n", __func__, phyaddr, phyreg, c, v, t, dr, dt, x, cur_rd, cur_td);*/ > + return (int)v; > +} > + > +static int sun8i_mdio_write(struct mii_bus *bus, int phyaddr, int phyreg, > u16 data) > +{ > + struct net_device *ndev = bus->priv; > + struct sun8i_emac_priv *priv = netdev_priv(ndev); > + u32 v; > + > + do { > + v = readl(priv->base + SUN8I_EMAC_MDIO_CMD); > + } while ((v & MII_BUSY) == 1); > + /* TODO timeout */ > + > + /*v = readl(priv->base + SUN8I_EMAC_MDIO_CMD); > + * */ > + > + v = ((0x07 << 20) & readl(priv->base + SUN8I_EMAC_MDIO_CMD)) | > (priv->mdc << 20); > + v |= (((phyaddr << 12) & (0x0001F000)) | > + ((phyreg << 4) & (0x000007F0))) | MII_WRITE | MII_BUSY; > + writel(data, priv->base + SUN8I_EMAC_MDIO_DATA); > + writel(v, priv->base + SUN8I_EMAC_MDIO_CMD); > + > + dev_info(priv->dev, "%s %d %d %x ctl=%x\n", __func__, phyaddr, phyreg, > data, v); > + > + do { > + v = readl(priv->base + SUN8I_EMAC_MDIO_CMD); > + } while ((v & MII_BUSY) == 1); > + /* TODO timeout */ > + > + return 0; > +} > + > +static int sun8i_mdio_reset(struct mii_bus *bus) > +{ > + struct net_device *ndev = bus->priv; > + struct sun8i_emac_priv *priv = netdev_priv(ndev); > + > + dev_info(priv->dev, "%s\n", __func__); > + > + /* No datasheet, undocumented */ > + /*writel((4 << 2), priv->base + SUN8I_EMAC_MDIO_CMD);*/ > + return 0; > +} > +/* END of need to moved in phy/ */ > + > +static void sun8i_phy_adjust_link(struct net_device *ndev) > +{ > + struct sun8i_emac_priv *priv = netdev_priv(ndev); > + struct phy_device *phydev = ndev->phydev; > + unsigned long flags; > + int new_state = 0; > + int i; > + > + dev_info(priv->dev, "%s link=%x duplex=%x speed=%x\n", __func__, > phydev->link, > + phydev->duplex, phydev->speed); > + if (!phydev) > + return; > + > + spin_lock_irqsave(&priv->lock, flags); > + 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, > + flow_ctrl, pause); > + > + if (phydev->speed != priv->speed) { > + new_state = 1; > + priv->speed = phydev->speed; > + } > + > + if (priv->link == 0) { > + new_state = 1; > + priv->link = phydev->link; > + } > + > + dev_info(priv->dev, "%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); > + > + for (i = 0 ; i < 0xd0; i += 16) > + dev_info(priv->dev, "%x %x %x %x %x %x %x %x", > + readl(priv->base + i), > + readl(priv->base + i + 4), > + readl(priv->base + i + 8), > + readl(priv->base + i + 12), > + i, i + 4, i + 8, i + 12); > + > + spin_unlock_irqrestore(&priv->lock, flags); > +} > + > +/* this function do lots of things that will be splited away (clk/phy) */ > +static int sun8i_phy_init(struct net_device *ndev) > +{ > + int value; > + struct mii_bus *new_bus; > + struct sun8i_emac_priv *priv = netdev_priv(ndev); > + struct phy_device *phydev = NULL; > + int err; > + void __iomem *sc; > + u32 v; > + int timeout; > + int do_ephy_clk = 1; > + > + dev_info(priv->dev, "%s\n", __func__); > + > + /* find type of PHY */ > + priv->phy_interface = of_get_phy_mode(priv->dev->of_node); > + dev_info(priv->dev, "%s %x\n", __func__, priv->phy_interface); > + /*priv->phy_interface = PHY_INTERFACE_MODE_RGMII;*/ > + > + > + /* fallback to integrate MII */ > + switch (priv->phy_interface) { > + case PHY_INTERFACE_MODE_MII: > + dev_info(priv->dev, "%s interface PHY_INTERFACE_MODE_MII\n", > __func__); > + break; > + case PHY_INTERFACE_MODE_RGMII: > + dev_info(priv->dev, "%s interface PHY_INTERFACE_MODE_RGMII\n", > __func__); > + break; > + case PHY_INTERFACE_MODE_RMII: > + dev_info(priv->dev, "%s interface PHY_INTERFACE_MODE_RMII\n", > __func__); > + break; > + case PHY_INTERFACE_MODE_GMII: > + dev_info(priv->dev, "%s interface PHY_INTERFACE_MODE_GMII\n", > __func__); > + break; > + default: > + dev_info(priv->dev, "Fallback to MII\n"); > + priv->phy_interface = PHY_INTERFACE_MODE_MII; > + } > + > + /* A83T power TODO: no regulator yet */ > + priv->power = regulator_get(NULL, "vc-ephy-io"); > + if (IS_ERR(priv->power)) { > + dev_err(priv->dev, "Cannot get regulator\n"); > + } else { > + err = regulator_set_voltage(priv->power, 3300000, 3300000); > + if (err) { > + dev_err(priv->dev, "Cannot set regulator voltage\n"); > + } else { > + err = regulator_enable(priv->power); > + if (err) > + dev_err(priv->dev, "Cannot enable regulator\n"); > + else > + dev_info(priv->dev, "Enabled regulator\n"); > + > + } > + } > + > + /* systemcontrol */ > + /* TODO put that in phy clock */ Yes, ... > + sc = ioremap(0x01C00030, 0x20); and put that in the DT. If you have an extra PHY clock driver (and DT node), this address can go there. Also it's really only one register, right? So <0x01c00030 0x4> should be the right reg value here. > + if (sc) { > + v = readl(sc); > + dev_info(priv->dev, "SystemControl %x\n", v); > + /* crappy switch to be moved */ > + switch (v) { > + case 0: /* A83T */ Do I get this right that you try to determine the existence of the internal PHY here from the reset value of that register? What if firmware (e.g. U-Boot) has changed this value already? Also I don't think that's the right approach here, as the _usage_ of the internal PHY is board specific, I think. So a board maker can decide to use it or not, even when using MII as the PHY interface mode. So I suggest: a) deduce the sheer existence of the internal PHY from the DT having the ephy reset and clock-output names. A83T and A64 don't have them. b) deduce the usage of the internal PHY from some additional DT property (allwinner,internal-phy;) or by having an extra PHY node which we reference to (?). > + do_ephy_clk = 0; > + switch (priv->phy_interface) { > + case PHY_INTERFACE_MODE_MII: > + v &= ~BIT(2); > + break; > + case PHY_INTERFACE_MODE_RGMII: > + v |= BIT(1); > + v |= BIT(2); > + v |= BIT(15); > + break; > + case PHY_INTERFACE_MODE_GMII: > + v &= ~BIT(2); > + break; > + default: > + dev_err(priv->dev, "Unknown PHY type %d\n", > priv->phy_interface); > + } > + break; > + case 0x58000: /* H3 */ > + switch (priv->phy_interface) { > + case PHY_INTERFACE_MODE_MII: > + /* PHY_SELECT: Internal PHY */ > + v |= BIT(15); So here you deduce internal/external PHY usage by the protocol being used, which is not right IMHO. Board vendor can still use an external MII PHY to avoid heat problems, for instance. > + /* SHUTDOWN: Power up */ > + v &= ~BIT(16); > + /* 24 Mhz */ > + /*v &= ~BIT(18);*/ > + /* LED POL */ > + v |= BIT(17); > + break; > + case PHY_INTERFACE_MODE_RGMII: > + v |= BIT(1); > + v |= BIT(2); > + /* External PHY */ > + v &= ~BIT(15); > + /* SHUTDOWN: Shutdown */ > + v |= BIT(16); > + break; > + /* TODO RMII */ > + default: > + dev_err(priv->dev, "Unknown PHY type %d\n", > priv->phy_interface); > + } > + break; > + default: > + dev_err(priv->dev, "Unknown platform %x\n", v); > + } > + dev_info(priv->dev, "SystemControl %x\n", v); > + writel(v, sc); > + iounmap(sc); > + } > + /* end phy clock */ > + > + /* PWM */ > + sc = ioremap(0x01C21400, 0x20); > + if (sc) { > + v = readl(sc); > + dev_info(priv->dev, "PWM %x\n", v); > + v = readl(sc + 0x04); > + dev_info(priv->dev, "PWM %x\n", v); > + iounmap(sc); > + } What is this about? Is this related to the MAC operation at all? > + > + if (do_ephy_clk == 1) { Again here you initialize the PHY clocks and resets based on the internal PHY's existence, not usage. What if an H3 uses an external RGMII PHY? Then you don't need the internal one. Cheers, Andre. > + priv->miiclk = devm_clk_get(priv->dev, "ahb1_ephy"); > + if (IS_ERR(priv->miiclk)) { > + err = PTR_ERR(priv->miiclk); > + dev_err(priv->dev, "Cannot get MII clock err=%d\n", > err); > + return err; > + } > + err = clk_prepare_enable(priv->miiclk); > + if (err != 0) { > + dev_err(priv->dev, "Cannot prepare_enable PHY\n"); > + return err; > + } else { > + dev_info(priv->dev, "PHY clk is enabled\n"); > + } > + > + priv->rst_phy = devm_reset_control_get(priv->dev, "ephy"); > + if (IS_ERR(priv->rst_phy)) { > + err = PTR_ERR(priv->rst_phy); > + dev_info(priv->dev, "no PHY reset control found %d\n", > err); > + priv->rst_phy = NULL; > + } > + if (priv->rst_phy) { > + err = reset_control_deassert(priv->rst_phy); > + if (err) > + dev_err(priv->dev, "Cannot deassert PHY\n"); > + else > + dev_info(priv->dev, "PHY is de-asserted\n"); > + } > + msleep(300); > + } > + > + new_bus = mdiobus_alloc(); > + if (!new_bus) { > + netdev_err(ndev, "Failed to alloc new mdio bus\n"); > + return -ENOMEM; > + } > + > + new_bus->name = dev_name(priv->dev); > + new_bus->read = &sun8i_mdio_read; > + new_bus->write = &sun8i_mdio_write; > + new_bus->reset = &sun8i_mdio_reset; > + snprintf(new_bus->id, MII_BUS_ID_SIZE, "%s-%x", new_bus->name, 0); > + > + new_bus->parent = priv->dev; > + new_bus->priv = ndev; > + > + err = mdiobus_register(new_bus); > + if (err) { > + dev_err(priv->dev, "%s: Cannot register as MDIO bus\n", > + new_bus->name); > + goto mdiobus_err; > + } > + > + priv->mii = new_bus; > + > + phydev = phy_find_first(new_bus); > + if (!phydev) { > + netdev_err(ndev, "No PHY found!\n"); > + err = PTR_ERR(phydev); > + goto mdiobus_unreg; > + } > + > + phydev->irq = PHY_POLL; > + > + /*priv->phy_interface = PHY_INTERFACE_MODE_MII;*/ > + > +/* phydev = of_phy_connect(ndev, */ > + phydev = phy_connect(ndev, dev_name(&phydev->dev), > + &sun8i_phy_adjust_link, priv->phy_interface); > + > + if (IS_ERR(phydev)) { > + netdev_err(ndev, "Could not attach to PHY\n"); > + err = PTR_ERR(phydev); > + goto mdiobus_unreg; > + } > + > + netdev_info(ndev, "%s: PHY ID %08x at %d IRQ %s (%s)\n", > + ndev->name, phydev->phy_id, phydev->addr, > + "poll", dev_name(&phydev->dev)); > + > + phy_write(phydev, MII_BMCR, BMCR_RESET); > + timeout = 0; > + while (BMCR_RESET & phy_read(phydev, MII_BMCR) && timeout++ < 15) > + msleep(3); > + if (timeout >= 15) > + dev_warn(priv->dev, "PHY reset timeout\n"); > + > + value = phy_read(phydev, MII_BMCR); > + phy_write(phydev, MII_BMCR, (value & ~BMCR_PDOWN)); > + > + dev_info(priv->dev, "PHY supported %x\n", phydev->supported); > + /*phydev->supported &= PHY_GBIT_FEATURES;*/ > + phydev->advertising = phydev->supported; > + phy_print_status(phydev); > + > + return 0; > + > +mdiobus_unreg: > + mdiobus_unregister(new_bus); > +mdiobus_err: > + mdiobus_free(new_bus); > + return err; > +} .... -- You received this message because you are subscribed to the Google Groups "linux-sunxi" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/d/optout.
