18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * drivers/net/phy/ste10Xp.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Driver for STMicroelectronics STe10Xp PHYs 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Copyright (c) 2008 STMicroelectronics Limited 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/sched.h> 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 178c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 188c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 198c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 208c2ecf20Sopenharmony_ci#include <linux/mii.h> 218c2ecf20Sopenharmony_ci#include <linux/phy.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define MII_XCIIS 0x11 /* Configuration Info IRQ & Status Reg */ 248c2ecf20Sopenharmony_ci#define MII_XIE 0x12 /* Interrupt Enable Register */ 258c2ecf20Sopenharmony_ci#define MII_XIE_DEFAULT_MASK 0x0070 /* ANE complete, Remote Fault, Link Down */ 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define STE101P_PHY_ID 0x00061c50 288c2ecf20Sopenharmony_ci#define STE100P_PHY_ID 0x1c040011 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic int ste10Xp_config_init(struct phy_device *phydev) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci int value, err; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci /* Software Reset PHY */ 358c2ecf20Sopenharmony_ci value = phy_read(phydev, MII_BMCR); 368c2ecf20Sopenharmony_ci if (value < 0) 378c2ecf20Sopenharmony_ci return value; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci value |= BMCR_RESET; 408c2ecf20Sopenharmony_ci err = phy_write(phydev, MII_BMCR, value); 418c2ecf20Sopenharmony_ci if (err < 0) 428c2ecf20Sopenharmony_ci return err; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci do { 458c2ecf20Sopenharmony_ci value = phy_read(phydev, MII_BMCR); 468c2ecf20Sopenharmony_ci } while (value & BMCR_RESET); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci return 0; 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic int ste10Xp_config_intr(struct phy_device *phydev) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci int err, value; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 568c2ecf20Sopenharmony_ci /* Enable all STe101P interrupts (PR12) */ 578c2ecf20Sopenharmony_ci err = phy_write(phydev, MII_XIE, MII_XIE_DEFAULT_MASK); 588c2ecf20Sopenharmony_ci /* clear any pending interrupts */ 598c2ecf20Sopenharmony_ci if (err == 0) { 608c2ecf20Sopenharmony_ci value = phy_read(phydev, MII_XCIIS); 618c2ecf20Sopenharmony_ci if (value < 0) 628c2ecf20Sopenharmony_ci err = value; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci } else 658c2ecf20Sopenharmony_ci err = phy_write(phydev, MII_XIE, 0); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci return err; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic int ste10Xp_ack_interrupt(struct phy_device *phydev) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci int err = phy_read(phydev, MII_XCIIS); 738c2ecf20Sopenharmony_ci if (err < 0) 748c2ecf20Sopenharmony_ci return err; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci return 0; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic struct phy_driver ste10xp_pdriver[] = { 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci .phy_id = STE101P_PHY_ID, 828c2ecf20Sopenharmony_ci .phy_id_mask = 0xfffffff0, 838c2ecf20Sopenharmony_ci .name = "STe101p", 848c2ecf20Sopenharmony_ci /* PHY_BASIC_FEATURES */ 858c2ecf20Sopenharmony_ci .config_init = ste10Xp_config_init, 868c2ecf20Sopenharmony_ci .ack_interrupt = ste10Xp_ack_interrupt, 878c2ecf20Sopenharmony_ci .config_intr = ste10Xp_config_intr, 888c2ecf20Sopenharmony_ci .suspend = genphy_suspend, 898c2ecf20Sopenharmony_ci .resume = genphy_resume, 908c2ecf20Sopenharmony_ci}, { 918c2ecf20Sopenharmony_ci .phy_id = STE100P_PHY_ID, 928c2ecf20Sopenharmony_ci .phy_id_mask = 0xffffffff, 938c2ecf20Sopenharmony_ci .name = "STe100p", 948c2ecf20Sopenharmony_ci /* PHY_BASIC_FEATURES */ 958c2ecf20Sopenharmony_ci .config_init = ste10Xp_config_init, 968c2ecf20Sopenharmony_ci .ack_interrupt = ste10Xp_ack_interrupt, 978c2ecf20Sopenharmony_ci .config_intr = ste10Xp_config_intr, 988c2ecf20Sopenharmony_ci .suspend = genphy_suspend, 998c2ecf20Sopenharmony_ci .resume = genphy_resume, 1008c2ecf20Sopenharmony_ci} }; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cimodule_phy_driver(ste10xp_pdriver); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic struct mdio_device_id __maybe_unused ste10Xp_tbl[] = { 1058c2ecf20Sopenharmony_ci { STE101P_PHY_ID, 0xfffffff0 }, 1068c2ecf20Sopenharmony_ci { STE100P_PHY_ID, 0xffffffff }, 1078c2ecf20Sopenharmony_ci { } 1088c2ecf20Sopenharmony_ci}; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(mdio, ste10Xp_tbl); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("STMicroelectronics STe10Xp PHY driver"); 1138c2ecf20Sopenharmony_ciMODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>"); 1148c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 115