cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

rockchip.c (4523B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * drivers/net/phy/rockchip.c
      4 *
      5 * Driver for ROCKCHIP Ethernet PHYs
      6 *
      7 * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd
      8 *
      9 * David Wu <david.wu@rock-chips.com>
     10 */
     11
     12#include <linux/ethtool.h>
     13#include <linux/kernel.h>
     14#include <linux/module.h>
     15#include <linux/mii.h>
     16#include <linux/netdevice.h>
     17#include <linux/phy.h>
     18
     19#define INTERNAL_EPHY_ID			0x1234d400
     20
     21#define MII_INTERNAL_CTRL_STATUS		17
     22#define SMI_ADDR_TSTCNTL			20
     23#define SMI_ADDR_TSTREAD1			21
     24#define SMI_ADDR_TSTREAD2			22
     25#define SMI_ADDR_TSTWRITE			23
     26#define MII_SPECIAL_CONTROL_STATUS		31
     27
     28#define MII_AUTO_MDIX_EN			BIT(7)
     29#define MII_MDIX_EN				BIT(6)
     30
     31#define MII_SPEED_10				BIT(2)
     32#define MII_SPEED_100				BIT(3)
     33
     34#define TSTCNTL_RD				(BIT(15) | BIT(10))
     35#define TSTCNTL_WR				(BIT(14) | BIT(10))
     36
     37#define TSTMODE_ENABLE				0x400
     38#define TSTMODE_DISABLE				0x0
     39
     40#define WR_ADDR_A7CFG				0x18
     41
     42static int rockchip_init_tstmode(struct phy_device *phydev)
     43{
     44	int ret;
     45
     46	/* Enable access to Analog and DSP register banks */
     47	ret = phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_ENABLE);
     48	if (ret)
     49		return ret;
     50
     51	ret = phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_DISABLE);
     52	if (ret)
     53		return ret;
     54
     55	return phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_ENABLE);
     56}
     57
     58static int rockchip_close_tstmode(struct phy_device *phydev)
     59{
     60	/* Back to basic register bank */
     61	return phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_DISABLE);
     62}
     63
     64static int rockchip_integrated_phy_analog_init(struct phy_device *phydev)
     65{
     66	int ret;
     67
     68	ret = rockchip_init_tstmode(phydev);
     69	if (ret)
     70		return ret;
     71
     72	/*
     73	 * Adjust tx amplitude to make sginal better,
     74	 * the default value is 0x8.
     75	 */
     76	ret = phy_write(phydev, SMI_ADDR_TSTWRITE, 0xB);
     77	if (ret)
     78		return ret;
     79	ret = phy_write(phydev, SMI_ADDR_TSTCNTL, TSTCNTL_WR | WR_ADDR_A7CFG);
     80	if (ret)
     81		return ret;
     82
     83	return rockchip_close_tstmode(phydev);
     84}
     85
     86static int rockchip_integrated_phy_config_init(struct phy_device *phydev)
     87{
     88	int val, ret;
     89
     90	/*
     91	 * The auto MIDX has linked problem on some board,
     92	 * workround to disable auto MDIX.
     93	 */
     94	val = phy_read(phydev, MII_INTERNAL_CTRL_STATUS);
     95	if (val < 0)
     96		return val;
     97	val &= ~MII_AUTO_MDIX_EN;
     98	ret = phy_write(phydev, MII_INTERNAL_CTRL_STATUS, val);
     99	if (ret)
    100		return ret;
    101
    102	return rockchip_integrated_phy_analog_init(phydev);
    103}
    104
    105static void rockchip_link_change_notify(struct phy_device *phydev)
    106{
    107	/*
    108	 * If mode switch happens from 10BT to 100BT, all DSP/AFE
    109	 * registers are set to default values. So any AFE/DSP
    110	 * registers have to be re-initialized in this case.
    111	 */
    112	if (phydev->state == PHY_RUNNING && phydev->speed == SPEED_100) {
    113		int ret = rockchip_integrated_phy_analog_init(phydev);
    114
    115		if (ret)
    116			phydev_err(phydev, "rockchip_integrated_phy_analog_init err: %d.\n",
    117				   ret);
    118	}
    119}
    120
    121static int rockchip_set_polarity(struct phy_device *phydev, int polarity)
    122{
    123	int reg, err, val;
    124
    125	/* get the current settings */
    126	reg = phy_read(phydev, MII_INTERNAL_CTRL_STATUS);
    127	if (reg < 0)
    128		return reg;
    129
    130	reg &= ~MII_AUTO_MDIX_EN;
    131	val = reg;
    132	switch (polarity) {
    133	case ETH_TP_MDI:
    134		val &= ~MII_MDIX_EN;
    135		break;
    136	case ETH_TP_MDI_X:
    137		val |= MII_MDIX_EN;
    138		break;
    139	case ETH_TP_MDI_AUTO:
    140	case ETH_TP_MDI_INVALID:
    141	default:
    142		return 0;
    143	}
    144
    145	if (val != reg) {
    146		/* Set the new polarity value in the register */
    147		err = phy_write(phydev, MII_INTERNAL_CTRL_STATUS, val);
    148		if (err)
    149			return err;
    150	}
    151
    152	return 0;
    153}
    154
    155static int rockchip_config_aneg(struct phy_device *phydev)
    156{
    157	int err;
    158
    159	err = rockchip_set_polarity(phydev, phydev->mdix);
    160	if (err < 0)
    161		return err;
    162
    163	return genphy_config_aneg(phydev);
    164}
    165
    166static int rockchip_phy_resume(struct phy_device *phydev)
    167{
    168	genphy_resume(phydev);
    169
    170	return rockchip_integrated_phy_config_init(phydev);
    171}
    172
    173static struct phy_driver rockchip_phy_driver[] = {
    174{
    175	.phy_id			= INTERNAL_EPHY_ID,
    176	.phy_id_mask		= 0xfffffff0,
    177	.name			= "Rockchip integrated EPHY",
    178	/* PHY_BASIC_FEATURES */
    179	.flags			= 0,
    180	.link_change_notify	= rockchip_link_change_notify,
    181	.soft_reset		= genphy_soft_reset,
    182	.config_init		= rockchip_integrated_phy_config_init,
    183	.config_aneg		= rockchip_config_aneg,
    184	.suspend		= genphy_suspend,
    185	.resume			= rockchip_phy_resume,
    186},
    187};
    188
    189module_phy_driver(rockchip_phy_driver);
    190
    191static struct mdio_device_id __maybe_unused rockchip_phy_tbl[] = {
    192	{ INTERNAL_EPHY_ID, 0xfffffff0 },
    193	{ }
    194};
    195
    196MODULE_DEVICE_TABLE(mdio, rockchip_phy_tbl);
    197
    198MODULE_AUTHOR("David Wu <david.wu@rock-chips.com>");
    199MODULE_DESCRIPTION("Rockchip Ethernet PHY driver");
    200MODULE_LICENSE("GPL");