This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / drivers / net / gianfar_phy.c
1 /* 
2  * drivers/net/gianfar_phy.c
3  *
4  * Gianfar Ethernet Driver -- PHY handling
5  * Driver for FEC on MPC8540 and TSEC on MPC8540/MPC8560
6  * Based on 8260_io/fcc_enet.c
7  *
8  * Author: Andy Fleming
9  * Maintainer: Kumar Gala (kumar.gala@freescale.com)
10  *
11  * Copyright 2004 Freescale Semiconductor, Inc
12  *
13  * This program is free software; you can redistribute  it and/or modify it
14  * under  the terms of  the GNU General  Public License as published by the
15  * Free Software Foundation;  either version 2 of the  License, or (at your
16  * option) any later version.
17  *
18  */
19
20 #include <linux/config.h>
21 #include <linux/kernel.h>
22 #include <linux/sched.h>
23 #include <linux/string.h>
24 #include <linux/errno.h>
25 #include <linux/slab.h>
26 #include <linux/interrupt.h>
27 #include <linux/init.h>
28 #include <linux/delay.h>
29 #include <linux/netdevice.h>
30 #include <linux/etherdevice.h>
31 #include <linux/skbuff.h>
32 #include <linux/spinlock.h>
33 #include <linux/mm.h>
34 #include <linux/mii.h>
35
36 #include <asm/io.h>
37 #include <asm/irq.h>
38 #include <asm/uaccess.h>
39 #include <linux/module.h>
40 #include <linux/version.h>
41 #include <linux/crc32.h>
42
43 #include "gianfar.h"
44 #include "gianfar_phy.h"
45
46 /* Write value to the PHY for this device to the register at regnum, */
47 /* waiting until the write is done before it returns.  All PHY */
48 /* configuration has to be done through the TSEC1 MIIM regs */
49 void write_phy_reg(struct net_device *dev, u16 regnum, u16 value)
50 {
51         struct gfar_private *priv = (struct gfar_private *) dev->priv;
52         struct gfar *regbase = priv->phyregs;
53         struct ocp_gfar_data *einfo = priv->einfo;
54
55         /* Set the PHY address and the register address we want to write */
56         gfar_write(&regbase->miimadd, ((einfo->phyid) << 8) | regnum);
57
58         /* Write out the value we want */
59         gfar_write(&regbase->miimcon, value);
60
61         /* Wait for the transaction to finish */
62         while (gfar_read(&regbase->miimind) & MIIMIND_BUSY)
63                 cpu_relax();
64 }
65
66 /* Reads from register regnum in the PHY for device dev, */
67 /* returning the value.  Clears miimcom first.  All PHY */
68 /* configuration has to be done through the TSEC1 MIIM regs */
69 u16 read_phy_reg(struct net_device *dev, u16 regnum)
70 {
71         struct gfar_private *priv = (struct gfar_private *) dev->priv;
72         struct gfar *regbase = priv->phyregs;
73         struct ocp_gfar_data *einfo = priv->einfo;
74         u16 value;
75
76         /* Set the PHY address and the register address we want to read */
77         gfar_write(&regbase->miimadd, ((einfo->phyid) << 8) | regnum);
78
79         /* Clear miimcom, and then initiate a read */
80         gfar_write(&regbase->miimcom, 0);
81         gfar_write(&regbase->miimcom, MIIM_READ_COMMAND);
82
83         /* Wait for the transaction to finish */
84         while (gfar_read(&regbase->miimind) & (MIIMIND_NOTVALID | MIIMIND_BUSY))
85                 cpu_relax();
86
87         /* Grab the value of the register from miimstat */
88         value = gfar_read(&regbase->miimstat);
89
90         return value;
91 }
92
93 /* returns which value to write to the control register. */
94 /* For 10/100 the value is slightly different. */
95 u16 mii_cr_init(u16 mii_reg, struct net_device * dev)
96 {
97         struct gfar_private *priv = (struct gfar_private *) dev->priv;
98         struct ocp_gfar_data *einfo = priv->einfo;
99
100         if (einfo->flags & GFAR_HAS_GIGABIT)
101                 return MIIM_CONTROL_INIT;
102         else
103                 return MIIM_CR_INIT;
104 }
105
106 #define BRIEF_GFAR_ERRORS
107 /* Wait for auto-negotiation to complete */
108 u16 mii_parse_sr(u16 mii_reg, struct net_device * dev)
109 {
110         struct gfar_private *priv = (struct gfar_private *) dev->priv;
111
112         unsigned int timeout = GFAR_AN_TIMEOUT;
113
114         if (mii_reg & MIIM_STATUS_LINK)
115                 priv->link = 1;
116         else
117                 priv->link = 0;
118
119         /* Only auto-negotiate if the link has just gone up */
120         if (priv->link && !priv->oldlink) {
121                 while ((!(mii_reg & MIIM_STATUS_AN_DONE)) && timeout--)
122                         mii_reg = read_phy_reg(dev, MIIM_STATUS);
123
124 #if defined(BRIEF_GFAR_ERRORS)
125                 if (mii_reg & MIIM_STATUS_AN_DONE)
126                         printk(KERN_INFO "%s: Auto-negotiation done\n",
127                                dev->name);
128                 else
129                         printk(KERN_INFO "%s: Auto-negotiation timed out\n",
130                                dev->name);
131 #endif
132         }
133
134         return 0;
135 }
136
137 /* Determine the speed and duplex which was negotiated */
138 u16 mii_parse_88E1011_psr(u16 mii_reg, struct net_device * dev)
139 {
140         struct gfar_private *priv = (struct gfar_private *) dev->priv;
141         unsigned int speed;
142
143         if (priv->link) {
144                 if (mii_reg & MIIM_88E1011_PHYSTAT_DUPLEX)
145                         priv->duplexity = 1;
146                 else
147                         priv->duplexity = 0;
148
149                 speed = (mii_reg & MIIM_88E1011_PHYSTAT_SPEED);
150
151                 switch (speed) {
152                 case MIIM_88E1011_PHYSTAT_GBIT:
153                         priv->speed = 1000;
154                         break;
155                 case MIIM_88E1011_PHYSTAT_100:
156                         priv->speed = 100;
157                         break;
158                 default:
159                         priv->speed = 10;
160                         break;
161                 }
162         } else {
163                 priv->speed = 0;
164                 priv->duplexity = 0;
165         }
166
167         return 0;
168 }
169
170 u16 mii_parse_cis8201(u16 mii_reg, struct net_device * dev)
171 {
172         struct gfar_private *priv = (struct gfar_private *) dev->priv;
173         unsigned int speed;
174
175         if (priv->link) {
176                 if (mii_reg & MIIM_CIS8201_AUXCONSTAT_DUPLEX)
177                         priv->duplexity = 1;
178                 else
179                         priv->duplexity = 0;
180
181                 speed = mii_reg & MIIM_CIS8201_AUXCONSTAT_SPEED;
182
183                 switch (speed) {
184                 case MIIM_CIS8201_AUXCONSTAT_GBIT:
185                         priv->speed = 1000;
186                         break;
187                 case MIIM_CIS8201_AUXCONSTAT_100:
188                         priv->speed = 100;
189                         break;
190                 default:
191                         priv->speed = 10;
192                         break;
193                 }
194         } else {
195                 priv->speed = 0;
196                 priv->duplexity = 0;
197         }
198
199         return 0;
200 }
201
202 u16 mii_parse_dm9161_scsr(u16 mii_reg, struct net_device * dev)
203 {
204         struct gfar_private *priv = (struct gfar_private *) dev->priv;
205
206         if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_100H))
207                 priv->speed = 100;
208         else
209                 priv->speed = 10;
210
211         if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_10F))
212                 priv->duplexity = 1;
213         else
214                 priv->duplexity = 0;
215
216         return 0;
217 }
218
219 u16 dm9161_wait(u16 mii_reg, struct net_device *dev)
220 {
221         int timeout = HZ;
222         int secondary = 10;
223         u16 temp;
224
225         do {
226
227                 /* Davicom takes a bit to come up after a reset,
228                  * so wait here for a bit */
229                 set_current_state(TASK_UNINTERRUPTIBLE);
230                 schedule_timeout(timeout);
231
232                 temp = read_phy_reg(dev, MIIM_STATUS);
233
234                 secondary--;
235         } while ((!(temp & MIIM_STATUS_AN_DONE)) && secondary);
236
237         return 0;
238 }
239
240 /*
241  * consult the BCM54xx auxilliary status register to find the link settings
242  */
243 u16 mii_parse_bcm54xx_sr(u16 mii_reg, struct net_device * dev)
244 {
245         struct gfar_private *priv = (struct gfar_private *) dev->priv;
246
247         /* Link modes of the BCM5400 PHY */
248         static const uint16_t link_table[8][3] = {
249                 { 0, 0          },      /* No link */
250                 { 0, 10         },      /* 10BT Half Duplex */
251                 { 1, 10         },      /* 10BT Full Duplex */
252                 { 0, 100        },      /* 100BT Half Duplex */
253                 { 0, 100        },      /* 100BT Half Duplex */
254                 { 1, 100        },      /* 100BT Full Duplex*/
255                 { 1, 1000       },      /* 1000BT */
256                 { 1, 1000       },      /* 1000BT */
257         };
258
259         uint16_t link_mode;
260
261         link_mode = mii_reg & MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK;
262         link_mode >>= MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT;
263
264         priv->duplexity = link_table[link_mode][0];
265         priv->speed = link_table[link_mode][1];
266
267         return 0;
268 }
269
270 static struct phy_info phy_info_M88E1011S = {
271         0x01410c6,
272         "Marvell 88E1011S",
273         4,
274         (const struct phy_cmd[]) {      /* config */
275                 /* Reset and configure the PHY */
276                 {MIIM_CONTROL, MIIM_CONTROL_INIT, mii_cr_init},
277                 {miim_end,}
278         },
279         (const struct phy_cmd[]) {      /* startup */
280                 /* Status is read once to clear old link state */
281                 {MIIM_STATUS, miim_read, NULL},
282                 /* Auto-negotiate */
283                 {MIIM_STATUS, miim_read, mii_parse_sr},
284                 /* Read the status */
285                 {MIIM_88E1011_PHY_STATUS, miim_read, mii_parse_88E1011_psr},
286                 /* Clear the IEVENT register */
287                 {MIIM_88E1011_IEVENT, miim_read, NULL},
288                 /* Set up the mask */
289                 {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_INIT, NULL},
290                 {miim_end,}
291         },
292         (const struct phy_cmd[]) {      /* ack_int */
293                 /* Clear the interrupt */
294                 {MIIM_88E1011_IEVENT, miim_read, NULL},
295                 /* Disable interrupts */
296                 {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_CLEAR, NULL},
297                 {miim_end,}
298         },
299         (const struct phy_cmd[]) {      /* handle_int */
300                 /* Read the Status (2x to make sure link is right) */
301                 {MIIM_STATUS, miim_read, NULL},
302                 /* Check the status */
303                 {MIIM_STATUS, miim_read, mii_parse_sr},
304                 {MIIM_88E1011_PHY_STATUS, miim_read, mii_parse_88E1011_psr},
305                         /* Enable Interrupts */
306                 {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_INIT, NULL},
307                 {miim_end,}
308         },
309         (const struct phy_cmd[]) {      /* shutdown */
310                 {MIIM_88E1011_IEVENT, miim_read, NULL},
311                 {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_CLEAR, NULL},
312                 {miim_end,}
313         },
314 };
315
316 /* Cicada 8204 */
317 static struct phy_info phy_info_cis8204 = {
318         0x3f11,
319         "Cicada Cis8204",
320         6,
321         (const struct phy_cmd[]) {      /* config */
322                 /* Override PHY config settings */
323                 {MIIM_CIS8201_AUX_CONSTAT, MIIM_CIS8201_AUXCONSTAT_INIT, NULL},
324                 /* Set up the interface mode */
325                 {MIIM_CIS8201_EXT_CON1, MIIM_CIS8201_EXTCON1_INIT, NULL},
326                 /* Configure some basic stuff */
327                 {MIIM_CONTROL, MIIM_CONTROL_INIT, mii_cr_init},
328                 {miim_end,}
329         },
330         (const struct phy_cmd[]) {      /* startup */
331                 /* Read the Status (2x to make sure link is right) */
332                 {MIIM_STATUS, miim_read, NULL},
333                 /* Auto-negotiate */
334                 {MIIM_STATUS, miim_read, mii_parse_sr},
335                 /* Read the status */
336                 {MIIM_CIS8201_AUX_CONSTAT, miim_read, mii_parse_cis8201},
337                 /* Clear the status register */
338                 {MIIM_CIS8204_ISTAT, miim_read, NULL},
339                 /* Enable interrupts */
340                 {MIIM_CIS8204_IMASK, MIIM_CIS8204_IMASK_MASK, NULL},
341                 {miim_end,}
342         },
343         (const struct phy_cmd[]) {      /* ack_int */
344                 /* Clear the status register */
345                 {MIIM_CIS8204_ISTAT, miim_read, NULL},
346                 /* Disable interrupts */
347                 {MIIM_CIS8204_IMASK, 0x0, NULL},
348                 {miim_end,}
349         },
350         (const struct phy_cmd[]) {      /* handle_int */
351                 /* Read the Status (2x to make sure link is right) */
352                 {MIIM_STATUS, miim_read, NULL},
353                 /* Auto-negotiate */
354                 {MIIM_STATUS, miim_read, mii_parse_sr},
355                 /* Read the status */
356                 {MIIM_CIS8201_AUX_CONSTAT, miim_read, mii_parse_cis8201},
357                 /* Enable interrupts */
358                 {MIIM_CIS8204_IMASK, MIIM_CIS8204_IMASK_MASK, NULL},
359                 {miim_end,}
360         },
361         (const struct phy_cmd[]) {      /* shutdown */
362                 /* Clear the status register */
363                 {MIIM_CIS8204_ISTAT, miim_read, NULL},
364                 /* Disable interrupts */
365                 {MIIM_CIS8204_IMASK, 0x0, NULL},
366                 {miim_end,}
367         },
368 };
369
370 /* Cicada 8201 */
371 static struct phy_info phy_info_cis8201 = {
372         0xfc41,
373         "CIS8201",
374         4,
375         (const struct phy_cmd[]) {      /* config */
376                 /* Override PHY config settings */
377                 {MIIM_CIS8201_AUX_CONSTAT, MIIM_CIS8201_AUXCONSTAT_INIT, NULL},
378                 /* Set up the interface mode */
379                 {MIIM_CIS8201_EXT_CON1, MIIM_CIS8201_EXTCON1_INIT, NULL},
380                 /* Configure some basic stuff */
381                 {MIIM_CONTROL, MIIM_CONTROL_INIT, mii_cr_init},
382                 {miim_end,}
383         },
384         (const struct phy_cmd[]) {      /* startup */
385                 /* Read the Status (2x to make sure link is right) */
386                 {MIIM_STATUS, miim_read, NULL},
387                 /* Auto-negotiate */
388                 {MIIM_STATUS, miim_read, mii_parse_sr},
389                 /* Read the status */
390                 {MIIM_CIS8201_AUX_CONSTAT, miim_read, mii_parse_cis8201},
391                 {miim_end,}
392         },
393         (const struct phy_cmd[]) {      /* ack_int */
394                 {miim_end,}
395         },
396         (const struct phy_cmd[]) {      /* handle_int */
397                 {miim_end,}
398         },
399         (const struct phy_cmd[]) {      /* shutdown */
400                 {miim_end,}
401         },
402 };
403
404 static struct phy_info phy_info_dm9161 = {
405         0x0181b88,
406         "Davicom DM9161E",
407         4,
408         (const struct phy_cmd[]) {      /* config */
409                 {MIIM_CONTROL, MIIM_DM9161_CR_STOP, NULL},
410                 /* Do not bypass the scrambler/descrambler */
411                 {MIIM_DM9161_SCR, MIIM_DM9161_SCR_INIT, NULL},
412                 /* Clear 10BTCSR to default */
413                 {MIIM_DM9161_10BTCSR, MIIM_DM9161_10BTCSR_INIT, NULL},
414                 /* Configure some basic stuff */
415                 {MIIM_CONTROL, MIIM_CR_INIT, NULL},
416                 {miim_end,}
417         },
418         (const struct phy_cmd[]) {      /* startup */
419                 /* Restart Auto Negotiation */
420                 {MIIM_CONTROL, MIIM_DM9161_CR_RSTAN, NULL},
421                 /* Status is read once to clear old link state */
422                 {MIIM_STATUS, miim_read, dm9161_wait},
423                 /* Auto-negotiate */
424                 {MIIM_STATUS, miim_read, mii_parse_sr},
425                 /* Read the status */
426                 {MIIM_DM9161_SCSR, miim_read, mii_parse_dm9161_scsr},
427                 /* Clear any pending interrupts */
428                 {MIIM_DM9161_INTR, miim_read, NULL},
429                 {miim_end,}
430         },
431         (const struct phy_cmd[]) {      /* ack_int */
432                 {MIIM_DM9161_INTR, miim_read, NULL},
433                 {miim_end,}
434         },
435         (const struct phy_cmd[]) {      /* handle_int */
436                 {MIIM_STATUS, miim_read, NULL},
437                 {MIIM_STATUS, miim_read, mii_parse_sr},
438                 {MIIM_DM9161_SCSR, miim_read, mii_parse_dm9161_scsr},
439                 {miim_end,}
440         },
441         (const struct phy_cmd[]) {      /* shutdown */
442                 {MIIM_DM9161_INTR, miim_read, NULL},
443                 {miim_end,}
444         },
445 };
446
447 /* Broadcom BCM5421S PHY */
448 static struct phy_info phy_info_bcm5421s = {
449         .id = 0x2060E1,
450         .name = "Broadcom BCM5421S",
451         .shift = 0,
452         .config = (const struct phy_cmd[]) {
453                 /* Configure some basic stuff */
454                 {MIIM_CONTROL, MIIM_CR_INIT, NULL},
455 #if 0 /* 5421 only */
456                 miim_write(MII_BCM5400_AUXCONTROL, 0x1007),
457                 miim_set_bits(MII_BCM5400_AUXCONTROL, 0x0400),
458                 miim_write(MII_BCM5400_AUXCONTROL, 0x0007),
459                 miim_set_bits(MII_BCM5400_AUXCONTROL, 0x0800),
460                 miim_write(0x17, 0x000a),
461                 miim_set_bits(MII_RERRCOUNTER, 0x0200),
462 #endif
463 #if 0 /* enable automatic low power */
464                 miim_write(MII_NCONFIG, 0x9002),
465                 miim_write(MII_NCONFIG, 0xa821),
466                 miim_write(MII_NCONFIG, 0x941d),
467 #endif
468                 {miim_end,}
469         },
470         .startup = (const struct phy_cmd[]) {
471                 /* Restart Auto Negotiation */
472                 miim_set_bits(MIIM_CONTROL, BMCR_ANENABLE | BMCR_ANRESTART),
473 #if 0
474                 /* Status is read once to clear old link state */
475                 {MIIM_STATUS, miim_read, dm9161_wait},
476 #endif
477                 /* Auto-negotiate */
478                 {MIIM_STATUS, miim_read, mii_parse_sr},
479
480                 /* Read the link status */
481                 {MIIM_BCM54xx_AUXSTATUS, miim_read, mii_parse_bcm54xx_sr},
482
483                 {miim_end,}
484         },
485         .ack_int = (const struct phy_cmd[]) {
486                 {miim_end,}
487         },
488         .handle_int = (const struct phy_cmd[]) {
489                 {MIIM_STATUS, miim_read, NULL},
490                 {MIIM_STATUS, miim_read, mii_parse_sr},
491                 {miim_end,}
492         },
493         .shutdown = (const struct phy_cmd[]) {
494                 {miim_end,}
495         },
496 };
497
498 static struct phy_info *phy_info[] = {
499         &phy_info_cis8201,
500         &phy_info_cis8204,
501         &phy_info_M88E1011S,
502         &phy_info_dm9161,
503         &phy_info_bcm5421s,
504         NULL
505 };
506
507 /* Use the PHY ID registers to determine what type of PHY is attached
508  * to device dev.  return a struct phy_info structure describing that PHY
509  */
510 struct phy_info * get_phy_info(struct net_device *dev)
511 {
512         u16 phy_reg;
513         u32 phy_ID;
514         int i;
515         struct phy_info *theInfo = NULL;
516
517         /* Grab the bits from PHYIR1, and put them in the upper half */
518         phy_reg = read_phy_reg(dev, MIIM_PHYIR1);
519         phy_ID = (phy_reg & 0xffff) << 16;
520
521         /* Grab the bits from PHYIR2, and put them in the lower half */
522         phy_reg = read_phy_reg(dev, MIIM_PHYIR2);
523         phy_ID |= (phy_reg & 0xffff);
524
525         /* loop through all the known PHY types, and find one that */
526         /* matches the ID we read from the PHY. */
527         for (i = 0; phy_info[i]; i++)
528                 if (phy_info[i]->id == (phy_ID >> phy_info[i]->shift))
529                         theInfo = phy_info[i];
530
531         if (theInfo == NULL) {
532                 printk("%s: PHY id %x is not supported!\n", dev->name, phy_ID);
533                 return NULL;
534         } else {
535                 printk("%s: PHY is %s (%x)\n", dev->name, theInfo->name,
536                        phy_ID);
537         }
538
539         return theInfo;
540 }
541
542 /* Take a list of struct phy_cmd, and, depending on the values, either */
543 /* read or write, using a helper function if provided */
544 /* It is assumed that all lists of struct phy_cmd will be terminated by */
545 /* mii_end. */
546 void phy_run_commands(struct net_device *dev, const struct phy_cmd *cmd)
547 {
548         int i;
549         u16 result;
550         struct gfar_private *priv = (struct gfar_private *) dev->priv;
551         struct gfar *phyregs = priv->phyregs;
552
553         /* Reset the management interface */
554         gfar_write(&phyregs->miimcfg, MIIMCFG_RESET);
555
556         /* Setup the MII Mgmt clock speed */
557         gfar_write(&phyregs->miimcfg, MIIMCFG_INIT_VALUE);
558
559         /* Wait until the bus is free */
560         while (gfar_read(&phyregs->miimind) & MIIMIND_BUSY)
561                 cpu_relax();
562
563         for (i = 0; cmd->mii_reg != miim_end; i++) {
564                 switch (cmd->mii_data >> 16) {
565                 case 0x0000:
566                         /* Otherwise, it's a write */
567                         /* If a function was supplied, it will provide 
568                          * the value to write */
569                         /* Otherwise, the value was supplied in cmd->mii_data */
570                         if (cmd->funct != NULL)
571                                 result = (*(cmd->funct)) (0, dev);
572                         else
573                                 result = cmd->mii_data;
574
575                         write_phy_reg(dev, cmd->mii_reg, result);
576                         break;
577
578                 case 0x0001:
579                         /* Read the value of the PHY reg */
580                         result = read_phy_reg(dev, cmd->mii_reg);
581
582                         /* If a function was supplied, we need to let it process */
583                         /* the result. */
584                         if (cmd->funct != NULL)
585                                 (*(cmd->funct)) (result, dev);
586                         break;
587
588                 case 0x0002:
589                         /* read the value, clear some bits and write it back */
590                         BUG_ON(cmd->funct);
591
592                         result = read_phy_reg(dev, cmd->mii_reg);
593                         result &= cmd->mii_data;
594                         write_phy_reg(dev, cmd->mii_reg, result);
595                         break;
596
597                 case 0x0003:
598                         /* read the value, set some bits and write it back */
599                         BUG_ON(cmd->funct);
600
601                         result = read_phy_reg(dev, cmd->mii_reg);
602                         result &= cmd->mii_data;
603                         write_phy_reg(dev, cmd->mii_reg, result);
604                         break;
605
606                 case 0x0004:
607                         /* read the value, flip some bits and write it back */
608                         BUG_ON(cmd->funct);
609
610                         result = read_phy_reg(dev, cmd->mii_reg);
611                         result &= cmd->mii_data;
612                         write_phy_reg(dev, cmd->mii_reg, result);
613                         break;
614
615                 default:
616                         printk("GIANFAR: Unknown MII command %08x\n",
617                                cmd->mii_data);
618                         BUG();
619                 }
620                 cmd++;
621         }
622 }