162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * drivers/net/phy/rockchip.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Driver for ROCKCHIP Ethernet PHYs 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * David Wu <david.wu@rock-chips.com> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/ethtool.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/mii.h> 1662306a36Sopenharmony_ci#include <linux/netdevice.h> 1762306a36Sopenharmony_ci#include <linux/phy.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define INTERNAL_EPHY_ID 0x1234d400 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define MII_INTERNAL_CTRL_STATUS 17 2262306a36Sopenharmony_ci#define SMI_ADDR_TSTCNTL 20 2362306a36Sopenharmony_ci#define SMI_ADDR_TSTREAD1 21 2462306a36Sopenharmony_ci#define SMI_ADDR_TSTREAD2 22 2562306a36Sopenharmony_ci#define SMI_ADDR_TSTWRITE 23 2662306a36Sopenharmony_ci#define MII_SPECIAL_CONTROL_STATUS 31 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define MII_AUTO_MDIX_EN BIT(7) 2962306a36Sopenharmony_ci#define MII_MDIX_EN BIT(6) 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define MII_SPEED_10 BIT(2) 3262306a36Sopenharmony_ci#define MII_SPEED_100 BIT(3) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define TSTCNTL_RD (BIT(15) | BIT(10)) 3562306a36Sopenharmony_ci#define TSTCNTL_WR (BIT(14) | BIT(10)) 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define TSTMODE_ENABLE 0x400 3862306a36Sopenharmony_ci#define TSTMODE_DISABLE 0x0 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define WR_ADDR_A7CFG 0x18 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic int rockchip_init_tstmode(struct phy_device *phydev) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci int ret; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci /* Enable access to Analog and DSP register banks */ 4762306a36Sopenharmony_ci ret = phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_ENABLE); 4862306a36Sopenharmony_ci if (ret) 4962306a36Sopenharmony_ci return ret; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci ret = phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_DISABLE); 5262306a36Sopenharmony_ci if (ret) 5362306a36Sopenharmony_ci return ret; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci return phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_ENABLE); 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic int rockchip_close_tstmode(struct phy_device *phydev) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci /* Back to basic register bank */ 6162306a36Sopenharmony_ci return phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_DISABLE); 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic int rockchip_integrated_phy_analog_init(struct phy_device *phydev) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci int ret; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci ret = rockchip_init_tstmode(phydev); 6962306a36Sopenharmony_ci if (ret) 7062306a36Sopenharmony_ci return ret; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* 7362306a36Sopenharmony_ci * Adjust tx amplitude to make sginal better, 7462306a36Sopenharmony_ci * the default value is 0x8. 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_ci ret = phy_write(phydev, SMI_ADDR_TSTWRITE, 0xB); 7762306a36Sopenharmony_ci if (ret) 7862306a36Sopenharmony_ci return ret; 7962306a36Sopenharmony_ci ret = phy_write(phydev, SMI_ADDR_TSTCNTL, TSTCNTL_WR | WR_ADDR_A7CFG); 8062306a36Sopenharmony_ci if (ret) 8162306a36Sopenharmony_ci return ret; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return rockchip_close_tstmode(phydev); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic int rockchip_integrated_phy_config_init(struct phy_device *phydev) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci int val, ret; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* 9162306a36Sopenharmony_ci * The auto MIDX has linked problem on some board, 9262306a36Sopenharmony_ci * workround to disable auto MDIX. 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_ci val = phy_read(phydev, MII_INTERNAL_CTRL_STATUS); 9562306a36Sopenharmony_ci if (val < 0) 9662306a36Sopenharmony_ci return val; 9762306a36Sopenharmony_ci val &= ~MII_AUTO_MDIX_EN; 9862306a36Sopenharmony_ci ret = phy_write(phydev, MII_INTERNAL_CTRL_STATUS, val); 9962306a36Sopenharmony_ci if (ret) 10062306a36Sopenharmony_ci return ret; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci return rockchip_integrated_phy_analog_init(phydev); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic void rockchip_link_change_notify(struct phy_device *phydev) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci /* 10862306a36Sopenharmony_ci * If mode switch happens from 10BT to 100BT, all DSP/AFE 10962306a36Sopenharmony_ci * registers are set to default values. So any AFE/DSP 11062306a36Sopenharmony_ci * registers have to be re-initialized in this case. 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_ci if (phydev->state == PHY_RUNNING && phydev->speed == SPEED_100) { 11362306a36Sopenharmony_ci int ret = rockchip_integrated_phy_analog_init(phydev); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (ret) 11662306a36Sopenharmony_ci phydev_err(phydev, "rockchip_integrated_phy_analog_init err: %d.\n", 11762306a36Sopenharmony_ci ret); 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic int rockchip_set_polarity(struct phy_device *phydev, int polarity) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci int reg, err, val; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* get the current settings */ 12662306a36Sopenharmony_ci reg = phy_read(phydev, MII_INTERNAL_CTRL_STATUS); 12762306a36Sopenharmony_ci if (reg < 0) 12862306a36Sopenharmony_ci return reg; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci reg &= ~MII_AUTO_MDIX_EN; 13162306a36Sopenharmony_ci val = reg; 13262306a36Sopenharmony_ci switch (polarity) { 13362306a36Sopenharmony_ci case ETH_TP_MDI: 13462306a36Sopenharmony_ci val &= ~MII_MDIX_EN; 13562306a36Sopenharmony_ci break; 13662306a36Sopenharmony_ci case ETH_TP_MDI_X: 13762306a36Sopenharmony_ci val |= MII_MDIX_EN; 13862306a36Sopenharmony_ci break; 13962306a36Sopenharmony_ci case ETH_TP_MDI_AUTO: 14062306a36Sopenharmony_ci case ETH_TP_MDI_INVALID: 14162306a36Sopenharmony_ci default: 14262306a36Sopenharmony_ci return 0; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (val != reg) { 14662306a36Sopenharmony_ci /* Set the new polarity value in the register */ 14762306a36Sopenharmony_ci err = phy_write(phydev, MII_INTERNAL_CTRL_STATUS, val); 14862306a36Sopenharmony_ci if (err) 14962306a36Sopenharmony_ci return err; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return 0; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic int rockchip_config_aneg(struct phy_device *phydev) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci int err; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci err = rockchip_set_polarity(phydev, phydev->mdix); 16062306a36Sopenharmony_ci if (err < 0) 16162306a36Sopenharmony_ci return err; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci return genphy_config_aneg(phydev); 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic int rockchip_phy_resume(struct phy_device *phydev) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci genphy_resume(phydev); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return rockchip_integrated_phy_config_init(phydev); 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic struct phy_driver rockchip_phy_driver[] = { 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci .phy_id = INTERNAL_EPHY_ID, 17662306a36Sopenharmony_ci .phy_id_mask = 0xfffffff0, 17762306a36Sopenharmony_ci .name = "Rockchip integrated EPHY", 17862306a36Sopenharmony_ci /* PHY_BASIC_FEATURES */ 17962306a36Sopenharmony_ci .flags = 0, 18062306a36Sopenharmony_ci .link_change_notify = rockchip_link_change_notify, 18162306a36Sopenharmony_ci .soft_reset = genphy_soft_reset, 18262306a36Sopenharmony_ci .config_init = rockchip_integrated_phy_config_init, 18362306a36Sopenharmony_ci .config_aneg = rockchip_config_aneg, 18462306a36Sopenharmony_ci .suspend = genphy_suspend, 18562306a36Sopenharmony_ci .resume = rockchip_phy_resume, 18662306a36Sopenharmony_ci}, 18762306a36Sopenharmony_ci}; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cimodule_phy_driver(rockchip_phy_driver); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic struct mdio_device_id __maybe_unused rockchip_phy_tbl[] = { 19262306a36Sopenharmony_ci { INTERNAL_EPHY_ID, 0xfffffff0 }, 19362306a36Sopenharmony_ci { } 19462306a36Sopenharmony_ci}; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(mdio, rockchip_phy_tbl); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ciMODULE_AUTHOR("David Wu <david.wu@rock-chips.com>"); 19962306a36Sopenharmony_ciMODULE_DESCRIPTION("Rockchip Ethernet PHY driver"); 20062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 201