drv_ksz8081.c 12.3 KB
Newer Older
1
/*
2
 * Copyright (c) 2006-2022, RT-Thread Development Team
3
 *
4 5 6 7 8
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2020-10-14     wangqiang    the first version
9
 * 2022-08-29     xjy198903    add rt1170 support
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
 */

#include <rtthread.h>

#ifdef PHY_USING_KSZ8081

#include <rtdevice.h>
#include "drv_gpio.h"
#include "drv_mdio.h"


/*******************************************************************************
 * Definitions
 ******************************************************************************/

/*! @brief Defines the PHY registers. */
#define PHY_BASICCONTROL_REG 0x00U      /*!< The PHY basic control register. */
#define PHY_BASICSTATUS_REG 0x01U       /*!< The PHY basic status register. */
#define PHY_ID1_REG 0x02U               /*!< The PHY ID one register. */
#define PHY_ID2_REG 0x03U               /*!< The PHY ID two register. */
#define PHY_AUTONEG_ADVERTISE_REG 0x04U /*!< The PHY auto-negotiate advertise register. */
#define PHY_CONTROL1_REG 0x1EU          /*!< The PHY control one register. */
#define PHY_CONTROL2_REG 0x1FU          /*!< The PHY control two register. */

#define PHY_CONTROL_ID1 0x22U /*!< The PHY ID1*/

/*! @brief Defines the mask flag in basic control register. */
#define PHY_BCTL_DUPLEX_MASK 0x0100U          /*!< The PHY duplex bit mask. */
#define PHY_BCTL_RESTART_AUTONEG_MASK 0x0200U /*!< The PHY restart auto negotiation mask. */
#define PHY_BCTL_AUTONEG_MASK 0x1000U         /*!< The PHY auto negotiation bit mask. */
#define PHY_BCTL_SPEED_MASK 0x2000U           /*!< The PHY speed bit mask. */
#define PHY_BCTL_LOOP_MASK 0x4000U            /*!< The PHY loop bit mask. */
#define PHY_BCTL_RESET_MASK 0x8000U           /*!< The PHY reset bit mask. */
#define PHY_BCTL_SPEED_100M_MASK 0x2000U      /*!< The PHY 100M speed mask. */

/*!@brief Defines the mask flag of operation mode in control two register*/
#define PHY_CTL2_REMOTELOOP_MASK 0x0004U    /*!< The PHY remote loopback mask. */
#define PHY_CTL2_REFCLK_SELECT_MASK 0x0080U /*!< The PHY RMII reference clock select. */
#define PHY_CTL1_10HALFDUPLEX_MASK 0x0001U  /*!< The PHY 10M half duplex mask. */
#define PHY_CTL1_100HALFDUPLEX_MASK 0x0002U /*!< The PHY 100M half duplex mask. */
#define PHY_CTL1_10FULLDUPLEX_MASK 0x0005U  /*!< The PHY 10M full duplex mask. */
#define PHY_CTL1_100FULLDUPLEX_MASK 0x0006U /*!< The PHY 100M full duplex mask. */
#define PHY_CTL1_SPEEDUPLX_MASK 0x0007U     /*!< The PHY speed and duplex mask. */
#define PHY_CTL1_ENERGYDETECT_MASK 0x10U    /*!< The PHY signal present on rx differential pair. */
#define PHY_CTL1_LINKUP_MASK 0x100U         /*!< The PHY link up. */
#define PHY_LINK_READY_MASK (PHY_CTL1_ENERGYDETECT_MASK | PHY_CTL1_LINKUP_MASK)

/*! @brief Defines the mask flag in basic status register. */
#define PHY_BSTATUS_LINKSTATUS_MASK 0x0004U  /*!< The PHY link status mask. */
#define PHY_BSTATUS_AUTONEGABLE_MASK 0x0008U /*!< The PHY auto-negotiation ability mask. */
#define PHY_BSTATUS_AUTONEGCOMP_MASK 0x0020U /*!< The PHY auto-negotiation complete mask. */

