1/*  *********************************************************************
2    *  Broadcom Common Firmware Environment (CFE)
3    *
4    *  PHY hacking commands			File: ui_phycmds.c
5    *
6    *  These commands let you directly muck with the PHYs
7    *  attached to the Ethernet controllers.
8    *
9    *  Author:  Mitch Lichtenberg
10    *
11    *********************************************************************
12    *
13    *  Copyright 2000,2001,2002,2003
14    *  Broadcom Corporation. All rights reserved.
15    *
16    *  This software is furnished under license and may be used and
17    *  copied only in accordance with the following terms and
18    *  conditions.  Subject to these conditions, you may download,
19    *  copy, install, use, modify and distribute modified or unmodified
20    *  copies of this software in source and/or binary form.  No title
21    *  or ownership is transferred hereby.
22    *
23    *  1) Any source code used, modified or distributed must reproduce
24    *     and retain this copyright notice and list of conditions
25    *     as they appear in the source file.
26    *
27    *  2) No right is granted to use any trade name, trademark, or
28    *     logo of Broadcom Corporation.  The "Broadcom Corporation"
29    *     name may not be used to endorse or promote products derived
30    *     from this software without the prior written permission of
31    *     Broadcom Corporation.
32    *
33    *  3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
34    *     IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
35    *     WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
36    *     PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
37    *     SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
38    *     PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT,
39    *     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
40    *     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
41    *     GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
42    *     BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
43    *     OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
44    *     TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF
45    *     THE POSSIBILITY OF SUCH DAMAGE.
46    ********************************************************************* */
47
48
49
50#include "cfe.h"
51#include "sbmips.h"
52
53#include "ui_command.h"
54
55#include "sb1250_defs.h"
56#include "sb1250_regs.h"
57#include "sb1250_mac.h"
58
59#include "mii.h"
60
61
62#define PHY_READCSR(t) (*((volatile uint64_t *) (t)))
63#define PHY_WRITECSR(t,v) *((volatile uint64_t *) (t)) = (v)
64
65#define M_MAC_MDIO_DIR_OUTPUT	0		/* for clarity */
66
67#ifdef __long64
68typedef volatile uint64_t phy_port_t;
69typedef uint64_t phy_physaddr_t;
70#define PHY_PORT(x) PHYS_TO_K1(x)
71#else
72typedef volatile uint32_t phy_port_t;
73typedef uint32_t phy_physaddr_t;
74#define PHY_PORT(x) PHYS_TO_K1(x)
75#endif
76
77typedef struct phy_s {
78    phy_port_t sbe_mdio;
79} phy_t;
80
81
82int ui_init_phycmds(void);
83
84static int ui_cmd_phydump(ui_cmdline_t *cmd,int argc,char *argv[]);
85static int ui_cmd_physet(ui_cmdline_t *cmd,int argc,char *argv[]);
86
87static void phy_mii_write(phy_t *s,int phyaddr,int regidx,
88		     unsigned int regval);
89static unsigned int phy_mii_read(phy_t *s,int phyaddr,int regidx);
90
91
92int ui_init_phycmds(void)
93{
94    cmd_addcmd("phy dump",
95	       ui_cmd_phydump,
96	       NULL,
97	       "Dump the registers on the PHY",
98	       "phy dump macid [reg]\n\n"
99	       "This command displays the contents of the registers on the PHY\n"
100	       "attached to the specified Ethernet controller.  macid is the\n"
101	       "Ethernet controller ID (0..2 for the BCM1250) and reg\n"
102	       "is an optional register number (0..31).  By default, all registers\n"
103	       "are displayed.",
104	       "-phy=*;Specify PHY address (default=1)");
105
106    cmd_addcmd("phy set",
107	       ui_cmd_physet,
108	       NULL,
109	       "Set the value of a PHY register",
110	       "phy set macid reg value\n\n"
111	       "Sets the value of a register on a PHY.  macid is the Ethernet\n"
112	       "controller number (0..2 for the BCM1250), reg is the register\n"
113	       "number (0..31), and value is the 16-bit value to write to the\n"
114	       "register.\n",
115	       "-phy=*;Specify PHY address (default=1)");
116
117    return 0;
118}
119
120static int ui_cmd_physet(ui_cmdline_t *cmd,int argc,char *argv[])
121{
122    phy_t phy;
123    int phynum;
124    int mac;
125    char *x;
126    unsigned int value;
127    unsigned int reg;
128
129    x = cmd_getarg(cmd,0);
130    if (!x) return ui_showusage(cmd);
131
132    mac = atoi(x);
133    if ((mac < 0) || (mac > 3)) {
134	return ui_showerror(CFE_ERR_INV_PARAM,"Invalid MAC number");
135	}
136    phy.sbe_mdio = PHY_PORT(A_MAC_REGISTER(mac,R_MAC_MDIO));
137
138    if (cmd_sw_value(cmd,"-phy",&x)) {
139	phynum = atoi(x);
140	}
141    else phynum = 1;
142
143    x = cmd_getarg(cmd,1);
144    if (!x) return ui_showusage(cmd);
145    reg = atoi(x);
146    if ((reg < 0) || (reg > 31)) {
147	return ui_showerror(CFE_ERR_INV_PARAM,"Invalid phy register number");
148	}
149
150    x = cmd_getarg(cmd,2);
151    if (!x) return ui_showusage(cmd);
152    value = atoi(x) & 0xFFFF;
153
154    phy_mii_write(&phy,phynum,reg,value);
155
156    printf("Wrote 0x%04X to phy %d register 0x%02X on mac %d\n",
157	   value,phynum,reg,mac);
158
159    return 0;
160}
161
162static int ui_cmd_phydump(ui_cmdline_t *cmd,int argc,char *argv[])
163{
164    phy_t phy;
165    int phynum;
166    int idx;
167    int mac;
168    char *x;
169    unsigned int reg;
170    int allreg = 1;
171
172    x = cmd_getarg(cmd,0);
173    if (!x) return ui_showusage(cmd);
174
175    mac = atoi(x);
176    if ((mac < 0) || (mac > 3)) {
177	return ui_showerror(CFE_ERR_INV_PARAM,"Invalid MAC number");
178	}
179    phy.sbe_mdio = PHY_PORT(A_MAC_REGISTER(mac,R_MAC_MDIO));
180
181    if (cmd_sw_value(cmd,"-phy",&x)) {
182	phynum = atoi(x);
183	}
184    else phynum = 1;
185
186    x = cmd_getarg(cmd,1);
187    reg = 0;
188    if (x) {
189	reg = atoi(x);
190	if ((reg < 0) || (reg > 31)) {
191	    return ui_showerror(CFE_ERR_INV_PARAM,"Invalid phy register number");
192	    }
193	allreg = 0;
194	}
195
196    if (allreg) {
197	printf("** PHY registers on MAC %d PHY %d **\n",mac,phynum);
198	for (idx = 0; idx < 31; idx+=2) {
199	    printf("Reg 0x%02X  =  0x%04X   |  ",idx,phy_mii_read(&phy,phynum,idx));
200	    printf("Reg 0x%02X  =  0x%04X",idx+1,phy_mii_read(&phy,phynum,idx+1));
201	    printf("\n");
202	    }
203	}
204    else {
205	printf("Reg %02X = %04X\n",reg,phy_mii_read(&phy,phynum,reg));
206	}
207
208    return 0;
209
210}
211
212
213
214
215/*  *********************************************************************
216    *  PHY_MII_SYNC(s)
217    *
218    *  Synchronize with the MII - send a pattern of bits to the MII
219    *  that will guarantee that it is ready to accept a command.
220    *
221    *  Input parameters:
222    *  	   s - sbmac structure
223    *
224    *  Return value:
225    *  	   nothing
226    ********************************************************************* */
227
228static void phy_mii_sync(phy_t *s)
229{
230    int cnt;
231    uint64_t bits;
232    int mac_mdio_genc; /*genc bit needs to be saved*/
233
234    mac_mdio_genc = PHY_READCSR(s->sbe_mdio) & M_MAC_GENC;
235
236    bits = M_MAC_MDIO_DIR_OUTPUT | M_MAC_MDIO_OUT;
237
238    PHY_WRITECSR(s->sbe_mdio,bits | mac_mdio_genc);
239
240    for (cnt = 0; cnt < 32; cnt++) {
241	PHY_WRITECSR(s->sbe_mdio,bits | M_MAC_MDC | mac_mdio_genc);
242	cfe_usleep(100);
243	PHY_WRITECSR(s->sbe_mdio,bits | mac_mdio_genc);
244	cfe_usleep(100);
245	}
246
247}
248
249/*  *********************************************************************
250    *  PHY_MII_SENDDATA(s,data,bitcnt)
251    *
252    *  Send some bits to the MII.  The bits to be sent are right-
253    *  justified in the 'data' parameter.
254    *
255    *  Input parameters:
256    *  	   s - sbmac structure
257    *  	   data - data to send
258    *  	   bitcnt - number of bits to send
259    ********************************************************************* */
260
261static void phy_mii_senddata(phy_t *s,unsigned int data, int bitcnt)
262{
263    int i;
264    uint64_t bits;
265    unsigned int curmask;
266    int mac_mdio_genc;
267
268    mac_mdio_genc = PHY_READCSR(s->sbe_mdio) & M_MAC_GENC;
269
270    bits = M_MAC_MDIO_DIR_OUTPUT;
271    PHY_WRITECSR(s->sbe_mdio,bits | mac_mdio_genc);
272
273    curmask = 1 << (bitcnt - 1);
274
275    for (i = 0; i < bitcnt; i++) {
276	if (data & curmask) bits |= M_MAC_MDIO_OUT;
277	else bits &= ~M_MAC_MDIO_OUT;
278	PHY_WRITECSR(s->sbe_mdio,bits | mac_mdio_genc);
279	cfe_usleep(100);
280	PHY_WRITECSR(s->sbe_mdio,bits | M_MAC_MDC | mac_mdio_genc);
281	cfe_usleep(100);
282	PHY_WRITECSR(s->sbe_mdio,bits | mac_mdio_genc);
283	cfe_usleep(100);
284	curmask >>= 1;
285	}
286}
287
288
289
290/*  *********************************************************************
291    *  PHY_MII_READ(s,phyaddr,regidx)
292    *
293    *  Read a PHY register.
294    *
295    *  Input parameters:
296    *  	   s - sbmac structure
297    *  	   phyaddr - PHY's address
298    *  	   regidx = index of register to read
299    *
300    *  Return value:
301    *  	   value read, or 0 if an error occured.
302    ********************************************************************* */
303
304static unsigned int phy_mii_read(phy_t *s,int phyaddr,int regidx)
305{
306    int idx;
307    int error;
308    int regval;
309    int mac_mdio_genc;
310
311    /*
312     * Synchronize ourselves so that the PHY knows the next
313     * thing coming down is a command
314     */
315
316    phy_mii_sync(s);
317
318    /*
319     * Send the data to the PHY.  The sequence is
320     * a "start" command (2 bits)
321     * a "read" command (2 bits)
322     * the PHY addr (5 bits)
323     * the register index (5 bits)
324     */
325
326    phy_mii_senddata(s,MII_COMMAND_START, 2);
327    phy_mii_senddata(s,MII_COMMAND_READ, 2);
328    phy_mii_senddata(s,phyaddr, 5);
329    phy_mii_senddata(s,regidx, 5);
330
331    mac_mdio_genc = PHY_READCSR(s->sbe_mdio) & M_MAC_GENC;
332
333    /*
334     * Switch the port around without a clock transition.
335     */
336    PHY_WRITECSR(s->sbe_mdio,M_MAC_MDIO_DIR_INPUT | mac_mdio_genc);
337
338    /*
339     * Send out a clock pulse to signal we want the status
340     */
341
342    PHY_WRITECSR(s->sbe_mdio,M_MAC_MDIO_DIR_INPUT | M_MAC_MDC | mac_mdio_genc);
343    cfe_usleep(100);
344    PHY_WRITECSR(s->sbe_mdio,M_MAC_MDIO_DIR_INPUT | mac_mdio_genc);
345    cfe_usleep(100);
346
347    /*
348     * If an error occured, the PHY will signal '1' back
349     */
350    error = PHY_READCSR(s->sbe_mdio) & M_MAC_MDIO_IN;
351
352    /*
353     * Issue an 'idle' clock pulse, but keep the direction
354     * the same.
355     */
356    PHY_WRITECSR(s->sbe_mdio,M_MAC_MDIO_DIR_INPUT | M_MAC_MDC | mac_mdio_genc);
357    cfe_usleep(100);
358    PHY_WRITECSR(s->sbe_mdio,M_MAC_MDIO_DIR_INPUT | mac_mdio_genc);
359    cfe_usleep(100);
360
361    regval = 0;
362
363    for (idx = 0; idx < 16; idx++) {
364	regval <<= 1;
365
366	if (error == 0) {
367	    if (PHY_READCSR(s->sbe_mdio) & M_MAC_MDIO_IN) regval |= 1;
368	    }
369
370	PHY_WRITECSR(s->sbe_mdio,M_MAC_MDIO_DIR_INPUT | M_MAC_MDC | mac_mdio_genc);
371	cfe_usleep(100);
372	PHY_WRITECSR(s->sbe_mdio,M_MAC_MDIO_DIR_INPUT | mac_mdio_genc);
373	cfe_usleep(100);
374	}
375
376    /* Switch back to output */
377    PHY_WRITECSR(s->sbe_mdio,M_MAC_MDIO_DIR_OUTPUT | mac_mdio_genc);
378
379    if (error == 0) return regval;
380    return 0;
381}
382
383
384/*  *********************************************************************
385    *  PHY_MII_WRITE(s,phyaddr,regidx,regval)
386    *
387    *  Write a value to a PHY register.
388    *
389    *  Input parameters:
390    *  	   s - sbmac structure
391    *  	   phyaddr - PHY to use
392    *  	   regidx - register within the PHY
393    *  	   regval - data to write to register
394    *
395    *  Return value:
396    *  	   nothing
397    ********************************************************************* */
398
399static void phy_mii_write(phy_t *s,int phyaddr,int regidx,
400			    unsigned int regval)
401{
402    int mac_mdio_genc;
403
404    phy_mii_sync(s);
405
406    phy_mii_senddata(s,MII_COMMAND_START,2);
407    phy_mii_senddata(s,MII_COMMAND_WRITE,2);
408    phy_mii_senddata(s,phyaddr, 5);
409    phy_mii_senddata(s,regidx, 5);
410    phy_mii_senddata(s,MII_COMMAND_ACK,2);
411    phy_mii_senddata(s,regval,16);
412
413    mac_mdio_genc = PHY_READCSR(s->sbe_mdio) & M_MAC_GENC;
414
415    PHY_WRITECSR(s->sbe_mdio,M_MAC_MDIO_DIR_OUTPUT | mac_mdio_genc);
416}
417
418
419
420