summaryrefslogtreecommitdiffstats
path: root/src/server.c
blob: e693fe953ead7f68b2d9de87ca54f46bbbf06fe4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
/*
 * server.c - functions for creating a SSH server
 *
 * This file is part of the SSH Library
 *
 * Copyright (c) 2004-2005 by Aris Adamantiadis
 *
 * The SSH Library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or (at your
 * option) any later version.
 *
 * The SSH Library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with the SSH Library; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA.
 */

#include "config.h"

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#ifdef _WIN32
# include <winsock2.h>
# include <ws2tcpip.h>

  /*
   * <wspiapi.h> is necessary for getaddrinfo before Windows XP, but it isn't
   * available on some platforms like MinGW.
   */
# ifdef HAVE_WSPIAPI_H
#  include <wspiapi.h>
# endif
#else
# include <netinet/in.h>
#endif

#include "libssh/priv.h"
#include "libssh/libssh.h"
#include "libssh/server.h"
#include "libssh/ssh2.h"
#include "libssh/keyfiles.h"
#include "libssh/buffer.h"
#include "libssh/packet.h"
#include "libssh/socket.h"
#include "libssh/session.h"
#include "libssh/misc.h"
#include "libssh/keys.h"
#include "libssh/dh.h"
#include "libssh/messages.h"

#define set_status(session, status) do {\
        if (session->callbacks && session->callbacks->connect_status_function) \
            session->callbacks->connect_status_function(session->callbacks->userdata, status); \
    } while (0)

static int dh_handshake_server(ssh_session session);


/**
 * @addtogroup libssh_server
 *
 * @{
 */

extern char *supported_methods[];
/** @internal
 * This functions sets the Key Exchange protocols to be accepted
 * by the server. They depend on
 * -What the user asked (via options)
 * -What is available (keys)
 * It should then accept the intersection of what the user asked
 * and what is available, and return an error if nothing matches
 */

static int server_set_kex(ssh_session session) {
  KEX *server = &session->server_kex;
  int i, j;
  char *wanted;

  ZERO_STRUCTP(server);
  ssh_get_random(server->cookie, 16, 0);
  if (session->dsa_key != NULL && session->rsa_key != NULL) {
    if (ssh_options_set_algo(session, SSH_HOSTKEYS,
          "ssh-dss,ssh-rsa") < 0) {
      return -1;
    }
  } else if (session->dsa_key != NULL) {
    if (ssh_options_set_algo(session, SSH_HOSTKEYS, "ssh-dss") < 0) {
      return -1;
    }
  } else {
    if (ssh_options_set_algo(session, SSH_HOSTKEYS, "ssh-rsa") < 0) {
      return -1;
    }
  }

  server->methods = malloc(10 * sizeof(char *));
  if (server->methods == NULL) {
    return -1;
  }

  for (i = 0; i < 10; i++) {
    if ((wanted = session->wanted_methods[i]) == NULL) {
      wanted = supported_methods[i];
    }
    server->methods[i] = strdup(wanted);
    if (server->methods[i] == NULL) {
      for (j = i - 1; j <= 0; j--) {
        SAFE_FREE(server->methods[j]);
      }
      SAFE_FREE(server->methods);
      return -1;
    }
  }

  return 0;
}

SSH_PACKET_CALLBACK(ssh_packet_kexdh_init){
  ssh_string e;
  (void)type;
  (void)user;enter_function();
  ssh_log(session,SSH_LOG_PACKET,"Received SSH_MSG_KEXDH_INIT");
  if(session->dh_handshake_state != DH_STATE_INIT){
    ssh_log(session,SSH_LOG_RARE,"Invalid state for SSH_MSG_KEXDH_INIT");
    goto error;
  }
  e = buffer_get_ssh_string(packet);
  if (e == NULL) {
    ssh_set_error(session, SSH_FATAL, "No e number in client request");
    return -1;
  }
  if (dh_import_e(session, e) < 0) {
    ssh_set_error(session, SSH_FATAL, "Cannot import e number");
    session->session_state=SSH_SESSION_STATE_ERROR;
  } else {
    session->dh_handshake_state=DH_STATE_INIT_SENT;
    dh_handshake_server(session);
  }
  ssh_string_free(e);

  error:
  leave_function();
  return SSH_PACKET_USED;
}

static int dh_handshake_server(ssh_session session) {
  ssh_string f;
  ssh_string pubkey;
  ssh_string sign;
  ssh_public_key pub;
  ssh_private_key prv;

  if (dh_generate_y(session) < 0) {
    ssh_set_error(session, SSH_FATAL, "Could not create y number");
    return -1;
  }
  if (dh_generate_f(session) < 0) {
    ssh_set_error(session, SSH_FATAL, "Could not create f number");
    return -1;
  }

  f = dh_get_f(session);
  if (f == NULL) {
    ssh_set_error(session, SSH_FATAL, "Could not get the f number");
    return -1;
  }

  switch(session->hostkeys){
    case SSH_KEYTYPE_DSS:
      prv = session->dsa_key;
      break;
    case SSH_KEYTYPE_RSA:
      prv = session->rsa_key;
      break;
    default:
      prv = NULL;
  }

  pub = publickey_from_privatekey(prv);
  if (pub == NULL) {
    ssh_set_error(session, SSH_FATAL,
        "Could not get the public key from the private key");
    ssh_string_free(f);
    return -1;
  }
  pubkey = publickey_to_string(pub);
  publickey_free(pub);
  if (pubkey == NULL) {
    ssh_set_error(session, SSH_FATAL, "Not enough space");
    ssh_string_free(f);
    return -1;
  }

  dh_import_pubkey(session, pubkey);
  if (dh_build_k(session) < 0) {
    ssh_set_error(session, SSH_FATAL, "Could not import the public key");
    ssh_string_free(f);
    return -1;
  }

  if (make_sessionid(session) != SSH_OK) {
    ssh_set_error(session, SSH_FATAL, "Could not create a session id");
    ssh_string_free(f);
    return -1;
  }

  sign = ssh_sign_session_id(session, prv);
  if (sign == NULL) {
    ssh_set_error(session, SSH_FATAL, "Could not sign the session id");
    ssh_string_free(f);
    return -1;
  }

  /* Free private keys as they should not be readable after this point */
  if (session->rsa_key) {
    privatekey_free(session->rsa_key);
    session->rsa_key = NULL;
  }
  if (session->dsa_key) {
    privatekey_free(session->dsa_key);
    session->dsa_key = NULL;
  }

  if (buffer_add_u8(session->out_buffer, SSH2_MSG_KEXDH_REPLY) < 0 ||
      buffer_add_ssh_string(session->out_buffer, pubkey) < 0 ||
      buffer_add_ssh_string(session->out_buffer, f) < 0 ||
      buffer_add_ssh_string(session->out_buffer, sign) < 0) {
    ssh_set_error(session, SSH_FATAL, "Not enough space");
    buffer_reinit(session->out_buffer);
    ssh_string_free(f);
    ssh_string_free(sign);
    return -1;
  }
  ssh_string_free(f);
  ssh_string_free(sign);
  if (packet_send(session) == SSH_ERROR) {
    return -1;
  }

  if (buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) {
    buffer_reinit(session->out_buffer);
    return -1;
  }

  if (packet_send(session) == SSH_ERROR) {
    return -1;
  }
  ssh_log(session, SSH_LOG_PACKET, "SSH_MSG_NEWKEYS sent");
  session->dh_handshake_state=DH_STATE_NEWKEYS_SENT;

  return 0;
}

/**
 * @internal
 *
 * @brief A function to be called each time a step has been done in the
 * connection.
 */
static void ssh_server_connection_callback(ssh_session session){
	int ssh1,ssh2;
	enter_function();
	switch(session->session_state){
		case SSH_SESSION_STATE_NONE:
		case SSH_SESSION_STATE_CONNECTING:
		case SSH_SESSION_STATE_SOCKET_CONNECTED:
			break;
		case SSH_SESSION_STATE_BANNER_RECEIVED:
		  if (session->clientbanner == NULL) {
		    goto error;
		  }
		  set_status(session, 0.4f);
		  ssh_log(session, SSH_LOG_RARE,
		      "SSH client banner: %s", session->clientbanner);

		  /* Here we analyze the different protocols the server allows. */
		  if (ssh_analyze_banner(session, 1, &ssh1, &ssh2) < 0) {
		    goto error;
		  }
		  /* Here we decide which version of the protocol to use. */
		  if (ssh2 && session->ssh2) {
		    session->version = 2;
		  } else if(ssh1 && session->ssh1) {
		    session->version = 1;
		  } else if(ssh1 && !session->ssh1){
#ifdef WITH_SSH1
		    ssh_set_error(session, SSH_FATAL,
		        "SSH-1 protocol not available (configure session to allow SSH-1)");
		    goto error;
#else
		    ssh_set_error(session, SSH_FATAL,
		        "SSH-1 protocol not available (libssh compiled without SSH-1 support)");
		    goto error;
#endif
		  } else {
		    ssh_set_error(session, SSH_FATAL,
		        "No version of SSH protocol usable (banner: %s)",
		        session->clientbanner);
		    goto error;
		  }
		  /* from now, the packet layer is handling incoming packets */
		  if(session->version==2)
		    session->socket_callbacks.data=ssh_packet_socket_callback;
#ifdef WITH_SSH1
		  else
		    session->socket_callbacks.data=ssh_packet_socket_callback1;
#endif
		  ssh_packet_set_default_callbacks(session);
		  set_status(session, 0.5f);
		  session->session_state=SSH_SESSION_STATE_INITIAL_KEX;
          if (ssh_send_kex(session, 1) < 0) {
			goto error;
		  }
		  break;
		case SSH_SESSION_STATE_INITIAL_KEX:
		/* TODO: This state should disappear in favor of get_key handle */
#ifdef WITH_SSH1
			if(session->version==1){
				if (ssh_get_kex1(session) < 0)
					goto error;
				set_status(session,0.6f);
				session->connected = 1;
				break;
			}
#endif
			break;
		case SSH_SESSION_STATE_KEXINIT_RECEIVED:
			set_status(session,0.6f);
			ssh_list_kex(session, &session->client_kex); // log client kex
            crypt_set_algorithms_server(session);
			if (set_kex(session) < 0) {
				goto error;
			}
			set_status(session,0.8f);
			session->session_state=SSH_SESSION_STATE_DH;
            break;
		case SSH_SESSION_STATE_DH:
			if(session->dh_handshake_state==DH_STATE_FINISHED){
                if (generate_session_keys(session) < 0) {
                  goto error;
                }

                /*
                 * Once we got SSH2_MSG_NEWKEYS we can switch next_crypto and
                 * current_crypto
                 */
                if (session->current_crypto) {
                  crypto_free(session->current_crypto);
                }

                /* FIXME TODO later, include a function to change keys */
                session->current_crypto = session->next_crypto;
                session->next_crypto = crypto_new();
                if (session->next_crypto == NULL) {
                  goto error;
                }
				set_status(session,1.0f);
				session->connected = 1;
				session->session_state=SSH_SESSION_STATE_AUTHENTICATING;
            }
			break;
		case SSH_SESSION_STATE_AUTHENTICATING:
			break;
		case SSH_SESSION_STATE_ERROR:
			goto error;
		default:
			ssh_set_error(session,SSH_FATAL,"Invalid state %d",session->session_state);
	}
	leave_function();
	return;
	error:
	ssh_socket_close(session->socket);
	session->alive = 0;
	session->session_state=SSH_SESSION_STATE_ERROR;
	leave_function();
}

/**
 * @internal
 *
 * @brief Gets the banner from socket and saves it in session.
 * Updates the session state
 *
 * @param  data pointer to the beginning of header
 * @param  len size of the banner
 * @param  user is a pointer to session
 * @returns Number of bytes processed, or zero if the banner is not complete.
 */
static int callback_receive_banner(const void *data, size_t len, void *user) {
    char *buffer = (char *) data;
    ssh_session session = (ssh_session) user;
    char *str = NULL;
    size_t i;
    int ret=0;

    enter_function();

    for (i = 0; i < len; i++) {
#ifdef WITH_PCAP
        if(session->pcap_ctx && buffer[i] == '\n') {
            ssh_pcap_context_write(session->pcap_ctx,
                                   SSH_PCAP_DIR_IN,
                                   buffer,
                                   i + 1,
                                   i + 1);
        }
#endif
        if (buffer[i] == '\r') {
            buffer[i]='\0';
        }

        if (buffer[i] == '\n') {
            buffer[i]='\0';

            str = strdup(buffer);
            /* number of bytes read */
            ret = i + 1;
            session->clientbanner = str;
            session->session_state = SSH_SESSION_STATE_BANNER_RECEIVED;
            ssh_log(session, SSH_LOG_PACKET, "Received banner: %s", str);
            session->ssh_connection_callback(session);

            leave_function();
            return ret;
        }

        if(i > 127) {
            /* Too big banner */
            session->session_state = SSH_SESSION_STATE_ERROR;
            ssh_set_error(session, SSH_FATAL, "Receiving banner: too large banner");

            leave_function();
            return 0;
        }
    }

    leave_function();
    return ret;
}

/* Do the banner and key exchange */
int ssh_handle_key_exchange(ssh_session session) {
    int rc;

    rc = ssh_send_banner(session, 1);
    if (rc < 0) {
        return SSH_ERROR;
    }

    session->alive = 1;

    session->ssh_connection_callback = ssh_server_connection_callback;
    session->session_state = SSH_SESSION_STATE_SOCKET_CONNECTED;
    ssh_socket_set_callbacks(session->socket,&session->socket_callbacks);
    session->socket_callbacks.data=callback_receive_banner;
    session->socket_callbacks.exception=ssh_socket_exception_callback;
    session->socket_callbacks.userdata=session;

    rc = server_set_kex(session);
    if (rc < 0) {
        return SSH_ERROR;
    }

    while (session->session_state != SSH_SESSION_STATE_ERROR &&
           session->session_state != SSH_SESSION_STATE_AUTHENTICATING &&
           session->session_state != SSH_SESSION_STATE_DISCONNECTED) {
        /*
         * loop until SSH_SESSION_STATE_BANNER_RECEIVED or
         * SSH_SESSION_STATE_ERROR
         */
        ssh_handle_packets(session,-1);
        ssh_log(session,SSH_LOG_PACKET, "ssh_handle_key_exchange: Actual state : %d",
                session->session_state);
    }

    if (session->session_state == SSH_SESSION_STATE_ERROR ||
        session->session_state == SSH_SESSION_STATE_DISCONNECTED) {
        return SSH_ERROR;
    }

  return SSH_OK;
}

/* messages */

static int ssh_message_auth_reply_default(ssh_message msg,int partial) {
  ssh_session session = msg->session;
  char methods_c[128] = {0};
  ssh_string methods = NULL;
  int rc = SSH_ERROR;

  enter_function();

  if (buffer_add_u8(session->out_buffer, SSH2_MSG_USERAUTH_FAILURE) < 0) {
    return rc;
  }

  if (session->auth_methods == 0) {
    session->auth_methods = SSH_AUTH_METHOD_PUBLICKEY | SSH_AUTH_METHOD_PASSWORD;
  }
  if (session->auth_methods & SSH_AUTH_METHOD_PUBLICKEY) {
    strcat(methods_c, "publickey,");
  }
  if (session->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) {
    strcat(methods_c, "keyboard-interactive,");
  }
  if (session->auth_methods & SSH_AUTH_METHOD_PASSWORD) {
    strcat(methods_c, "password,");
  }
  if (session->auth_methods & SSH_AUTH_METHOD_HOSTBASED) {
    strcat(methods_c, "hostbased,");
  }

  /* Strip the comma. */
  methods_c[strlen(methods_c) - 1] = '\0'; // strip the comma. We are sure there is at

  ssh_log(session, SSH_LOG_PACKET,
      "Sending a auth failure. methods that can continue: %s", methods_c);

  methods = ssh_string_from_char(methods_c);
  if (methods == NULL) {
    goto error;
  }

  if (buffer_add_ssh_string(msg->session->out_buffer, methods) < 0) {
    goto error;
  }

  if (partial) {
    if (buffer_add_u8(session->out_buffer, 1) < 0) {
      goto error;
    }
  } else {
    if (buffer_add_u8(session->out_buffer, 0) < 0) {
      goto error;
    }
  }

  rc = packet_send(msg->session);
error:
  ssh_string_free(methods);

  leave_function();
  return rc;
}

static int ssh_message_channel_request_open_reply_default(ssh_message msg) {
  ssh_log(msg->session, SSH_LOG_FUNCTIONS, "Refusing a channel");

  if (buffer_add_u8(msg->session->out_buffer
        , SSH2_MSG_CHANNEL_OPEN_FAILURE) < 0) {
    goto error;
  }
  if (buffer_add_u32(msg->session->out_buffer,
        htonl(msg->channel_request_open.sender)) < 0) {
    goto error;
  }
  if (buffer_add_u32(msg->session->out_buffer,
        htonl(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED)) < 0) {
    goto error;
  }
  /* reason is an empty string */
  if (buffer_add_u32(msg->session->out_buffer, 0) < 0) {
    goto error;
  }
  /* language too */
  if (buffer_add_u32(msg->session->out_buffer, 0) < 0) {
    goto error;
  }

  return packet_send(msg->session);
error:
  return SSH_ERROR;
}

static int ssh_message_channel_request_reply_default(ssh_message msg) {
  uint32_t channel;

  if (msg->channel_request.want_reply) {
    channel = msg->channel_request.channel->remote_channel;

    ssh_log(msg->session, SSH_LOG_PACKET,
        "Sending a default channel_request denied to channel %d", channel);

    if (buffer_add_u8(msg->session->out_buffer, SSH2_MSG_CHANNEL_FAILURE) < 0) {
      return SSH_ERROR;
    }
    if (buffer_add_u32(msg->session->out_buffer, htonl(channel)) < 0) {
      return SSH_ERROR;
    }

    return packet_send(msg->session);
  }

  ssh_log(msg->session, SSH_LOG_PACKET,
      "The client doesn't want to know the request failed!");

  return SSH_OK;
}

static int ssh_message_service_request_reply_default(ssh_message msg) {
  /* The only return code accepted by specifications are success or disconnect */
  return ssh_message_service_reply_success(msg);
}

int ssh_message_service_reply_success(ssh_message msg) {
  struct ssh_string_struct *service;
  ssh_session session;

  if (msg == NULL) {
    return SSH_ERROR;
  }
  session = msg->session;

  ssh_log(session, SSH_LOG_PACKET,
      "Sending a SERVICE_ACCEPT for service %s", msg->service_request.service);
  if (buffer_add_u8(session->out_buffer, SSH2_MSG_SERVICE_ACCEPT) < 0) {
    return -1;
  }
  service=ssh_string_from_char(msg->service_request.service);
  if (buffer_add_ssh_string(session->out_buffer, service) < 0) {
    ssh_string_free(service);
    return -1;
  }
  ssh_string_free(service);
  return packet_send(msg->session);
}

int ssh_message_global_request_reply_success(ssh_message msg, uint16_t bound_port) {
    ssh_log(msg->session, SSH_LOG_FUNCTIONS, "Accepting a global request");

    if (msg->global_request.want_reply) {
        if (buffer_add_u8(msg->session->out_buffer
                    , SSH2_MSG_REQUEST_SUCCESS) < 0) {
            goto error;
        }

        if(msg->global_request.type == SSH_GLOBAL_REQUEST_TCPIP_FORWARD 
                                && msg->global_request.bind_port == 0) {
            if (buffer_add_u32(msg->session->out_buffer, htonl(bound_port)) < 0) {
                goto error;
            }
        }

        return packet_send(msg->session);
    }

    if(msg->global_request.type == SSH_GLOBAL_REQUEST_TCPIP_FORWARD 
                                && msg->global_request.bind_port == 0) {
        ssh_log(msg->session, SSH_LOG_PACKET,
                "The client doesn't want to know the remote port!");
    }

    return SSH_OK;
error:
    return SSH_ERROR;
}

static int ssh_message_global_request_reply_default(ssh_message msg) {
    ssh_log(msg->session, SSH_LOG_FUNCTIONS, "Refusing a global request");

    if (msg->global_request.want_reply) {
        if (buffer_add_u8(msg->session->out_buffer
                    , SSH2_MSG_REQUEST_FAILURE) < 0) {
            goto error;
        }
        return packet_send(msg->session);
    }
    ssh_log(msg->session, SSH_LOG_PACKET,
            "The client doesn't want to know the request failed!");

    return SSH_OK;
error:
    return SSH_ERROR;
}

int ssh_message_reply_default(ssh_message msg) {
  if (msg == NULL) {
    return -1;
  }

  switch(msg->type) {
    case SSH_REQUEST_AUTH:
      return ssh_message_auth_reply_default(msg, 0);
    case SSH_REQUEST_CHANNEL_OPEN:
      return ssh_message_channel_request_open_reply_default(msg);
    case SSH_REQUEST_CHANNEL:
      return ssh_message_channel_request_reply_default(msg);
    case SSH_REQUEST_SERVICE:
      return ssh_message_service_request_reply_default(msg);
    case SSH_REQUEST_GLOBAL:
      return ssh_message_global_request_reply_default(msg);
    default:
      ssh_log(msg->session, SSH_LOG_PACKET,
          "Don't know what to default reply to %d type",
          msg->type);
      break;
  }

  return -1;
}

