xref: /openbmc/linux/drivers/net/phy/vitesse.c (revision 643d1f7f)
1 /*
2  * Driver for Vitesse PHYs
3  *
4  * Author: Kriston Carson
5  *
6  * Copyright (c) 2005 Freescale Semiconductor, Inc.
7  *
8  * This program is free software; you can redistribute  it and/or modify it
9  * under  the terms of  the GNU General  Public License as published by the
10  * Free Software Foundation;  either version 2 of the  License, or (at your
11  * option) any later version.
12  *
13  */
14 
15 #include <linux/kernel.h>
16 #include <linux/module.h>
17 #include <linux/mii.h>
18 #include <linux/ethtool.h>
19 #include <linux/phy.h>
20 
21 /* Vitesse Extended Control Register 1 */
22 #define MII_VSC8244_EXT_CON1           0x17
23 #define MII_VSC8244_EXTCON1_INIT       0x0000
24 #define MII_VSC8244_EXTCON1_TX_SKEW_MASK	0x0c00
25 #define MII_VSC8244_EXTCON1_RX_SKEW_MASK	0x0300
26 #define MII_VSC8244_EXTCON1_TX_SKEW	0x0800
27 #define MII_VSC8244_EXTCON1_RX_SKEW	0x0200
28 
29 /* Vitesse Interrupt Mask Register */
30 #define MII_VSC8244_IMASK		0x19
31 #define MII_VSC8244_IMASK_IEN		0x8000
32 #define MII_VSC8244_IMASK_SPEED		0x4000
33 #define MII_VSC8244_IMASK_LINK		0x2000
34 #define MII_VSC8244_IMASK_DUPLEX	0x1000
35 #define MII_VSC8244_IMASK_MASK		0xf000
36 
37 /* Vitesse Interrupt Status Register */
38 #define MII_VSC8244_ISTAT		0x1a
39 #define MII_VSC8244_ISTAT_STATUS	0x8000
40 #define MII_VSC8244_ISTAT_SPEED		0x4000
41 #define MII_VSC8244_ISTAT_LINK		0x2000
42 #define MII_VSC8244_ISTAT_DUPLEX	0x1000
43 
44 /* Vitesse Auxiliary Control/Status Register */
45 #define MII_VSC8244_AUX_CONSTAT        	0x1c
46 #define MII_VSC8244_AUXCONSTAT_INIT    	0x0000
47 #define MII_VSC8244_AUXCONSTAT_DUPLEX  	0x0020
48 #define MII_VSC8244_AUXCONSTAT_SPEED   	0x0018
49 #define MII_VSC8244_AUXCONSTAT_GBIT    	0x0010
50 #define MII_VSC8244_AUXCONSTAT_100     	0x0008
51 
52 MODULE_DESCRIPTION("Vitesse PHY driver");
53 MODULE_AUTHOR("Kriston Carson");
54 MODULE_LICENSE("GPL");
55 
56 static int vsc824x_config_init(struct phy_device *phydev)
57 {
58 	int extcon;
59 	int err;
60 
61 	err = phy_write(phydev, MII_VSC8244_AUX_CONSTAT,
62 			MII_VSC8244_AUXCONSTAT_INIT);
63 	if (err < 0)
64 		return err;
65 
66 	extcon = phy_read(phydev, MII_VSC8244_EXT_CON1);
67 
68 	if (extcon < 0)
69 		return err;
70 
71 	extcon &= ~(MII_VSC8244_EXTCON1_TX_SKEW_MASK |
72 			MII_VSC8244_EXTCON1_RX_SKEW_MASK);
73 
74 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
75 		extcon |= (MII_VSC8244_EXTCON1_TX_SKEW |
76 				MII_VSC8244_EXTCON1_RX_SKEW);
77 
78 	err = phy_write(phydev, MII_VSC8244_EXT_CON1, extcon);
79 
80 	return err;
81 }
82 
83 static int vsc824x_ack_interrupt(struct phy_device *phydev)
84 {
85 	int err = 0;
86 
87 	/*
88 	 * Don't bother to ACK the interrupts if interrupts
89 	 * are disabled.  The 824x cannot clear the interrupts
90 	 * if they are disabled.
91 	 */
92 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
93 		err = phy_read(phydev, MII_VSC8244_ISTAT);
94 
95 	return (err < 0) ? err : 0;
96 }
97 
98 static int vsc824x_config_intr(struct phy_device *phydev)
99 {
100 	int err;
101 
102 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
103 		err = phy_write(phydev, MII_VSC8244_IMASK,
104 				MII_VSC8244_IMASK_MASK);
105 	else {
106 		/*
107 		 * The Vitesse PHY cannot clear the interrupt
108 		 * once it has disabled them, so we clear them first
109 		 */
110 		err = phy_read(phydev, MII_VSC8244_ISTAT);
111 
112 		if (err < 0)
113 			return err;
114 
115 		err = phy_write(phydev, MII_VSC8244_IMASK, 0);
116 	}
117 
118 	return err;
119 }
120 
121 /* Vitesse 824x */
122 static struct phy_driver vsc8244_driver = {
123 	.phy_id		= 0x000fc6c0,
124 	.name		= "Vitesse VSC8244",
125 	.phy_id_mask	= 0x000fffc0,
126 	.features	= PHY_GBIT_FEATURES,
127 	.flags		= PHY_HAS_INTERRUPT,
128 	.config_init	= &vsc824x_config_init,
129 	.config_aneg	= &genphy_config_aneg,
130 	.read_status	= &genphy_read_status,
131 	.ack_interrupt	= &vsc824x_ack_interrupt,
132 	.config_intr	= &vsc824x_config_intr,
133 	.driver 	= { .owner = THIS_MODULE,},
134 };
135 
136 static int __init vsc8244_init(void)
137 {
138 	return phy_driver_register(&vsc8244_driver);
139 }
140 
141 static void __exit vsc8244_exit(void)
142 {
143 	phy_driver_unregister(&vsc8244_driver);
144 }
145 
146 module_init(vsc8244_init);
147 module_exit(vsc8244_exit);
148