amd.c 2.3 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0+
H
Heiko Schocher 已提交
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
 * Driver for AMD am79c PHYs
 *
 * Author: Heiko Schocher <hs@denx.de>
 *
 * Copyright (c) 2011 DENX Software Engineering GmbH
 */
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mii.h>
#include <linux/phy.h>

#define PHY_ID_AM79C874		0x0022561b

#define MII_AM79C_IR		17	/* Interrupt Status/Control Register */
#define MII_AM79C_IR_EN_LINK	0x0400	/* IR enable Linkstate */
#define MII_AM79C_IR_EN_ANEG	0x0100	/* IR enable Aneg Complete */
#define MII_AM79C_IR_IMASK_INIT	(MII_AM79C_IR_EN_LINK | MII_AM79C_IR_EN_ANEG)

23 24 25 26
#define MII_AM79C_IR_LINK_DOWN	BIT(2)
#define MII_AM79C_IR_ANEG_DONE	BIT(0)
#define MII_AM79C_IR_IMASK_STAT	(MII_AM79C_IR_LINK_DOWN | MII_AM79C_IR_ANEG_DONE)

H
Heiko Schocher 已提交
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
MODULE_DESCRIPTION("AMD PHY driver");
MODULE_AUTHOR("Heiko Schocher <hs@denx.de>");
MODULE_LICENSE("GPL");

static int am79c_ack_interrupt(struct phy_device *phydev)
{
	int err;

	err = phy_read(phydev, MII_BMSR);
	if (err < 0)
		return err;

	err = phy_read(phydev, MII_AM79C_IR);
	if (err < 0)
		return err;

	return 0;
}

static int am79c_config_init(struct phy_device *phydev)
{
	return 0;
}

static int am79c_config_intr(struct phy_device *phydev)
{
	int err;

55 56 57 58 59
	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
		err = am79c_ack_interrupt(phydev);
		if (err)
			return err;

H
Heiko Schocher 已提交
60
		err = phy_write(phydev, MII_AM79C_IR, MII_AM79C_IR_IMASK_INIT);
61
	} else {
H
Heiko Schocher 已提交
62
		err = phy_write(phydev, MII_AM79C_IR, 0);
63 64 65 66 67
		if (err)
			return err;

		err = am79c_ack_interrupt(phydev);
	}
H
Heiko Schocher 已提交
68 69 70 71

	return err;
}

72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
static irqreturn_t am79c_handle_interrupt(struct phy_device *phydev)
{
	int irq_status;

	irq_status = phy_read(phydev, MII_AM79C_IR);
	if (irq_status < 0) {
		phy_error(phydev);
		return IRQ_NONE;
	}

	if (!(irq_status & MII_AM79C_IR_IMASK_STAT))
		return IRQ_NONE;

	phy_trigger_machine(phydev);

	return IRQ_HANDLED;
}

90
static struct phy_driver am79c_driver[] = { {
H
Heiko Schocher 已提交
91 92 93
	.phy_id		= PHY_ID_AM79C874,
	.name		= "AM79C874",
	.phy_id_mask	= 0xfffffff0,
94
	/* PHY_BASIC_FEATURES */
H
Heiko Schocher 已提交
95 96
	.config_init	= am79c_config_init,
	.config_intr	= am79c_config_intr,
97
	.handle_interrupt = am79c_handle_interrupt,
98
} };
H
Heiko Schocher 已提交
99

100
module_phy_driver(am79c_driver);
H
Heiko Schocher 已提交
101 102 103 104 105 106 107

static struct mdio_device_id __maybe_unused amd_tbl[] = {
	{ PHY_ID_AM79C874, 0xfffffff0 },
	{ }
};

MODULE_DEVICE_TABLE(mdio, amd_tbl);