/*! @brief Defines the mask flag in PHY auto-negotiation advertise register. */
#define PHY_100BaseT4_ABILITY_MASK 0x200U    /*!< The PHY have the T4 ability. */
#define PHY_100BASETX_FULLDUPLEX_MASK 0x100U /*!< The PHY has the 100M full duplex ability.*/
#define PHY_100BASETX_HALFDUPLEX_MASK 0x080U /*!< The PHY has the 100M full duplex ability.*/
#define PHY_10BASETX_FULLDUPLEX_MASK 0x040U  /*!< The PHY has the 10M full duplex ability.*/
#define PHY_10BASETX_HALFDUPLEX_MASK 0x020U  /*!< The PHY has the 10M full duplex ability.*/



/*! @brief Defines the timeout macro. */
#define PHY_TIMEOUT_COUNT 0x3FFFFFFU

/* defined the Reset pin, PORT and PIN config by menuconfig */
75 76 77
#ifdef SOC_IMXRT1170_SERIES
#define RESET_PIN GET_PIN(PHY_RESET_KSZ8081_PORT, PHY_RESET_KSZ8081_PIN)
#else
78
#define RESET_PIN GET_PIN(PHY_RESET_PORT, PHY_RESET_PIN)
79 80
#endif

81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176

/*******************************************************************************
 * Prototypes
 ******************************************************************************/


/*******************************************************************************
 * Variables
 ******************************************************************************/
static struct rt_phy_device phy_ksz8081;

/*******************************************************************************
 * Code
 ******************************************************************************/



static inline rt_bool_t read_reg(rt_mdio_t *bus, rt_uint32_t addr, rt_uint32_t reg_id, rt_uint32_t *value)
{
    if (4 != bus->ops->read(bus, addr, reg_id, value, 4))
    {
        return RT_FALSE;
    }
    return RT_TRUE;
}

static inline rt_bool_t write_reg(rt_mdio_t *bus, rt_uint32_t addr, rt_uint32_t reg_id, rt_uint32_t value)
{
    if (4 != bus->ops->write(bus, addr, reg_id, &value, 4))
    {
        return RT_FALSE;
    }
    return RT_TRUE;
}

