1/*
2 * @TAG(OTHER_GPL)
3 */
4
5/*
6 * (C) Copyright 2009 Ilya Yanok, Emcraft Systems Ltd <yanok@emcraft.com>
7 * (C) Copyright 2008,2009 Eric Jarrige <eric.jarrige@armadeus.org>
8 * (C) Copyright 2008 Armadeus Systems nc
9 * (C) Copyright 2007 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
10 * (C) Copyright 2007 Pengutronix, Juergen Beisert <j.beisert@pengutronix.de>
11 * (C) Copyright 2018, NXP
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License as
15 * published by the Free Software Foundation; either version 2 of
16 * the License, or (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
26 * MA 02111-1307 USA
27 */
28
29#include "common.h"
30#include "miiphy.h"
31#include "fec_mxc.h"
32
33#include "imx-regs.h"
34#include "../io.h"
35
36#include "micrel.h"
37#include <errno.h>
38#include <stdlib.h>
39#include <string.h>
40#include "../enet.h"
41#include "../ocotp_ctrl.h"
42/*
43 * Timeout the transfer after 5 mS. This is usually a bit more, since
44 * the code in the tightloops this timeout is used in adds some overhead.
45 */
46#define FEC_XFER_TIMEOUT    5000
47
48#ifndef CONFIG_MII
49#error "CONFIG_MII has to be defined!"
50#endif
51
52#ifndef CONFIG_FEC_XCV_TYPE
53#define CONFIG_FEC_XCV_TYPE MII100
54#endif
55
56#undef DEBUG
57
58int fec_phy_read(struct mii_dev *bus, int phyAddr, int dev_addr, int regAddr)
59{
60    return enet_mdio_read((struct enet *)bus->priv, phyAddr, regAddr);
61}
62
63int fec_phy_write(struct mii_dev *bus, int phyAddr, int dev_addr, int regAddr,
64                  uint16_t data)
65{
66    return enet_mdio_write((struct enet *)bus->priv, phyAddr, regAddr, data);
67}
68
69/**
70 * Halt the FEC engine
71 * @param[in] dev Our device to handle
72 */
73void fec_halt(struct eth_device *dev)
74{
75#if 0
76    struct fec_priv *fec = (struct fec_priv *)dev->priv;
77    int counter = 0xffff;
78    /* issue graceful stop command to the FEC transmitter if necessary */
79    writel(FEC_TCNTRL_GTS | readl(&fec->eth->x_cntrl), &fec->eth->x_cntrl);
80    /* wait for graceful stop to register */
81    while ((counter--) && (!(readl(&fec->eth->ievent) & FEC_IEVENT_GRA))) {
82        udelay(1);
83    }
84    writel(readl(&fec->eth->ecntrl) & ~FEC_ECNTRL_ETHER_EN, &fec->eth->ecntrl);
85    fec->rbd_index = 0;
86    fec->tbd_index = 0;
87#else
88    assert(!"unimplemented");
89#endif
90}
91int fec_init(unsigned phy_mask, struct enet *enet)
92{
93    struct eth_device *edev;
94    struct phy_device *phydev;
95    struct mii_dev *bus;
96    int ret = 0;
97    struct eth_device _eth;
98    /* create and fill edev struct */
99    edev = &_eth;
100    memset(edev, 0, sizeof(*edev));
101
102    edev->priv = (void *)enet;
103    edev->write_hwaddr = NULL;
104
105    /* Alocate the mdio bus */
106    bus = mdio_alloc();
107    if (!bus) {
108        return -1;
109    }
110    bus->read = fec_phy_read;
111    bus->write = fec_phy_write;
112    bus->priv = enet;
113    strcpy(bus->name, edev->name);
114    ret = mdio_register(bus);
115    if (ret) {
116        free(bus);
117        return -1;
118    }
119
120    /****** Configure phy ******/
121    phydev = phy_connect_by_mask(bus, phy_mask, edev, PHY_INTERFACE_MODE_RGMII);
122    if (!phydev) {
123        return -1;
124    }
125
126    if (config_set(CONFIG_PLAT_IMX8MQ_EVK)) {
127        /* enable rgmii rxc skew and phy mode select to RGMII copper */
128        phy_write(phydev, MDIO_DEVAD_NONE, 0x1d, 0x1f);
129        phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0x8);
130        phy_write(phydev, MDIO_DEVAD_NONE, 0x1d, 0x05);
131        phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0x100);
132
133        if (phydev->drv->config) {
134            phydev->drv->config(phydev);
135        }
136
137        if (phydev->drv->startup) {
138            phydev->drv->startup(phydev);
139        }
140    } else if (config_set(CONFIG_PLAT_IMX6)) {
141        /* min rx data delay */
142        ksz9021_phy_extended_write(phydev, MII_KSZ9021_EXT_RGMII_RX_DATA_SKEW, 0x0);
143        /* min tx data delay */
144        ksz9021_phy_extended_write(phydev, MII_KSZ9021_EXT_RGMII_TX_DATA_SKEW, 0x0);
145        /* max rx/tx clock delay, min rx/tx control */
146        ksz9021_phy_extended_write(phydev, MII_KSZ9021_EXT_RGMII_CLOCK_SKEW, 0xf0f0);
147        ksz9021_config(phydev);
148
149        /* Start up the PHY */
150        ret = ksz9021_startup(phydev);
151        if (ret) {
152            printf("Could not initialize PHY %s\n", phydev->dev->name);
153            return ret;
154        }
155
156    }
157
158    printf("\n  * Link speed: %4i Mbps, ", phydev->speed);
159    if (phydev->duplex == DUPLEX_FULL) {
160        enet_set_speed(enet, phydev->speed, 1);
161        printf("full-duplex *\n");
162    } else {
163        enet_set_speed(enet, phydev->speed, 0);
164        printf("half-duplex *\n");
165    }
166
167    udelay(100000);
168    return 0;
169}
170