18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * drivers/net/phy/national.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Driver for National Semiconductor PHYs 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Stuart Menefy <stuart.menefy@st.com> 88c2ecf20Sopenharmony_ci * Maintainer: Giuseppe Cavallaro <peppe.cavallaro@st.com> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Copyright (c) 2008 STMicroelectronics Limited 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/mii.h> 188c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 198c2ecf20Sopenharmony_ci#include <linux/phy.h> 208c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define DEBUG 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* DP83865 phy identifier values */ 258c2ecf20Sopenharmony_ci#define DP83865_PHY_ID 0x20005c7a 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define DP83865_INT_STATUS 0x14 288c2ecf20Sopenharmony_ci#define DP83865_INT_MASK 0x15 298c2ecf20Sopenharmony_ci#define DP83865_INT_CLEAR 0x17 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define DP83865_INT_REMOTE_FAULT 0x0008 328c2ecf20Sopenharmony_ci#define DP83865_INT_ANE_COMPLETED 0x0010 338c2ecf20Sopenharmony_ci#define DP83865_INT_LINK_CHANGE 0xe000 348c2ecf20Sopenharmony_ci#define DP83865_INT_MASK_DEFAULT (DP83865_INT_REMOTE_FAULT | \ 358c2ecf20Sopenharmony_ci DP83865_INT_ANE_COMPLETED | \ 368c2ecf20Sopenharmony_ci DP83865_INT_LINK_CHANGE) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* Advanced proprietary configuration */ 398c2ecf20Sopenharmony_ci#define NS_EXP_MEM_CTL 0x16 408c2ecf20Sopenharmony_ci#define NS_EXP_MEM_DATA 0x1d 418c2ecf20Sopenharmony_ci#define NS_EXP_MEM_ADD 0x1e 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define LED_CTRL_REG 0x13 448c2ecf20Sopenharmony_ci#define AN_FALLBACK_AN 0x0001 458c2ecf20Sopenharmony_ci#define AN_FALLBACK_CRC 0x0002 468c2ecf20Sopenharmony_ci#define AN_FALLBACK_IE 0x0004 478c2ecf20Sopenharmony_ci#define ALL_FALLBACK_ON (AN_FALLBACK_AN | AN_FALLBACK_CRC | AN_FALLBACK_IE) 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cienum hdx_loopback { 508c2ecf20Sopenharmony_ci hdx_loopback_on = 0, 518c2ecf20Sopenharmony_ci hdx_loopback_off = 1, 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic u8 ns_exp_read(struct phy_device *phydev, u16 reg) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci phy_write(phydev, NS_EXP_MEM_ADD, reg); 578c2ecf20Sopenharmony_ci return phy_read(phydev, NS_EXP_MEM_DATA); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic void ns_exp_write(struct phy_device *phydev, u16 reg, u8 data) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci phy_write(phydev, NS_EXP_MEM_ADD, reg); 638c2ecf20Sopenharmony_ci phy_write(phydev, NS_EXP_MEM_DATA, data); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int ns_config_intr(struct phy_device *phydev) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci int err; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 718c2ecf20Sopenharmony_ci err = phy_write(phydev, DP83865_INT_MASK, 728c2ecf20Sopenharmony_ci DP83865_INT_MASK_DEFAULT); 738c2ecf20Sopenharmony_ci else 748c2ecf20Sopenharmony_ci err = phy_write(phydev, DP83865_INT_MASK, 0); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci return err; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic int ns_ack_interrupt(struct phy_device *phydev) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci int ret = phy_read(phydev, DP83865_INT_STATUS); 828c2ecf20Sopenharmony_ci if (ret < 0) 838c2ecf20Sopenharmony_ci return ret; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* Clear the interrupt status bit by writing a “1” 868c2ecf20Sopenharmony_ci * to the corresponding bit in INT_CLEAR (2:0 are reserved) */ 878c2ecf20Sopenharmony_ci ret = phy_write(phydev, DP83865_INT_CLEAR, ret & ~0x7); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci return ret; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic void ns_giga_speed_fallback(struct phy_device *phydev, int mode) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci int bmcr = phy_read(phydev, MII_BMCR); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci phy_write(phydev, MII_BMCR, (bmcr | BMCR_PDOWN)); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* Enable 8 bit expended memory read/write (no auto increment) */ 998c2ecf20Sopenharmony_ci phy_write(phydev, NS_EXP_MEM_CTL, 0); 1008c2ecf20Sopenharmony_ci phy_write(phydev, NS_EXP_MEM_ADD, 0x1C0); 1018c2ecf20Sopenharmony_ci phy_write(phydev, NS_EXP_MEM_DATA, 0x0008); 1028c2ecf20Sopenharmony_ci phy_write(phydev, MII_BMCR, (bmcr & ~BMCR_PDOWN)); 1038c2ecf20Sopenharmony_ci phy_write(phydev, LED_CTRL_REG, mode); 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic void ns_10_base_t_hdx_loopack(struct phy_device *phydev, int disable) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci u16 lb_dis = BIT(1); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (disable) 1118c2ecf20Sopenharmony_ci ns_exp_write(phydev, 0x1c0, 1128c2ecf20Sopenharmony_ci ns_exp_read(phydev, 0x1c0) | lb_dis); 1138c2ecf20Sopenharmony_ci else 1148c2ecf20Sopenharmony_ci ns_exp_write(phydev, 0x1c0, 1158c2ecf20Sopenharmony_ci ns_exp_read(phydev, 0x1c0) & ~lb_dis); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci pr_debug("10BASE-T HDX loopback %s\n", 1188c2ecf20Sopenharmony_ci (ns_exp_read(phydev, 0x1c0) & lb_dis) ? "off" : "on"); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int ns_config_init(struct phy_device *phydev) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci ns_giga_speed_fallback(phydev, ALL_FALLBACK_ON); 1248c2ecf20Sopenharmony_ci /* In the latest MAC or switches design, the 10 Mbps loopback 1258c2ecf20Sopenharmony_ci is desired to be turned off. */ 1268c2ecf20Sopenharmony_ci ns_10_base_t_hdx_loopack(phydev, hdx_loopback_off); 1278c2ecf20Sopenharmony_ci return ns_ack_interrupt(phydev); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic struct phy_driver dp83865_driver[] = { { 1318c2ecf20Sopenharmony_ci .phy_id = DP83865_PHY_ID, 1328c2ecf20Sopenharmony_ci .phy_id_mask = 0xfffffff0, 1338c2ecf20Sopenharmony_ci .name = "NatSemi DP83865", 1348c2ecf20Sopenharmony_ci /* PHY_GBIT_FEATURES */ 1358c2ecf20Sopenharmony_ci .config_init = ns_config_init, 1368c2ecf20Sopenharmony_ci .ack_interrupt = ns_ack_interrupt, 1378c2ecf20Sopenharmony_ci .config_intr = ns_config_intr, 1388c2ecf20Sopenharmony_ci} }; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cimodule_phy_driver(dp83865_driver); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NatSemi PHY driver"); 1438c2ecf20Sopenharmony_ciMODULE_AUTHOR("Stuart Menefy"); 1448c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic struct mdio_device_id __maybe_unused ns_tbl[] = { 1478c2ecf20Sopenharmony_ci { DP83865_PHY_ID, 0xfffffff0 }, 1488c2ecf20Sopenharmony_ci { } 1498c2ecf20Sopenharmony_ci}; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(mdio, ns_tbl); 152