^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0+
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * drivers/net/phy/national.c
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Driver for National Semiconductor PHYs
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Author: Stuart Menefy <stuart.menefy@st.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * Maintainer: Giuseppe Cavallaro <peppe.cavallaro@st.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) * Copyright (c) 2008 STMicroelectronics Limited
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/mii.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/ethtool.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/phy.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <linux/netdevice.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #define DEBUG
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) /* DP83865 phy identifier values */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #define DP83865_PHY_ID 0x20005c7a
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) #define DP83865_INT_STATUS 0x14
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) #define DP83865_INT_MASK 0x15
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) #define DP83865_INT_CLEAR 0x17
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) #define DP83865_INT_REMOTE_FAULT 0x0008
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) #define DP83865_INT_ANE_COMPLETED 0x0010
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) #define DP83865_INT_LINK_CHANGE 0xe000
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) #define DP83865_INT_MASK_DEFAULT (DP83865_INT_REMOTE_FAULT | \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) DP83865_INT_ANE_COMPLETED | \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) DP83865_INT_LINK_CHANGE)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) /* Advanced proprietary configuration */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) #define NS_EXP_MEM_CTL 0x16
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) #define NS_EXP_MEM_DATA 0x1d
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) #define NS_EXP_MEM_ADD 0x1e
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) #define LED_CTRL_REG 0x13
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) #define AN_FALLBACK_AN 0x0001
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) #define AN_FALLBACK_CRC 0x0002
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) #define AN_FALLBACK_IE 0x0004
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) #define ALL_FALLBACK_ON (AN_FALLBACK_AN | AN_FALLBACK_CRC | AN_FALLBACK_IE)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) enum hdx_loopback {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) hdx_loopback_on = 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) hdx_loopback_off = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) static u8 ns_exp_read(struct phy_device *phydev, u16 reg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) phy_write(phydev, NS_EXP_MEM_ADD, reg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) return phy_read(phydev, NS_EXP_MEM_DATA);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) static void ns_exp_write(struct phy_device *phydev, u16 reg, u8 data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) phy_write(phydev, NS_EXP_MEM_ADD, reg);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) phy_write(phydev, NS_EXP_MEM_DATA, data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) static int ns_config_intr(struct phy_device *phydev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) int err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) err = phy_write(phydev, DP83865_INT_MASK,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) DP83865_INT_MASK_DEFAULT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) err = phy_write(phydev, DP83865_INT_MASK, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) static int ns_ack_interrupt(struct phy_device *phydev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) int ret = phy_read(phydev, DP83865_INT_STATUS);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) /* Clear the interrupt status bit by writing a “1”
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) * to the corresponding bit in INT_CLEAR (2:0 are reserved) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) ret = phy_write(phydev, DP83865_INT_CLEAR, ret & ~0x7);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) static void ns_giga_speed_fallback(struct phy_device *phydev, int mode)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) int bmcr = phy_read(phydev, MII_BMCR);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) phy_write(phydev, MII_BMCR, (bmcr | BMCR_PDOWN));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) /* Enable 8 bit expended memory read/write (no auto increment) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) phy_write(phydev, NS_EXP_MEM_CTL, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) phy_write(phydev, NS_EXP_MEM_ADD, 0x1C0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) phy_write(phydev, NS_EXP_MEM_DATA, 0x0008);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) phy_write(phydev, MII_BMCR, (bmcr & ~BMCR_PDOWN));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) phy_write(phydev, LED_CTRL_REG, mode);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) static void ns_10_base_t_hdx_loopack(struct phy_device *phydev, int disable)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) u16 lb_dis = BIT(1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) if (disable)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) ns_exp_write(phydev, 0x1c0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) ns_exp_read(phydev, 0x1c0) | lb_dis);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) ns_exp_write(phydev, 0x1c0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) ns_exp_read(phydev, 0x1c0) & ~lb_dis);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) pr_debug("10BASE-T HDX loopback %s\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) (ns_exp_read(phydev, 0x1c0) & lb_dis) ? "off" : "on");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) static int ns_config_init(struct phy_device *phydev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) ns_giga_speed_fallback(phydev, ALL_FALLBACK_ON);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) /* In the latest MAC or switches design, the 10 Mbps loopback
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) is desired to be turned off. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) ns_10_base_t_hdx_loopack(phydev, hdx_loopback_off);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) return ns_ack_interrupt(phydev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) static struct phy_driver dp83865_driver[] = { {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) .phy_id = DP83865_PHY_ID,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) .phy_id_mask = 0xfffffff0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) .name = "NatSemi DP83865",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) /* PHY_GBIT_FEATURES */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) .config_init = ns_config_init,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) .ack_interrupt = ns_ack_interrupt,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) .config_intr = ns_config_intr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) } };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) module_phy_driver(dp83865_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) MODULE_DESCRIPTION("NatSemi PHY driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) MODULE_AUTHOR("Stuart Menefy");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) MODULE_LICENSE("GPL");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) static struct mdio_device_id __maybe_unused ns_tbl[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) { DP83865_PHY_ID, 0xfffffff0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) { }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) MODULE_DEVICE_TABLE(mdio, ns_tbl);