const char *ssh_message_service_service(ssh_message msg){
  if (msg == NULL) {
    return NULL;
  }
  return msg->service_request.service;
}

const char *ssh_message_auth_user(ssh_message msg) {
  if (msg == NULL) {
    return NULL;
  }

  return msg->auth_request.username;
}

const char *ssh_message_auth_password(ssh_message msg){
  if (msg == NULL) {
    return NULL;
  }

  return msg->auth_request.password;
}

/* Get the publickey of an auth request */
ssh_public_key ssh_message_auth_publickey(ssh_message msg){
  if (msg == NULL) {
    return NULL;
  }

  return msg->auth_request.public_key;
}

enum ssh_publickey_state_e ssh_message_auth_publickey_state(ssh_message msg){
	if (msg == NULL) {
	    return -1;
	  }
	  return msg->auth_request.signature_state;
}

int ssh_message_auth_kbdint_is_response(ssh_message msg) {
  if (msg == NULL) {
    return -1;
  }

  return msg->auth_request.kbdint_response != 0;
}

int ssh_message_auth_set_methods(ssh_message msg, int methods) {
  if (msg == NULL || msg->session == NULL) {
    return -1;
  }

  msg->session->auth_methods = methods;

  return 0;
}

int ssh_message_auth_interactive_request(ssh_message msg, const char *name,
                            const char *instruction, unsigned int num_prompts,
                            const char **prompts, char *echo) {
  int r;
  unsigned int i = 0;
  ssh_string tmp = NULL;

  if(name == NULL || instruction == NULL) {
    return SSH_ERROR;
  }
  if(num_prompts > 0 && (prompts == NULL || echo == NULL)) {
    return SSH_ERROR;
  }

  if (buffer_add_u8(msg->session->out_buffer, SSH2_MSG_USERAUTH_INFO_REQUEST) < 0) {
    return SSH_ERROR;
  }

  /* name */
  tmp = ssh_string_from_char(name);
  if (buffer_add_ssh_string(msg->session->out_buffer, tmp) < 0) {
    return SSH_ERROR;
  }
  ssh_string_free(tmp); tmp = NULL;

  /* instruction */
  tmp = ssh_string_from_char(instruction);
  if (buffer_add_ssh_string(msg->session->out_buffer, tmp) < 0) {
    return SSH_ERROR;
  }
  ssh_string_free(tmp); tmp = NULL;

  /* language tag */
  tmp = ssh_string_from_char("");
  if (buffer_add_ssh_string(msg->session->out_buffer, tmp) < 0) {
    return SSH_ERROR;
  }
  ssh_string_free(tmp); tmp = NULL;

  /* num prompts */
  if (buffer_add_u32(msg->session->out_buffer, ntohl(num_prompts)) < 0) {
    return SSH_ERROR;
  }

  for(i = 0; i < num_prompts; i++) {
    /* prompt[i] */
    tmp = ssh_string_from_char(prompts[i]);
    if (buffer_add_ssh_string(msg->session->out_buffer, tmp) < 0) {
        goto error;
    }
    ssh_string_free(tmp); tmp = NULL;

    /* echo[i] */
    if (buffer_add_u8(msg->session->out_buffer, echo[i]) < 0) {
        return SSH_ERROR;
    }
  }

  r = packet_send(msg->session);

  /* fill in the kbdint structure */
  if (msg->session->kbdint == NULL) {
    ssh_log(msg->session, SSH_LOG_PROTOCOL, "Warning: Got a "
                                        "keyboard-interactive response but it "
                                        "seems we didn't send the request.");

    msg->session->kbdint = kbdint_new();
    if (msg->session->kbdint == NULL) {
      ssh_set_error_oom(msg->session);

      return SSH_ERROR;
    }
  } else {
    kbdint_clean(msg->session->kbdint);
  }

  msg->session->kbdint->name = strdup(name);
  if(msg->session->kbdint->name == NULL) {
      ssh_set_error_oom(msg->session);
      kbdint_free(msg->session->kbdint);
      msg->session->kbdint = NULL;
      return SSH_PACKET_USED;
  }
  msg->session->kbdint->instruction = strdup(instruction);
  if(msg->session->kbdint->instruction == NULL) {
      ssh_set_error_oom(msg->session);
      kbdint_free(msg->session->kbdint);
      msg->session->kbdint = NULL;
      return SSH_PACKET_USED;
  }

  msg->session->kbdint->nprompts = num_prompts;
  if(num_prompts > 0) {
    msg->session->kbdint->prompts = malloc(num_prompts * sizeof(char *));
    if (msg->session->kbdint->prompts == NULL) {
      msg->session->kbdint->nprompts = 0;
      ssh_set_error_oom(msg->session);
      kbdint_free(msg->session->kbdint);
      msg->session->kbdint = NULL;
      return SSH_ERROR;
    }
    msg->session->kbdint->echo = malloc(num_prompts * sizeof(char));
    if (msg->session->kbdint->echo == NULL) {
      ssh_set_error_oom(msg->session);
      kbdint_free(msg->session->kbdint);
      msg->session->kbdint = NULL;
      return SSH_ERROR;
    }
    for (i = 0; i < num_prompts; i++) {
      msg->session->kbdint->echo[i] = echo[i];
      msg->session->kbdint->prompts[i] = strdup(prompts[i]);
      if (msg->session->kbdint->prompts[i] == NULL) {
        ssh_set_error_oom(msg->session);
        msg->session->kbdint->nprompts = i;
        kbdint_free(msg->session->kbdint);
        msg->session->kbdint = NULL;
        return SSH_PACKET_USED;
      }
    }
  } else {
    msg->session->kbdint->prompts = NULL;
    msg->session->kbdint->echo = NULL;
  }

  return r;
error:
  if(tmp) ssh_string_free(tmp);
  return SSH_ERROR;
}

int ssh_message_auth_reply_success(ssh_message msg, int partial) {
  int r;

	if (msg == NULL) {
    return SSH_ERROR;
  }

  if (partial) {
    return ssh_message_auth_reply_default(msg, partial);
  }

  if (buffer_add_u8(msg->session->out_buffer,SSH2_MSG_USERAUTH_SUCCESS) < 0) {
    return SSH_ERROR;
  }

  r = packet_send(msg->session);
  if(msg->session->current_crypto && msg->session->current_crypto->delayed_compress_out){
  	ssh_log(msg->session,SSH_LOG_PROTOCOL,"Enabling delayed compression OUT");
  	msg->session->current_crypto->do_compress_out=1;
  }
  if(msg->session->current_crypto && msg->session->current_crypto->delayed_compress_in){
  	ssh_log(msg->session,SSH_LOG_PROTOCOL,"Enabling delayed compression IN");
  	msg->session->current_crypto->do_compress_in=1;
  }
  return r;
}

/* Answer OK to a pubkey auth request */
int ssh_message_auth_reply_pk_ok(ssh_message msg, ssh_string algo, ssh_string pubkey) {
  if (msg == NULL) {
    return SSH_ERROR;
  }

  if (buffer_add_u8(msg->session->out_buffer, SSH2_MSG_USERAUTH_PK_OK) < 0 ||
      buffer_add_ssh_string(msg->session->out_buffer, algo) < 0 ||
      buffer_add_ssh_string(msg->session->out_buffer, pubkey) < 0) {
    return SSH_ERROR;
  }

  return packet_send(msg->session);
}

int ssh_message_auth_reply_pk_ok_simple(ssh_message msg) {
	ssh_string algo;
	ssh_string pubkey;
	int ret;
	algo=ssh_string_from_char(msg->auth_request.public_key->type_c);
	pubkey=publickey_to_string(msg->auth_request.public_key);
	ret=ssh_message_auth_reply_pk_ok(msg,algo,pubkey);
	ssh_string_free(algo);
	ssh_string_free(pubkey);
	return ret;
}


const char *ssh_message_channel_request_open_originator(ssh_message msg){
    return msg->channel_request_open.originator;
}

int ssh_message_channel_request_open_originator_port(ssh_message msg){
    return msg->channel_request_open.originator_port;
}

const char *ssh_message_channel_request_open_destination(ssh_message msg){
    return msg->channel_request_open.destination;
}

int ssh_message_channel_request_open_destination_port(ssh_message msg){
    return msg->channel_request_open.destination_port;
}

ssh_channel ssh_message_channel_request_channel(ssh_message msg){
    return msg->channel_request.channel;
}

const char *ssh_message_channel_request_pty_term(ssh_message msg){
    return msg->channel_request.TERM;
}

int ssh_message_channel_request_pty_width(ssh_message msg){
    return msg->channel_request.width;
}

int ssh_message_channel_request_pty_height(ssh_message msg){
    return msg->channel_request.height;
}

int ssh_message_channel_request_pty_pxwidth(ssh_message msg){
    return msg->channel_request.pxwidth;
}

int ssh_message_channel_request_pty_pxheight(ssh_message msg){
    return msg->channel_request.pxheight;
}

const char *ssh_message_channel_request_env_name(ssh_message msg){
    return msg->channel_request.var_name;
}

const char *ssh_message_channel_request_env_value(ssh_message msg){
    return msg->channel_request.var_value;
}

const char *ssh_message_channel_request_command(ssh_message msg){
    return msg->channel_request.command;
}

const char *ssh_message_channel_request_subsystem(ssh_message msg){
    return msg->channel_request.subsystem;
}

int ssh_message_channel_request_x11_single_connection(ssh_message msg){
    return msg->channel_request.x11_single_connection ? 1 : 0;
}

const char *ssh_message_channel_request_x11_auth_protocol(ssh_message msg){
    return msg->channel_request.x11_auth_protocol;
}

const char *ssh_message_channel_request_x11_auth_cookie(ssh_message msg){
    return msg->channel_request.x11_auth_cookie;
}

int ssh_message_channel_request_x11_screen_number(ssh_message msg){
    return msg->channel_request.x11_screen_number;
}

const char *ssh_message_global_request_address(ssh_message msg){
    return msg->global_request.bind_address;
}

int ssh_message_global_request_port(ssh_message msg){
    return msg->global_request.bind_port;
}

/** @brief defines the ssh_message callback
 * @param session the current ssh session
 * @param[in] ssh_bind_message_callback a function pointer to a callback taking the
 * current ssh session and received message as parameters. the function returns
 * 0 if the message has been parsed and treated successfully, 1 otherwise (libssh
 * must take care of the response).
 * @param[in] data void pointer to be passed to callback functions
 */
void ssh_set_message_callback(ssh_session session,
        int(*ssh_bind_message_callback)(ssh_session session, ssh_message msg, void *data),
        void *data) {
  session->ssh_message_callback = ssh_bind_message_callback;
  session->ssh_message_callback_data = data;
}

int ssh_execute_message_callbacks(ssh_session session){
  ssh_message msg=NULL;
  int ret;
  ssh_handle_packets(session, 0);
  if(!session->ssh_message_list)
    return SSH_OK;
  if(session->ssh_message_callback){
    while((msg=ssh_message_pop_head(session)) != NULL) {
      ret=session->ssh_message_callback(session,msg,
                                        session->ssh_message_callback_data);
      if(ret==1){
        ret = ssh_message_reply_default(msg);
        ssh_message_free(msg);
        if(ret != SSH_OK)
          return ret;
      } else {
        ssh_message_free(msg);
      }
    }
  } else {
    while((msg=ssh_message_pop_head(session)) != NULL) {
      ret = ssh_message_reply_default(msg);
      ssh_message_free(msg);
      if(ret != SSH_OK)
        return ret;
    }
  }
  return SSH_OK;
}

/** @} */

