RTL9000是百兆PHY芯片,调试的板子是使用的RGMII。
调试一款phy芯片最简单直接的方式是查看内核中是否支持这类型号。路径如下:
kernel/drivers/net/phy/realtek.c
查看代码中只是支持了8211系列。
static struct phy_driver realtek_drvs[] = {
{
.phy_id = 0x00008201,
.name = "RTL8201CP Ethernet",
.phy_id_mask = 0x0000ffff,
.features = PHY_BASIC_FEATURES,
.flags = PHY_HAS_INTERRUPT,
.config_aneg = &genphy_config_aneg,
.read_status = &genphy_read_status,
}, {
.phy_id = 0x001cc912,
.name = "RTL8211B Gigabit Ethernet",
.phy_id_mask = 0x001fffff,
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
.config_aneg = &genphy_config_aneg,
.read_status = &genphy_read_status,
.ack_interrupt = &rtl821x_ack_interrupt,
.config_intr = &rtl8211b_config_intr,
.read_mmd = &genphy_read_mmd_unsupported,
.write_mmd = &genphy_write_mmd_unsupported,
}, {
.phy_id = 0x001cc914,
.name = "RTL8211DN Gigabit Ethernet",
.phy_id_mask = 0x001fffff,
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
.ack_interrupt = rtl821x_ack_interrupt,
.config_intr = rtl8211e_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
}, {
.phy_id = 0x001cc915,
.name = "RTL8211E Gigabit Ethernet",
.phy_id_mask = 0x001fffff,
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
.config_aneg = &genphy_config_aneg,
.read_status = &genphy_read_status,
.ack_interrupt = &rtl821x_ack_interrupt,
.config_intr = &rtl8211e_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
}, {
.phy_id = 0x001cc916,
.name = "RTL8211F Gigabit Ethernet",
.phy_id_mask = 0x001fffff,
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
.config_aneg = &genphy_config_aneg,
.config_init = &rtl8211f_config_init,
.read_status = &genphy_read_status,
.ack_interrupt = &rtl8211f_ack_interrupt,
.config_intr = &rtl8211f_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
},
};
所以需要自己适配RTL9000。
1.配置设备树
ðernet {
status = "okay";
phy-handle = <&phy1>;
phy-mode = "rgmii-id";
phy-reset-gpios = <112>;
phy-reset-duration = <12>;
phy-reset-post-delay = <10>;
mdio {
#address-cells = <0x1>;
#size-cells = <0x0>;
phy1: phy@1 {
compatible = "RTL8211F Gigabit Ethernet","ethernet-phy-ieee802.3-c45";
reg = <0x1>;
};
};
};
2.明确phy id
根据寄存器值可以确定phy id是0x001ccb00
3.填充驱动
/*
* drivers/net/phy/realtek.c
*
* Driver for Realtek PHYs
*
* Author: Johnson Leung <r58129@freescale.com>
*
* Copyright (c) 2004 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/phy.h>
#include <linux/module.h>
#include <linux/delay.h>
#define RTL821x_PHYSR 0x11
#define RTL821x_PHYSR_DUPLEX 0x2000
#define RTL821x_PHYSR_SPEED 0xc000
#define RTL821x_INER 0x12
#define RTL821x_INER_INIT 0x6400
#define RTL821x_INSR 0x13
#define RTL8211E_INER_LINK_STATUS 0x400
#define RTL8211F_INER_LINK_STATUS 0x0010
#define RTL8211F_INSR 0x1d
#define RTL8211F_PAGE_SELECT 0x1f
#define RTL8211F_TX_DELAY 0x100
#define MIIM_RTL9000_SRAM_ADDR 0x1b
#define MIIM_RTL9000_SRAM_DATA 0x1c
#define MIIM_RTL9000_PAGE_SELECT 0x1f
/*
* For RTL9000 RGMII Control Registers
* special register
*/
#define MIIM_RTL9000_RGRXCR 0xd082
#define MIIM_RTL9000_TX_DELAY 0x10<<8
#define MIIM_RTL9000_PHY_STATUS 0x1a
#define MIIM_RTL9000_PHYSTAT_LINK 0x0004
#define MIIM_RTL9000_PHYSTAT_DUPLEX 0x0100
#define MIIM_RTL9000_PHYSTAT_SPEED 0x2040
unsigned int param_check[6*3]={0, 0x8017, 0xFB03, 0, 0x8092, 0x6000,
0, 0x80AF, 0x6000, 0, 0x807D, 0x4443, 0, 0x809A, 0x4443, 0, 0x81A3, 0x0F00};
MODULE_DESCRIPTION("Realtek PHY driver");
MODULE_AUTHOR("Johnson Leung");
MODULE_LICENSE("GPL");
static int rtl821x_ack_interrupt(struct phy_device *phydev)
{
int err;
err = phy_read(phydev, RTL821x_INSR);
return (err < 0) ? err : 0;
}
static int rtl8211f_ack_interrupt(struct phy_device *phydev)
{
int err;
phy_write(phydev, RTL8211F_PAGE_SELECT, 0xa43);
err = phy_read(phydev, RTL8211F_INSR);
/* restore to default page 0 */
phy_write(phydev, RTL8211F_PAGE_SELECT, 0x0);
return (err < 0) ? err : 0;
}
static int rtl8211b_config_intr(struct phy_device *phydev)
{
int err;
if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
err = phy_write(phydev, RTL821x_INER,
RTL821x_INER_INIT);
else
err = phy_write(phydev, RTL821x_INER, 0);
return err;
}
static int rtl8211e_config_intr(struct phy_device *phydev)
{
int err;
if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
err = phy_write(phydev, RTL821x_INER,
RTL8211E_INER_LINK_STATUS);
else
err = phy_write(phydev, RTL821x_INER, 0);
return err;
}
static int rtl8211f_config_intr(struct phy_device *phydev)
{
int err;
if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
err = phy_write(phydev, RTL821x_INER,
RTL8211F_INER_LINK_STATUS);
else
err = phy_write(phydev, RTL821x_INER, 0);
return err;
}
static int rtl8211f_config_init(struct phy_device *phydev)
{
int ret;
u16 reg;
ret = genphy_config_init(phydev);
if (ret < 0)
return ret;
phy_write(phydev, RTL8211F_PAGE_SELECT, 0xd08);
reg = phy_read(phydev, 0x11);
/* enable TX-delay for rgmii-id and rgmii-txid, otherwise disable it */
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
reg |= RTL8211F_TX_DELAY;
else
reg &= ~RTL8211F_TX_DELAY;
phy_write(phydev, 0x11, reg);
/* restore to default page 0 */
phy_write(phydev, RTL8211F_PAGE_SELECT, 0x0);
return 0;
}
static void RTL9000Bxx_Initial_Config_Erratum1(struct phy_device *phydev)
{
int io_data = 0;
int timer = 2000;
//sample_code 04
//phy_write(phydev, 0x1F, 0x0A43);
phy_write(phydev, 0x1B, 0x8017);
phy_write(phydev, 0x1C, 0xFB03); // PHY patch request
phy_write(phydev, 0x1B, 0x8092);
phy_write(phydev, 0x1C, 0x6000);
phy_write(phydev, 0x1B, 0x80AF);
phy_write(phydev, 0x1C, 0x6000);
phy_write(phydev, 0x1B, 0x807D);
phy_write(phydev, 0x1C, 0x4443);
phy_write(phydev, 0x1B, 0x809A);
phy_write(phydev, 0x1C, 0x4443);
phy_write(phydev, 0x1B, 0x81A3);
phy_write(phydev, 0x1C, 0x0F00);
phy_write(phydev, 0x1F, 0x0A81);
phy_write(phydev, 0x12, 0x0004);
phy_write(phydev, 0x1B, 0x83C8);
phy_write(phydev, 0x1C, 0x0005);
phy_write(phydev, 0x1F, 0x0A5C);
phy_write(phydev, 0x12, 0x0003);
phy_write(phydev, 0x1B, 0xB820);
phy_write(phydev, 0x1C, 0x0010);
phy_write(phydev, 0x1B, 0xB830);
phy_write(phydev, 0x1C, 0x8000);
//phy_write(phydev, 0x1B, 0xB800);
do {
phy_write(phydev, 0x1B, 0xB800);
io_data = (phy_read(phydev, 0x1C)) & 0x40;
timer--;
if (timer == 0) {
timer = 2000;
break;
}
} while (io_data == 0);
phy_write(phydev, 0x1B, 0x8020);
phy_write(phydev, 0x1C, 0x3300);
phy_write(phydev, 0x1B, 0xB82E);
phy_write(phydev, 0x1C, 0x0001);
phy_write(phydev, 0x1B, 0xB820);
phy_write(phydev, 0x1C, 0x0290);
phy_write(phydev, 0x1B, 0xA012);
phy_write(phydev, 0x1C, 0x0000);
phy_write(phydev, 0x1B, 0xA014);
phy_write(phydev, 0x1C, 0x401C);
phy_write(phydev, 0x1C, 0xA610);
phy_write(phydev, 0x1C, 0x8520);
phy_write(phydev, 0x1C, 0xA510);
phy_write(phydev, 0x1C, 0x21F4);
phy_write(phydev, 0x1B, 0xA01A);
phy_write(phydev, 0x1C, 0x0000);
phy_write(phydev, 0x1B, 0xA000);
phy_write(phydev, 0x1C, 0x11EF);
phy_write(phydev, 0x1B, 0xB820);
phy_write(phydev, 0x1C, 0x0210);
phy_write(phydev, 0x1B, 0xB82E);
phy_write(phydev, 0x1C, 0x0000);
phy_write(phydev, 0x1B, 0x8020);
phy_write(phydev, 0x1C, 0x0000);
phy_write(phydev, 0x1B, 0xB820);
phy_write(phydev, 0x1C, 0x0000);
//phy_write(phydev, 0x1B, 0xB800);
do {
phy_write(phydev, 0x1B, 0xB800);
io_data = (phy_read(phydev, 0x1C)) & 0x40;
timer--;
if (timer == 0) {
break;
}
} while (io_data != 0);
}
static int RTL9000Bxx_init(struct phy_device *phydev)
{
int mdio_data = 0;
int ret;
int set_master = 0;
int power_section = 5;
unsigned int mdio_data_chk = 0;
unsigned int page;
unsigned int reg, i;
/* check PHY accessible */
while (mdio_data != 0x0003) {
phy_write(phydev, 0x1F, 0x0A42);
mdio_data = phy_read(phydev, 0x10);
mdio_data = mdio_data & 0x0007;
}
/* hardware reset */
// phy_write(phydev, 0x1B, 0xDD00);
// mdio_data = phy_read(phydev, 0x1C);
// phy_write(phydev, 0x1C, (mdio_data | 0x0020));
phy_write(phydev, 0x1F, 0x0A42);//change page to default value
//check PHY accessible
mdio_data = 0;
while (mdio_data != 0x0003) {
mdio_data = phy_read(phydev, 0x10);
mdio_data = mdio_data & 0x0007;
}
/* init parameter */
RTL9000Bxx_Initial_Config_Erratum1(phydev);
/* check init success */
for(i = 0; i < 6*3; i = i+3) {
page = param_check[i];
mdio_data_chk = param_check[i+2];
reg = param_check[i+1];
if(page == 0) {
phy_write(phydev, 0x1B, reg);
mdio_data = phy_read(phydev, 0x1C);
} else {
phy_write(phydev, 0x1F, reg);
mdio_data = phy_read(phydev, reg);
}
if(mdio_data_chk!= mdio_data) {
printk(("%dth param error page=0x%04X reg=0x%04X data=0x%04X\n",
i/3, page, reg, mdio_data));
printk("phyInit error\n");
}
}
printk("phy Init success\n");
/* I/O Power Sllection */
phy_write(phydev, 0x1F, 0x0A4C);//change page to default value
mdio_data = phy_read(phydev, 0x12);
mdio_data &= 0xC7FF;
mdio_data |= 4 << 11;
phy_write(phydev, 0x12, mdio_data);
/***************************here is optional **********************/
/* role setting */
if (set_master == 1) {
// Master or Slave mode depends on customer's application.
mdio_data = phy_read(phydev, 9);
phy_write(phydev, 9, (mdio_data | 0x0800)); // Set as Master
} else {
mdio_data = phy_read(phydev, 9);
phy_write(phydev, 9, (mdio_data & 0xF7FF)); // Set as Slave
}
//.....other applications
/*****************************************************************/
/* power supply selection */
switch (power_section) {
case 0://external 3.3V
phy_write(phydev, 0x1F, 0x0A43); //change page to 0xa43
phy_write(phydev, 0x1B, 0xDC06);
phy_write(phydev, 0x1C, 0x0DF8);
break;
case 1://internal LDO 2.5V
phy_write(phydev, 0x1F, 0x0A43);
phy_write(phydev, 0x1B, 0xDC06);
phy_write(phydev, 0x1C, 0x5DF9);
break;
case 2://external 2.5V
phy_write(phydev, 0x1F, 0x0A43);
phy_write(phydev, 0x1B, 0xDC06);
phy_write(phydev, 0x1C, 0xDDF9);
break;
case 3://internal LDO 1.8v
phy_write(phydev, 0x1F, 0x0A43);
phy_write(phydev, 0x1B, 0xDC06);
phy_write(phydev, 0x1C, 0xADFA);
break;
case 4://external 1.8v
phy_write(phydev, 0x1F, 0x0A43);
phy_write(phydev, 0x1B, 0xDC06);
phy_write(phydev, 0x1C, 0xEDFA);
break;
case 5://RTL9000BRG
//phy_write(phydev, 0x1F, 0x0A43);
phy_write(phydev, 0x1B, 0xD414);
ret = (phy_read(phydev, 0x1C)) | 0x1010;
phy_write(phydev, 0x1C, ret);
phy_write(phydev, 0x1B, 0xD416);
ret = (phy_read(phydev, 0x1C)) | 0x1010;
phy_write(phydev, 0x1C, ret);
phy_write(phydev, 0x1B, 0xD418);
ret = (phy_read(phydev, 0x1C)) | 0x1000;
phy_write(phydev, 0x1C, ret);
phy_write(phydev, 0x1B, 0xD41A);
ret = (phy_read(phydev, 0x1C)) | 0x1000;
phy_write(phydev, 0x1C, ret);
phy_write(phydev, 0x1B, 0xD42E);
phy_write(phydev, 0x1C, 0x7E7E);
}
/* Settting delay */
phy_write(phydev, 0x1F, 0x0a43);
phy_write(phydev, 0x1B, 0xD082);
reg = phy_read(phydev, 0x1C);
/* enable TX-delay for rgmii-id and rgmii-txid, otherwise disable it */
if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
reg &= 0xFCF8;
reg |= 7; //RX delay
//reg |= 1 << 8; //TX delay
} else {
reg &= ~(0x10 << 8);
}
phy_write(phydev, 0x1B, 0xD082);
phy_write(phydev, 0x1C, reg);
phy_write(phydev, 0x1F, 0xa42);
//software reset
phy_write(phydev, 0, 0x8000); // PHY soft-reset
mdelay(30);
/* Check soft-reset complete */
mdio_data = 0;
mdio_data = phy_read(phydev, 0);
int num = 0;
while (mdio_data != 0x2100) {
mdio_data = phy_read(phydev, 0);
if(num == 9)
break;
++num;
}
return 0;
}
static int rtl9000_config_init(struct phy_device *phydev)
{
unsigned int reg;
phy_write(phydev, 0, 0x8000); // PHY soft-reset
mdelay(30);
RTL9000Bxx_init(phydev);
/* Set green LED for Link, yellow LED for Active */
// phy_write(phydev, MDIO_DEVAD_NONE,
// MIIM_RTL9000_PAGE_SELECT, 0xd04);
// phy_write(phydev, MDIO_DEVAD_NONE, 0x10, 0x617f);
// phy_write(phydev, MDIO_DEVAD_NONE,
// MIIM_RTL9000_PAGE_SELECT, 0x0);
phydev->autoneg = AUTONEG_DISABLE;
phydev->duplex = DUPLEX_FULL;
//genphy_config_aneg(phydev);
phy_write(phydev, MIIM_RTL9000_PAGE_SELECT, 0x0);
reg = phy_read(phydev, MII_BMCR);
if((reg & MIIM_RTL9000_PHYSTAT_SPEED) == 0x2000) {
printk("phy speed: %d\n", SPEED_100);
phydev->speed = SPEED_100;
} else {
printk("error speed : 0x%x\n", reg);
return -EINVAL;
}
return 0;
}
static int rtl9000_config_aneg(struct phy_device *phydev)
{
phydev->autoneg = AUTONEG_DISABLE;
phydev->duplex = DUPLEX_FULL;
return 0;
}
static int rtl9000_ack_interrupt(struct phy_device *phydev)
{
return 0;
}
static int rtl9000_config_intr(struct phy_device *phydev)
{
return 0;
}
static int rtl9000_suspend(struct phy_device *phydev)
{
return 0;
}
static int rtl9000_resume(struct phy_device *phydev)
{
return 0;
}
static int rtl9000_read_status(struct phy_device *phydev)
{
if(phydev){
phydev->duplex = DUPLEX_FULL;
phydev->speed = SPEED_100;
phydev->pause = 0;
phydev->asym_pause = 0;
phydev->link = 1;
} else {
printk(KERN_ERR "%s phydev NULL\n", __func__);
}
return 0;
}
static int rtl9000_aneg_done(struct phy_device *phydev)
{
return 0;
}
static int rtl9000_soft_reset(struct phy_device *phydev)
{
int mdio_data = 0;
phy_write(phydev, 0, 0x8000); // PHY soft-reset
mdelay(30);
mdio_data = phy_read(phydev, 0);
int num = 0;
while (mdio_data != 0x2100) {
mdio_data = phy_read(phydev, 0);
if(num == 9)
break;
++num;
}
return 0;
}
static struct phy_driver realtek_drvs[] = {
{
.phy_id = 0x00008201,
.name = "RTL8201CP Ethernet",
.phy_id_mask = 0x0000ffff,
.features = PHY_BASIC_FEATURES,
.flags = PHY_HAS_INTERRUPT,
.config_aneg = &genphy_config_aneg,
.read_status = &genphy_read_status,
}, {
.phy_id = 0x001cc912,
.name = "RTL8211B Gigabit Ethernet",
.phy_id_mask = 0x001fffff,
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
.config_aneg = &genphy_config_aneg,
.read_status = &genphy_read_status,
.ack_interrupt = &rtl821x_ack_interrupt,
.config_intr = &rtl8211b_config_intr,
.read_mmd = &genphy_read_mmd_unsupported,
.write_mmd = &genphy_write_mmd_unsupported,
}, {
.phy_id = 0x001cc914,
.name = "RTL8211DN Gigabit Ethernet",
.phy_id_mask = 0x001fffff,
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
.ack_interrupt = rtl821x_ack_interrupt,
.config_intr = rtl8211e_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
}, {
.phy_id = 0x001cc915,
.name = "RTL8211E Gigabit Ethernet",
.phy_id_mask = 0x001fffff,
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
.config_aneg = &genphy_config_aneg,
.read_status = &genphy_read_status,
.ack_interrupt = &rtl821x_ack_interrupt,
.config_intr = &rtl8211e_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
}, {
.phy_id = 0x001cc916,
.name = "RTL8211F Gigabit Ethernet",
.phy_id_mask = 0x001fffff,
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
.config_aneg = &genphy_config_aneg,
.config_init = &rtl8211f_config_init,
.read_status = &genphy_read_status,
.ack_interrupt = &rtl8211f_ack_interrupt,
.config_intr = &rtl8211f_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
}, {
.phy_id = 0x001ccb00,
.name = "RTL9000BRG Gigabit Ethernet",
.phy_id_mask = 0x001fffff,
.features = PHY_BASIC_FEATURES,
.flags = PHY_HAS_INTERRUPT,
.soft_reset = &rtl9000_soft_reset,
.config_init = &rtl9000_config_init,
.config_aneg = &rtl9000_config_aneg,
.read_status = &rtl9000_read_status,
.ack_interrupt = &rtl9000_ack_interrupt,
.config_intr = &rtl9000_config_intr,
.aneg_done = &rtl9000_aneg_done,
.suspend = genphy_suspend,
.resume = genphy_resume,
},
};
module_phy_driver(realtek_drvs);
static struct mdio_device_id __maybe_unused realtek_tbl[] = {
{ 0x001cc912, 0x001fffff },
{ 0x001cc914, 0x001fffff },
{ 0x001cc915, 0x001fffff },
{ 0x001cc916, 0x001fffff },
{ 0x001ccb00, 0x001fffff },
{ }
};
MODULE_DEVICE_TABLE(mdio, realtek_tbl);