static rt_phy_status rt_phy_init(void *object, rt_uint32_t phy_addr, rt_uint32_t src_clock_hz)
{
    rt_bool_t ret;
    rt_phy_status result;
    rt_uint32_t counter = PHY_TIMEOUT_COUNT;
    rt_uint32_t id_reg = 0;
    rt_uint32_t time_delay;
    rt_uint32_t bss_reg;
    rt_uint32_t ctl_reg = 0;

    // reset phy device by gpio
    rt_pin_mode(RESET_PIN, PIN_MODE_OUTPUT);
    rt_pin_write(RESET_PIN, PIN_LOW);
    rt_thread_mdelay(100);
    rt_pin_write(RESET_PIN, PIN_HIGH);

    rt_mdio_t *mdio_bus = rt_hw_mdio_register(object, "phy_mdio");
    if (RT_NULL == mdio_bus)
    {
        return PHY_STATUS_FAIL;
    }
    phy_ksz8081.bus = mdio_bus;
    phy_ksz8081.addr = phy_addr;
    ret = mdio_bus->ops->init(mdio_bus, src_clock_hz);
    if ( !ret )
    {
        return PHY_STATUS_FAIL;
    }

    /* Initialization after PHY stars to work. */
    while ((id_reg != PHY_CONTROL_ID1) && (counter != 0))
    {
        phy_ksz8081.ops->read(PHY_ID1_REG, &id_reg);
        counter--;
    }

    if (!counter)
    {
        return PHY_STATUS_FAIL;
    }

    /* Reset PHY. */
    counter = PHY_TIMEOUT_COUNT;
    result = phy_ksz8081.ops->write(PHY_BASICCONTROL_REG, PHY_BCTL_RESET_MASK);
    if (PHY_STATUS_OK == result)
    {
        #if defined(FSL_FEATURE_PHYKSZ8081_USE_RMII50M_MODE)
        rt_uint32_t data = 0;
        result = phy_ksz8081.ops->read(PHY_CONTROL2_REG, &data);
        if (PHY_STATUS_FAIL == result)
        {
            return PHY_STATUS_FAIL;
        }
        result = phy_ksz8081.ops->write(PHY_CONTROL2_REG, (data | PHY_CTL2_REFCLK_SELECT_MASK));
        if (PHY_STATUS_FAIL == result)
        {
            return PHY_STATUS_FAIL;
        }
        #endif  /* FSL_FEATURE_PHYKSZ8081_USE_RMII50M_MODE */

        /* Set the negotiation. */
177
        result = phy_ksz8081.ops->write(PHY_AUTONEG_ADVERTISE_REG,
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
                                        (PHY_100BASETX_FULLDUPLEX_MASK | PHY_100BASETX_HALFDUPLEX_MASK |
                                        PHY_10BASETX_FULLDUPLEX_MASK | PHY_10BASETX_HALFDUPLEX_MASK | 0x1U));
        if (PHY_STATUS_OK == result)
        {
            result = phy_ksz8081.ops->write(PHY_BASICCONTROL_REG, (PHY_BCTL_AUTONEG_MASK | PHY_BCTL_RESTART_AUTONEG_MASK));
            if (PHY_STATUS_OK == result)
            {
                /* Check auto negotiation complete. */
                while (counter--)
                {
                    result = phy_ksz8081.ops->read(PHY_BASICSTATUS_REG, &bss_reg);
                    if (PHY_STATUS_OK == result)
                    {
                        phy_ksz8081.ops->read(PHY_CONTROL1_REG, &ctl_reg);
                        if (((bss_reg & PHY_BSTATUS_AUTONEGCOMP_MASK) != 0) && (ctl_reg & PHY_LINK_READY_MASK))
                        {
                            /* Wait a moment for Phy status stable. */
                            for (time_delay = 0; time_delay < PHY_TIMEOUT_COUNT; time_delay++)
                            {
                                __ASM("nop");
                            }
                            break;
                        }
                    }

                    if (!counter)
                    {
                        return PHY_STATUS_FAIL;
                    }
                }
            }
        }
    }

    return PHY_STATUS_OK;
}


static rt_phy_status rt_phy_read(rt_uint32_t reg, rt_uint32_t *data)
{
    rt_mdio_t *mdio_bus = phy_ksz8081.bus;
    rt_uint32_t device_id = phy_ksz8081.addr;

    if (read_reg(mdio_bus, device_id, reg, data))
    {
        return PHY_STATUS_OK;
    }
    return PHY_STATUS_FAIL;
}

static rt_phy_status rt_phy_write(rt_uint32_t reg, rt_uint32_t data)
{
    rt_mdio_t *mdio_bus = phy_ksz8081.bus;
    rt_uint32_t device_id = phy_ksz8081.addr;

    if (write_reg(mdio_bus, device_id, reg, data))
    {
        return PHY_STATUS_OK;
    }
    return PHY_STATUS_FAIL;
}

