mirror of
https://github.com/torvalds/linux.git
synced 2024-12-15 23:51:46 +00:00
be01da72b1
Rather than have each driver set the driver owner field, do it once in the core code. This will also help with later changes, when the device structure will move. Signed-off-by: Andrew Lunn <andrew@lunn.ch> Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
188 lines
4.6 KiB
C
188 lines
4.6 KiB
C
/*
|
|
* 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>
|
|
|
|
#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
|
|
|
|
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;
|
|
|
|
if (phydev->interface == PHY_INTERFACE_MODE_RGMII) {
|
|
/* enable TXDLY */
|
|
phy_write(phydev, RTL8211F_PAGE_SELECT, 0xd08);
|
|
reg = phy_read(phydev, 0x11);
|
|
reg |= RTL8211F_TX_DELAY;
|
|
phy_write(phydev, 0x11, reg);
|
|
/* restore to default page 0 */
|
|
phy_write(phydev, RTL8211F_PAGE_SELECT, 0x0);
|
|
}
|
|
|
|
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,
|
|
}, {
|
|
.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,
|
|
},
|
|
};
|
|
|
|
module_phy_driver(realtek_drvs);
|
|
|
|
static struct mdio_device_id __maybe_unused realtek_tbl[] = {
|
|
{ 0x001cc912, 0x001fffff },
|
|
{ 0x001cc914, 0x001fffff },
|
|
{ 0x001cc915, 0x001fffff },
|
|
{ 0x001cc916, 0x001fffff },
|
|
{ }
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(mdio, realtek_tbl);
|