1/*
2 * Copyright 2017, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13/**
14 *  \file   phy.c
15 *
16 *  \brief  APIs for configuring ethernet PHYs
17 *
18 *   This file contains the device abstraction APIs for ethernet PHYs.
19 */
20
21/*
22* Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/
23*/
24/*
25*  Redistribution and use in source and binary forms, with or without
26*  modification, are permitted provided that the following conditions
27*  are met:
28*
29*    Redistributions of source code must retain the above copyright
30*    notice, this list of conditions and the following disclaimer.
31*
32*    Redistributions in binary form must reproduce the above copyright
33*    notice, this list of conditions and the following disclaimer in the
34*    documentation and/or other materials provided with the
35*    distribution.
36*
37*    Neither the name of Texas Instruments Incorporated nor the names of
38*    its contributors may be used to endorse or promote products derived
39*    from this software without specific prior written permission.
40*
41*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
42*  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
43*  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
44*  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
45*  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
46*  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
47*  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
48*  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
49*  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
50*  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
51*  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
52*
53*/
54
55#include <ethdrivers/plat/hw/hw_types.h>
56#include <ethdrivers/plat/mdio.h>
57#include <ethdrivers/plat/phy.h>
58
59#define PHY_ADV_VAL_MASK                 (0xff10)
60
61/*******************************************************************************
62*                        API FUNCTION DEFINITIONS
63*******************************************************************************/
64/**
65 * \brief   Reads the PHY ID.
66 *
67 * \param   mdioBaseAddr  Base Address of the MDIO Module Registers.
68 * \param   phyAddr       PHY Adress.
69 *
70 * \return  32 bit PHY ID (ID1:ID2)
71 *
72 **/
73unsigned int PhyIDGet(unsigned int mdioBaseAddr, unsigned int phyAddr)
74{
75    unsigned int id = 0;
76    unsigned short data;
77
78    /* read the ID1 register */
79    MDIOPhyRegRead(mdioBaseAddr, phyAddr, PHY_ID1, &data);
80
81    /* update the ID1 value */
82    id = data << PHY_ID_SHIFT;
83
84    /* read the ID2 register */
85    MDIOPhyRegRead(mdioBaseAddr, phyAddr, PHY_ID2, &data);
86
87    /* update the ID2 value */
88    id |= data;
89
90    /* return the ID in ID1:ID2 format */
91    return id;
92}
93
94/**
95 * \brief   Reads a register from the the PHY
96 *
97 * \param   mdioBaseAddr  Base Address of the MDIO Module Registers.
98 * \param   phyAddr       PHY Adress.
99 * \param   regIdx        Index of the register to be read
100 * \param   regValAdr     address where value of the register will be written
101 *
102 * \return  status of the read
103 *
104 **/
105unsigned int PhyRegRead(unsigned int mdioBaseAddr, unsigned int phyAddr,
106                        unsigned int regIdx, unsigned short *regValAdr)
107{
108    return (MDIOPhyRegRead(mdioBaseAddr, phyAddr, regIdx, regValAdr));
109}
110
111/**
112 * \brief   Writes a register with the input
113 *
114 * \param   mdioBaseAddr  Base Address of the MDIO Module Registers.
115 * \param   phyAddr       PHY Adress.
116 * \param   regIdx        Index of the register to be read
117 * \param   regValAdr     value to be written
118 *
119 * \return  None
120 *
121 **/
122void PhyRegWrite(unsigned int mdioBaseAddr, unsigned int phyAddr,
123                 unsigned int regIdx, unsigned short regVal)
124{
125    MDIOPhyRegWrite(mdioBaseAddr, phyAddr, regIdx, regVal);
126}
127
128/**
129 * \brief   Enables Loop Back mode
130 *
131 * \param   mdioBaseAddr  Base Address of the MDIO Module Registers.
132 * \param   phyAddr       PHY Adress.
133 *
134 * \return  status after enabling.  \n
135 *          TRUE if loop back is enabled \n
136 *          FALSE if not able to enable
137 *
138 **/
139unsigned int PhyLoopBackEnable(unsigned int mdioBaseAddr, unsigned int phyAddr)
140{
141    unsigned short data;
142
143    if (MDIOPhyRegRead(mdioBaseAddr, phyAddr, PHY_BCR, &data) != TRUE ) {
144        return FALSE;
145    }
146
147    data |= PHY_LPBK_ENABLE;
148
149    /* Enable loop back */
150    MDIOPhyRegWrite(mdioBaseAddr, phyAddr, PHY_BCR, data);
151
152    return TRUE;
153}
154
155/**
156 * \brief   Disables Loop Back mode
157 *
158 * \param   mdioBaseAddr  Base Address of the MDIO Module Registers.
159 * \param   phyAddr       PHY Adress.
160 *
161 * \return  status after enabling.  \n
162 *          TRUE if loop back is disabled \n
163 *          FALSE if not able to disable
164 *
165 **/
166unsigned int PhyLoopBackDisable(unsigned int mdioBaseAddr, unsigned int phyAddr)
167{
168    unsigned short data;
169
170    if (MDIOPhyRegRead(mdioBaseAddr, phyAddr, PHY_BCR, &data) != TRUE ) {
171        return FALSE;
172    }
173
174    data &= ~(PHY_LPBK_ENABLE);
175
176    /* Disable loop back */
177    MDIOPhyRegWrite(mdioBaseAddr, phyAddr, PHY_BCR, data);
178
179    return TRUE;
180}
181
182/**
183 * \brief   This function ask the phy device to start auto negotiation.
184 *
185 *
186 * \param   mdioBaseAddr  Base Address of the MDIO Module Registers.
187 * \param   phyAddr       PHY Adress.
188 * \param   advVal        Autonegotiation advertisement value
189 * \param   gigAdvVal     Gigabit capability advertisement value
190 *          advVal can take the following any OR combination of the values \n
191 *               PHY_100BTX - 100BaseTX \n
192 *               PHY_100BTX_FD - Full duplex capabilty for 100BaseTX \n
193 *               PHY_10BT - 10BaseT \n
194 *               PHY_10BT_FD - Full duplex capability for 10BaseT \n
195 *          gigAdvVal can take one of the following values \n
196 *               PHY_NO_1000BT - No 1000Base-T capability\n
197 *               PHY_1000BT_FD - Full duplex capabilty for 1000 Base-T \n
198 *               PHY_1000BT_HD - Half duplex capabilty for 1000 Base-T \n
199 *               FALSE - It is passed as an argument if phy dosen't support
200 *                       Giga bit capability
201 *
202 * \return  status after autonegotiation \n
203 *          TRUE if autonegotiation started
204 *          FALSE if autonegotiation not started
205 *
206 **/
207unsigned int PhyAutoNegotiate(unsigned int mdioBaseAddr, unsigned int phyAddr,
208                              unsigned short *advPtr, unsigned short *gigAdvPtr)
209{
210    volatile unsigned short data;
211    volatile unsigned short anar;
212
213    if (MDIOPhyRegRead(mdioBaseAddr, phyAddr, PHY_BCR, &data) != TRUE ) {
214        return FALSE;
215    }
216
217    data |= PHY_AUTONEG_ENABLE;
218
219    if (*gigAdvPtr != 0) {
220        /* Set phy for gigabit speed */
221        data &= PHY_SPEED_MASK;
222        data |= PHY_SPEED_1000MBPS;
223    }
224
225    /* Enable Auto Negotiation */
226    MDIOPhyRegWrite(mdioBaseAddr, phyAddr, PHY_BCR, data);
227
228    if (MDIOPhyRegRead(mdioBaseAddr, phyAddr, PHY_BCR, &data) != TRUE ) {
229        return FALSE;
230    }
231
232    /* Write Auto Negotiation capabilities */
233    MDIOPhyRegRead(mdioBaseAddr, phyAddr, PHY_AUTONEG_ADV, &anar);
234    anar &= ~PHY_ADV_VAL_MASK;
235    MDIOPhyRegWrite(mdioBaseAddr, phyAddr, PHY_AUTONEG_ADV, (anar | (*advPtr)));
236
237    if (*gigAdvPtr != 0) {
238        /* Write the gigabit capabilities */
239        MDIOPhyRegWrite(mdioBaseAddr, phyAddr, PHY_1000BT_CONTROL, (*gigAdvPtr));
240    }
241
242    data |= PHY_AUTONEG_RESTART;
243
244    /* Start Auto Negotiation */
245    MDIOPhyRegWrite(mdioBaseAddr, phyAddr, PHY_BCR, data);
246
247    return TRUE;
248}
249
250/**
251 * \brief   Returns the status of Auto Negotiation completion.
252 *
253 * \param   mdioBaseAddr  Base Address of the MDIO Module Registers.
254 * \param   phyAddr       PHY Adress.
255 *
256 * \return  Auto negotiation completion status \n
257 *          TRUE if auto negotiation is completed
258 *          FALSE if auto negotiation is not completed
259 **/
260unsigned int PhyAutoNegStatusGet(unsigned int mdioBaseAddr, unsigned int phyAddr)
261{
262    volatile unsigned short data;
263
264    MDIOPhyRegRead(mdioBaseAddr, phyAddr, PHY_BSR, &data);
265
266    /* Auto negotiation completion status */
267    if (PHY_AUTONEG_COMPLETE == (data & (PHY_AUTONEG_STATUS))) {
268        return TRUE;
269    }
270
271    return FALSE;
272}
273
274/**
275 * \brief   Reads the Link Partner Ability register of the PHY.
276 *
277 * \param   mdioBaseAddr    Base Address of the MDIO Module Registers.
278 * \param   phyAddr         PHY Adress.
279 * \param   ptnerAblty      Pointer to which partner ability will be written.
280 * \param   gbpsPtnerAblty  Pointer to which Giga bit capability will be written.
281 *
282 *          gbpsPtnerAblty can take following Macros.\n
283 *
284 *          TRUE  - It is passed as argument if phy supports Giga bit capability.\n
285 *          FALSE - It is passed as argument if phy dosen't supports Giga bit
286 *                  capability.\n
287 *
288 * \return  status after reading \n
289 *          TRUE if reading successful
290 *          FALSE if reading failed
291 **/
292unsigned int PhyPartnerAbilityGet(unsigned int mdioBaseAddr,
293                                  unsigned int phyAddr,
294                                  unsigned short *ptnerAblty,
295                                  unsigned short *gbpsPtnerAblty)
296{
297    unsigned int status;
298
299    status = MDIOPhyRegRead(mdioBaseAddr, phyAddr, PHY_LINK_PARTNER_ABLTY,
300                            ptnerAblty);
301
302    if (*gbpsPtnerAblty != 0) {
303        status = status | MDIOPhyRegRead(mdioBaseAddr, phyAddr, PHY_1000BT_STATUS,
304                                         gbpsPtnerAblty);
305    }
306
307    return status;
308}
309
310/**
311 * \brief   Reads the link status of the PHY.
312 *
313 * \param   mdioBaseAddr  Base Address of the MDIO Module Registers.
314 * \param   phyAddr       PHY Adress.
315 * \param   retries       The number of retries before indicating down status
316 *
317 * \return  link status after reading \n
318 *          TRUE if link is up
319 *          FALSE if link is down \n
320 *
321 * \note    This reads both the basic status register of the PHY and the
322 *          link register of MDIO for double check
323 **/
324unsigned int PhyLinkStatusGet(unsigned int mdioBaseAddr,
325                              unsigned int phyAddr,
326                              volatile unsigned int retries)
327{
328    volatile unsigned short linkStatus;
329
330    retries++;
331    while (retries) {
332        /* First read the BSR of the PHY */
333        MDIOPhyRegRead(mdioBaseAddr, phyAddr, PHY_BSR, &linkStatus);
334
335        if (linkStatus & PHY_LINK_STATUS) {
336            return TRUE;
337        }
338
339        retries--;
340    }
341
342    return FALSE;
343}
344
345
346/* The next two functions were copied from 'bb-sel4' project at:
347 *    https://github.com/juli1/bb-sel4
348 * In particular, they were taken from:
349 *    https://github.com/juli1/bb-sel4/blob/701e87f4d4a403d3b0c02326d42e8580a7ff4b7f/bb-eth/components/ConsumerThreadImpl/src/phy.c
350 */
351unsigned int PhyReset(unsigned int mdioBaseAddr, unsigned int phyAddr)
352{
353    unsigned short data;
354
355    data = PHY_SOFTRESET;
356
357    /* Reset the phy */
358    MDIOPhyRegWrite(mdioBaseAddr, phyAddr, PHY_BCR, data);
359
360    /* wait till the reset bit is auto cleared */
361    while(data & PHY_SOFTRESET)
362    {
363        /* Read the reset */
364        if(MDIOPhyRegRead(mdioBaseAddr, phyAddr, PHY_BCR, &data) != TRUE)
365        {
366            return FALSE;
367        }
368    }
369
370    return TRUE;
371}
372
373unsigned int PhyConfigure(unsigned int mdioBaseAddr, unsigned int phyAddr,
374                              unsigned short speed, unsigned short duplexMode)
375{
376    /* Set the configurations */
377    MDIOPhyRegWrite(mdioBaseAddr, phyAddr, PHY_BCR, (speed | duplexMode));
378
379        return TRUE;
380}
381
382/**************************** End Of File ***********************************/
383