]> git.donarmstrong.com Git - qmk_firmware.git/blob - tool/mbed/mbed-sdk/libraries/net/eth/lwip-eth/arch/TARGET_NXP/lpc_phy_dp83848.c
Squashed 'tmk_core/' changes from 7967731..b9e0ea0
[qmk_firmware.git] / tool / mbed / mbed-sdk / libraries / net / eth / lwip-eth / arch / TARGET_NXP / lpc_phy_dp83848.c
1 /**********************************************************************
2 * $Id$          lpc_phy_dp83848.c                       2011-11-20
3 *//**
4 * @file         lpc_phy_dp83848.c
5 * @brief        DP83848C PHY status and control.
6 * @version      1.0
7 * @date         20 Nov. 2011
8 * @author       NXP MCU SW Application Team
9 *
10 * Copyright(C) 2011, NXP Semiconductor
11 * All rights reserved.
12 *
13 ***********************************************************************
14 * Software that is described herein is for illustrative purposes only
15 * which provides customers with programming information regarding the
16 * products. This software is supplied "AS IS" without any warranties.
17 * NXP Semiconductors assumes no responsibility or liability for the
18 * use of the software, conveys no license or title under any patent,
19 * copyright, or mask work right to the product. NXP Semiconductors
20 * reserves the right to make changes in the software without
21 * notification. NXP Semiconductors also make no representation or
22 * warranty that such application will be suitable for the specified
23 * use without further testing or modification.
24 **********************************************************************/
25
26 #include "lwip/opt.h"
27 #include "lwip/err.h"
28 #include "lwip/tcpip.h"
29 #include "lwip/snmp.h"
30 #include "lpc_emac_config.h"
31 #include "lpc_phy.h"
32 #include "lpc17xx_emac.h"
33
34 /** @defgroup dp83848_phy       PHY status and control for the DP83848.
35  * @ingroup lwip_phy
36  *
37  * Various functions for controlling and monitoring the status of the
38  * DP83848 PHY. In polled (standalone) systems, the PHY state must be
39  * monitored as part of the application. In a threaded (RTOS) system,
40  * the PHY state is monitored by the PHY handler thread. The MAC
41  * driver will not transmit unless the PHY link is active.
42  * @{
43  */
44
45 /** \brief  DP83848 PHY register offsets */
46 #define DP8_BMCR_REG        0x0  /**< Basic Mode Control Register */
47 #define DP8_BMSR_REG        0x1  /**< Basic Mode Status Reg */
48 #define DP8_IDR1_REG        0x2  /**< Basic Mode Status Reg */
49 #define DP8_IDR2_REG        0x3  /**< Basic Mode Status Reg */
50 #define DP8_ANADV_REG       0x4  /**< Auto_Neg Advt Reg  */
51 #define DP8_ANLPA_REG       0x5  /**< Auto_neg Link Partner Ability Reg */
52 #define DP8_ANEEXP_REG      0x6  /**< Auto-neg Expansion Reg  */
53 #define DP8_PHY_STAT_REG    0x10 /**< PHY Status Register  */
54 #define DP8_PHY_INT_CTL_REG 0x11 /**< PHY Interrupt Control Register */
55 #define DP8_PHY_RBR_REG     0x17 /**< PHY RMII and Bypass Register  */
56 #define DP8_PHY_STS_REG     0x19 /**< PHY Status Register  */
57
58 #define DP8_PHY_SCSR_REG    0x1f /**< PHY Special Control/Status Register (LAN8720)  */
59
60 /** \brief DP83848 Control register definitions */
61 #define DP8_RESET          (1 << 15)  /**< 1= S/W Reset */
62 #define DP8_LOOPBACK       (1 << 14)  /**< 1=loopback Enabled */
63 #define DP8_SPEED_SELECT   (1 << 13)  /**< 1=Select 100MBps */
64 #define DP8_AUTONEG        (1 << 12)  /**< 1=Enable auto-negotiation */
65 #define DP8_POWER_DOWN     (1 << 11)  /**< 1=Power down PHY */
66 #define DP8_ISOLATE        (1 << 10)  /**< 1=Isolate PHY */
67 #define DP8_RESTART_AUTONEG (1 << 9)  /**< 1=Restart auto-negoatiation */
68 #define DP8_DUPLEX_MODE    (1 << 8)   /**< 1=Full duplex mode */
69 #define DP8_COLLISION_TEST (1 << 7)   /**< 1=Perform collsion test */
70
71 /** \brief DP83848 Status register definitions */
72 #define DP8_100BASE_T4     (1 << 15)  /**< T4 mode */
73 #define DP8_100BASE_TX_FD  (1 << 14)  /**< 100MBps full duplex */
74 #define DP8_100BASE_TX_HD  (1 << 13)  /**< 100MBps half duplex */
75 #define DP8_10BASE_T_FD    (1 << 12)  /**< 100Bps full duplex */
76 #define DP8_10BASE_T_HD    (1 << 11)  /**< 10MBps half duplex */
77 #define DP8_MF_PREAMB_SUPPR (1 << 6)  /**< Preamble suppress */
78 #define DP8_AUTONEG_COMP   (1 << 5)   /**< Auto-negotation complete */
79 #define DP8_RMT_FAULT      (1 << 4)   /**< Fault */
80 #define DP8_AUTONEG_ABILITY (1 << 3)  /**< Auto-negotation supported */
81 #define DP8_LINK_STATUS    (1 << 2)   /**< 1=Link active */
82 #define DP8_JABBER_DETECT  (1 << 1)   /**< Jabber detect */
83 #define DP8_EXTEND_CAPAB   (1 << 0)   /**< Supports extended capabilities */
84
85 /** \brief DP83848 PHY RBR MII dode definitions */
86 #define DP8_RBR_RMII_MODE  (1 << 5)   /**< Use RMII mode */
87
88 /** \brief DP83848 PHY status definitions */
89 #define DP8_REMOTEFAULT    (1 << 6)   /**< Remote fault */
90 #define DP8_FULLDUPLEX     (1 << 2)   /**< 1=full duplex */
91 #define DP8_SPEED10MBPS    (1 << 1)   /**< 1=10MBps speed */
92 #define DP8_VALID_LINK     (1 << 0)   /**< 1=Link active */
93
94 /** \brief DP83848 PHY ID register definitions */
95 #define DP8_PHYID1_OUI     0x2000     /**< Expected PHY ID1 */
96 #define DP8_PHYID2_OUI     0x5c90     /**< Expected PHY ID2 */
97
98 /** \brief LAN8720 PHY Special Control/Status Register */
99 #define PHY_SCSR_100MBIT    0x0008    /**< Speed: 1=100 MBit, 0=10Mbit */
100 #define PHY_SCSR_DUPLEX     0x0010    /**< PHY Duplex Mask             */
101
102 /** \brief Link status bits */
103 #define LNK_STAT_VALID       0x01 
104 #define LNK_STAT_FULLDUPLEX  0x02
105 #define LNK_STAT_SPEED10MPS  0x04
106
107 /** \brief PHY ID definitions */
108 #define DP83848C_ID         0x20005C90  /**< PHY Identifier - DP83848C */
109 #define LAN8720_ID          0x0007C0F0  /**< PHY Identifier - LAN8720  */
110
111 /** \brief PHY status structure used to indicate current status of PHY.
112  */
113 typedef struct {
114         u32_t     phy_speed_100mbs:1; /**< 10/100 MBS connection speed flag. */
115         u32_t     phy_full_duplex:1;  /**< Half/full duplex connection speed flag. */
116         u32_t     phy_link_active:1;  /**< Phy link active flag. */
117 } PHY_STATUS_TYPE;
118
119 /** \brief  PHY update flags */
120 static PHY_STATUS_TYPE physts;
121
122 /** \brief  Last PHY update flags, used for determing if something has changed */
123 static PHY_STATUS_TYPE olddphysts;
124
125 /** \brief  PHY update counter for state machine */
126 static s32_t phyustate;
127
128 /** \brief  Holds the PHY ID */
129 static u32_t phy_id;
130
131 /** \brief  Temporary holder of link status for LAN7420 */
132 static u32_t phy_lan7420_sts_tmp;
133
134 /* Write a value via the MII link (non-blocking) */
135 void lpc_mii_write_noblock(u32_t PhyReg, u32_t Value)
136 {
137         /* Write value at PHY address and register */
138         LPC_EMAC->MADR = (LPC_PHYDEF_PHYADDR << 8) | PhyReg;
139         LPC_EMAC->MWTD = Value;
140 }
141
142 /* Write a value via the MII link (blocking) */
143 err_t lpc_mii_write(u32_t PhyReg, u32_t Value)
144 {
145         u32_t mst = 250;
146         err_t sts = ERR_OK;
147
148         /* Write value at PHY address and register */
149         lpc_mii_write_noblock(PhyReg, Value);
150
151         /* Wait for unbusy status */
152         while (mst > 0) {
153                 sts = LPC_EMAC->MIND;
154                 if ((sts & EMAC_MIND_BUSY) == 0)
155                         mst = 0;
156                 else {
157                         mst--;
158                         osDelay(1);
159                 }
160         }
161
162         if (sts != 0)
163                 sts = ERR_TIMEOUT;
164
165         return sts;
166 }
167
168 /* Reads current MII link busy status */
169 u32_t lpc_mii_is_busy(void)
170 {
171         return (u32_t) (LPC_EMAC->MIND & EMAC_MIND_BUSY);
172 }
173
174 /* Starts a read operation via the MII link (non-blocking) */
175 u32_t lpc_mii_read_data(void)
176 {
177         u32_t data = LPC_EMAC->MRDD;
178         LPC_EMAC->MCMD = 0;
179
180         return data;
181 }
182
183 /* Starts a read operation via the MII link (non-blocking) */
184 void lpc_mii_read_noblock(u32_t PhyReg)
185 {
186         /* Read value at PHY address and register */
187         LPC_EMAC->MADR = (LPC_PHYDEF_PHYADDR << 8) | PhyReg;
188         LPC_EMAC->MCMD = EMAC_MCMD_READ;
189 }
190
191 /* Read a value via the MII link (blocking) */
192 err_t lpc_mii_read(u32_t PhyReg, u32_t *data)
193 {
194         u32_t mst = 250;
195         err_t sts = ERR_OK;
196
197         /* Read value at PHY address and register */
198         lpc_mii_read_noblock(PhyReg);
199
200         /* Wait for unbusy status */
201         while (mst > 0) {
202                 sts = LPC_EMAC->MIND & ~EMAC_MIND_MII_LINK_FAIL;
203                 if ((sts & EMAC_MIND_BUSY) == 0) {
204                         mst = 0;
205                         *data = LPC_EMAC->MRDD;
206                 } else {
207                         mst--;
208                         osDelay(1);
209                 }
210         }
211
212         LPC_EMAC->MCMD = 0;
213
214         if (sts != 0)
215                 sts = ERR_TIMEOUT;
216
217         return sts;
218 }
219
220
221
222 /** \brief  Update PHY status from passed value
223  *
224  *  This function updates the current PHY status based on the
225  *  passed PHY status word. The PHY status indicate if the link
226  *  is active, the connection speed, and duplex.
227  *
228  *  \param[in]    netif   NETIF structure
229  *  \param[in]    linksts Status word from PHY
230  *  \return        1 if the status has changed, otherwise 0
231  */
232 static s32_t lpc_update_phy_sts(struct netif *netif, u32_t linksts)
233 {
234         s32_t changed = 0;
235
236         /* Update link active status */
237         if (linksts & LNK_STAT_VALID)
238                 physts.phy_link_active = 1;
239         else
240                 physts.phy_link_active = 0;
241
242         /* Full or half duplex */
243         if (linksts & LNK_STAT_FULLDUPLEX)
244                 physts.phy_full_duplex = 1;
245         else
246                 physts.phy_full_duplex = 0;
247
248         /* Configure 100MBit/10MBit mode. */
249         if (linksts & LNK_STAT_SPEED10MPS)
250                 physts.phy_speed_100mbs = 0;
251         else
252                 physts.phy_speed_100mbs = 1;
253
254         if (physts.phy_speed_100mbs != olddphysts.phy_speed_100mbs) {
255                 changed = 1;
256                 if (physts.phy_speed_100mbs) {
257                         /* 100MBit mode. */
258                         lpc_emac_set_speed(1);
259
260                         NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, 100000000);
261                 }
262                 else {
263                         /* 10MBit mode. */
264                         lpc_emac_set_speed(0);
265
266                         NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, 10000000);
267                 }
268
269                 olddphysts.phy_speed_100mbs = physts.phy_speed_100mbs;
270         }
271
272         if (physts.phy_full_duplex != olddphysts.phy_full_duplex) {
273                 changed = 1;
274                 if (physts.phy_full_duplex)
275                         lpc_emac_set_duplex(1);
276                 else
277                         lpc_emac_set_duplex(0);
278
279                 olddphysts.phy_full_duplex = physts.phy_full_duplex;
280         }
281
282         if (physts.phy_link_active != olddphysts.phy_link_active) {
283                 changed = 1;
284 #if NO_SYS == 1
285                 if (physts.phy_link_active)
286                         netif_set_link_up(netif);
287                 else
288                         netif_set_link_down(netif);
289 #else
290         if (physts.phy_link_active)
291             tcpip_callback_with_block((tcpip_callback_fn) netif_set_link_up,
292                 (void*) netif, 1);
293          else
294             tcpip_callback_with_block((tcpip_callback_fn) netif_set_link_down,
295                 (void*) netif, 1);
296 #endif
297
298                 olddphysts.phy_link_active = physts.phy_link_active;
299         }
300
301         return changed;
302 }
303
304 /** \brief  Initialize the DP83848 PHY.
305  *
306  *  This function initializes the DP83848 PHY. It will block until
307  *  complete. This function is called as part of the EMAC driver
308  *  initialization. Configuration of the PHY at startup is
309  *  controlled by setting up configuration defines in lpc_phy.h.
310  *
311  *  \param[in]     netif   NETIF structure
312  *  \param[in]     rmii    If set, configures the PHY for RMII mode
313  *  \return         ERR_OK if the setup was successful, otherwise ERR_TIMEOUT
314  */
315 err_t lpc_phy_init(struct netif *netif, int rmii)
316 {
317         u32_t tmp;
318         s32_t i;
319
320         physts.phy_speed_100mbs = olddphysts.phy_speed_100mbs = 0;
321         physts.phy_full_duplex = olddphysts.phy_full_duplex = 0;
322         physts.phy_link_active = olddphysts.phy_link_active = 0;
323         phyustate = 0;
324
325         /* Only first read and write are checked for failure */
326         /* Put the DP83848C in reset mode and wait for completion */
327         if (lpc_mii_write(DP8_BMCR_REG, DP8_RESET) != 0)
328                 return ERR_TIMEOUT;
329         i = 400;
330         while (i > 0) {
331             osDelay(1);   /* 1 ms */
332                 if (lpc_mii_read(DP8_BMCR_REG, &tmp) != 0)
333                         return ERR_TIMEOUT;
334
335                 if (!(tmp & (DP8_RESET | DP8_POWER_DOWN)))
336                         i = -1;
337                 else
338                         i--;
339         }
340         /* Timeout? */
341         if (i == 0)
342                 return ERR_TIMEOUT;
343
344         // read PHY ID
345         lpc_mii_read(DP8_IDR1_REG, &tmp);
346         phy_id = (tmp << 16);
347         lpc_mii_read(DP8_IDR2_REG, &tmp);    
348         phy_id |= (tmp & 0XFFF0);    
349
350         /* Setup link based on configuration options */
351 #if PHY_USE_AUTONEG==1
352         tmp = DP8_AUTONEG;
353 #else
354         tmp = 0;
355 #endif
356 #if PHY_USE_100MBS==1
357         tmp |= DP8_SPEED_SELECT;
358 #endif
359 #if PHY_USE_FULL_DUPLEX==1
360         tmp |= DP8_DUPLEX_MODE;
361 #endif
362         lpc_mii_write(DP8_BMCR_REG, tmp);
363
364     /* Enable RMII mode for PHY */
365     if (rmii)
366         lpc_mii_write(DP8_PHY_RBR_REG, DP8_RBR_RMII_MODE);
367
368         /* The link is not set active at this point, but will be detected
369        later */
370
371         return ERR_OK;
372 }
373
374 /* Phy status update state machine */
375 s32_t lpc_phy_sts_sm(struct netif *netif)
376 {
377         s32_t changed = 0;
378         u32_t data = 0;
379         u32_t tmp;
380
381         switch (phyustate) {
382                 default:
383                 case 0:
384                         if (phy_id == DP83848C_ID) {    
385                                 lpc_mii_read_noblock(DP8_PHY_STAT_REG);
386                                 phyustate = 2;
387                         }
388                         else if (phy_id == LAN8720_ID) {
389                                 lpc_mii_read_noblock(DP8_PHY_SCSR_REG);
390                                 phyustate = 1;        
391                         }
392                         break;
393
394                 case 1:
395                         if (phy_id == LAN8720_ID) {
396                                 tmp = lpc_mii_read_data();
397                                 // we get speed and duplex here. 
398                                 phy_lan7420_sts_tmp =  (tmp & PHY_SCSR_DUPLEX)  ? LNK_STAT_FULLDUPLEX : 0;
399                                 phy_lan7420_sts_tmp |= (tmp & PHY_SCSR_100MBIT) ? 0 : LNK_STAT_SPEED10MPS;
400
401                                 //read the status register to get link status 
402                                 lpc_mii_read_noblock(DP8_BMSR_REG);
403                                 phyustate = 2;        
404                         }
405                         break;
406
407                 case 2:
408                         /* Wait for read status state */
409                         if (!lpc_mii_is_busy()) {
410                                 /* Update PHY status */
411                                 tmp = lpc_mii_read_data();
412
413                                 if (phy_id == DP83848C_ID) {
414                                         // STS register contains all needed status bits
415                                         data  = (tmp & DP8_VALID_LINK) ? LNK_STAT_VALID : 0;
416                                         data |= (tmp & DP8_FULLDUPLEX) ? LNK_STAT_FULLDUPLEX : 0;
417                                         data |= (tmp & DP8_SPEED10MBPS) ? LNK_STAT_SPEED10MPS : 0;
418                                 }
419                                 else if (phy_id == LAN8720_ID) {    
420                                         // we only get the link status here.
421                                         phy_lan7420_sts_tmp |= (tmp & DP8_LINK_STATUS) ? LNK_STAT_VALID : 0;
422                                         data = phy_lan7420_sts_tmp;          
423                                 }
424
425                                 changed = lpc_update_phy_sts(netif, data);        
426                                 phyustate = 0;                
427                         }
428                         break;
429         }
430
431         return changed;
432 }
433
434 /**
435  * @}
436  */
437
438 /* --------------------------------- End Of File ------------------------------ */