linux/drivers/net/phy/amd.c
<<
>>
Prefs
   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
  27MODULE_DESCRIPTION("AMD PHY driver");
  28MODULE_AUTHOR("Heiko Schocher <hs@denx.de>");
  29MODULE_LICENSE("GPL");
  30
  31static 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
  46static int am79c_config_init(struct phy_device *phydev)
  47{
  48        return 0;
  49}
  50
  51static 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
  72static 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
  90static 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
 100module_phy_driver(am79c_driver);
 101
 102static struct mdio_device_id __maybe_unused amd_tbl[] = {
 103        { PHY_ID_AM79C874, 0xfffffff0 },
 104        { }
 105};
 106
 107MODULE_DEVICE_TABLE(mdio, amd_tbl);
 108