xref: /openbmc/linux/drivers/net/phy/amd.c (revision 347917c7)
1a2443fd1SAndrew Lunn // SPDX-License-Identifier: GPL-2.0+
249099122SHeiko Schocher /*
349099122SHeiko Schocher  * Driver for AMD am79c PHYs
449099122SHeiko Schocher  *
549099122SHeiko Schocher  * Author: Heiko Schocher <hs@denx.de>
649099122SHeiko Schocher  *
749099122SHeiko Schocher  * Copyright (c) 2011 DENX Software Engineering GmbH
849099122SHeiko Schocher  */
949099122SHeiko Schocher #include <linux/kernel.h>
1049099122SHeiko Schocher #include <linux/errno.h>
1149099122SHeiko Schocher #include <linux/init.h>
1249099122SHeiko Schocher #include <linux/module.h>
1349099122SHeiko Schocher #include <linux/mii.h>
1449099122SHeiko Schocher #include <linux/phy.h>
1549099122SHeiko Schocher 
1649099122SHeiko Schocher #define PHY_ID_AM79C874		0x0022561b
1749099122SHeiko Schocher 
1849099122SHeiko Schocher #define MII_AM79C_IR		17	/* Interrupt Status/Control Register */
1949099122SHeiko Schocher #define MII_AM79C_IR_EN_LINK	0x0400	/* IR enable Linkstate */
2049099122SHeiko Schocher #define MII_AM79C_IR_EN_ANEG	0x0100	/* IR enable Aneg Complete */
2149099122SHeiko Schocher #define MII_AM79C_IR_IMASK_INIT	(MII_AM79C_IR_EN_LINK | MII_AM79C_IR_EN_ANEG)
2249099122SHeiko Schocher 
23d995a36bSIoana Ciornei #define MII_AM79C_IR_LINK_DOWN	BIT(2)
24d995a36bSIoana Ciornei #define MII_AM79C_IR_ANEG_DONE	BIT(0)
25d995a36bSIoana Ciornei #define MII_AM79C_IR_IMASK_STAT	(MII_AM79C_IR_LINK_DOWN | MII_AM79C_IR_ANEG_DONE)
26d995a36bSIoana Ciornei 
2749099122SHeiko Schocher MODULE_DESCRIPTION("AMD PHY driver");
2849099122SHeiko Schocher MODULE_AUTHOR("Heiko Schocher <hs@denx.de>");
2949099122SHeiko Schocher MODULE_LICENSE("GPL");
3049099122SHeiko Schocher 
am79c_ack_interrupt(struct phy_device * phydev)3149099122SHeiko Schocher static int am79c_ack_interrupt(struct phy_device *phydev)
3249099122SHeiko Schocher {
3349099122SHeiko Schocher 	int err;
3449099122SHeiko Schocher 
3549099122SHeiko Schocher 	err = phy_read(phydev, MII_BMSR);
3649099122SHeiko Schocher 	if (err < 0)
3749099122SHeiko Schocher 		return err;
3849099122SHeiko Schocher 
3949099122SHeiko Schocher 	err = phy_read(phydev, MII_AM79C_IR);
4049099122SHeiko Schocher 	if (err < 0)
4149099122SHeiko Schocher 		return err;
4249099122SHeiko Schocher 
4349099122SHeiko Schocher 	return 0;
4449099122SHeiko Schocher }
4549099122SHeiko Schocher 
am79c_config_init(struct phy_device * phydev)4649099122SHeiko Schocher static int am79c_config_init(struct phy_device *phydev)
4749099122SHeiko Schocher {
4849099122SHeiko Schocher 	return 0;
4949099122SHeiko Schocher }
5049099122SHeiko Schocher 
am79c_config_intr(struct phy_device * phydev)5149099122SHeiko Schocher static int am79c_config_intr(struct phy_device *phydev)
5249099122SHeiko Schocher {
5349099122SHeiko Schocher 	int err;
5449099122SHeiko Schocher 
55*347917c7SIoana Ciornei 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
56*347917c7SIoana Ciornei 		err = am79c_ack_interrupt(phydev);
57*347917c7SIoana Ciornei 		if (err)
58*347917c7SIoana Ciornei 			return err;
59*347917c7SIoana Ciornei 
6049099122SHeiko Schocher 		err = phy_write(phydev, MII_AM79C_IR, MII_AM79C_IR_IMASK_INIT);
61*347917c7SIoana Ciornei 	} else {
6249099122SHeiko Schocher 		err = phy_write(phydev, MII_AM79C_IR, 0);
63*347917c7SIoana Ciornei 		if (err)
64*347917c7SIoana Ciornei 			return err;
65*347917c7SIoana Ciornei 
66*347917c7SIoana Ciornei 		err = am79c_ack_interrupt(phydev);
67*347917c7SIoana Ciornei 	}
6849099122SHeiko Schocher 
6949099122SHeiko Schocher 	return err;
7049099122SHeiko Schocher }
7149099122SHeiko Schocher 
am79c_handle_interrupt(struct phy_device * phydev)72d995a36bSIoana Ciornei static irqreturn_t am79c_handle_interrupt(struct phy_device *phydev)
73d995a36bSIoana Ciornei {
74d995a36bSIoana Ciornei 	int irq_status;
75d995a36bSIoana Ciornei 
76d995a36bSIoana Ciornei 	irq_status = phy_read(phydev, MII_AM79C_IR);
77d995a36bSIoana Ciornei 	if (irq_status < 0) {
78d995a36bSIoana Ciornei 		phy_error(phydev);
79d995a36bSIoana Ciornei 		return IRQ_NONE;
80d995a36bSIoana Ciornei 	}
81d995a36bSIoana Ciornei 
82d995a36bSIoana Ciornei 	if (!(irq_status & MII_AM79C_IR_IMASK_STAT))
83d995a36bSIoana Ciornei 		return IRQ_NONE;
84d995a36bSIoana Ciornei 
85d995a36bSIoana Ciornei 	phy_trigger_machine(phydev);
86d995a36bSIoana Ciornei 
87d995a36bSIoana Ciornei 	return IRQ_HANDLED;
88d995a36bSIoana Ciornei }
89d995a36bSIoana Ciornei 
90116dffa0SJohan Hovold static struct phy_driver am79c_driver[] = { {
9149099122SHeiko Schocher 	.phy_id		= PHY_ID_AM79C874,
9249099122SHeiko Schocher 	.name		= "AM79C874",
9349099122SHeiko Schocher 	.phy_id_mask	= 0xfffffff0,
94dcdecdcfSHeiner Kallweit 	/* PHY_BASIC_FEATURES */
9549099122SHeiko Schocher 	.config_init	= am79c_config_init,
9649099122SHeiko Schocher 	.config_intr	= am79c_config_intr,
97d995a36bSIoana Ciornei 	.handle_interrupt = am79c_handle_interrupt,
98116dffa0SJohan Hovold } };
9949099122SHeiko Schocher 
100116dffa0SJohan Hovold module_phy_driver(am79c_driver);
10149099122SHeiko Schocher 
10249099122SHeiko Schocher static struct mdio_device_id __maybe_unused amd_tbl[] = {
10349099122SHeiko Schocher 	{ PHY_ID_AM79C874, 0xfffffff0 },
10449099122SHeiko Schocher 	{ }
10549099122SHeiko Schocher };
10649099122SHeiko Schocher 
10749099122SHeiko Schocher MODULE_DEVICE_TABLE(mdio, amd_tbl);
108