blob: dd79ea6ba02315973deb26337bbecbc8dd42e5b6 [file] [log] [blame]
Arun Parameswarana1cba562015-10-06 12:25:48 -07001/*
2 * Copyright (C) 2015 Broadcom Corporation
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation version 2.
7 *
8 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
9 * kind, whether express or implied; without even the implied warranty
10 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include "bcm-phy-lib.h"
15#include <linux/brcmphy.h>
16#include <linux/export.h>
17#include <linux/mdio.h>
18#include <linux/phy.h>
19
20#define MII_BCM_CHANNEL_WIDTH 0x2000
21#define BCM_CL45VEN_EEE_ADV 0x3c
22
23int bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val)
24{
25 int rc;
26
27 rc = phy_write(phydev, MII_BCM54XX_EXP_SEL, reg);
28 if (rc < 0)
29 return rc;
30
31 return phy_write(phydev, MII_BCM54XX_EXP_DATA, val);
32}
33EXPORT_SYMBOL_GPL(bcm_phy_write_exp);
34
35int bcm_phy_read_exp(struct phy_device *phydev, u16 reg)
36{
37 int val;
38
39 val = phy_write(phydev, MII_BCM54XX_EXP_SEL, reg);
40 if (val < 0)
41 return val;
42
43 val = phy_read(phydev, MII_BCM54XX_EXP_DATA);
44
45 /* Restore default value. It's O.K. if this write fails. */
46 phy_write(phydev, MII_BCM54XX_EXP_SEL, 0);
47
48 return val;
49}
50EXPORT_SYMBOL_GPL(bcm_phy_read_exp);
51
52int bcm_phy_write_misc(struct phy_device *phydev,
53 u16 reg, u16 chl, u16 val)
54{
55 int rc;
56 int tmp;
57
58 rc = phy_write(phydev, MII_BCM54XX_AUX_CTL,
59 MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
60 if (rc < 0)
61 return rc;
62
63 tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL);
64 tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA;
65 rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp);
66 if (rc < 0)
67 return rc;
68
69 tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg;
70 rc = bcm_phy_write_exp(phydev, tmp, val);
71
72 return rc;
73}
74EXPORT_SYMBOL_GPL(bcm_phy_write_misc);
75
76int bcm_phy_read_misc(struct phy_device *phydev,
77 u16 reg, u16 chl)
78{
79 int rc;
80 int tmp;
81
82 rc = phy_write(phydev, MII_BCM54XX_AUX_CTL,
83 MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
84 if (rc < 0)
85 return rc;
86
87 tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL);
88 tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA;
89 rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp);
90 if (rc < 0)
91 return rc;
92
93 tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg;
94 rc = bcm_phy_read_exp(phydev, tmp);
95
96 return rc;
97}
98EXPORT_SYMBOL_GPL(bcm_phy_read_misc);
99
100int bcm_phy_ack_intr(struct phy_device *phydev)
101{
102 int reg;
103
104 /* Clear pending interrupts. */
105 reg = phy_read(phydev, MII_BCM54XX_ISR);
106 if (reg < 0)
107 return reg;
108
109 return 0;
110}
111EXPORT_SYMBOL_GPL(bcm_phy_ack_intr);
112
113int bcm_phy_config_intr(struct phy_device *phydev)
114{
115 int reg;
116
117 reg = phy_read(phydev, MII_BCM54XX_ECR);
118 if (reg < 0)
119 return reg;
120
121 if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
122 reg &= ~MII_BCM54XX_ECR_IM;
123 else
124 reg |= MII_BCM54XX_ECR_IM;
125
126 return phy_write(phydev, MII_BCM54XX_ECR, reg);
127}
128EXPORT_SYMBOL_GPL(bcm_phy_config_intr);
129
130int bcm_phy_read_shadow(struct phy_device *phydev, u16 shadow)
131{
132 phy_write(phydev, MII_BCM54XX_SHD, MII_BCM54XX_SHD_VAL(shadow));
133 return MII_BCM54XX_SHD_DATA(phy_read(phydev, MII_BCM54XX_SHD));
134}
135EXPORT_SYMBOL_GPL(bcm_phy_read_shadow);
136
137int bcm_phy_write_shadow(struct phy_device *phydev, u16 shadow,
138 u16 val)
139{
140 return phy_write(phydev, MII_BCM54XX_SHD,
141 MII_BCM54XX_SHD_WRITE |
142 MII_BCM54XX_SHD_VAL(shadow) |
143 MII_BCM54XX_SHD_DATA(val));
144}
145EXPORT_SYMBOL_GPL(bcm_phy_write_shadow);
146
147int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down)
148{
149 int val;
150
151 if (dll_pwr_down) {
152 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3);
153 if (val < 0)
154 return val;
155
156 val |= BCM54XX_SHD_SCR3_DLLAPD_DIS;
157 bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val);
158 }
159
160 val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD);
161 if (val < 0)
162 return val;
163
164 /* Clear APD bits */
165 val &= BCM_APD_CLR_MASK;
166
167 if (phydev->autoneg == AUTONEG_ENABLE)
168 val |= BCM54XX_SHD_APD_EN;
169 else
170 val |= BCM_NO_ANEG_APD_EN;
171
172 /* Enable energy detect single link pulse for easy wakeup */
173 val |= BCM_APD_SINGLELP_EN;
174
175 /* Enable Auto Power-Down (APD) for the PHY */
176 return bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val);
177}
178EXPORT_SYMBOL_GPL(bcm_phy_enable_apd);
179
180int bcm_phy_enable_eee(struct phy_device *phydev)
181{
182 int val;
183
184 /* Enable EEE at PHY level */
185 val = phy_read_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL,
186 MDIO_MMD_AN, phydev->addr);
187 if (val < 0)
188 return val;
189
190 val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X;
191
192 phy_write_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL,
193 MDIO_MMD_AN, phydev->addr, (u32)val);
194
195 /* Advertise EEE */
196 val = phy_read_mmd_indirect(phydev, BCM_CL45VEN_EEE_ADV,
197 MDIO_MMD_AN, phydev->addr);
198 if (val < 0)
199 return val;
200
201 val |= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T);
202
203 phy_write_mmd_indirect(phydev, BCM_CL45VEN_EEE_ADV,
204 MDIO_MMD_AN, phydev->addr, (u32)val);
205
206 return 0;
207}
208EXPORT_SYMBOL_GPL(bcm_phy_enable_eee);