1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Driver for AMD am79c PHYs 4 * 5 * Author: Heiko Schocher <hs@denx.de> 6 * 7 * Copyright (c) 2011 DENX Software Engineering GmbH 8 */ 9 #include <linux/kernel.h> 10 #include <linux/errno.h> 11 #include <linux/init.h> 12 #include <linux/module.h> 13 #include <linux/mii.h> 14 #include <linux/phy.h> 15 16 #define PHY_ID_AM79C874 0x0022561b 17 18 #define MII_AM79C_IR 17 /* Interrupt Status/Control Register */ 19 #define MII_AM79C_IR_EN_LINK 0x0400 /* IR enable Linkstate */ 20 #define MII_AM79C_IR_EN_ANEG 0x0100 /* IR enable Aneg Complete */ 21 #define MII_AM79C_IR_IMASK_INIT (MII_AM79C_IR_EN_LINK | MII_AM79C_IR_EN_ANEG) 22 23 #define MII_AM79C_IR_LINK_DOWN BIT(2) 24 #define MII_AM79C_IR_ANEG_DONE BIT(0) 25 #define MII_AM79C_IR_IMASK_STAT (MII_AM79C_IR_LINK_DOWN | MII_AM79C_IR_ANEG_DONE) 26 27 MODULE_DESCRIPTION("AMD PHY driver"); 28 MODULE_AUTHOR("Heiko Schocher <hs@denx.de>"); 29 MODULE_LICENSE("GPL"); 30 31 static int am79c_ack_interrupt(struct phy_device *phydev) 32 { 33 int err; 34 35 err = phy_read(phydev, MII_BMSR); 36 if (err < 0) 37 return err; 38 39 err = phy_read(phydev, MII_AM79C_IR); 40 if (err < 0) 41 return err; 42 43 return 0; 44 } 45 46 static int am79c_config_init(struct phy_device *phydev) 47 { 48 return 0; 49 } 50 51 static int am79c_config_intr(struct phy_device *phydev) 52 { 53 int err; 54 55 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 56 err = am79c_ack_interrupt(phydev); 57 if (err) 58 return err; 59 60 err = phy_write(phydev, MII_AM79C_IR, MII_AM79C_IR_IMASK_INIT); 61 } else { 62 err = phy_write(phydev, MII_AM79C_IR, 0); 63 if (err) 64 return err; 65 66 err = am79c_ack_interrupt(phydev); 67 } 68 69 return err; 70 } 71 72 static irqreturn_t am79c_handle_interrupt(struct phy_device *phydev) 73 { 74 int irq_status; 75 76 irq_status = phy_read(phydev, MII_AM79C_IR); 77 if (irq_status < 0) { 78 phy_error(phydev); 79 return IRQ_NONE; 80 } 81 82 if (!(irq_status & MII_AM79C_IR_IMASK_STAT)) 83 return IRQ_NONE; 84 85 phy_trigger_machine(phydev); 86 87 return IRQ_HANDLED; 88 } 89 90 static struct phy_driver am79c_driver[] = { { 91 .phy_id = PHY_ID_AM79C874, 92 .name = "AM79C874", 93 .phy_id_mask = 0xfffffff0, 94 /* PHY_BASIC_FEATURES */ 95 .config_init = am79c_config_init, 96 .config_intr = am79c_config_intr, 97 .handle_interrupt = am79c_handle_interrupt, 98 } }; 99 100 module_phy_driver(am79c_driver); 101 102 static struct mdio_device_id __maybe_unused amd_tbl[] = { 103 { PHY_ID_AM79C874, 0xfffffff0 }, 104 { } 105 }; 106 107 MODULE_DEVICE_TABLE(mdio, amd_tbl); 108