1 // SPDX-License-Identifier: GPL-2.0+ 2 /** 3 * 4 * Driver for ROCKCHIP RK630 Ethernet PHYs 5 * 6 * Copyright (c) 2020, Fuzhou Rockchip Electronics Co., Ltd 7 * 8 * David Wu <david.wu@rock-chips.com> 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 2 of the License, or 13 * (at your option) any later version. 14 * 15 */ 16 17 #include <config.h> 18 #include <common.h> 19 #include <linux/bitops.h> 20 #include <phy.h> 21 22 #define RK630_PHY_ID 0x00441400 23 24 /* PAGE 0 */ 25 #define REG_INTERRUPT_STATUS 0X10 26 #define REG_INTERRUPT_MASK 0X11 27 #define REG_GLOBAL_CONFIGURATION 0X13 28 #define REG_MAC_ADDRESS0 0x16 29 #define REG_MAC_ADDRESS1 0x17 30 #define REG_MAC_ADDRESS2 0x18 31 32 #define REG_PAGE_SEL 0x1F 33 34 /* PAGE 1 */ 35 #define REG_PAGE1_APS_CTRL 0x12 36 #define REG_PAGE1_UAPS_CONFIGURE 0X13 37 #define REG_PAGE1_EEE_CONFIGURE 0x17 38 39 /* PAGE 2 */ 40 #define REG_PAGE2_AFE_CTRL 0x18 41 42 /* PAGE 6 */ 43 #define REG_PAGE6_ADC_ANONTROL 0x10 44 #define REG_PAGE6_AFE_RX_CTRL 0x13 45 #define REG_PAGE6_AFE_TX_CTRL 0x14 46 #define REG_PAGE6_AFE_DRIVER2 0x15 47 48 /* PAGE 8 */ 49 #define REG_PAGE8_AFE_CTRL 0x18 50 51 static void rk630_phy_ieee_set(struct phy_device *phydev, bool enable) 52 { 53 u32 value; 54 55 /* Switch to page 1 */ 56 phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE_SEL, 0x0100); 57 value = phy_read(phydev, MDIO_DEVAD_NONE, REG_PAGE1_EEE_CONFIGURE); 58 if (enable) 59 value |= BIT(3); 60 else 61 value &= ~BIT(3); 62 phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE1_EEE_CONFIGURE, value); 63 /* Switch to page 0 */ 64 phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE_SEL, 0x0000); 65 } 66 67 static void rk630_phy_set_uaps(struct phy_device *phydev) 68 { 69 u32 value; 70 71 /* Switch to page 1 */ 72 phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE_SEL, 0x0100); 73 value = phy_read(phydev, MDIO_DEVAD_NONE, REG_PAGE1_UAPS_CONFIGURE); 74 value |= BIT(15); 75 phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE1_UAPS_CONFIGURE, value); 76 /* Switch to page 0 */ 77 phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE_SEL, 0x0000); 78 } 79 80 static int rk630_phy_config_init(struct phy_device *phydev) 81 { 82 u32 value; 83 84 phy_write(phydev, 0, MDIO_DEVAD_NONE, 85 phy_read(phydev, MDIO_DEVAD_NONE, 0) & ~BIT(13)); 86 87 /* Switch to page 1 */ 88 phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE_SEL, 0x0100); 89 /* Disable APS */ 90 phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE1_APS_CTRL, 0x4824); 91 /* Switch to page 2 */ 92 phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE_SEL, 0x0200); 93 /* PHYAFE TRX optimization */ 94 phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE2_AFE_CTRL, 0x0000); 95 /* Switch to page 6 */ 96 phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE_SEL, 0x0600); 97 /* PHYAFE TX optimization */ 98 phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE6_AFE_TX_CTRL, 0x708f); 99 /* PHYAFE RX optimization */ 100 phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE6_AFE_RX_CTRL, 0xf000); 101 phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE6_AFE_DRIVER2, 0x1530); 102 103 /* Switch to page 8 */ 104 phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE_SEL, 0x0800); 105 /* PHYAFE TRX optimization */ 106 phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE8_AFE_CTRL, 0x00bc); 107 108 /* Adjust tx level, bypass */ 109 value = phy_read(phydev, MDIO_DEVAD_NONE, 0x1d); 110 value |= BIT(11); 111 phy_write(phydev, 0x1d, MDIO_DEVAD_NONE, value); 112 /* switch to page6 */ 113 phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE_SEL, 0x0600); 114 /* Enable tx level control */ 115 value = phy_read(phydev, MDIO_DEVAD_NONE, REG_PAGE6_ADC_ANONTROL); 116 value &= ~BIT(6); 117 phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE6_ADC_ANONTROL, value); 118 /* Set tx level */ 119 value = phy_read(phydev, MDIO_DEVAD_NONE, REG_PAGE6_AFE_DRIVER2); 120 value &= ~GENMASK(15, 8); 121 value |= 0x121a; 122 phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE6_AFE_DRIVER2, value); 123 124 /* Switch to page 0 */ 125 phy_write(phydev, MDIO_DEVAD_NONE, REG_PAGE_SEL, 0x0000); 126 127 rk630_phy_ieee_set(phydev, true); 128 /* 129 * Ultra Auto-Power Saving Mode (UAPS) is designed to 130 * save power when cable is not plugged into PHY. 131 */ 132 rk630_phy_set_uaps(phydev); 133 134 return 0; 135 } 136 137 static int rk630_phy_probe(struct phy_device *phydev) 138 { 139 return 0; 140 } 141 142 static struct phy_driver RK630_driver = { 143 .name = "Rockchip RK630", 144 .uid = RK630_PHY_ID, 145 .mask = 0xffffff, 146 .features = PHY_BASIC_FEATURES, 147 .probe = &rk630_phy_probe, 148 .config = &rk630_phy_config_init, 149 .shutdown = &genphy_shutdown, 150 }; 151 152 int phy_rk630_init(void) 153 { 154 phy_register(&RK630_driver); 155 156 return 0; 157 } 158