static rt_phy_status rt_phy_loopback(rt_uint32_t mode, rt_uint32_t speed, rt_bool_t enable)
{
    rt_uint32_t data = 0;
    rt_phy_status result;

    /* Set the loop mode. */
    if (enable)
    {
        if (PHY_LOCAL_LOOP == mode)
        {
            if (PHY_SPEED_100M == speed)
            {
                data = PHY_BCTL_SPEED_100M_MASK | PHY_BCTL_DUPLEX_MASK | PHY_BCTL_LOOP_MASK;
            }
            else
            {
                data = PHY_BCTL_DUPLEX_MASK | PHY_BCTL_LOOP_MASK;
            }
            return phy_ksz8081.ops->write(PHY_BASICCONTROL_REG, data);
        }
        else
        {
            /* First read the current status in control register. */
            result = phy_ksz8081.ops->read(PHY_CONTROL2_REG, &data);
            if (PHY_STATUS_OK == result)
            {
                return phy_ksz8081.ops->write(PHY_CONTROL2_REG, (data | PHY_CTL2_REMOTELOOP_MASK));
            }
        }
    }
    else
    {
        /* Disable the loop mode. */
        if (PHY_LOCAL_LOOP == mode)
        {
            /* First read the current status in control register. */
            result = phy_ksz8081.ops->read(PHY_BASICCONTROL_REG, &data);
            if (PHY_STATUS_OK == result)
            {
                data &= ~PHY_BCTL_LOOP_MASK;
                return phy_ksz8081.ops->write(PHY_BASICCONTROL_REG, (data | PHY_BCTL_RESTART_AUTONEG_MASK));
            }
        }
        else
        {
            /* First read the current status in control one register. */
            result = phy_ksz8081.ops->read(PHY_CONTROL2_REG, &data);
            if (PHY_STATUS_OK == result)
            {
                return phy_ksz8081.ops->write(PHY_CONTROL2_REG, (data & ~PHY_CTL2_REMOTELOOP_MASK));
            }
        }
    }
    return result;
}

static rt_phy_status get_link_status(rt_bool_t *status)
{
    rt_phy_status result;
    rt_uint32_t data;

    /* Read the basic status register. */
    result = phy_ksz8081.ops->read(PHY_BASICSTATUS_REG, &data);
    if (PHY_STATUS_OK == result)
    {
        if (!(PHY_BSTATUS_LINKSTATUS_MASK & data))
        {
            /* link down. */
            *status = RT_FALSE;
        }
        else
        {
            /* link up. */
            *status = RT_TRUE;
        }
    }
    return result;
}
static rt_phy_status get_link_speed_duplex(rt_uint32_t *speed, rt_uint32_t *duplex)
{
    rt_phy_status result = PHY_STATUS_OK;
    rt_uint32_t data, ctl_reg;

    /* Read the control two register. */
    result = phy_ksz8081.ops->read(PHY_CONTROL1_REG, &ctl_reg);
    if (PHY_STATUS_OK == result)
    {
        data = ctl_reg & PHY_CTL1_SPEEDUPLX_MASK;
        if ((PHY_CTL1_10FULLDUPLEX_MASK == data) || (PHY_CTL1_100FULLDUPLEX_MASK == data))
        {
            /* Full duplex. */
            *duplex = PHY_FULL_DUPLEX;
        }
        else
        {
            /* Half duplex. */
            *duplex = PHY_HALF_DUPLEX;
        }

        data = ctl_reg & PHY_CTL1_SPEEDUPLX_MASK;
        if ((PHY_CTL1_100HALFDUPLEX_MASK == data) || (PHY_CTL1_100FULLDUPLEX_MASK == data))
        {
            /* 100M speed. */
            *speed = PHY_SPEED_100M;
        }
        else
        { /* 10M speed. */
            *speed = PHY_SPEED_10M;
        }
    }

    return result;
}

static struct rt_phy_ops phy_ops =
{
    .init = rt_phy_init,
    .read = rt_phy_read,
    .write = rt_phy_write,
    .loopback = rt_phy_loopback,
    .get_link_status = get_link_status,
    .get_link_speed_duplex = get_link_speed_duplex,
};

static int rt_phy_ksz8081_register( void )
{
    phy_ksz8081.ops = &phy_ops;
367
    rt_hw_phy_register(&phy_ksz8081, "ksz8081");
368 369 370 371 372 373
    return 1;
}

INIT_DEVICE_EXPORT(rt_phy_ksz8081_register);

#endif /* PHY_USING_KSZ8081 */