/* vim: set ts=4 sw=4 et cindent: */
pt">) << 4; int size4Aling = (size >> 2) << 2; int notAlign = 0; if (size % 16) notAlign = 1; for (i = addr; (u32) i < (u32) addr + size16Aling; i += 16) printk("0x%08x: %08x %08x %08x %08x\r\n", (u32) i, *((u32 *) (i)), *((u32 *) (i + 4)), *((u32 *) (i + 8)), *((u32 *) (i + 12))); if (notAlign == 1) printk("0x%08x: ", (u32) i); for (; (u32) i < (u32) addr + size4Aling; i += 4) printk("%08x ", *((u32 *) (i))); for (; (u32) i < (u32) addr + size; i++) printk("%02x", *((u8 *) (i))); if (notAlign == 1) printk("\r\n"); } #endif /* DEBUG */ static struct list_head *dequeue(struct list_head *lh) { unsigned long flags; spin_lock_irqsave(&ugeth_lock, flags); if (!list_empty(lh)) { struct list_head *node = lh->next; list_del(node); spin_unlock_irqrestore(&ugeth_lock, flags); return node; } else { spin_unlock_irqrestore(&ugeth_lock, flags); return NULL; } } static struct sk_buff *get_new_skb(struct ucc_geth_private *ugeth, u8 __iomem *bd) { struct sk_buff *skb = NULL; skb = dev_alloc_skb(ugeth->ug_info->uf_info.max_rx_buf_length + UCC_GETH_RX_DATA_BUF_ALIGNMENT); if (skb == NULL) return NULL; /* We need the data buffer to be aligned properly. We will reserve * as many bytes as needed to align the data properly */ skb_reserve(skb, UCC_GETH_RX_DATA_BUF_ALIGNMENT - (((unsigned)skb->data) & (UCC_GETH_RX_DATA_BUF_ALIGNMENT - 1))); skb->dev = ugeth->dev; out_be32(&((struct qe_bd __iomem *)bd)->buf, dma_map_single(&ugeth->dev->dev, skb->data, ugeth->ug_info->uf_info.max_rx_buf_length + UCC_GETH_RX_DATA_BUF_ALIGNMENT, DMA_FROM_DEVICE)); out_be32((u32 __iomem *)bd, (R_E | R_I | (in_be32((u32 __iomem*)bd) & R_W))); return skb; } static int rx_bd_buffer_set(struct ucc_geth_private *ugeth, u8 rxQ) { u8 __iomem *bd; u32 bd_status; struct sk_buff *skb; int i; bd = ugeth->p_rx_bd_ring[rxQ]; i = 0; do { bd_status = in_be32((u32 __iomem *)bd); skb = get_new_skb(ugeth, bd); if (!skb) /* If can not allocate data buffer, abort. Cleanup will be elsewhere */ return -ENOMEM; ugeth->rx_skbuff[rxQ][i] = skb; /* advance the BD pointer */ bd += sizeof(struct qe_bd); i++; } while (!(bd_status & R_W)); return 0; } static int fill_init_enet_entries(struct ucc_geth_private *ugeth, u32 *p_start, u8 num_entries, u32 thread_size, u32 thread_alignment, enum qe_risc_allocation risc, int skip_page_for_first_entry) { u32 init_enet_offset; u8 i; int snum; for (i = 0; i < num_entries; i++) { if ((snum = qe_get_snum()) < 0) { if (netif_msg_ifup(ugeth)) ugeth_err("fill_init_enet_entries: Can not get SNUM."); return snum; } if ((i == 0) && skip_page_for_first_entry) /* First entry of Rx does not have page */ init_enet_offset = 0; else { init_enet_offset = qe_muram_alloc(thread_size, thread_alignment); if (IS_ERR_VALUE(init_enet_offset)) { if (netif_msg_ifup(ugeth)) ugeth_err("fill_init_enet_entries: Can not allocate DPRAM memory."); qe_put_snum((u8) snum); return -ENOMEM; } } *(p_start++) = ((u8) snum << ENET_INIT_PARAM_SNUM_SHIFT) | init_enet_offset | risc; } return 0; } static int return_init_enet_entries(struct ucc_geth_private *ugeth, u32 *p_start, u8 num_entries, enum qe_risc_allocation risc, int skip_page_for_first_entry) { u32 init_enet_offset; u8 i; int snum; for (i = 0; i < num_entries; i++) { u32 val = *p_start; /* Check that this entry was actually valid -- needed in case failed in allocations */ if ((val & ENET_INIT_PARAM_RISC_MASK) == risc) { snum = (u32) (val & ENET_INIT_PARAM_SNUM_MASK) >> ENET_INIT_PARAM_SNUM_SHIFT; qe_put_snum((u8) snum); if (!((i == 0) && skip_page_for_first_entry)) { /* First entry of Rx does not have page */ init_enet_offset = (val & ENET_INIT_PARAM_PTR_MASK); qe_muram_free(init_enet_offset); } *p_start++ = 0; } } return 0; } #ifdef DEBUG static int dump_init_enet_entries(struct ucc_geth_private *ugeth, u32 __iomem *p_start, u8 num_entries, u32 thread_size, enum qe_risc_allocation risc, int skip_page_for_first_entry) { u32 init_enet_offset; u8 i; int snum; for (i = 0; i < num_entries; i++) { u32 val = in_be32(p_start); /* Check that this entry was actually valid -- needed in case failed in allocations */ if ((val & ENET_INIT_PARAM_RISC_MASK) == risc) { snum = (u32) (val & ENET_INIT_PARAM_SNUM_MASK) >> ENET_INIT_PARAM_SNUM_SHIFT; qe_put_snum((u8) snum); if (!((i == 0) && skip_page_for_first_entry)) { /* First entry of Rx does not have page */ init_enet_offset = (in_be32(p_start) & ENET_INIT_PARAM_PTR_MASK); ugeth_info("Init enet entry %d:", i); ugeth_info("Base address: 0x%08x", (u32) qe_muram_addr(init_enet_offset)); mem_disp(qe_muram_addr(init_enet_offset), thread_size); } p_start++; } } return 0; } #endif static void put_enet_addr_container(struct enet_addr_container *enet_addr_cont) { kfree(enet_addr_cont); } static void set_mac_addr(__be16 __iomem *reg, u8 *mac) { out_be16(&reg[0], ((u16)mac[5] << 8) | mac[4]); out_be16(&reg[1], ((u16)mac[3] << 8) | mac[2]); out_be16(&reg[2], ((u16)mac[1] << 8) | mac[0]); } static int hw_clear_addr_in_paddr(struct ucc_geth_private *ugeth, u8 paddr_num) { struct ucc_geth_82xx_address_filtering_pram __iomem *p_82xx_addr_filt; if (!(paddr_num < NUM_OF_PADDRS)) { ugeth_warn("%s: Illagel paddr_num.", __func__); return -EINVAL; } p_82xx_addr_filt = (struct ucc_geth_82xx_address_filtering_pram __iomem *) ugeth->p_rx_glbl_pram-> addressfiltering; /* Writing address ff.ff.ff.ff.ff.ff disables address recognition for this register */ out_be16(&p_82xx_addr_filt->paddr[paddr_num].h, 0xffff); out_be16(&p_82xx_addr_filt->paddr[paddr_num].m, 0xffff); out_be16(&p_82xx_addr_filt->paddr[paddr_num].l, 0xffff); return 0; } static void hw_add_addr_in_hash(struct ucc_geth_private *ugeth, u8 *p_enet_addr) { struct ucc_geth_82xx_address_filtering_pram __iomem *p_82xx_addr_filt; u32 cecr_subblock; p_82xx_addr_filt = (struct ucc_geth_82xx_address_filtering_pram __iomem *) ugeth->p_rx_glbl_pram-> addressfiltering; cecr_subblock = ucc_fast_get_qe_cr_subblock(ugeth->ug_info->uf_info.ucc_num); /* Ethernet frames are defined in Little Endian mode, therefor to insert */ /* the address to the hash (Big Endian mode), we reverse the bytes.*/ set_mac_addr(&p_82xx_addr_filt->taddr.h, p_enet_addr); qe_issue_cmd(QE_SET_GROUP_ADDRESS, cecr_subblock, QE_CR_PROTOCOL_ETHERNET, 0); } #ifdef CONFIG_UGETH_MAGIC_PACKET static void magic_packet_detection_enable(struct ucc_geth_private *ugeth) { struct ucc_fast_private *uccf; struct ucc_geth __iomem *ug_regs; uccf = ugeth->uccf; ug_regs = ugeth->ug_regs; /* Enable interrupts for magic packet detection */ setbits32(uccf->p_uccm, UCC_GETH_UCCE_MPD); /* Enable magic packet detection */ setbits32(&ug_regs->maccfg2, MACCFG2_MPE); } static void magic_packet_detection_disable(struct ucc_geth_private *ugeth) { struct ucc_fast_private *uccf; struct ucc_geth __iomem *ug_regs; uccf = ugeth->uccf; ug_regs = ugeth->ug_regs; /* Disable interrupts for magic packet detection */ clrbits32(uccf->p_uccm, UCC_GETH_UCCE_MPD); /* Disable magic packet detection */ clrbits32(&ug_regs->maccfg2, MACCFG2_MPE); } #endif /* MAGIC_PACKET */ static inline int compare_addr(u8 **addr1, u8 **addr2) { return memcmp(addr1, addr2, ENET_NUM_OCTETS_PER_ADDRESS); } #ifdef DEBUG static void get_statistics(struct ucc_geth_private *ugeth, struct ucc_geth_tx_firmware_statistics * tx_firmware_statistics, struct ucc_geth_rx_firmware_statistics * rx_firmware_statistics, struct ucc_geth_hardware_statistics *hardware_statistics) { struct ucc_fast __iomem *uf_regs; struct ucc_geth __iomem *ug_regs; struct ucc_geth_tx_firmware_statistics_pram *p_tx_fw_statistics_pram; struct ucc_geth_rx_firmware_statistics_pram *p_rx_fw_statistics_pram; ug_regs = ugeth->ug_regs; uf_regs = (struct ucc_fast __iomem *) ug_regs; p_tx_fw_statistics_pram = ugeth->p_tx_fw_statistics_pram; p_rx_fw_statistics_pram = ugeth->p_rx_fw_statistics_pram; /* Tx firmware only if user handed pointer and driver actually gathers Tx firmware statistics */ if (tx_firmware_statistics && p_tx_fw_statistics_pram) { tx_firmware_statistics->sicoltx = in_be32(&p_tx_fw_statistics_pram->sicoltx); tx_firmware_statistics->mulcoltx = in_be32(&p_tx_fw_statistics_pram->mulcoltx); tx_firmware_statistics->latecoltxfr = in_be32(&p_tx_fw_statistics_pram->latecoltxfr); tx_firmware_statistics->frabortduecol = in_be32(&p_tx_fw_statistics_pram->frabortduecol); tx_firmware_statistics->frlostinmactxer = in_be32(&p_tx_fw_statistics_pram->frlostinmactxer); tx_firmware_statistics->carriersenseertx = in_be32(&p_tx_fw_statistics_pram->carriersenseertx); tx_firmware_statistics->frtxok = in_be32(&p_tx_fw_statistics_pram->frtxok); tx_firmware_statistics->txfrexcessivedefer = in_be32(&p_tx_fw_statistics_pram->txfrexcessivedefer); tx_firmware_statistics->txpkts256 = in_be32(&p_tx_fw_statistics_pram->txpkts256); tx_firmware_statistics->txpkts512 = in_be32(&p_tx_fw_statistics_pram->txpkts512); tx_firmware_statistics->txpkts1024 = in_be32(&p_tx_fw_statistics_pram->txpkts1024); tx_firmware_statistics->txpktsjumbo = in_be32(&p_tx_fw_statistics_pram->txpktsjumbo); } /* Rx firmware only if user handed pointer and driver actually * gathers Rx firmware statistics */ if (rx_firmware_statistics && p_rx_fw_statistics_pram) { int i; rx_firmware_statistics->frrxfcser = in_be32(&p_rx_fw_statistics_pram->frrxfcser); rx_firmware_statistics->fraligner = in_be32(&p_rx_fw_statistics_pram->fraligner); rx_firmware_statistics->inrangelenrxer = in_be32(&p_rx_fw_statistics_pram->inrangelenrxer); rx_firmware_statistics->outrangelenrxer = in_be32(&p_rx_fw_statistics_pram->outrangelenrxer); rx_firmware_statistics->frtoolong = in_be32(&p_rx_fw_statistics_pram->frtoolong); rx_firmware_statistics->runt = in_be32(&p_rx_fw_statistics_pram->runt); rx_firmware_statistics->verylongevent = in_be32(&p_rx_fw_statistics_pram->verylongevent); rx_firmware_statistics->symbolerror = in_be32(&p_rx_fw_statistics_pram->symbolerror); rx_firmware_statistics->dropbsy = in_be32(&p_rx_fw_statistics_pram->dropbsy); for (i = 0; i < 0x8; i++) rx_firmware_statistics->res0[i] = p_rx_fw_statistics_pram->res0[i]; rx_firmware_statistics->mismatchdrop = in_be32(&p_rx_fw_statistics_pram->mismatchdrop); rx_firmware_statistics->underpkts = in_be32(&p_rx_fw_statistics_pram->underpkts); rx_firmware_statistics->pkts256 = in_be32(&p_rx_fw_statistics_pram->pkts256); rx_firmware_statistics->pkts512 = in_be32(&p_rx_fw_statistics_pram->pkts512); rx_firmware_statistics->pkts1024 = in_be32(&p_rx_fw_statistics_pram->pkts1024); rx_firmware_statistics->pktsjumbo = in_be32(&p_rx_fw_statistics_pram->pktsjumbo); rx_firmware_statistics->frlossinmacer = in_be32(&p_rx_fw_statistics_pram->frlossinmacer); rx_firmware_statistics->pausefr = in_be32(&p_rx_fw_statistics_pram->pausefr); for (i = 0; i < 0x4; i++) rx_firmware_statistics->res1[i] = p_rx_fw_statistics_pram->res1[i]; rx_firmware_statistics->removevlan = in_be32(&p_rx_fw_statistics_pram->removevlan); rx_firmware_statistics->replacevlan = in_be32(&p_rx_fw_statistics_pram->replacevlan); rx_firmware_statistics->insertvlan = in_be32(&p_rx_fw_statistics_pram->insertvlan); } /* Hardware only if user handed pointer and driver actually gathers hardware statistics */ if (hardware_statistics && (in_be32(&uf_regs->upsmr) & UCC_GETH_UPSMR_HSE)) { hardware_statistics->tx64 = in_be32(&ug_regs->tx64); hardware_statistics->tx127 = in_be32(&ug_regs->tx127); hardware_statistics->tx255 = in_be32(&ug_regs->tx255); hardware_statistics->rx64 = in_be32(&ug_regs->rx64); hardware_statistics->rx127 = in_be32(&ug_regs->rx127); hardware_statistics->rx255 = in_be32(&ug_regs->rx255); hardware_statistics->txok = in_be32(&ug_regs->txok); hardware_statistics->txcf = in_be16(&ug_regs->txcf); hardware_statistics->tmca = in_be32(&ug_regs->tmca); hardware_statistics->tbca = in_be32(&ug_regs->tbca); hardware_statistics->rxfok = in_be32(&ug_regs->rxfok); hardware_statistics->rxbok = in_be32(&ug_regs->rxbok); hardware_statistics->rbyt = in_be32(&ug_regs->rbyt); hardware_statistics->rmca = in_be32(&ug_regs->rmca); hardware_statistics->rbca = in_be32(&ug_regs->rbca); } } static void dump_bds(struct ucc_geth_private *ugeth) { int i; int length; for (i = 0; i < ugeth->ug_info->numQueuesTx; i++) { if (ugeth->p_tx_bd_ring[i]) { length = (ugeth->ug_info->bdRingLenTx[i] * sizeof(struct qe_bd)); ugeth_info("TX BDs[%d]", i); mem_disp(ugeth->p_tx_bd_ring[i], length); } } for (i = 0; i < ugeth->ug_info->numQueuesRx; i++) { if (ugeth->p_rx_bd_ring[i]) { length = (ugeth->ug_info->bdRingLenRx[i] * sizeof(struct qe_bd)); ugeth_info("RX BDs[%d]", i); mem_disp(ugeth->p_rx_bd_ring[i], length); } } } static void dump_regs(struct ucc_geth_private *ugeth) { int i; ugeth_info("UCC%d Geth registers:", ugeth->ug_info->uf_info.ucc_num); ugeth_info("Base address: 0x%08x", (u32) ugeth->ug_regs); ugeth_info("maccfg1 : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->maccfg1, in_be32(&ugeth->ug_regs->maccfg1)); ugeth_info("maccfg2 : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->maccfg2, in_be32(&ugeth->ug_regs->maccfg2)); ugeth_info("ipgifg : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->ipgifg, in_be32(&ugeth->ug_regs->ipgifg)); ugeth_info("hafdup : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->hafdup, in_be32(&ugeth->ug_regs->hafdup)); ugeth_info("ifctl : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->ifctl, in_be32(&ugeth->ug_regs->ifctl)); ugeth_info("ifstat : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->ifstat, in_be32(&ugeth->ug_regs->ifstat)); ugeth_info("macstnaddr1: addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->macstnaddr1, in_be32(&ugeth->ug_regs->macstnaddr1)); ugeth_info("macstnaddr2: addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->macstnaddr2, in_be32(&ugeth->ug_regs->macstnaddr2)); ugeth_info("uempr : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->uempr, in_be32(&ugeth->ug_regs->uempr)); ugeth_info("utbipar : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->utbipar, in_be32(&ugeth->ug_regs->utbipar)); ugeth_info("uescr : addr - 0x%08x, val - 0x%04x", (u32) & ugeth->ug_regs->uescr, in_be16(&ugeth->ug_regs->uescr)); ugeth_info("tx64 : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->tx64, in_be32(&ugeth->ug_regs->tx64)); ugeth_info("tx127 : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->tx127, in_be32(&ugeth->ug_regs->tx127)); ugeth_info("tx255 : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->tx255, in_be32(&ugeth->ug_regs->tx255)); ugeth_info("rx64 : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->rx64, in_be32(&ugeth->ug_regs->rx64)); ugeth_info("rx127 : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->rx127, in_be32(&ugeth->ug_regs->rx127)); ugeth_info("rx255 : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->rx255, in_be32(&ugeth->ug_regs->rx255)); ugeth_info("txok : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->txok, in_be32(&ugeth->ug_regs->txok)); ugeth_info("txcf : addr - 0x%08x, val - 0x%04x", (u32) & ugeth->ug_regs->txcf, in_be16(&ugeth->ug_regs->txcf)); ugeth_info("tmca : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->tmca, in_be32(&ugeth->ug_regs->tmca)); ugeth_info("tbca : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->tbca, in_be32(&ugeth->ug_regs->tbca)); ugeth_info("rxfok : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->rxfok, in_be32(&ugeth->ug_regs->rxfok)); ugeth_info("rxbok : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->rxbok, in_be32(&ugeth->ug_regs->rxbok)); ugeth_info("rbyt : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->rbyt, in_be32(&ugeth->ug_regs->rbyt)); ugeth_info("rmca : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->rmca, in_be32(&ugeth->ug_regs->rmca)); ugeth_info("rbca : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->rbca, in_be32(&ugeth->ug_regs->rbca)); ugeth_info("scar : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->scar, in_be32(&ugeth->ug_regs->scar)); ugeth_info("scam : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->scam, in_be32(&ugeth->ug_regs->scam)); if (ugeth->p_thread_data_tx) { int numThreadsTxNumerical; switch (ugeth->ug_info->numThreadsTx) { case UCC_GETH_NUM_OF_THREADS_1: numThreadsTxNumerical = 1; break; case UCC_GETH_NUM_OF_THREADS_2: numThreadsTxNumerical = 2; break; case UCC_GETH_NUM_OF_THREADS_4: numThreadsTxNumerical = 4; break; case UCC_GETH_NUM_OF_THREADS_6: numThreadsTxNumerical = 6; break; case UCC_GETH_NUM_OF_THREADS_8: numThreadsTxNumerical = 8; break; default: numThreadsTxNumerical = 0; break; } ugeth_info("Thread data TXs:"); ugeth_info("Base address: 0x%08x", (u32) ugeth->p_thread_data_tx); for (i = 0; i < numThreadsTxNumerical; i++) { ugeth_info("Thread data TX[%d]:", i); ugeth_info("Base address: 0x%08x", (u32) & ugeth->p_thread_data_tx[i]); mem_disp((u8 *) & ugeth->p_thread_data_tx[i], sizeof(struct ucc_geth_thread_data_tx)); } } if (ugeth->p_thread_data_rx) { int numThreadsRxNumerical; switch (ugeth->ug_info->numThreadsRx) { case UCC_GETH_NUM_OF_THREADS_1: numThreadsRxNumerical = 1; break; case UCC_GETH_NUM_OF_THREADS_2: numThreadsRxNumerical = 2; break; case UCC_GETH_NUM_OF_THREADS_4: numThreadsRxNumerical = 4; break; case UCC_GETH_NUM_OF_THREADS_6: numThreadsRxNumerical = 6; break; case UCC_GETH_NUM_OF_THREADS_8: numThreadsRxNumerical = 8; break; default: numThreadsRxNumerical = 0; break; } ugeth_info("Thread data RX:"); ugeth_info("Base address: 0x%08x", (u32) ugeth->p_thread_data_rx); for (i = 0; i < numThreadsRxNumerical; i++) { ugeth_info("Thread data RX[%d]:", i); ugeth_info("Base address: 0x%08x", (u32) & ugeth->p_thread_data_rx[i]); mem_disp((u8 *) & ugeth->p_thread_data_rx[i], sizeof(struct ucc_geth_thread_data_rx)); } } if (ugeth->p_exf_glbl_param) { ugeth_info("EXF global param:"); ugeth_info("Base address: 0x%08x", (u32) ugeth->p_exf_glbl_param); mem_disp((u8 *) ugeth->p_exf_glbl_param, sizeof(*ugeth->p_exf_glbl_param)); } if (ugeth->p_tx_glbl_pram) { ugeth_info("TX global param:"); ugeth_info("Base address: 0x%08x", (u32) ugeth->p_tx_glbl_pram); ugeth_info("temoder : addr - 0x%08x, val - 0x%04x", (u32) & ugeth->p_tx_glbl_pram->temoder, in_be16(&ugeth->p_tx_glbl_pram->temoder)); ugeth_info("sqptr : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_tx_glbl_pram->sqptr, in_be32(&ugeth->p_tx_glbl_pram->sqptr)); ugeth_info("schedulerbasepointer: addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_tx_glbl_pram->schedulerbasepointer, in_be32(&ugeth->p_tx_glbl_pram-> schedulerbasepointer)); ugeth_info("txrmonbaseptr: addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_tx_glbl_pram->txrmonbaseptr, in_be32(&ugeth->p_tx_glbl_pram->txrmonbaseptr)); ugeth_info("tstate : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_tx_glbl_pram->tstate, in_be32(&ugeth->p_tx_glbl_pram->tstate)); ugeth_info("iphoffset[0] : addr - 0x%08x, val - 0x%02x", (u32) & ugeth->p_tx_glbl_pram->iphoffset[0], ugeth->p_tx_glbl_pram->iphoffset[0]); ugeth_info("iphoffset[1] : addr - 0x%08x, val - 0x%02x", (u32) & ugeth->p_tx_glbl_pram->iphoffset[1], ugeth->p_tx_glbl_pram->iphoffset[1]); ugeth_info("iphoffset[2] : addr - 0x%08x, val - 0x%02x", (u32) & ugeth->p_tx_glbl_pram->iphoffset[2], ugeth->p_tx_glbl_pram->iphoffset[2]); ugeth_info("iphoffset[3] : addr - 0x%08x, val - 0x%02x", (u32) & ugeth->p_tx_glbl_pram->iphoffset[3], ugeth->p_tx_glbl_pram->iphoffset[3]); ugeth_info("iphoffset[4] : addr - 0x%08x, val - 0x%02x", (u32) & ugeth->p_tx_glbl_pram->iphoffset[4], ugeth->p_tx_glbl_pram->iphoffset[4]); ugeth_info("iphoffset[5] : addr - 0x%08x, val - 0x%02x", (u32) & ugeth->p_tx_glbl_pram->iphoffset[5], ugeth->p_tx_glbl_pram->iphoffset[5]); ugeth_info("iphoffset[6] : addr - 0x%08x, val - 0x%02x", (u32) & ugeth->p_tx_glbl_pram->iphoffset[6], ugeth->p_tx_glbl_pram->iphoffset[6]); ugeth_info("iphoffset[7] : addr - 0x%08x, val - 0x%02x", (u32) & ugeth->p_tx_glbl_pram->iphoffset[7], ugeth->p_tx_glbl_pram->iphoffset[7]); ugeth_info("vtagtable[0] : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_tx_glbl_pram->vtagtable[0], in_be32(&ugeth->p_tx_glbl_pram->vtagtable[0])); ugeth_info("vtagtable[1] : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_tx_glbl_pram->vtagtable[1], in_be32(&ugeth->p_tx_glbl_pram->vtagtable[1])); ugeth_info("vtagtable[2] : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_tx_glbl_pram->vtagtable[2], in_be32(&ugeth->p_tx_glbl_pram->vtagtable[2])); ugeth_info("vtagtable[3] : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_tx_glbl_pram->vtagtable[3], in_be32(&ugeth->p_tx_glbl_pram->vtagtable[3])); ugeth_info("vtagtable[4] : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_tx_glbl_pram->vtagtable[4], in_be32(&ugeth->p_tx_glbl_pram->vtagtable[4])); ugeth_info("vtagtable[5] : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_tx_glbl_pram->vtagtable[5], in_be32(&ugeth->p_tx_glbl_pram->vtagtable[5])); ugeth_info("vtagtable[6] : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_tx_glbl_pram->vtagtable[6], in_be32(&ugeth->p_tx_glbl_pram->vtagtable[6])); ugeth_info("vtagtable[7] : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_tx_glbl_pram->vtagtable[7], in_be32(&ugeth->p_tx_glbl_pram->vtagtable[7])); ugeth_info("tqptr : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_tx_glbl_pram->tqptr, in_be32(&ugeth->p_tx_glbl_pram->tqptr)); } if (ugeth->p_rx_glbl_pram) { ugeth_info("RX global param:"); ugeth_info("Base address: 0x%08x", (u32) ugeth->p_rx_glbl_pram); ugeth_info("remoder : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_rx_glbl_pram->remoder, in_be32(&ugeth->p_rx_glbl_pram->remoder)); ugeth_info("rqptr : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_rx_glbl_pram->rqptr, in_be32(&ugeth->p_rx_glbl_pram->rqptr)); ugeth_info("typeorlen : addr - 0x%08x, val - 0x%04x", (u32) & ugeth->p_rx_glbl_pram->typeorlen, in_be16(&ugeth->p_rx_glbl_pram->typeorlen)); ugeth_info("rxgstpack : addr - 0x%08x, val - 0x%02x", (u32) & ugeth->p_rx_glbl_pram->rxgstpack, ugeth->p_rx_glbl_pram->rxgstpack); ugeth_info("rxrmonbaseptr : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_rx_glbl_pram->rxrmonbaseptr, in_be32(&ugeth->p_rx_glbl_pram->rxrmonbaseptr)); ugeth_info("intcoalescingptr: addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_rx_glbl_pram->intcoalescingptr, in_be32(&ugeth->p_rx_glbl_pram->intcoalescingptr)); ugeth_info("rstate : addr - 0x%08x, val - 0x%02x", (u32) & ugeth->p_rx_glbl_pram->rstate, ugeth->p_rx_glbl_pram->rstate); ugeth_info("mrblr : addr - 0x%08x, val - 0x%04x", (u32) & ugeth->p_rx_glbl_pram->mrblr, in_be16(&ugeth->p_rx_glbl_pram->mrblr)); ugeth_info("rbdqptr : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_rx_glbl_pram->rbdqptr, in_be32(&ugeth->p_rx_glbl_pram->rbdqptr)); ugeth_info("mflr : addr - 0x%08x, val - 0x%04x", (u32) & ugeth->p_rx_glbl_pram->mflr, in_be16(&ugeth->p_rx_glbl_pram->mflr)); ugeth_info("minflr : addr - 0x%08x, val - 0x%04x", (u32) & ugeth->p_rx_glbl_pram->minflr, in_be16(&ugeth->p_rx_glbl_pram->minflr)); ugeth_info("maxd1 : addr - 0x%08x, val - 0x%04x", (u32) & ugeth->p_rx_glbl_pram->maxd1, in_be16(&ugeth->p_rx_glbl_pram->maxd1)); ugeth_info("maxd2 : addr - 0x%08x, val - 0x%04x", (u32) & ugeth->p_rx_glbl_pram->maxd2, in_be16(&ugeth->p_rx_glbl_pram->maxd2)); ugeth_info("ecamptr : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_rx_glbl_pram->ecamptr, in_be32(&ugeth->p_rx_glbl_pram->ecamptr)); ugeth_info("l2qt : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_rx_glbl_pram->l2qt, in_be32(&ugeth->p_rx_glbl_pram->l2qt)); ugeth_info("l3qt[0] : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_rx_glbl_pram->l3qt[0], in_be32(&ugeth->p_rx_glbl_pram->l3qt[0])); ugeth_info("l3qt[1] : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_rx_glbl_pram->l3qt[1], in_be32(&ugeth->p_rx_glbl_pram->l3qt[1])); ugeth_info("l3qt[2] : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_rx_glbl_pram->l3qt[2], in_be32(&ugeth->p_rx_glbl_pram->l3qt[2])); ugeth_info("l3qt[3] : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_rx_glbl_pram->l3qt[3], in_be32(&ugeth->p_rx_glbl_pram->l3qt[3])); ugeth_info("l3qt[4] : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_rx_glbl_pram->l3qt[4], in_be32(&ugeth->p_rx_glbl_pram->l3qt[4])); ugeth_info("l3qt[5] : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_rx_glbl_pram->l3qt[5], in_be32(&ugeth->p_rx_glbl_pram->l3qt[5])); ugeth_info("l3qt[6] : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_rx_glbl_pram->l3qt[6], in_be32(&ugeth->p_rx_glbl_pram->l3qt[6])); ugeth_info("l3qt[7] : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_rx_glbl_pram->l3qt[7], in_be32(&ugeth->p_rx_glbl_pram->l3qt[7])); ugeth_info("vlantype : addr - 0x%08x, val - 0x%04x", (u32) & ugeth->p_rx_glbl_pram->vlantype, in_be16(&ugeth->p_rx_glbl_pram->vlantype)); ugeth_info("vlantci : addr - 0x%08x, val - 0x%04x", (u32) & ugeth->p_rx_glbl_pram->vlantci, in_be16(&ugeth->p_rx_glbl_pram->vlantci)); for (i = 0; i < 64; i++) ugeth_info ("addressfiltering[%d]: addr - 0x%08x, val - 0x%02x", i, (u32) & ugeth->p_rx_glbl_pram->addressfiltering[i], ugeth->p_rx_glbl_pram->addressfiltering[i]); ugeth_info("exfGlobalParam : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_rx_glbl_pram->exfGlobalParam, in_be32(&ugeth->p_rx_glbl_pram->exfGlobalParam)); } if (ugeth->p_send_q_mem_reg) { ugeth_info("Send Q memory registers:"); ugeth_info("Base address: 0x%08x", (u32) ugeth->p_send_q_mem_reg); for (i = 0; i < ugeth->ug_info->numQueuesTx; i++) { ugeth_info("SQQD[%d]:", i); ugeth_info("Base address: 0x%08x", (u32) & ugeth->p_send_q_mem_reg->sqqd[i]); mem_disp((u8 *) & ugeth->p_send_q_mem_reg->sqqd[i], sizeof(struct ucc_geth_send_queue_qd)); } } if (ugeth->p_scheduler) { ugeth_info("Scheduler:"); ugeth_info("Base address: 0x%08x", (u32) ugeth->p_scheduler); mem_disp((u8 *) ugeth->p_scheduler, sizeof(*ugeth->p_scheduler)); } if (ugeth->p_tx_fw_statistics_pram) { ugeth_info("TX FW statistics pram:"); ugeth_info("Base address: 0x%08x", (u32) ugeth->p_tx_fw_statistics_pram); mem_disp((u8 *) ugeth->p_tx_fw_statistics_pram, sizeof(*ugeth->p_tx_fw_statistics_pram)); } if (ugeth->p_rx_fw_statistics_pram) { ugeth_info("RX FW statistics pram:"); ugeth_info("Base address: 0x%08x", (u32) ugeth->p_rx_fw_statistics_pram); mem_disp((u8 *) ugeth->p_rx_fw_statistics_pram, sizeof(*ugeth->p_rx_fw_statistics_pram)); } if (ugeth->p_rx_irq_coalescing_tbl) { ugeth_info("RX IRQ coalescing tables:"); ugeth_info("Base address: 0x%08x", (u32) ugeth->p_rx_irq_coalescing_tbl); for (i = 0; i < ugeth->ug_info->numQueuesRx; i++) { ugeth_info("RX IRQ coalescing table entry[%d]:", i); ugeth_info("Base address: 0x%08x", (u32) & ugeth->p_rx_irq_coalescing_tbl-> coalescingentry[i]); ugeth_info ("interruptcoalescingmaxvalue: addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_rx_irq_coalescing_tbl-> coalescingentry[i].interruptcoalescingmaxvalue, in_be32(&ugeth->p_rx_irq_coalescing_tbl-> coalescingentry[i]. interruptcoalescingmaxvalue)); ugeth_info ("interruptcoalescingcounter : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_rx_irq_coalescing_tbl-> coalescingentry[i].interruptcoalescingcounter, in_be32(&ugeth->p_rx_irq_coalescing_tbl-> coalescingentry[i]. interruptcoalescingcounter)); } } if (ugeth->p_rx_bd_qs_tbl) { ugeth_info("RX BD QS tables:"); ugeth_info("Base address: 0x%08x", (u32) ugeth->p_rx_bd_qs_tbl); for (i = 0; i < ugeth->ug_info->numQueuesRx; i++) { ugeth_info("RX BD QS table[%d]:", i); ugeth_info("Base address: 0x%08x", (u32) & ugeth->p_rx_bd_qs_tbl[i]); ugeth_info ("bdbaseptr : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_rx_bd_qs_tbl[i].bdbaseptr, in_be32(&ugeth->p_rx_bd_qs_tbl[i].bdbaseptr)); ugeth_info ("bdptr : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_rx_bd_qs_tbl[i].bdptr, in_be32(&ugeth->p_rx_bd_qs_tbl[i].bdptr)); ugeth_info ("externalbdbaseptr: addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_rx_bd_qs_tbl[i].externalbdbaseptr, in_be32(&ugeth->p_rx_bd_qs_tbl[i]. externalbdbaseptr)); ugeth_info ("externalbdptr : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->p_rx_bd_qs_tbl[i].externalbdptr, in_be32(&ugeth->p_rx_bd_qs_tbl[i].externalbdptr)); ugeth_info("ucode RX Prefetched BDs:"); ugeth_info("Base address: 0x%08x", (u32) qe_muram_addr(in_be32 (&ugeth->p_rx_bd_qs_tbl[i]. bdbaseptr))); mem_disp((u8 *) qe_muram_addr(in_be32 (&ugeth->p_rx_bd_qs_tbl[i]. bdbaseptr)), sizeof(struct ucc_geth_rx_prefetched_bds)); } } if (ugeth->p_init_enet_param_shadow) { int size; ugeth_info("Init enet param shadow:"); ugeth_info("Base address: 0x%08x", (u32) ugeth->p_init_enet_param_shadow); mem_disp((u8 *) ugeth->p_init_enet_param_shadow, sizeof(*ugeth->p_init_enet_param_shadow)); size = sizeof(struct ucc_geth_thread_rx_pram); if (ugeth->ug_info->rxExtendedFiltering) { size += THREAD_RX_PRAM_ADDITIONAL_FOR_EXTENDED_FILTERING; if (ugeth->ug_info->largestexternallookupkeysize == QE_FLTR_TABLE_LOOKUP_KEY_SIZE_8_BYTES) size += THREAD_RX_PRAM_ADDITIONAL_FOR_EXTENDED_FILTERING_8; if (ugeth->ug_info->largestexternallookupkeysize == QE_FLTR_TABLE_LOOKUP_KEY_SIZE_16_BYTES) size += THREAD_RX_PRAM_ADDITIONAL_FOR_EXTENDED_FILTERING_16; } dump_init_enet_entries(ugeth, &(ugeth->p_init_enet_param_shadow-> txthread[0]), ENET_INIT_PARAM_MAX_ENTRIES_TX, sizeof(struct ucc_geth_thread_tx_pram), ugeth->ug_info->riscTx, 0); dump_init_enet_entries(ugeth, &(ugeth->p_init_enet_param_shadow-> rxthread[0]), ENET_INIT_PARAM_MAX_ENTRIES_RX, size, ugeth->ug_info->riscRx, 1); } } #endif /* DEBUG */ static void init_default_reg_vals(u32 __iomem *upsmr_register, u32 __iomem *maccfg1_register, u32 __iomem *maccfg2_register) { out_be32(upsmr_register, UCC_GETH_UPSMR_INIT); out_be32(maccfg1_register, UCC_GETH_MACCFG1_INIT); out_be32(maccfg2_register, UCC_GETH_MACCFG2_INIT); } static int init_half_duplex_params(int alt_beb, int back_pressure_no_backoff, int no_backoff, int excess_defer, u8 alt_beb_truncation, u8 max_retransmissions, u8 collision_window, u32 __iomem *hafdup_register) { u32 value = 0; if ((alt_beb_truncation > HALFDUP_ALT_BEB_TRUNCATION_MAX) || (max_retransmissions > HALFDUP_MAX_RETRANSMISSION_MAX) || (collision_window > HALFDUP_COLLISION_WINDOW_MAX)) return -EINVAL; value = (u32) (alt_beb_truncation << HALFDUP_ALT_BEB_TRUNCATION_SHIFT); if (alt_beb) value |= HALFDUP_ALT_BEB; if (back_pressure_no_backoff) value |= HALFDUP_BACK_PRESSURE_NO_BACKOFF; if (no_backoff) value |= HALFDUP_NO_BACKOFF; if (excess_defer) value |= HALFDUP_EXCESSIVE_DEFER; value |= (max_retransmissions << HALFDUP_MAX_RETRANSMISSION_SHIFT); value |= collision_window; out_be32(hafdup_register, value); return 0; } static int init_inter_frame_gap_params(u8 non_btb_cs_ipg, u8 non_btb_ipg, u8 min_ifg, u8 btb_ipg, u32 __iomem *ipgifg_register) { u32 value = 0; /* Non-Back-to-back IPG part 1 should be <= Non-Back-to-back IPG part 2 */ if (non_btb_cs_ipg > non_btb_ipg) return -EINVAL; if ((non_btb_cs_ipg > IPGIFG_NON_BACK_TO_BACK_IFG_PART1_MAX) || (non_btb_ipg > IPGIFG_NON_BACK_TO_BACK_IFG_PART2_MAX) || /*(min_ifg > IPGIFG_MINIMUM_IFG_ENFORCEMENT_MAX) || */ (btb_ipg > IPGIFG_BACK_TO_BACK_IFG_MAX)) return -EINVAL; value |= ((non_btb_cs_ipg << IPGIFG_NON_BACK_TO_BACK_IFG_PART1_SHIFT) & IPGIFG_NBTB_CS_IPG_MASK); value |= ((non_btb_ipg << IPGIFG_NON_BACK_TO_BACK_IFG_PART2_SHIFT) & IPGIFG_NBTB_IPG_MASK); value |= ((min_ifg << IPGIFG_MINIMUM_IFG_ENFORCEMENT_SHIFT) & IPGIFG_MIN_IFG_MASK); value |= (btb_ipg & IPGIFG_BTB_IPG_MASK); out_be32(ipgifg_register, value); return 0; } int init_flow_control_params(u32 automatic_flow_control_mode, int rx_flow_control_enable, int tx_flow_control_enable, u16 pause_period, u16 extension_field, u32 __iomem *upsmr_register, u32 __iomem *uempr_register, u32 __iomem *maccfg1_register) { u32 value = 0; /* Set UEMPR register */ value = (u32) pause_period << UEMPR_PAUSE_TIME_VALUE_SHIFT; value |= (u32) extension_field << UEMPR_EXTENDED_PAUSE_TIME_VALUE_SHIFT; out_be32(uempr_register, value); /* Set UPSMR register */ setbits32(upsmr_register, automatic_flow_control_mode); value = in_be32(maccfg1_register); if (rx_flow_control_enable) value |= MACCFG1_FLOW_RX; if (tx_flow_control_enable) value |= MACCFG1_FLOW_TX; out_be32(maccfg1_register, value); return 0; } static int init_hw_statistics_gathering_mode(int enable_hardware_statistics, int auto_zero_hardware_statistics, u32 __iomem *upsmr_register, u16 __iomem *uescr_register) { u16 uescr_value = 0; /* Enable hardware statistics gathering if requested */ if (enable_hardware_statistics) setbits32(upsmr_register, UCC_GETH_UPSMR_HSE); /* Clear hardware statistics counters */ uescr_value = in_be16(uescr_register); uescr_value |= UESCR_CLRCNT; /* Automatically zero hardware statistics counters on read, if requested */ if (auto_zero_hardware_statistics) uescr_value |= UESCR_AUTOZ; out_be16(uescr_register, uescr_value); return 0; } static int init_firmware_statistics_gathering_mode(int enable_tx_firmware_statistics, int enable_rx_firmware_statistics, u32 __iomem *tx_rmon_base_ptr, u32 tx_firmware_statistics_structure_address, u32 __iomem *rx_rmon_base_ptr, u32 rx_firmware_statistics_structure_address, u16 __iomem *temoder_register, u32 __iomem *remoder_register) { /* Note: this function does not check if */ /* the parameters it receives are NULL */ if (enable_tx_firmware_statistics) { out_be32(tx_rmon_base_ptr, tx_firmware_statistics_structure_address); setbits16(temoder_register, TEMODER_TX_RMON_STATISTICS_ENABLE); } if (enable_rx_firmware_statistics) { out_be32(rx_rmon_base_ptr, rx_firmware_statistics_structure_address); setbits32(remoder_register, REMODER_RX_RMON_STATISTICS_ENABLE); } return 0; } static int init_mac_station_addr_regs(u8 address_byte_0, u8 address_byte_1, u8 address_byte_2, u8 address_byte_3, u8 address_byte_4, u8 address_byte_5, u32 __iomem *macstnaddr1_register, u32 __iomem *macstnaddr2_register) { u32 value = 0; /* Example: for a station address of 0x12345678ABCD, */ /* 0x12 is byte 0, 0x34 is byte 1 and so on and 0xCD is byte 5 */ /* MACSTNADDR1 Register: */ /* 0 7 8 15 */ /* station address byte 5 station address byte 4 */ /* 16 23 24 31 */ /* station address byte 3 station address byte 2 */ value |= (u32) ((address_byte_2 << 0) & 0x000000FF); value |= (u32) ((address_byte_3 << 8) & 0x0000FF00); value |= (u32) ((address_byte_4 << 16) & 0x00FF0000); value |= (u32) ((address_byte_5 << 24) & 0xFF000000); out_be32(macstnaddr1_register, value); /* MACSTNADDR2 Register: */ /* 0 7 8 15 */ /* station address byte 1 station address byte 0 */ /* 16 23 24 31 */ /* reserved reserved */ value = 0; value |= (u32) ((address_byte_0 << 16) & 0x00FF0000); value |= (u32) ((address_byte_1 << 24) & 0xFF000000); out_be32(macstnaddr2_register, value); return 0; } static int init_check_frame_length_mode(int length_check, u32 __iomem *maccfg2_register) { u32 value = 0; value = in_be32(maccfg2_register); if (length_check) value |= MACCFG2_LC; else value &= ~MACCFG2_LC; out_be32(maccfg2_register, value); return 0; } static int init_preamble_length(u8 preamble_length, u32 __iomem *maccfg2_register) { if ((preamble_length < 3) || (preamble_length > 7)) return -EINVAL; clrsetbits_be32(maccfg2_register, MACCFG2_PREL_MASK, preamble_length << MACCFG2_PREL_SHIFT); return 0; } static int init_rx_parameters(int reject_broadcast, int receive_short_frames, int promiscuous, u32 __iomem *upsmr_register) { u32 value = 0; value = in_be32(upsmr_register); if (reject_broadcast) value |= UCC_GETH_UPSMR_BRO; else value &= ~UCC_GETH_UPSMR_BRO; if (receive_short_frames) value |= UCC_GETH_UPSMR_RSH; else value &= ~UCC_GETH_UPSMR_RSH; if (promiscuous) value |= UCC_GETH_UPSMR_PRO; else value &= ~UCC_GETH_UPSMR_PRO; out_be32(upsmr_register, value); return 0; } static int init_max_rx_buff_len(u16 max_rx_buf_len, u16 __iomem *mrblr_register) { /* max_rx_buf_len value must be a multiple of 128 */ if ((max_rx_buf_len == 0) || (max_rx_buf_len % UCC_GETH_MRBLR_ALIGNMENT)) return -EINVAL; out_be16(mrblr_register, max_rx_buf_len); return 0; } static int init_min_frame_len(u16 min_frame_length, u16 __iomem *minflr_register, u16 __iomem *mrblr_register) { u16 mrblr_value = 0; mrblr_value = in_be16(mrblr_register); if (min_frame_length >= (mrblr_value - 4)) return -EINVAL; out_be16(minflr_register, min_frame_length); return 0; } static int adjust_enet_interface(struct ucc_geth_private *ugeth) { struct ucc_geth_info *ug_info; struct ucc_geth __iomem *ug_regs; struct ucc_fast __iomem *uf_regs; int ret_val; u32 upsmr, maccfg2, tbiBaseAddress; u16 value; ugeth_vdbg("%s: IN", __func__); ug_info = ugeth->ug_info; ug_regs = ugeth->ug_regs; uf_regs = ugeth->uccf->uf_regs; /* Set MACCFG2 */ maccfg2 = in_be32(&ug_regs->maccfg2); maccfg2 &= ~MACCFG2_INTERFACE_MODE_MASK; if ((ugeth->max_speed == SPEED_10) || (ugeth->max_speed == SPEED_100)) maccfg2 |= MACCFG2_INTERFACE_MODE_NIBBLE; else if (ugeth->max_speed == SPEED_1000) maccfg2 |= MACCFG2_INTERFACE_MODE_BYTE; maccfg2 |= ug_info->padAndCrc; out_be32(&ug_regs->maccfg2, maccfg2); /* Set UPSMR */ upsmr = in_be32(&uf_regs->upsmr); upsmr &= ~(UCC_GETH_UPSMR_RPM | UCC_GETH_UPSMR_R10M | UCC_GETH_UPSMR_TBIM | UCC_GETH_UPSMR_RMM); if ((ugeth->phy_interface == PHY_INTERFACE_MODE_RMII) || (ugeth->phy_interface == PHY_INTERFACE_MODE_RGMII) || (ugeth->phy_interface == PHY_INTERFACE_MODE_RGMII_ID) || (ugeth->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID) || (ugeth->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID) || (ugeth->phy_interface == PHY_INTERFACE_MODE_RTBI)) { upsmr |= UCC_GETH_UPSMR_RPM; switch (ugeth->max_speed) { case SPEED_10: upsmr |= UCC_GETH_UPSMR_R10M; /* FALLTHROUGH */ case SPEED_100: if (ugeth->phy_interface != PHY_INTERFACE_MODE_RTBI) upsmr |= UCC_GETH_UPSMR_RMM; } } if ((ugeth->phy_interface == PHY_INTERFACE_MODE_TBI) || (ugeth->phy_interface == PHY_INTERFACE_MODE_RTBI)) { upsmr |= UCC_GETH_UPSMR_TBIM; } out_be32(&uf_regs->upsmr, upsmr); /* Disable autonegotiation in tbi mode, because by default it comes up in autonegotiation mode. */ /* Note that this depends on proper setting in utbipar register. */ if ((ugeth->phy_interface == PHY_INTERFACE_MODE_TBI) || (ugeth->phy_interface == PHY_INTERFACE_MODE_RTBI)) { tbiBaseAddress = in_be32(&ug_regs->utbipar); tbiBaseAddress &= UTBIPAR_PHY_ADDRESS_MASK; tbiBaseAddress >>= UTBIPAR_PHY_ADDRESS_SHIFT; value = ugeth->phydev->bus->read(ugeth->phydev->bus, (u8) tbiBaseAddress, ENET_TBI_MII_CR); value &= ~0x1000; /* Turn off autonegotiation */ ugeth->phydev->bus->write(ugeth->phydev->bus, (u8) tbiBaseAddress, ENET_TBI_MII_CR, value); } init_check_frame_length_mode(ug_info->lengthCheckRx, &ug_regs->maccfg2); ret_val = init_preamble_length(ug_info->prel, &ug_regs->maccfg2); if (ret_val != 0) { if (netif_msg_probe(ugeth)) ugeth_err("%s: Preamble length must be between 3 and 7 inclusive.", __func__); return ret_val; } return 0; } /* Called every time the controller might need to be made * aware of new link state. The PHY code conveys this * information through variables in the ugeth structure, and this * function converts those variables into the appropriate * register values, and can bring down the device if needed. */ static void adjust_link(struct net_device *dev) { struct ucc_geth_private *ugeth = netdev_priv(dev); struct ucc_geth __iomem *ug_regs; struct ucc_fast __iomem *uf_regs; struct phy_device *phydev = ugeth->phydev; unsigned long flags; int new_state = 0; ug_regs = ugeth->ug_regs; uf_regs = ugeth->uccf->uf_regs; spin_lock_irqsave(&ugeth->lock, flags); if (phydev->link) { u32 tempval = in_be32(&ug_regs->maccfg2); u32 upsmr = in_be32(&uf_regs->upsmr); /* Now we make sure that we can be in full duplex mode. * If not, we operate in half-duplex mode. */ if (phydev->duplex != ugeth->oldduplex) { new_state = 1; if (!(phydev->duplex)) tempval &= ~(MACCFG2_FDX); else tempval |= MACCFG2_FDX; ugeth->oldduplex = phydev->duplex; } if (phydev->speed != ugeth->oldspeed) { new_state = 1; switch (phydev->speed) { case SPEED_1000: tempval = ((tempval & ~(MACCFG2_INTERFACE_MODE_MASK)) | MACCFG2_INTERFACE_MODE_BYTE); break; case SPEED_100: case SPEED_10: tempval = ((tempval & ~(MACCFG2_INTERFACE_MODE_MASK)) | MACCFG2_INTERFACE_MODE_NIBBLE); /* if reduced mode, re-set UPSMR.R10M */ if ((ugeth->phy_interface == PHY_INTERFACE_MODE_RMII) || (ugeth->phy_interface == PHY_INTERFACE_MODE_RGMII) || (ugeth->phy_interface == PHY_INTERFACE_MODE_RGMII_ID) || (ugeth->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID) || (ugeth->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID) || (ugeth->phy_interface == PHY_INTERFACE_MODE_RTBI)) { if (phydev->speed == SPEED_10) upsmr |= UCC_GETH_UPSMR_R10M; else upsmr &= ~UCC_GETH_UPSMR_R10M; } break; default: if (netif_msg_link(ugeth)) ugeth_warn( "%s: Ack! Speed (%d) is not 10/100/1000!", dev->name, phydev->speed); break; } ugeth->oldspeed = phydev->speed; } out_be32(&ug_regs->maccfg2, tempval); out_be32(&uf_regs->upsmr, upsmr); if (!ugeth->oldlink) { new_state = 1; ugeth->oldlink = 1; } } else if (ugeth->oldlink) { new_state = 1; ugeth->oldlink = 0; ugeth->oldspeed = 0; ugeth->oldduplex = -1; } if (new_state && netif_msg_link(ugeth)) phy_print_status(phydev); spin_unlock_irqrestore(&ugeth->lock, flags); } /* Configure the PHY for dev. * returns 0 if success. -1 if failure */ static int init_phy(struct net_device *dev) { struct ucc_geth_private *priv = netdev_priv(dev); struct device_node *np = priv->node; struct device_node *phy, *mdio; const phandle *ph; char bus_name[MII_BUS_ID_SIZE]; const unsigned int *id; struct phy_device *phydev; char phy_id[BUS_ID_SIZE]; priv->oldlink = 0; priv->oldspeed = 0; priv->oldduplex = -1; ph = of_get_property(np, "phy-handle", NULL); phy = of_find_node_by_phandle(*ph); mdio = of_get_parent(phy); id = of_get_property(phy, "reg", NULL); of_node_put(phy); of_node_put(mdio); uec_mdio_bus_name(bus_name, mdio); snprintf(phy_id, sizeof(phy_id), "%s:%02x", bus_name, *id); phydev = phy_connect(dev, phy_id, &adjust_link, 0, priv->phy_interface); if (IS_ERR(phydev)) { printk("%s: Could not attach to PHY\n", dev->name); return PTR_ERR(phydev); } phydev->supported &= (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full); if (priv->max_speed == SPEED_1000) phydev->supported |= ADVERTISED_1000baseT_Full; phydev->advertising = phydev->supported; priv->phydev = phydev; return 0; } static int ugeth_graceful_stop_tx(struct ucc_geth_private *ugeth) { struct ucc_fast_private *uccf; u32 cecr_subblock; u32 temp; int i = 10; uccf = ugeth->uccf; /* Mask GRACEFUL STOP TX interrupt bit and clear it */ clrbits32(uccf->p_uccm, UCC_GETH_UCCE_GRA); out_be32(uccf->p_ucce, UCC_GETH_UCCE_GRA); /* clear by writing 1 */ /* Issue host command */ cecr_subblock = ucc_fast_get_qe_cr_subblock(ugeth->ug_info->uf_info.ucc_num); qe_issue_cmd(QE_GRACEFUL_STOP_TX, cecr_subblock, QE_CR_PROTOCOL_ETHERNET, 0); /* Wait for command to complete */ do { msleep(10); temp = in_be32(uccf->p_ucce); } while (!(temp & UCC_GETH_UCCE_GRA) && --i); uccf->stopped_tx = 1; return 0; } static int ugeth_graceful_stop_rx(struct ucc_geth_private * ugeth) { struct ucc_fast_private *uccf; u32 cecr_subblock; u8 temp; int i = 10; uccf = ugeth->uccf; /* Clear acknowledge bit */ temp = in_8(&ugeth->p_rx_glbl_pram->rxgstpack); temp &= ~GRACEFUL_STOP_ACKNOWLEDGE_RX; out_8(&ugeth->p_rx_glbl_pram->rxgstpack, temp); /* Keep issuing command and checking acknowledge bit until it is asserted, according to spec */ do { /* Issue host command */ cecr_subblock = ucc_fast_get_qe_cr_subblock(ugeth->ug_info->uf_info. ucc_num); qe_issue_cmd(QE_GRACEFUL_STOP_RX, cecr_subblock, QE_CR_PROTOCOL_ETHERNET, 0); msleep(10); temp = in_8(&ugeth->p_rx_glbl_pram->rxgstpack); } while (!(temp & GRACEFUL_STOP_ACKNOWLEDGE_RX) && --i); uccf->stopped_rx = 1; return 0; } static int ugeth_restart_tx(struct ucc_geth_private *ugeth) { struct ucc_fast_private *uccf; u32 cecr_subblock; uccf = ugeth->uccf; cecr_subblock = ucc_fast_get_qe_cr_subblock(ugeth->ug_info->uf_info.ucc_num); qe_issue_cmd(QE_RESTART_TX, cecr_subblock, QE_CR_PROTOCOL_ETHERNET, 0); uccf->stopped_tx = 0; return 0; } static int ugeth_restart_rx(struct ucc_geth_private *ugeth) { struct ucc_fast_private *uccf; u32 cecr_subblock; uccf = ugeth->uccf; cecr_subblock = ucc_fast_get_qe_cr_subblock(ugeth->ug_info->uf_info.ucc_num); qe_issue_cmd(QE_RESTART_RX, cecr_subblock, QE_CR_PROTOCOL_ETHERNET, 0); uccf->stopped_rx = 0; return 0; } static int ugeth_enable(struct ucc_geth_private *ugeth, enum comm_dir mode) { struct ucc_fast_private *uccf; int enabled_tx, enabled_rx; uccf = ugeth->uccf; /* check if the UCC number is in range. */ if (ugeth->ug_info->uf_info.ucc_num >= UCC_MAX_NUM) { if (netif_msg_probe(ugeth)) ugeth_err("%s: ucc_num out of range.", __func__); return -EINVAL; } enabled_tx = uccf->enabled_tx; enabled_rx = uccf->enabled_rx; /* Get Tx and Rx going again, in case this channel was actively disabled. */ if ((mode & COMM_DIR_TX) && (!enabled_tx) && uccf->stopped_tx) ugeth_restart_tx(ugeth); if ((mode & COMM_DIR_RX) && (!enabled_rx) && uccf->stopped_rx) ugeth_restart_rx(ugeth); ucc_fast_enable(uccf, mode); /* OK to do even if not disabled */ return 0; } static int ugeth_disable(struct ucc_geth_private * ugeth, enum comm_dir mode) { struct ucc_fast_private *uccf; uccf = ugeth->uccf; /* check if the UCC number is in range. */ if (ugeth->ug_info->uf_info.ucc_num >= UCC_MAX_NUM) { if (netif_msg_probe(ugeth)) ugeth_err("%s: ucc_num out of range.", __func__); return -EINVAL; } /* Stop any transmissions */ if ((mode & COMM_DIR_TX) && uccf->enabled_tx && !uccf->stopped_tx) ugeth_graceful_stop_tx(ugeth); /* Stop any receptions */ if ((mode & COMM_DIR_RX) && uccf->enabled_rx && !uccf->stopped_rx) ugeth_graceful_stop_rx(ugeth); ucc_fast_disable(ugeth->uccf, mode); /* OK to do even if not enabled */ return 0; } static void ugeth_dump_regs(struct ucc_geth_private *ugeth) { #ifdef DEBUG ucc_fast_dump_regs(ugeth->uccf); dump_regs(ugeth); dump_bds(ugeth); #endif } static int ugeth_82xx_filtering_clear_all_addr_in_hash(struct ucc_geth_private * ugeth, enum enet_addr_type enet_addr_type) { struct ucc_geth_82xx_address_filtering_pram __iomem *p_82xx_addr_filt; struct ucc_fast_private *uccf; enum comm_dir comm_dir; struct list_head *p_lh; u16 i, num; u32 __iomem *addr_h; u32 __iomem *addr_l; u8 *p_counter; uccf = ugeth->uccf; p_82xx_addr_filt = (struct ucc_geth_82xx_address_filtering_pram __iomem *) ugeth->p_rx_glbl_pram->addressfiltering; if (enet_addr_type == ENET_ADDR_TYPE_GROUP) { addr_h = &(p_82xx_addr_filt->gaddr_h); addr_l = &(p_82xx_addr_filt->gaddr_l); p_lh = &ugeth->group_hash_q; p_counter = &(ugeth->numGroupAddrInHash); } else if (enet_addr_type == ENET_ADDR_TYPE_INDIVIDUAL) { addr_h = &(p_82xx_addr_filt->iaddr_h); addr_l = &(p_82xx_addr_filt->iaddr_l); p_lh = &ugeth->ind_hash_q; p_counter = &(ugeth->numIndAddrInHash); } else return -EINVAL; comm_dir = 0; if (uccf->enabled_tx) comm_dir |= COMM_DIR_TX; if (uccf->enabled_rx) comm_dir |= COMM_DIR_RX; if (comm_dir) ugeth_disable(ugeth, comm_dir); /* Clear the hash table. */ out_be32(addr_h, 0x00000000); out_be32(addr_l, 0x00000000); if (!p_lh) return 0; num = *p_counter; /* Delete all remaining CQ elements */ for (i = 0; i < num; i++) put_enet_addr_container(ENET_ADDR_CONT_ENTRY(dequeue(p_lh))); *p_counter = 0; if (comm_dir) ugeth_enable(ugeth, comm_dir); return 0; } static int ugeth_82xx_filtering_clear_addr_in_paddr(struct ucc_geth_private *ugeth, u8 paddr_num) { ugeth->indAddrRegUsed[paddr_num] = 0; /* mark this paddr as not used */ return hw_clear_addr_in_paddr(ugeth, paddr_num);/* clear in hardware */ } static void ucc_geth_memclean(struct ucc_geth_private *ugeth) { u16 i, j; u8 __iomem *bd; if (!ugeth) return; if (ugeth->uccf) { ucc_fast_free(ugeth->uccf); ugeth->uccf = NULL; } if (ugeth->p_thread_data_tx) { qe_muram_free(ugeth->thread_dat_tx_offset); ugeth->p_thread_data_tx = NULL; } if (ugeth->p_thread_data_rx) { qe_muram_free(ugeth->thread_dat_rx_offset); ugeth->p_thread_data_rx = NULL; } if (ugeth->p_exf_glbl_param) { qe_muram_free(ugeth->exf_glbl_param_offset); ugeth->p_exf_glbl_param = NULL; } if (ugeth->p_rx_glbl_pram) { qe_muram_free(ugeth->rx_glbl_pram_offset); ugeth->p_rx_glbl_pram = NULL; } if (ugeth->p_tx_glbl_pram) { qe_muram_free(ugeth->tx_glbl_pram_offset); ugeth->p_tx_glbl_pram = NULL; } if (ugeth->p_send_q_mem_reg) { qe_muram_free(ugeth->send_q_mem_reg_offset); ugeth->p_send_q_mem_reg = NULL; } if (ugeth->p_scheduler) { qe_muram_free(ugeth->scheduler_offset); ugeth->p_scheduler = NULL; } if (ugeth->p_tx_fw_statistics_pram) { qe_muram_free(ugeth->tx_fw_statistics_pram_offset); ugeth->p_tx_fw_statistics_pram = NULL; } if (ugeth->p_rx_fw_statistics_pram) { qe_muram_free(ugeth->rx_fw_statistics_pram_offset); ugeth->p_rx_fw_statistics_pram = NULL; } if (ugeth->p_rx_irq_coalescing_tbl) { qe_muram_free(ugeth->rx_irq_coalescing_tbl_offset); ugeth->p_rx_irq_coalescing_tbl = NULL; } if (ugeth->p_rx_bd_qs_tbl) { qe_muram_free(ugeth->rx_bd_qs_tbl_offset); ugeth->p_rx_bd_qs_tbl = NULL; } if (ugeth->p_init_enet_param_shadow) { return_init_enet_entries(ugeth, &(ugeth->p_init_enet_param_shadow-> rxthread[0]), ENET_INIT_PARAM_MAX_ENTRIES_RX, ugeth->ug_info->riscRx, 1); return_init_enet_entries(ugeth, &(ugeth->p_init_enet_param_shadow-> txthread[0]), ENET_INIT_PARAM_MAX_ENTRIES_TX, ugeth->ug_info->riscTx, 0); kfree(ugeth->p_init_enet_param_shadow); ugeth->p_init_enet_param_shadow = NULL; } for (i = 0; i < ugeth->ug_info->numQueuesTx; i++) { bd = ugeth->p_tx_bd_ring[i]; if (!bd) continue; for (j = 0; j < ugeth->ug_info->bdRingLenTx[i]; j++) { if (ugeth->tx_skbuff[i][j]) { dma_unmap_single(&ugeth->dev->dev, in_be32(&((struct qe_bd __iomem *)bd)->buf), (in_be32((u32 __iomem *)bd) & BD_LENGTH_MASK), DMA_TO_DEVICE); dev_kfree_skb_any(ugeth->tx_skbuff[i][j]); ugeth->tx_skbuff[i][j] = NULL; } } kfree(ugeth->tx_skbuff[i]); if (ugeth->p_tx_bd_ring[i]) { if (ugeth->ug_info->uf_info.bd_mem_part == MEM_PART_SYSTEM) kfree((void *)ugeth->tx_bd_ring_offset[i]); else if (ugeth->ug_info->uf_info.bd_mem_part == MEM_PART_MURAM) qe_muram_free(ugeth->tx_bd_ring_offset[i]); ugeth->p_tx_bd_ring[i] = NULL; } } for (i = 0; i < ugeth->ug_info->numQueuesRx; i++) { if (ugeth->p_rx_bd_ring[i]) { /* Return existing data buffers in ring */ bd = ugeth->p_rx_bd_ring[i]; for (j = 0; j < ugeth->ug_info->bdRingLenRx[i]; j++) { if (ugeth->rx_skbuff[i][j]) { dma_unmap_single(&ugeth->dev->dev, in_be32(&((struct qe_bd __iomem *)bd)->buf), ugeth->ug_info-> uf_info.max_rx_buf_length + UCC_GETH_RX_DATA_BUF_ALIGNMENT, DMA_FROM_DEVICE); dev_kfree_skb_any( ugeth->rx_skbuff[i][j]); ugeth->rx_skbuff[i][j] = NULL; } bd += sizeof(struct qe_bd); } kfree(ugeth->rx_skbuff[i]); if (ugeth->ug_info->uf_info.bd_mem_part == MEM_PART_SYSTEM) kfree((void *)ugeth->rx_bd_ring_offset[i]); else if (ugeth->ug_info->uf_info.bd_mem_part == MEM_PART_MURAM) qe_muram_free(ugeth->rx_bd_ring_offset[i]); ugeth->p_rx_bd_ring[i] = NULL; } } while (!list_empty(&ugeth->group_hash_q)) put_enet_addr_container(ENET_ADDR_CONT_ENTRY (dequeue(&ugeth->group_hash_q))); while (!list_empty(&ugeth->ind_hash_q)) put_enet_addr_container(ENET_ADDR_CONT_ENTRY (dequeue(&ugeth->ind_hash_q))); if (ugeth->ug_regs) { iounmap(ugeth->ug_regs); ugeth->ug_regs = NULL; } } static void ucc_geth_set_multi(struct net_device *dev) { struct ucc_geth_private *ugeth; struct dev_mc_list *dmi; struct ucc_fast __iomem *uf_regs; struct ucc_geth_82xx_address_filtering_pram __iomem *p_82xx_addr_filt; int i; ugeth = netdev_priv(dev); uf_regs = ugeth->uccf->uf_regs; if (dev->flags & IFF_PROMISC) { setbits32(&uf_regs->upsmr, UCC_GETH_UPSMR_PRO); } else { clrbits32(&uf_regs->upsmr, UCC_GETH_UPSMR_PRO); p_82xx_addr_filt = (struct ucc_geth_82xx_address_filtering_pram __iomem *) ugeth-> p_rx_glbl_pram->addressfiltering; if (dev->flags & IFF_ALLMULTI) { /* Catch all multicast addresses, so set the * filter to all 1's. */ out_be32(&p_82xx_addr_filt->gaddr_h, 0xffffffff); out_be32(&p_82xx_addr_filt->gaddr_l, 0xffffffff); } else { /* Clear filter and add the addresses in the list. */ out_be32(&p_82xx_addr_filt->gaddr_h, 0x0); out_be32(&p_82xx_addr_filt->gaddr_l, 0x0); dmi = dev->mc_list; for (i = 0; i < dev->mc_count; i++, dmi = dmi->next) { /* Only support group multicast for now. */ if (!(dmi->dmi_addr[0] & 1)) continue; /* Ask CPM to run CRC and set bit in * filter mask. */ hw_add_addr_in_hash(ugeth, dmi->dmi_addr); } } } } static void ucc_geth_stop(struct ucc_geth_private *ugeth) { struct ucc_geth __iomem *ug_regs = ugeth->ug_regs; struct phy_device *phydev = ugeth->phydev; ugeth_vdbg("%s: IN", __func__); /* Disable the controller */ ugeth_disable(ugeth, COMM_DIR_RX_AND_TX); /* Tell the kernel the link is down */ phy_stop(phydev); /* Mask all interrupts */ out_be32(ugeth->uccf->p_uccm, 0x00000000); /* Clear all interrupts */ out_be32(ugeth->uccf->p_ucce, 0xffffffff); /* Disable Rx and Tx */ clrbits32(&ug_regs->maccfg1, MACCFG1_ENABLE_RX | MACCFG1_ENABLE_TX); ucc_geth_memclean(ugeth); } static int ucc_struct_init(struct ucc_geth_private *ugeth) { struct ucc_geth_info *ug_info; struct ucc_fast_info *uf_info; int i; ug_info = ugeth->ug_info; uf_info = &ug_info->uf_info; if (!((uf_info->bd_mem_part == MEM_PART_SYSTEM) || (uf_info->bd_mem_part == MEM_PART_MURAM))) { if (netif_msg_probe(ugeth)) ugeth_err("%s: Bad memory partition value.", __func__); return -EINVAL; } /* Rx BD lengths */ for (i = 0; i < ug_info->numQueuesRx; i++) { if ((ug_info->bdRingLenRx[i] < UCC_GETH_RX_BD_RING_SIZE_MIN) || (ug_info->bdRingLenRx[i] % UCC_GETH_RX_BD_RING_SIZE_ALIGNMENT)) { if (netif_msg_probe(ugeth)) ugeth_err ("%s: Rx BD ring length must be multiple of 4, no smaller than 8.", __func__); return -EINVAL; } } /* Tx BD lengths */ for (i = 0; i < ug_info->numQueuesTx; i++) { if (ug_info->bdRingLenTx[i] < UCC_GETH_TX_BD_RING_SIZE_MIN) { if (netif_msg_probe(ugeth)) ugeth_err ("%s: Tx BD ring length must be no smaller than 2.", __func__); return -EINVAL; } } /* mrblr */ if ((uf_info->max_rx_buf_length == 0) || (uf_info->max_rx_buf_length % UCC_GETH_MRBLR_ALIGNMENT)) { if (netif_msg_probe(ugeth)) ugeth_err ("%s: max_rx_buf_length must be non-zero multiple of 128.", __func__); return -EINVAL; } /* num Tx queues */ if (ug_info->numQueuesTx > NUM_TX_QUEUES) { if (netif_msg_probe(ugeth)) ugeth_err("%s: number of tx queues too large.", __func__); return -EINVAL; } /* num Rx queues */ if (ug_info->numQueuesRx > NUM_RX_QUEUES) { if (netif_msg_probe(ugeth)) ugeth_err("%s: number of rx queues too large.", __func__); return -EINVAL; } /* l2qt */ for (i = 0; i < UCC_GETH_VLAN_PRIORITY_MAX; i++) { if (ug_info->l2qt[i] >= ug_info->numQueuesRx) { if (netif_msg_probe(ugeth)) ugeth_err ("%s: VLAN priority table entry must not be" " larger than number of Rx queues.", __func__); return -EINVAL; } } /* l3qt */ for (i = 0; i < UCC_GETH_IP_PRIORITY_MAX; i++) { if (ug_info->l3qt[i] >= ug_info->numQueuesRx) { if (netif_msg_probe(ugeth)) ugeth_err ("%s: IP priority table entry must not be" " larger than number of Rx queues.", __func__); return -EINVAL; } } if (ug_info->cam && !ug_info->ecamptr) { if (netif_msg_probe(ugeth)) ugeth_err("%s: If cam mode is chosen, must supply cam ptr.", __func__); return -EINVAL; } if ((ug_info->numStationAddresses != UCC_GETH_NUM_OF_STATION_ADDRESSES_1) && ug_info->rxExtendedFiltering) { if (netif_msg_probe(ugeth)) ugeth_err("%s: Number of station addresses greater than 1 " "not allowed in extended parsing mode.", __func__); return -EINVAL; } /* Generate uccm_mask for receive */ uf_info->uccm_mask = ug_info->eventRegMask & UCCE_OTHER;/* Errors */ for (i = 0; i < ug_info->numQueuesRx; i++) uf_info->uccm_mask |= (UCC_GETH_UCCE_RXF0 << i); for (i = 0; i < ug_info->numQueuesTx; i++) uf_info->uccm_mask |= (UCC_GETH_UCCE_TXB0 << i); /* Initialize the general fast UCC block. */ if (ucc_fast_init(uf_info, &ugeth->uccf)) { if (netif_msg_probe(ugeth)) ugeth_err("%s: Failed to init uccf.", __func__); return -ENOMEM; } ugeth->ug_regs = ioremap(uf_info->regs, sizeof(*ugeth->ug_regs)); if (!ugeth->ug_regs) { if (netif_msg_probe(ugeth)) ugeth_err("%s: Failed to ioremap regs.", __func__); return -ENOMEM; } return 0; } static int ucc_geth_startup(struct ucc_geth_private *ugeth) { struct ucc_geth_82xx_address_filtering_pram __iomem *p_82xx_addr_filt; struct ucc_geth_init_pram __iomem *p_init_enet_pram; struct ucc_fast_private *uccf; struct ucc_geth_info *ug_info; struct ucc_fast_info *uf_info; struct ucc_fast __iomem *uf_regs; struct ucc_geth __iomem *ug_regs; int ret_val = -EINVAL; u32 remoder = UCC_GETH_REMODER_INIT; u32 init_enet_pram_offset, cecr_subblock, command; u32 ifstat, i, j, size, l2qt, l3qt, length; u16 temoder = UCC_GETH_TEMODER_INIT; u16 test; u8 function_code = 0; u8 __iomem *bd; u8 __iomem *endOfRing; u8 numThreadsRxNumerical, numThreadsTxNumerical; ugeth_vdbg("%s: IN", __func__); uccf = ugeth->uccf; ug_info = ugeth->ug_info; uf_info = &ug_info->uf_info; uf_regs = uccf->uf_regs; ug_regs = ugeth->ug_regs; switch (ug_info->numThreadsRx) { case UCC_GETH_NUM_OF_THREADS_1: numThreadsRxNumerical = 1; break; case UCC_GETH_NUM_OF_THREADS_2: numThreadsRxNumerical = 2; break; case UCC_GETH_NUM_OF_THREADS_4: numThreadsRxNumerical = 4; break; case UCC_GETH_NUM_OF_THREADS_6: numThreadsRxNumerical = 6; break; case UCC_GETH_NUM_OF_THREADS_8: numThreadsRxNumerical = 8; break; default: if (netif_msg_ifup(ugeth)) ugeth_err("%s: Bad number of Rx threads value.", __func__); return -EINVAL; break; } switch (ug_info->numThreadsTx) { case UCC_GETH_NUM_OF_THREADS_1: numThreadsTxNumerical = 1; break; case UCC_GETH_NUM_OF_THREADS_2: numThreadsTxNumerical = 2; break; case UCC_GETH_NUM_OF_THREADS_4: numThreadsTxNumerical = 4; break; case UCC_GETH_NUM_OF_THREADS_6: numThreadsTxNumerical = 6; break; case UCC_GETH_NUM_OF_THREADS_8: numThreadsTxNumerical = 8; break; default: if (netif_msg_ifup(ugeth)) ugeth_err("%s: Bad number of Tx threads value.", __func__); return -EINVAL; break; } /* Calculate rx_extended_features */ ugeth->rx_non_dynamic_extended_features = ug_info->ipCheckSumCheck || ug_info->ipAddressAlignment || (ug_info->numStationAddresses != UCC_GETH_NUM_OF_STATION_ADDRESSES_1); ugeth->rx_extended_features = ugeth->rx_non_dynamic_extended_features || (ug_info->vlanOperationTagged != UCC_GETH_VLAN_OPERATION_TAGGED_NOP) || (ug_info->vlanOperationNonTagged != UCC_GETH_VLAN_OPERATION_NON_TAGGED_NOP); init_default_reg_vals(&uf_regs->upsmr, &ug_regs->maccfg1, &ug_regs->maccfg2); /* Set UPSMR */ /* For more details see the hardware spec. */ init_rx_parameters(ug_info->bro, ug_info->rsh, ug_info->pro, &uf_regs->upsmr); /* We're going to ignore other registers for now, */ /* except as needed to get up and running */ /* Set MACCFG1 */ /* For more details see the hardware spec. */ init_flow_control_params(ug_info->aufc, ug_info->receiveFlowControl, ug_info->transmitFlowControl, ug_info->pausePeriod, ug_info->extensionField, &uf_regs->upsmr, &ug_regs->uempr, &ug_regs->maccfg1); setbits32(&ug_regs->maccfg1, MACCFG1_ENABLE_RX | MACCFG1_ENABLE_TX); /* Set IPGIFG */ /* For more details see the hardware spec. */ ret_val = init_inter_frame_gap_params(ug_info->nonBackToBackIfgPart1, ug_info->nonBackToBackIfgPart2, ug_info-> miminumInterFrameGapEnforcement, ug_info->backToBackInterFrameGap, &ug_regs->ipgifg); if (ret_val != 0) { if (netif_msg_ifup(ugeth)) ugeth_err("%s: IPGIFG initialization parameter too large.", __func__); return ret_val; } /* Set HAFDUP */ /* For more details see the hardware spec. */ ret_val = init_half_duplex_params(ug_info->altBeb, ug_info->backPressureNoBackoff, ug_info->noBackoff, ug_info->excessDefer, ug_info->altBebTruncation, ug_info->maxRetransmission, ug_info->collisionWindow, &ug_regs->hafdup); if (ret_val != 0) { if (netif_msg_ifup(ugeth)) ugeth_err("%s: Half Duplex initialization parameter too large.", __func__); return ret_val; } /* Set IFSTAT */ /* For more details see the hardware spec. */ /* Read only - resets upon read */ ifstat = in_be32(&ug_regs->ifstat); /* Clear UEMPR */ /* For more details see the hardware spec. */ out_be32(&ug_regs->uempr, 0); /* Set UESCR */ /* For more details see the hardware spec. */ init_hw_statistics_gathering_mode((ug_info->statisticsMode & UCC_GETH_STATISTICS_GATHERING_MODE_HARDWARE), 0, &uf_regs->upsmr, &ug_regs->uescr); /* Allocate Tx bds */ for (j = 0; j < ug_info->numQueuesTx; j++) { /* Allocate in multiple of UCC_GETH_TX_BD_RING_SIZE_MEMORY_ALIGNMENT, according to spec */ length = ((ug_info->bdRingLenTx[j] * sizeof(struct qe_bd)) / UCC_GETH_TX_BD_RING_SIZE_MEMORY_ALIGNMENT) * UCC_GETH_TX_BD_RING_SIZE_MEMORY_ALIGNMENT; if ((ug_info->bdRingLenTx[j] * sizeof(struct qe_bd)) % UCC_GETH_TX_BD_RING_SIZE_MEMORY_ALIGNMENT) length += UCC_GETH_TX_BD_RING_SIZE_MEMORY_ALIGNMENT; if (uf_info->bd_mem_part == MEM_PART_SYSTEM) { u32 align = 4; if (UCC_GETH_TX_BD_RING_ALIGNMENT > 4) align = UCC_GETH_TX_BD_RING_ALIGNMENT; ugeth->tx_bd_ring_offset[j] = (u32) kmalloc((u32) (length + align), GFP_KERNEL); if (ugeth->tx_bd_ring_offset[j] != 0) ugeth->p_tx_bd_ring[j] = (u8 __iomem *)((ugeth->tx_bd_ring_offset[j] + align) & ~(align - 1)); } else if (uf_info->bd_mem_part == MEM_PART_MURAM) { ugeth->tx_bd_ring_offset[j] = qe_muram_alloc(length, UCC_GETH_TX_BD_RING_ALIGNMENT); if (!IS_ERR_VALUE(ugeth->tx_bd_ring_offset[j])) ugeth->p_tx_bd_ring[j] = (u8 __iomem *) qe_muram_addr(ugeth-> tx_bd_ring_offset[j]); } if (!ugeth->p_tx_bd_ring[j]) { if (netif_msg_ifup(ugeth)) ugeth_err ("%s: Can not allocate memory for Tx bd rings.", __func__); return -ENOMEM; } /* Zero unused end of bd ring, according to spec */ memset_io((void __iomem *)(ugeth->p_tx_bd_ring[j] + ug_info->bdRingLenTx[j] * sizeof(struct qe_bd)), 0, length - ug_info->bdRingLenTx[j] * sizeof(struct qe_bd)); } /* Allocate Rx bds */ for (j = 0; j < ug_info->numQueuesRx; j++) { length = ug_info->bdRingLenRx[j] * sizeof(struct qe_bd); if (uf_info->bd_mem_part == MEM_PART_SYSTEM) { u32 align = 4; if (UCC_GETH_RX_BD_RING_ALIGNMENT > 4) align = UCC_GETH_RX_BD_RING_ALIGNMENT; ugeth->rx_bd_ring_offset[j] = (u32) kmalloc((u32) (length + align), GFP_KERNEL); if (ugeth->rx_bd_ring_offset[j] != 0) ugeth->p_rx_bd_ring[j] = (u8 __iomem *)((ugeth->rx_bd_ring_offset[j] + align) & ~(align - 1)); } else if (uf_info->bd_mem_part == MEM_PART_MURAM) { ugeth->rx_bd_ring_offset[j] = qe_muram_alloc(length, UCC_GETH_RX_BD_RING_ALIGNMENT); if (!IS_ERR_VALUE(ugeth->rx_bd_ring_offset[j])) ugeth->p_rx_bd_ring[j] = (u8 __iomem *) qe_muram_addr(ugeth-> rx_bd_ring_offset[j]); } if (!ugeth->p_rx_bd_ring[j]) { if (netif_msg_ifup(ugeth)) ugeth_err ("%s: Can not allocate memory for Rx bd rings.", __func__); return -ENOMEM; } } /* Init Tx bds */ for (j = 0; j < ug_info->numQueuesTx; j++) { /* Setup the skbuff rings */ ugeth->tx_skbuff[j] = kmalloc(sizeof(struct sk_buff *) * ugeth->ug_info->bdRingLenTx[j], GFP_KERNEL); if (ugeth->tx_skbuff[j] == NULL) { if (netif_msg_ifup(ugeth)) ugeth_err("%s: Could not allocate tx_skbuff", __func__); return -ENOMEM; } for (i = 0; i < ugeth->ug_info->bdRingLenTx[j]; i++) ugeth->tx_skbuff[j][i] = NULL; ugeth->skb_curtx[j] = ugeth->skb_dirtytx[j] = 0; bd = ugeth->confBd[j] = ugeth->txBd[j] = ugeth->p_tx_bd_ring[j]; for (i = 0; i < ug_info->bdRingLenTx[j]; i++) { /* clear bd buffer */ out_be32(&((struct qe_bd __iomem *)bd)->buf, 0); /* set bd status and length */ out_be32((u32 __iomem *)bd, 0); bd += sizeof(struct qe_bd); } bd -= sizeof(struct qe_bd); /* set bd status and length */ out_be32((u32 __iomem *)bd, T_W); /* for last BD set Wrap bit */ } /* Init Rx bds */ for (j = 0; j < ug_info->numQueuesRx; j++) { /* Setup the skbuff rings */ ugeth->rx_skbuff[j] = kmalloc(sizeof(struct sk_buff *) * ugeth->ug_info->bdRingLenRx[j], GFP_KERNEL); if (ugeth->rx_skbuff[j] == NULL) { if (netif_msg_ifup(ugeth)) ugeth_err("%s: Could not allocate rx_skbuff", __func__); return -ENOMEM; } for (i = 0; i < ugeth->ug_info->bdRingLenRx[j]; i++) ugeth->rx_skbuff[j][i] = NULL; ugeth->skb_currx[j] = 0; bd = ugeth->rxBd[j] = ugeth->p_rx_bd_ring[j]; for (i = 0; i < ug_info->bdRingLenRx[j]; i++) { /* set bd status and length */ out_be32((u32 __iomem *)bd, R_I); /* clear bd buffer */ out_be32(&((struct qe_bd __iomem *)bd)->buf, 0); bd += sizeof(struct qe_bd); } bd -= sizeof(struct qe_bd); /* set bd status and length */ out_be32((u32 __iomem *)bd, R_W); /* for last BD set Wrap bit */ } /* * Global PRAM */ /* Tx global PRAM */ /* Allocate global tx parameter RAM page */ ugeth->tx_glbl_pram_offset = qe_muram_alloc(sizeof(struct ucc_geth_tx_global_pram), UCC_GETH_TX_GLOBAL_PRAM_ALIGNMENT); if (IS_ERR_VALUE(ugeth->tx_glbl_pram_offset)) { if (netif_msg_ifup(ugeth)) ugeth_err ("%s: Can not allocate DPRAM memory for p_tx_glbl_pram.", __func__); return -ENOMEM; } ugeth->p_tx_glbl_pram = (struct ucc_geth_tx_global_pram __iomem *) qe_muram_addr(ugeth-> tx_glbl_pram_offset); /* Zero out p_tx_glbl_pram */ memset_io((void __iomem *)ugeth->p_tx_glbl_pram, 0, sizeof(struct ucc_geth_tx_global_pram)); /* Fill global PRAM */ /* TQPTR */ /* Size varies with number of Tx threads */ ugeth->thread_dat_tx_offset = qe_muram_alloc(numThreadsTxNumerical * sizeof(struct ucc_geth_thread_data_tx) + 32 * (numThreadsTxNumerical == 1), UCC_GETH_THREAD_DATA_ALIGNMENT); if (IS_ERR_VALUE(ugeth->thread_dat_tx_offset)) { if (netif_msg_ifup(ugeth)) ugeth_err ("%s: Can not allocate DPRAM memory for p_thread_data_tx.", __func__); return -ENOMEM; } ugeth->p_thread_data_tx = (struct ucc_geth_thread_data_tx __iomem *) qe_muram_addr(ugeth-> thread_dat_tx_offset); out_be32(&ugeth->p_tx_glbl_pram->tqptr, ugeth->thread_dat_tx_offset); /* vtagtable */ for (i = 0; i < UCC_GETH_TX_VTAG_TABLE_ENTRY_MAX; i++) out_be32(&ugeth->p_tx_glbl_pram->vtagtable[i], ug_info->vtagtable[i]); /* iphoffset */ for (i = 0; i < TX_IP_OFFSET_ENTRY_MAX; i++) out_8(&ugeth->p_tx_glbl_pram->iphoffset[i], ug_info->iphoffset[i]); /* SQPTR */ /* Size varies with number of Tx queues */ ugeth->send_q_mem_reg_offset = qe_muram_alloc(ug_info->numQueuesTx * sizeof(struct ucc_geth_send_queue_qd), UCC_GETH_SEND_QUEUE_QUEUE_DESCRIPTOR_ALIGNMENT); if (IS_ERR_VALUE(ugeth->send_q_mem_reg_offset)) { if (netif_msg_ifup(ugeth)) ugeth_err ("%s: Can not allocate DPRAM memory for p_send_q_mem_reg.", __func__); return -ENOMEM; } ugeth->p_send_q_mem_reg = (struct ucc_geth_send_queue_mem_region __iomem *) qe_muram_addr(ugeth-> send_q_mem_reg_offset); out_be32(&ugeth->p_tx_glbl_pram->sqptr, ugeth->send_q_mem_reg_offset); /* Setup the table */ /* Assume BD rings are already established */ for (i = 0; i < ug_info->numQueuesTx; i++) { endOfRing = ugeth->p_tx_bd_ring[i] + (ug_info->bdRingLenTx[i] - 1) * sizeof(struct qe_bd); if (ugeth->ug_info->uf_info.bd_mem_part == MEM_PART_SYSTEM) { out_be32(&ugeth->p_send_q_mem_reg->sqqd[i].bd_ring_base, (u32) virt_to_phys(ugeth->p_tx_bd_ring[i])); out_be32(&ugeth->p_send_q_mem_reg->sqqd[i]. last_bd_completed_address, (u32) virt_to_phys(endOfRing)); } else if (ugeth->ug_info->uf_info.bd_mem_part == MEM_PART_MURAM) { out_be32(&ugeth->p_send_q_mem_reg->sqqd[i].bd_ring_base, (u32) immrbar_virt_to_phys(ugeth-> p_tx_bd_ring[i])); out_be32(&ugeth->p_send_q_mem_reg->sqqd[i]. last_bd_completed_address, (u32) immrbar_virt_to_phys(endOfRing)); } } /* schedulerbasepointer */ if (ug_info->numQueuesTx > 1) { /* scheduler exists only if more than 1 tx queue */ ugeth->scheduler_offset = qe_muram_alloc(sizeof(struct ucc_geth_scheduler), UCC_GETH_SCHEDULER_ALIGNMENT); if (IS_ERR_VALUE(ugeth->scheduler_offset)) { if (netif_msg_ifup(ugeth)) ugeth_err ("%s: Can not allocate DPRAM memory for p_scheduler.", __func__); return -ENOMEM; } ugeth->p_scheduler = (struct ucc_geth_scheduler __iomem *) qe_muram_addr(ugeth-> scheduler_offset); out_be32(&ugeth->p_tx_glbl_pram->schedulerbasepointer, ugeth->scheduler_offset); /* Zero out p_scheduler */ memset_io((void __iomem *)ugeth->p_scheduler, 0, sizeof(struct ucc_geth_scheduler)); /* Set values in scheduler */ out_be32(&ugeth->p_scheduler->mblinterval, ug_info->mblinterval); out_be16(&ugeth->p_scheduler->nortsrbytetime, ug_info->nortsrbytetime); out_8(&ugeth->p_scheduler->fracsiz, ug_info->fracsiz); out_8(&ugeth->p_scheduler->strictpriorityq, ug_info->strictpriorityq); out_8(&ugeth->p_scheduler->txasap, ug_info->txasap); out_8(&ugeth->p_scheduler->extrabw, ug_info->extrabw); for (i = 0; i < NUM_TX_QUEUES; i++) out_8(&ugeth->p_scheduler->weightfactor[i], ug_info->weightfactor[i]); /* Set pointers to cpucount registers in scheduler */ ugeth->p_cpucount[0] = &(ugeth->p_scheduler->cpucount0); ugeth->p_cpucount[1] = &(ugeth->p_scheduler->cpucount1); ugeth->p_cpucount[2] = &(ugeth->p_scheduler->cpucount2); ugeth->p_cpucount[3] = &(ugeth->p_scheduler->cpucount3); ugeth->p_cpucount[4] = &(ugeth->p_scheduler->cpucount4); ugeth->p_cpucount[5] = &(ugeth->p_scheduler->cpucount5); ugeth->p_cpucount[6] = &(ugeth->p_scheduler->cpucount6); ugeth->p_cpucount[7] = &(ugeth->p_scheduler->cpucount7); } /* schedulerbasepointer */ /* TxRMON_PTR (statistics) */ if (ug_info-> statisticsMode & UCC_GETH_STATISTICS_GATHERING_MODE_FIRMWARE_TX) { ugeth->tx_fw_statistics_pram_offset = qe_muram_alloc(sizeof (struct ucc_geth_tx_firmware_statistics_pram), UCC_GETH_TX_STATISTICS_ALIGNMENT); if (IS_ERR_VALUE(ugeth->tx_fw_statistics_pram_offset)) { if (netif_msg_ifup(ugeth)) ugeth_err ("%s: Can not allocate DPRAM memory for" " p_tx_fw_statistics_pram.", __func__); return -ENOMEM; } ugeth->p_tx_fw_statistics_pram = (struct ucc_geth_tx_firmware_statistics_pram __iomem *) qe_muram_addr(ugeth->tx_fw_statistics_pram_offset); /* Zero out p_tx_fw_statistics_pram */ memset_io((void __iomem *)ugeth->p_tx_fw_statistics_pram, 0, sizeof(struct ucc_geth_tx_firmware_statistics_pram)); } /* temoder */ /* Already has speed set */ if (ug_info->numQueuesTx > 1) temoder |= TEMODER_SCHEDULER_ENABLE; if (ug_info->ipCheckSumGenerate) temoder |= TEMODER_IP_CHECKSUM_GENERATE; temoder |= ((ug_info->numQueuesTx - 1) << TEMODER_NUM_OF_QUEUES_SHIFT); out_be16(&ugeth->p_tx_glbl_pram->temoder, temoder); test = in_be16(&ugeth->p_tx_glbl_pram->temoder); /* Function code register value to be used later */ function_code = UCC_BMR_BO_BE | UCC_BMR_GBL; /* Required for QE */ /* function code register */ out_be32(&ugeth->p_tx_glbl_pram->tstate, ((u32) function_code) << 24); /* Rx global PRAM */ /* Allocate global rx parameter RAM page */ ugeth->rx_glbl_pram_offset = qe_muram_alloc(sizeof(struct ucc_geth_rx_global_pram), UCC_GETH_RX_GLOBAL_PRAM_ALIGNMENT); if (IS_ERR_VALUE(ugeth->rx_glbl_pram_offset)) { if (netif_msg_ifup(ugeth)) ugeth_err ("%s: Can not allocate DPRAM memory for p_rx_glbl_pram.", __func__); return -ENOMEM; } ugeth->p_rx_glbl_pram = (struct ucc_geth_rx_global_pram __iomem *) qe_muram_addr(ugeth-> rx_glbl_pram_offset); /* Zero out p_rx_glbl_pram */ memset_io((void __iomem *)ugeth->p_rx_glbl_pram, 0, sizeof(struct ucc_geth_rx_global_pram)); /* Fill global PRAM */ /* RQPTR */ /* Size varies with number of Rx threads */ ugeth->thread_dat_rx_offset = qe_muram_alloc(numThreadsRxNumerical * sizeof(struct ucc_geth_thread_data_rx), UCC_GETH_THREAD_DATA_ALIGNMENT); if (IS_ERR_VALUE(ugeth->thread_dat_rx_offset)) { if (netif_msg_ifup(ugeth)) ugeth_err ("%s: Can not allocate DPRAM memory for p_thread_data_rx.", __func__); return -ENOMEM; } ugeth->p_thread_data_rx = (struct ucc_geth_thread_data_rx __iomem *) qe_muram_addr(ugeth-> thread_dat_rx_offset); out_be32(&ugeth->p_rx_glbl_pram->rqptr, ugeth->thread_dat_rx_offset); /* typeorlen */ out_be16(&ugeth->p_rx_glbl_pram->typeorlen, ug_info->typeorlen); /* rxrmonbaseptr (statistics) */ if (ug_info-> statisticsMode & UCC_GETH_STATISTICS_GATHERING_MODE_FIRMWARE_RX) { ugeth->rx_fw_statistics_pram_offset = qe_muram_alloc(sizeof (struct ucc_geth_rx_firmware_statistics_pram), UCC_GETH_RX_STATISTICS_ALIGNMENT); if (IS_ERR_VALUE(ugeth->rx_fw_statistics_pram_offset)) { if (netif_msg_ifup(ugeth)) ugeth_err ("%s: Can not allocate DPRAM memory for" " p_rx_fw_statistics_pram.", __func__); return -ENOMEM; } ugeth->p_rx_fw_statistics_pram = (struct ucc_geth_rx_firmware_statistics_pram __iomem *) qe_muram_addr(ugeth->rx_fw_statistics_pram_offset); /* Zero out p_rx_fw_statistics_pram */ memset_io((void __iomem *)ugeth->p_rx_fw_statistics_pram, 0, sizeof(struct ucc_geth_rx_firmware_statistics_pram)); } /* intCoalescingPtr */ /* Size varies with number of Rx queues */ ugeth->rx_irq_coalescing_tbl_offset = qe_muram_alloc(ug_info->numQueuesRx * sizeof(struct ucc_geth_rx_interrupt_coalescing_entry) + 4, UCC_GETH_RX_INTERRUPT_COALESCING_ALIGNMENT); if (IS_ERR_VALUE(ugeth->rx_irq_coalescing_tbl_offset)) { if (netif_msg_ifup(ugeth)) ugeth_err ("%s: Can not allocate DPRAM memory for" " p_rx_irq_coalescing_tbl.", __func__); return -ENOMEM; } ugeth->p_rx_irq_coalescing_tbl = (struct ucc_geth_rx_interrupt_coalescing_table __iomem *) qe_muram_addr(ugeth->rx_irq_coalescing_tbl_offset); out_be32(&ugeth->p_rx_glbl_pram->intcoalescingptr, ugeth->rx_irq_coalescing_tbl_offset); /* Fill interrupt coalescing table */ for (i = 0; i < ug_info->numQueuesRx; i++) { out_be32(&ugeth->p_rx_irq_coalescing_tbl->coalescingentry[i]. interruptcoalescingmaxvalue, ug_info->interruptcoalescingmaxvalue[i]); out_be32(&ugeth->p_rx_irq_coalescing_tbl->coalescingentry[i]. interruptcoalescingcounter, ug_info->interruptcoalescingmaxvalue[i]); } /* MRBLR */ init_max_rx_buff_len(uf_info->max_rx_buf_length, &ugeth->p_rx_glbl_pram->mrblr); /* MFLR */ out_be16(&ugeth->p_rx_glbl_pram->mflr, ug_info->maxFrameLength); /* MINFLR */ init_min_frame_len(ug_info->minFrameLength, &ugeth->p_rx_glbl_pram->minflr, &ugeth->p_rx_glbl_pram->mrblr); /* MAXD1 */ out_be16(&ugeth->p_rx_glbl_pram->maxd1, ug_info->maxD1Length); /* MAXD2 */ out_be16(&ugeth->p_rx_glbl_pram->maxd2, ug_info->maxD2Length); /* l2qt */ l2qt = 0; for (i = 0; i < UCC_GETH_VLAN_PRIORITY_MAX; i++) l2qt |= (ug_info->l2qt[i] << (28 - 4 * i)); out_be32(&ugeth->p_rx_glbl_pram->l2qt, l2qt); /* l3qt */ for (j = 0; j < UCC_GETH_IP_PRIORITY_MAX; j += 8) { l3qt = 0; for (i = 0; i < 8; i++) l3qt |= (ug_info->l3qt[j + i] << (28 - 4 * i)); out_be32(&ugeth->p_rx_glbl_pram->l3qt[j/8], l3qt); } /* vlantype */ out_be16(&ugeth->p_rx_glbl_pram->vlantype, ug_info->vlantype); /* vlantci */ out_be16(&ugeth->p_rx_glbl_pram->vlantci, ug_info->vlantci); /* ecamptr */ out_be32(&ugeth->p_rx_glbl_pram->ecamptr, ug_info->ecamptr); /* RBDQPTR */ /* Size varies with number of Rx queues */ ugeth->rx_bd_qs_tbl_offset = qe_muram_alloc(ug_info->numQueuesRx * (sizeof(struct ucc_geth_rx_bd_queues_entry) + sizeof(struct ucc_geth_rx_prefetched_bds)), UCC_GETH_RX_BD_QUEUES_ALIGNMENT); if (IS_ERR_VALUE(ugeth->rx_bd_qs_tbl_offset)) { if (netif_msg_ifup(ugeth)) ugeth_err ("%s: Can not allocate DPRAM memory for p_rx_bd_qs_tbl.", __func__); return -ENOMEM; } ugeth->p_rx_bd_qs_tbl = (struct ucc_geth_rx_bd_queues_entry __iomem *) qe_muram_addr(ugeth-> rx_bd_qs_tbl_offset); out_be32(&ugeth->p_rx_glbl_pram->rbdqptr, ugeth->rx_bd_qs_tbl_offset); /* Zero out p_rx_bd_qs_tbl */ memset_io((void __iomem *)ugeth->p_rx_bd_qs_tbl, 0, ug_info->numQueuesRx * (sizeof(struct ucc_geth_rx_bd_queues_entry) + sizeof(struct ucc_geth_rx_prefetched_bds))); /* Setup the table */ /* Assume BD rings are already established */ for (i = 0; i < ug_info->numQueuesRx; i++) { if (ugeth->ug_info->uf_info.bd_mem_part == MEM_PART_SYSTEM) { out_be32(&ugeth->p_rx_bd_qs_tbl[i].externalbdbaseptr, (u32) virt_to_phys(ugeth->p_rx_bd_ring[i])); } else if (ugeth->ug_info->uf_info.bd_mem_part == MEM_PART_MURAM) { out_be32(&ugeth->p_rx_bd_qs_tbl[i].externalbdbaseptr, (u32) immrbar_virt_to_phys(ugeth-> p_rx_bd_ring[i])); } /* rest of fields handled by QE */ } /* remoder */ /* Already has speed set */ if (ugeth->rx_extended_features) remoder |= REMODER_RX_EXTENDED_FEATURES; if (ug_info->rxExtendedFiltering) remoder |= REMODER_RX_EXTENDED_FILTERING; if (ug_info->dynamicMaxFrameLength) remoder |= REMODER_DYNAMIC_MAX_FRAME_LENGTH; if (ug_info->dynamicMinFrameLength) remoder |= REMODER_DYNAMIC_MIN_FRAME_LENGTH; remoder |= ug_info->vlanOperationTagged << REMODER_VLAN_OPERATION_TAGGED_SHIFT; remoder |= ug_info-> vlanOperationNonTagged << REMODER_VLAN_OPERATION_NON_TAGGED_SHIFT; remoder |= ug_info->rxQoSMode << REMODER_RX_QOS_MODE_SHIFT; remoder |= ((ug_info->numQueuesRx - 1) << REMODER_NUM_OF_QUEUES_SHIFT); if (ug_info->ipCheckSumCheck) remoder |= REMODER_IP_CHECKSUM_CHECK; if (ug_info->ipAddressAlignment) remoder |= REMODER_IP_ADDRESS_ALIGNMENT; out_be32(&ugeth->p_rx_glbl_pram->remoder, remoder); /* Note that this function must be called */ /* ONLY AFTER p_tx_fw_statistics_pram */ /* andp_UccGethRxFirmwareStatisticsPram are allocated ! */ init_firmware_statistics_gathering_mode((ug_info-> statisticsMode & UCC_GETH_STATISTICS_GATHERING_MODE_FIRMWARE_TX), (ug_info->statisticsMode & UCC_GETH_STATISTICS_GATHERING_MODE_FIRMWARE_RX), &ugeth->p_tx_glbl_pram->txrmonbaseptr, ugeth->tx_fw_statistics_pram_offset, &ugeth->p_rx_glbl_pram->rxrmonbaseptr, ugeth->rx_fw_statistics_pram_offset, &ugeth->p_tx_glbl_pram->temoder, &ugeth->p_rx_glbl_pram->remoder); /* function code register */ out_8(&ugeth->p_rx_glbl_pram->rstate, function_code); /* initialize extended filtering */ if (ug_info->rxExtendedFiltering) { if (!ug_info->extendedFilteringChainPointer) { if (netif_msg_ifup(ugeth)) ugeth_err("%s: Null Extended Filtering Chain Pointer.", __func__); return -EINVAL; } /* Allocate memory for extended filtering Mode Global Parameters */ ugeth->exf_glbl_param_offset = qe_muram_alloc(sizeof(struct ucc_geth_exf_global_pram), UCC_GETH_RX_EXTENDED_FILTERING_GLOBAL_PARAMETERS_ALIGNMENT); if (IS_ERR_VALUE(ugeth->exf_glbl_param_offset)) { if (netif_msg_ifup(ugeth)) ugeth_err ("%s: Can not allocate DPRAM memory for" " p_exf_glbl_param.", __func__); return -ENOMEM; } ugeth->p_exf_glbl_param = (struct ucc_geth_exf_global_pram __iomem *) qe_muram_addr(ugeth-> exf_glbl_param_offset); out_be32(&ugeth->p_rx_glbl_pram->exfGlobalParam, ugeth->exf_glbl_param_offset); out_be32(&ugeth->p_exf_glbl_param->l2pcdptr, (u32) ug_info->extendedFilteringChainPointer); } else { /* initialize 82xx style address filtering */ /* Init individual address recognition registers to disabled */ for (j = 0; j < NUM_OF_PADDRS; j++) ugeth_82xx_filtering_clear_addr_in_paddr(ugeth, (u8) j); p_82xx_addr_filt = (struct ucc_geth_82xx_address_filtering_pram __iomem *) ugeth-> p_rx_glbl_pram->addressfiltering; ugeth_82xx_filtering_clear_all_addr_in_hash(ugeth, ENET_ADDR_TYPE_GROUP); ugeth_82xx_filtering_clear_all_addr_in_hash(ugeth, ENET_ADDR_TYPE_INDIVIDUAL); } /* * Initialize UCC at QE level */ command = QE_INIT_TX_RX; /* Allocate shadow InitEnet command parameter structure. * This is needed because after the InitEnet command is executed, * the structure in DPRAM is released, because DPRAM is a premium * resource. * This shadow structure keeps a copy of what was done so that the * allocated resources can be released when the channel is freed. */ if (!(ugeth->p_init_enet_param_shadow = kmalloc(sizeof(struct ucc_geth_init_pram), GFP_KERNEL))) { if (netif_msg_ifup(ugeth)) ugeth_err ("%s: Can not allocate memory for" " p_UccInitEnetParamShadows.", __func__); return -ENOMEM; } /* Zero out *p_init_enet_param_shadow */ memset((char *)ugeth->p_init_enet_param_shadow, 0, sizeof(struct ucc_geth_init_pram)); /* Fill shadow InitEnet command parameter structure */ ugeth->p_init_enet_param_shadow->resinit1 = ENET_INIT_PARAM_MAGIC_RES_INIT1; ugeth->p_init_enet_param_shadow->resinit2 = ENET_INIT_PARAM_MAGIC_RES_INIT2; ugeth->p_init_enet_param_shadow->resinit3 = ENET_INIT_PARAM_MAGIC_RES_INIT3; ugeth->p_init_enet_param_shadow->resinit4 = ENET_INIT_PARAM_MAGIC_RES_INIT4; ugeth->p_init_enet_param_shadow->resinit5 = ENET_INIT_PARAM_MAGIC_RES_INIT5; ugeth->p_init_enet_param_shadow->rgftgfrxglobal |= ((u32) ug_info->numThreadsRx) << ENET_INIT_PARAM_RGF_SHIFT; ugeth->p_init_enet_param_shadow->rgftgfrxglobal |= ((u32) ug_info->numThreadsTx) << ENET_INIT_PARAM_TGF_SHIFT; ugeth->p_init_enet_param_shadow->rgftgfrxglobal |= ugeth->rx_glbl_pram_offset | ug_info->riscRx; if ((ug_info->largestexternallookupkeysize != QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_NONE) && (ug_info->largestexternallookupkeysize != QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_8_BYTES) && (ug_info->largestexternallookupkeysize != QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_16_BYTES)) { if (netif_msg_ifup(ugeth)) ugeth_err("%s: Invalid largest External Lookup Key Size.", __func__); return -EINVAL; } ugeth->p_init_enet_param_shadow->largestexternallookupkeysize = ug_info->largestexternallookupkeysize; size = sizeof(struct ucc_geth_thread_rx_pram); if (ug_info->rxExtendedFiltering) { size += THREAD_RX_PRAM_ADDITIONAL_FOR_EXTENDED_FILTERING; if (ug_info->largestexternallookupkeysize == QE_FLTR_TABLE_LOOKUP_KEY_SIZE_8_BYTES) size += THREAD_RX_PRAM_ADDITIONAL_FOR_EXTENDED_FILTERING_8; if (ug_info->largestexternallookupkeysize == QE_FLTR_TABLE_LOOKUP_KEY_SIZE_16_BYTES) size += THREAD_RX_PRAM_ADDITIONAL_FOR_EXTENDED_FILTERING_16; } if ((ret_val = fill_init_enet_entries(ugeth, &(ugeth-> p_init_enet_param_shadow->rxthread[0]), (u8) (numThreadsRxNumerical + 1) /* Rx needs one extra for terminator */ , size, UCC_GETH_THREAD_RX_PRAM_ALIGNMENT, ug_info->riscRx, 1)) != 0) { if (netif_msg_ifup(ugeth)) ugeth_err("%s: Can not fill p_init_enet_param_shadow.", __func__); return ret_val; } ugeth->p_init_enet_param_shadow->txglobal = ugeth->tx_glbl_pram_offset | ug_info->riscTx; if ((ret_val = fill_init_enet_entries(ugeth, &(ugeth->p_init_enet_param_shadow-> txthread[0]), numThreadsTxNumerical, sizeof(struct ucc_geth_thread_tx_pram), UCC_GETH_THREAD_TX_PRAM_ALIGNMENT, ug_info->riscTx, 0)) != 0) { if (netif_msg_ifup(ugeth)) ugeth_err("%s: Can not fill p_init_enet_param_shadow.", __func__); return ret_val; } /* Load Rx bds with buffers */ for (i = 0; i < ug_info->numQueuesRx; i++) { if ((ret_val = rx_bd_buffer_set(ugeth, (u8) i)) != 0) { if (netif_msg_ifup(ugeth)) ugeth_err("%s: Can not fill Rx bds with buffers.", __func__); return ret_val; } } /* Allocate InitEnet command parameter structure */ init_enet_pram_offset = qe_muram_alloc(sizeof(struct ucc_geth_init_pram), 4); if (IS_ERR_VALUE(init_enet_pram_offset)) { if (netif_msg_ifup(ugeth)) ugeth_err ("%s: Can not allocate DPRAM memory for p_init_enet_pram.", __func__); return -ENOMEM; } p_init_enet_pram = (struct ucc_geth_init_pram __iomem *) qe_muram_addr(init_enet_pram_offset); /* Copy shadow InitEnet command parameter structure into PRAM */ out_8(&p_init_enet_pram->resinit1, ugeth->p_init_enet_param_shadow->resinit1); out_8(&p_init_enet_pram->resinit2, ugeth->p_init_enet_param_shadow->resinit2); out_8(&p_init_enet_pram->resinit3, ugeth->p_init_enet_param_shadow->resinit3); out_8(&p_init_enet_pram->resinit4, ugeth->p_init_enet_param_shadow->resinit4); out_be16(&p_init_enet_pram->resinit5, ugeth->p_init_enet_param_shadow->resinit5); out_8(&p_init_enet_pram->largestexternallookupkeysize, ugeth->p_init_enet_param_shadow->largestexternallookupkeysize); out_be32(&p_init_enet_pram->rgftgfrxglobal, ugeth->p_init_enet_param_shadow->rgftgfrxglobal); for (i = 0; i < ENET_INIT_PARAM_MAX_ENTRIES_RX; i++) out_be32(&p_init_enet_pram->rxthread[i], ugeth->p_init_enet_param_shadow->rxthread[i]); out_be32(&p_init_enet_pram->txglobal, ugeth->p_init_enet_param_shadow->txglobal); for (i = 0; i < ENET_INIT_PARAM_MAX_ENTRIES_TX; i++) out_be32(&p_init_enet_pram->txthread[i], ugeth->p_init_enet_param_shadow->txthread[i]); /* Issue QE command */ cecr_subblock = ucc_fast_get_qe_cr_subblock(ugeth->ug_info->uf_info.ucc_num); qe_issue_cmd(command, cecr_subblock, QE_CR_PROTOCOL_ETHERNET, init_enet_pram_offset); /* Free InitEnet command parameter */ qe_muram_free(init_enet_pram_offset); return 0; } /* This is called by the kernel when a frame is ready for transmission. */ /* It is pointed to by the dev->hard_start_xmit function pointer */ static int ucc_geth_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct ucc_geth_private *ugeth = netdev_priv(dev); #ifdef CONFIG_UGETH_TX_ON_DEMAND struct ucc_fast_private *uccf; #endif u8 __iomem *bd; /* BD pointer */ u32 bd_status; u8 txQ = 0; ugeth_vdbg("%s: IN", __func__); spin_lock_irq(&ugeth->lock); dev->stats.tx_bytes += skb->len; /* Start from the next BD that should be filled */ bd = ugeth->txBd[txQ]; bd_status = in_be32((u32 __iomem *)bd); /* Save the skb pointer so we can free it later */ ugeth->tx_skbuff[txQ][ugeth->skb_curtx[txQ]] = skb; /* Update the current skb pointer (wrapping if this was the last) */ ugeth->skb_curtx[txQ] = (ugeth->skb_curtx[txQ] + 1) & TX_RING_MOD_MASK(ugeth->ug_info->bdRingLenTx[txQ]); /* set up the buffer descriptor */ out_be32(&((struct qe_bd __iomem *)bd)->buf, dma_map_single(&ugeth->dev->dev, skb->data, skb->len, DMA_TO_DEVICE)); /* printk(KERN_DEBUG"skb->data is 0x%x\n",skb->data); */ bd_status = (bd_status & T_W) | T_R | T_I | T_L | skb->len; /* set bd status and length */ out_be32((u32 __iomem *)bd, bd_status); dev->trans_start = jiffies; /* Move to next BD in the ring */ if (!(bd_status & T_W)) bd += sizeof(struct qe_bd); else bd = ugeth->p_tx_bd_ring[txQ]; /* If the next BD still needs to be cleaned up, then the bds are full. We need to tell the kernel to stop sending us stuff. */ if (bd == ugeth->confBd[txQ]) { if (!netif_queue_stopped(dev)) netif_stop_queue(dev); } ugeth->txBd[txQ] = bd; if (ugeth->p_scheduler) { ugeth->cpucount[txQ]++; /* Indicate to QE that there are more Tx bds ready for transmission */ /* This is done by writing a running counter of the bd count to the scheduler PRAM. */ out_be16(ugeth->p_cpucount[txQ], ugeth->cpucount[txQ]); } #ifdef CONFIG_UGETH_TX_ON_DEMAND uccf = ugeth->uccf; out_be16(uccf->p_utodr, UCC_FAST_TOD); #endif spin_unlock_irq(&ugeth->lock); return 0; } static int ucc_geth_rx(struct ucc_geth_private *ugeth, u8 rxQ, int rx_work_limit) { struct sk_buff *skb; u8 __iomem *bd; u16 length, howmany = 0; u32 bd_status; u8 *bdBuffer; struct net_device *dev; ugeth_vdbg("%s: IN", __func__); dev = ugeth->dev; /* collect received buffers */ bd = ugeth->rxBd[rxQ]; bd_status = in_be32((u32 __iomem *)bd); /* while there are received buffers and BD is full (~R_E) */ while (!((bd_status & (R_E)) || (--rx_work_limit < 0))) { bdBuffer = (u8 *) in_be32(&((struct qe_bd __iomem *)bd)->buf); length = (u16) ((bd_status & BD_LENGTH_MASK) - 4); skb = ugeth->rx_skbuff[rxQ][ugeth->skb_currx[rxQ]]; /* determine whether buffer is first, last, first and last (single buffer frame) or middle (not first and not last) */ if (!skb || (!(bd_status & (R_F | R_L))) || (bd_status & R_ERRORS_FATAL)) { if (netif_msg_rx_err(ugeth)) ugeth_err("%s, %d: ERROR!!! skb - 0x%08x", __func__, __LINE__, (u32) skb); if (skb) dev_kfree_skb_any(skb); ugeth->rx_skbuff[rxQ][ugeth->skb_currx[rxQ]] = NULL; dev->stats.rx_dropped++; } else { dev->stats.rx_packets++; howmany++; /* Prep the skb for the packet */ skb_put(skb, length); /* Tell the skb what kind of packet this is */ skb->protocol = eth_type_trans(skb, ugeth->dev); dev->stats.rx_bytes += length; /* Send the packet up the stack */ netif_receive_skb(skb); } skb = get_new_skb(ugeth, bd); if (!skb) { if (netif_msg_rx_err(ugeth)) ugeth_warn("%s: No Rx Data Buffer", __func__); dev->stats.rx_dropped++; break; } ugeth->rx_skbuff[rxQ][ugeth->skb_currx[rxQ]] = skb; /* update to point at the next skb */ ugeth->skb_currx[rxQ] = (ugeth->skb_currx[rxQ] + 1) & RX_RING_MOD_MASK(ugeth->ug_info->bdRingLenRx[rxQ]); if (bd_status & R_W) bd = ugeth->p_rx_bd_ring[rxQ]; else bd += sizeof(struct qe_bd); bd_status = in_be32((u32 __iomem *)bd); } ugeth->rxBd[rxQ] = bd; return howmany; } static int ucc_geth_tx(struct net_device *dev, u8 txQ) { /* Start from the next BD that should be filled */ struct ucc_geth_private *ugeth = netdev_priv(dev); u8 __iomem *bd; /* BD pointer */ u32 bd_status; bd = ugeth->confBd[txQ]; bd_status = in_be32((u32 __iomem *)bd); /* Normal processing. */ while ((bd_status & T_R) == 0) { /* BD contains already transmitted buffer. */ /* Handle the transmitted buffer and release */ /* the BD to be used with the current frame */ if ((bd == ugeth->txBd[txQ]) && (netif_queue_stopped(dev) == 0)) break; dev->stats.tx_packets++; /* Free the sk buffer associated with this TxBD */ dev_kfree_skb_irq(ugeth-> tx_skbuff[txQ][ugeth->skb_dirtytx[txQ]]); ugeth->tx_skbuff[txQ][ugeth->skb_dirtytx[txQ]] = NULL; ugeth->skb_dirtytx[txQ] = (ugeth->skb_dirtytx[txQ] + 1) & TX_RING_MOD_MASK(ugeth->ug_info->bdRingLenTx[txQ]); /* We freed a buffer, so now we can restart transmission */ if (netif_queue_stopped(dev)) netif_wake_queue(dev); /* Advance the confirmation BD pointer */ if (!(bd_status & T_W)) bd += sizeof(struct qe_bd); else bd = ugeth->p_tx_bd_ring[txQ]; bd_status = in_be32((u32 __iomem *)bd); } ugeth->confBd[txQ] = bd; return 0; } static int ucc_geth_poll(struct napi_struct *napi, int budget) { struct ucc_geth_private *ugeth = container_of(napi, struct ucc_geth_private, napi); struct ucc_geth_info *ug_info; int howmany, i; ug_info = ugeth->ug_info; howmany = 0; for (i = 0; i < ug_info->numQueuesRx; i++) howmany += ucc_geth_rx(ugeth, i, budget - howmany); if (howmany < budget) { netif_rx_complete(napi); setbits32(ugeth->uccf->p_uccm, UCCE_RX_EVENTS); } return howmany; } static irqreturn_t ucc_geth_irq_handler(int irq, void *info) { struct net_device *dev = info; struct ucc_geth_private *ugeth = netdev_priv(dev); struct ucc_fast_private *uccf; struct ucc_geth_info *ug_info; register u32 ucce; register u32 uccm; register u32 tx_mask; u8 i; ugeth_vdbg("%s: IN", __func__); uccf = ugeth->uccf; ug_info = ugeth->ug_info; /* read and clear events */ ucce = (u32) in_be32(uccf->p_ucce); uccm = (u32) in_be32(uccf->p_uccm); ucce &= uccm; out_be32(uccf->p_ucce, ucce); /* check for receive events that require processing */ if (ucce & UCCE_RX_EVENTS) { if (netif_rx_schedule_prep(&ugeth->napi)) { uccm &= ~UCCE_RX_EVENTS; out_be32(uccf->p_uccm, uccm); __netif_rx_schedule(&ugeth->napi); } } /* Tx event processing */ if (ucce & UCCE_TX_EVENTS) { spin_lock(&ugeth->lock); tx_mask = UCC_GETH_UCCE_TXB0; for (i = 0; i < ug_info->numQueuesTx; i++) { if (ucce & tx_mask) ucc_geth_tx(dev, i); ucce &= ~tx_mask; tx_mask <<= 1; } spin_unlock(&ugeth->lock); } /* Errors and other events */ if (ucce & UCCE_OTHER) { if (ucce & UCC_GETH_UCCE_BSY) dev->stats.rx_errors++; if (ucce & UCC_GETH_UCCE_TXE) dev->stats.tx_errors++; } return IRQ_HANDLED; } #ifdef CONFIG_NET_POLL_CONTROLLER /* * Polling 'interrupt' - used by things like netconsole to send skbs * without having to re-enable interrupts. It's not called while * the interrupt routine is executing. */ static void ucc_netpoll(struct net_device *dev) { struct ucc_geth_private *ugeth = netdev_priv(dev); int irq = ugeth->ug_info->uf_info.irq; disable_irq(irq); ucc_geth_irq_handler(irq, dev); enable_irq(irq); } #endif /* CONFIG_NET_POLL_CONTROLLER */ /* Called when something needs to use the ethernet device */ /* Returns 0 for success. */ static int ucc_geth_open(struct net_device *dev) { struct ucc_geth_private *ugeth = netdev_priv(dev); int err; ugeth_vdbg("%s: IN", __func__); /* Test station address */ if (dev->dev_addr[0] & ENET_GROUP_ADDR) { if (netif_msg_ifup(ugeth)) ugeth_err("%s: Multicast address used for station address" " - is this what you wanted?", __func__); return -EINVAL; } err = ucc_struct_init(ugeth); if (err) { if (netif_msg_ifup(ugeth)) ugeth_err("%s: Cannot configure internal struct, aborting.", dev->name); goto out_err_stop; } napi_enable(&ugeth->napi); err = ucc_geth_startup(ugeth); if (err) { if (netif_msg_ifup(ugeth)) ugeth_err("%s: Cannot configure net device, aborting.", dev->name); goto out_err; } err = adjust_enet_interface(ugeth); if (err) { if (netif_msg_ifup(ugeth)) ugeth_err("%s: Cannot configure net device, aborting.", dev->name); goto out_err; } /* Set MACSTNADDR1, MACSTNADDR2 */ /* For more details see the hardware spec. */ init_mac_station_addr_regs(dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5], &ugeth->ug_regs->macstnaddr1, &ugeth->ug_regs->macstnaddr2); err = init_phy(dev); if (err) { if (netif_msg_ifup(ugeth)) ugeth_err("%s: Cannot initialize PHY, aborting.", dev->name); goto out_err; } phy_start(ugeth->phydev); err = ugeth_enable(ugeth, COMM_DIR_RX_AND_TX); if (err) { if (netif_msg_ifup(ugeth)) ugeth_err("%s: Cannot enable net device, aborting.", dev->name); goto out_err; } err = request_irq(ugeth->ug_info->uf_info.irq, ucc_geth_irq_handler, 0, "UCC Geth", dev); if (err) { if (netif_msg_ifup(ugeth)) ugeth_err("%s: Cannot get IRQ for net device, aborting.", dev->name); goto out_err; } netif_start_queue(dev); return err; out_err: napi_disable(&ugeth->napi); out_err_stop: ucc_geth_stop(ugeth); return err; } /* Stops the kernel queue, and halts the controller */ static int ucc_geth_close(struct net_device *dev) { struct ucc_geth_private *ugeth = netdev_priv(dev); ugeth_vdbg("%s: IN", __func__); napi_disable(&ugeth->napi); ucc_geth_stop(ugeth); free_irq(ugeth->ug_info->uf_info.irq, ugeth->dev); phy_disconnect(ugeth->phydev); ugeth->phydev = NULL; netif_stop_queue(dev); return 0; } /* Reopen device. This will reset the MAC and PHY. */ static void ucc_geth_timeout_work(struct work_struct *work) { struct ucc_geth_private *ugeth; struct net_device *dev; ugeth = container_of(work, struct ucc_geth_private, timeout_work); dev = ugeth->dev; ugeth_vdbg("%s: IN", __func__); dev->stats.tx_errors++; ugeth_dump_regs(ugeth); if (dev->flags & IFF_UP) { /* * Must reset MAC *and* PHY. This is done by reopening * the device. */ ucc_geth_close(dev); ucc_geth_open(dev); } netif_tx_schedule_all(dev); } /* * ucc_geth_timeout gets called when a packet has not been * transmitted after a set amount of time. */ static void ucc_geth_timeout(struct net_device *dev) { struct ucc_geth_private *ugeth = netdev_priv(dev); netif_carrier_off(dev); schedule_work(&ugeth->timeout_work); } static phy_interface_t to_phy_interface(const char *phy_connection_type) { if (strcasecmp(phy_connection_type, "mii") == 0) return PHY_INTERFACE_MODE_MII; if (strcasecmp(phy_connection_type, "gmii") == 0) return PHY_INTERFACE_MODE_GMII; if (strcasecmp(phy_connection_type, "tbi") == 0) return PHY_INTERFACE_MODE_TBI; if (strcasecmp(phy_connection_type, "rmii") == 0) return PHY_INTERFACE_MODE_RMII; if (strcasecmp(phy_connection_type, "rgmii") == 0) return PHY_INTERFACE_MODE_RGMII; if (strcasecmp(phy_connection_type, "rgmii-id") == 0) return PHY_INTERFACE_MODE_RGMII_ID; if (strcasecmp(phy_connection_type, "rgmii-txid") == 0) return PHY_INTERFACE_MODE_RGMII_TXID; if (strcasecmp(phy_connection_type, "rgmii-rxid") == 0) return PHY_INTERFACE_MODE_RGMII_RXID; if (strcasecmp(phy_connection_type, "rtbi") == 0) return PHY_INTERFACE_MODE_RTBI; return PHY_INTERFACE_MODE_MII; } static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *match) { struct device *device = &ofdev->dev; struct device_node *np = ofdev->node; struct device_node *mdio; struct net_device *dev = NULL; struct ucc_geth_private *ugeth = NULL; struct ucc_geth_info *ug_info; struct resource res; struct device_node *phy; int err, ucc_num, max_speed = 0; const phandle *ph; const u32 *fixed_link; const unsigned int *prop; const char *sprop; const void *mac_addr; phy_interface_t phy_interface; static const int enet_to_speed[] = { SPEED_10, SPEED_10, SPEED_10, SPEED_100, SPEED_100, SPEED_100, SPEED_1000, SPEED_1000, SPEED_1000, SPEED_1000, }; static const phy_interface_t enet_to_phy_interface[] = { PHY_INTERFACE_MODE_MII, PHY_INTERFACE_MODE_RMII, PHY_INTERFACE_MODE_RGMII, PHY_INTERFACE_MODE_MII, PHY_INTERFACE_MODE_RMII, PHY_INTERFACE_MODE_RGMII, PHY_INTERFACE_MODE_GMII, PHY_INTERFACE_MODE_RGMII, PHY_INTERFACE_MODE_TBI, PHY_INTERFACE_MODE_RTBI, }; ugeth_vdbg("%s: IN", __func__); prop = of_get_property(np, "cell-index", NULL); if (!prop) { prop = of_get_property(np, "device-id", NULL); if (!prop) return -ENODEV; } ucc_num = *prop - 1; if ((ucc_num < 0) || (ucc_num > 7)) return -ENODEV; ug_info = &ugeth_info[ucc_num]; if (ug_info == NULL) { if (netif_msg_probe(&debug)) ugeth_err("%s: [%d] Missing additional data!", __func__, ucc_num); return -ENODEV; } ug_info->uf_info.ucc_num = ucc_num; sprop = of_get_property(np, "rx-clock-name", NULL); if (sprop) { ug_info->uf_info.rx_clock = qe_clock_source(sprop); if ((ug_info->uf_info.rx_clock < QE_CLK_NONE) || (ug_info->uf_info.rx_clock > QE_CLK24)) { printk(KERN_ERR "ucc_geth: invalid rx-clock-name property\n"); return -EINVAL; } } else { prop = of_get_property(np, "rx-clock", NULL); if (!prop) { /* If both rx-clock-name and rx-clock are missing, we want to tell people to use rx-clock-name. */ printk(KERN_ERR "ucc_geth: missing rx-clock-name property\n"); return -EINVAL; } if ((*prop < QE_CLK_NONE) || (*prop > QE_CLK24)) { printk(KERN_ERR "ucc_geth: invalid rx-clock propperty\n"); return -EINVAL; } ug_info->uf_info.rx_clock = *prop; } sprop = of_get_property(np, "tx-clock-name", NULL); if (sprop) { ug_info->uf_info.tx_clock = qe_clock_source(sprop); if ((ug_info->uf_info.tx_clock < QE_CLK_NONE) || (ug_info->uf_info.tx_clock > QE_CLK24)) { printk(KERN_ERR "ucc_geth: invalid tx-clock-name property\n"); return -EINVAL; } } else { prop = of_get_property(np, "tx-clock", NULL); if (!prop) { printk(KERN_ERR "ucc_geth: mising tx-clock-name property\n"); return -EINVAL; } if ((*prop < QE_CLK_NONE) || (*prop > QE_CLK24)) { printk(KERN_ERR "ucc_geth: invalid tx-clock property\n"); return -EINVAL; } ug_info->uf_info.tx_clock = *prop; } err = of_address_to_resource(np, 0, &res); if (err) return -EINVAL; ug_info->uf_info.regs = res.start; ug_info->uf_info.irq = irq_of_parse_and_map(np, 0); fixed_link = of_get_property(np, "fixed-link", NULL); if (fixed_link) { snprintf(ug_info->mdio_bus, MII_BUS_ID_SIZE, "0"); ug_info->phy_address = fixed_link[0]; phy = NULL; } else { ph = of_get_property(np, "phy-handle", NULL); phy = of_find_node_by_phandle(*ph); if (phy == NULL) return -ENODEV; /* set the PHY address */ prop = of_get_property(phy, "reg", NULL); if (prop == NULL) return -1; ug_info->phy_address = *prop; /* Set the bus id */ mdio = of_get_parent(phy); if (mdio == NULL) return -1; err = of_address_to_resource(mdio, 0, &res); of_node_put(mdio); if (err) return -1; snprintf(ug_info->mdio_bus, MII_BUS_ID_SIZE, "%x", res.start); } /* get the phy interface type, or default to MII */ prop = of_get_property(np, "phy-connection-type", NULL); if (!prop) { /* handle interface property present in old trees */ prop = of_get_property(phy, "interface", NULL); if (prop != NULL) { phy_interface = enet_to_phy_interface[*prop]; max_speed = enet_to_speed[*prop]; } else phy_interface = PHY_INTERFACE_MODE_MII; } else { phy_interface = to_phy_interface((const char *)prop); } /* get speed, or derive from PHY interface */ if (max_speed == 0) switch (phy_interface) { case PHY_INTERFACE_MODE_GMII: case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_TXID: case PHY_INTERFACE_MODE_TBI: case PHY_INTERFACE_MODE_RTBI: max_speed = SPEED_1000; break; default: max_speed = SPEED_100; break; } if (max_speed == SPEED_1000) { /* configure muram FIFOs for gigabit operation */ ug_info->uf_info.urfs = UCC_GETH_URFS_GIGA_INIT; ug_info->uf_info.urfet = UCC_GETH_URFET_GIGA_INIT; ug_info->uf_info.urfset = UCC_GETH_URFSET_GIGA_INIT; ug_info->uf_info.utfs = UCC_GETH_UTFS_GIGA_INIT; ug_info->uf_info.utfet = UCC_GETH_UTFET_GIGA_INIT; ug_info->uf_info.utftt = UCC_GETH_UTFTT_GIGA_INIT; ug_info->numThreadsTx = UCC_GETH_NUM_OF_THREADS_4; ug_info->numThreadsRx = UCC_GETH_NUM_OF_THREADS_4; } if (netif_msg_probe(&debug)) printk(KERN_INFO "ucc_geth: UCC%1d at 0x%8x (irq = %d) \n", ug_info->uf_info.ucc_num + 1, ug_info->uf_info.regs, ug_info->uf_info.irq); /* Create an ethernet device instance */ dev = alloc_etherdev(sizeof(*ugeth)); if (dev == NULL) return -ENOMEM; ugeth = netdev_priv(dev); spin_lock_init(&ugeth->lock); /* Create CQs for hash tables */ INIT_LIST_HEAD(&ugeth->group_hash_q); INIT_LIST_HEAD(&ugeth->ind_hash_q); dev_set_drvdata(device, dev); /* Set the dev->base_addr to the gfar reg region */ dev->base_addr = (unsigned long)(ug_info->uf_info.regs); SET_NETDEV_DEV(dev, device); /* Fill in the dev structure */ uec_set_ethtool_ops(dev); dev->open = ucc_geth_open; dev->hard_start_xmit = ucc_geth_start_xmit; dev->tx_timeout = ucc_geth_timeout; dev->watchdog_timeo = TX_TIMEOUT; INIT_WORK(&ugeth->timeout_work, ucc_geth_timeout_work); netif_napi_add(dev, &ugeth->napi, ucc_geth_poll, UCC_GETH_DEV_WEIGHT); #ifdef CONFIG_NET_POLL_CONTROLLER dev->poll_controller = ucc_netpoll; #endif dev->stop = ucc_geth_close; // dev->change_mtu = ucc_geth_change_mtu; dev->mtu = 1500; dev->set_multicast_list = ucc_geth_set_multi; ugeth->msg_enable = netif_msg_init(debug.msg_enable, UGETH_MSG_DEFAULT); ugeth->phy_interface = phy_interface; ugeth->max_speed = max_speed; err = register_netdev(dev); if (err) { if (netif_msg_probe(ugeth)) ugeth_err("%s: Cannot register net device, aborting.", dev->name); free_netdev(dev); return err; } mac_addr = of_get_mac_address(np); if (mac_addr) memcpy(dev->dev_addr, mac_addr, 6); ugeth->ug_info = ug_info; ugeth->dev = dev; ugeth->node = np; return 0; } static int ucc_geth_remove(struct of_device* ofdev) { struct device *device = &ofdev->dev; struct net_device *dev = dev_get_drvdata(device); struct ucc_geth_private *ugeth = netdev_priv(dev); unregister_netdev(dev); free_netdev(dev); ucc_geth_memclean(ugeth); dev_set_drvdata(device, NULL); return 0; } static struct of_device_id ucc_geth_match[] = { { .type = "network", .compatible = "ucc_geth", }, {}, }; MODULE_DEVICE_TABLE(of, ucc_geth_match); static struct of_platform_driver ucc_geth_driver = { .name = DRV_NAME, .match_table = ucc_geth_match, .probe = ucc_geth_probe, .remove = ucc_geth_remove, }; static int __init ucc_geth_init(void) { int i, ret; ret = uec_mdio_init(); if (ret) return ret; if (netif_msg_drv(&debug)) printk(KERN_INFO "ucc_geth: " DRV_DESC "\n"); for (i = 0; i < 8; i++) memcpy(&(ugeth_info[i]), &ugeth_primary_info, sizeof(ugeth_primary_info)); ret = of_register_platform_driver(&ucc_geth_driver); if (ret) uec_mdio_exit(); return ret; } static void __exit ucc_geth_exit(void) { of_unregister_platform_driver(&ucc_geth_driver); uec_mdio_exit(); } module_init(ucc_geth_init); module_exit(ucc_geth_exit); MODULE_AUTHOR("Freescale Semiconductor, Inc"); MODULE_DESCRIPTION(DRV_DESC); MODULE_VERSION(DRV_VERSION); MODULE_LICENSE("GPL");