1
1
/*
2
2
* net/dsa/dsa.c - Hardware switch handling
3
3
* Copyright (c) 2008-2009 Marvell Semiconductor
4
+ * Copyright (c) 2013 Florian Fainelli <florian@openwrt.org>
4
5
*
5
6
* This program is free software; you can redistribute it and/or modify
6
7
* it under the terms of the GNU General Public License as published by
14
15
#include <linux/slab.h>
15
16
#include <linux/module.h>
16
17
#include <net/dsa.h>
18
+ #include <linux/of.h>
19
+ #include <linux/of_mdio.h>
20
+ #include <linux/of_platform.h>
17
21
#include "dsa_priv.h"
18
22
19
23
char dsa_driver_version [] = "0.1" ;
@@ -287,34 +291,239 @@ static struct net_device *dev_to_net_device(struct device *dev)
287
291
return NULL ;
288
292
}
289
293
294
+ #ifdef CONFIG_OF
295
+ static int dsa_of_setup_routing_table (struct dsa_platform_data * pd ,
296
+ struct dsa_chip_data * cd ,
297
+ int chip_index ,
298
+ struct device_node * link )
299
+ {
300
+ int ret ;
301
+ const __be32 * reg ;
302
+ int link_port_addr ;
303
+ int link_sw_addr ;
304
+ struct device_node * parent_sw ;
305
+ int len ;
306
+
307
+ parent_sw = of_get_parent (link );
308
+ if (!parent_sw )
309
+ return - EINVAL ;
310
+
311
+ reg = of_get_property (parent_sw , "reg" , & len );
312
+ if (!reg || (len != sizeof (* reg ) * 2 ))
313
+ return - EINVAL ;
314
+
315
+ link_sw_addr = be32_to_cpup (reg + 1 );
316
+
317
+ if (link_sw_addr >= pd -> nr_chips )
318
+ return - EINVAL ;
319
+
320
+ /* First time routing table allocation */
321
+ if (!cd -> rtable ) {
322
+ cd -> rtable = kmalloc (pd -> nr_chips * sizeof (s8 ), GFP_KERNEL );
323
+ if (!cd -> rtable )
324
+ return - ENOMEM ;
325
+
326
+ /* default to no valid uplink/downlink */
327
+ memset (cd -> rtable , -1 , pd -> nr_chips * sizeof (s8 ));
328
+ }
329
+
330
+ reg = of_get_property (link , "reg" , NULL );
331
+ if (!reg ) {
332
+ ret = - EINVAL ;
333
+ goto out ;
334
+ }
335
+
336
+ link_port_addr = be32_to_cpup (reg );
337
+
338
+ cd -> rtable [link_sw_addr ] = link_port_addr ;
339
+
340
+ return 0 ;
341
+ out :
342
+ kfree (cd -> rtable );
343
+ return ret ;
344
+ }
345
+
346
+ static int dsa_of_probe (struct platform_device * pdev )
347
+ {
348
+ struct device_node * np = pdev -> dev .of_node ;
349
+ struct device_node * child , * mdio , * ethernet , * port , * link ;
350
+ struct mii_bus * mdio_bus ;
351
+ struct platform_device * ethernet_dev ;
352
+ struct dsa_platform_data * pd ;
353
+ struct dsa_chip_data * cd ;
354
+ const char * port_name ;
355
+ int chip_index , port_index ;
356
+ const unsigned int * sw_addr , * port_reg ;
357
+ int ret , i ;
358
+
359
+ mdio = of_parse_phandle (np , "dsa,mii-bus" , 0 );
360
+ if (!mdio )
361
+ return - EINVAL ;
362
+
363
+ mdio_bus = of_mdio_find_bus (mdio );
364
+ if (!mdio_bus )
365
+ return - EINVAL ;
366
+
367
+ ethernet = of_parse_phandle (np , "dsa,ethernet" , 0 );
368
+ if (!ethernet )
369
+ return - EINVAL ;
370
+
371
+ ethernet_dev = of_find_device_by_node (ethernet );
372
+ if (!ethernet_dev )
373
+ return - ENODEV ;
374
+
375
+ pd = kzalloc (sizeof (* pd ), GFP_KERNEL );
376
+ if (!pd )
377
+ return - ENOMEM ;
378
+
379
+ pdev -> dev .platform_data = pd ;
380
+ pd -> netdev = & ethernet_dev -> dev ;
381
+ pd -> nr_chips = of_get_child_count (np );
382
+ if (pd -> nr_chips > DSA_MAX_SWITCHES )
383
+ pd -> nr_chips = DSA_MAX_SWITCHES ;
384
+
385
+ pd -> chip = kzalloc (pd -> nr_chips * sizeof (struct dsa_chip_data ),
386
+ GFP_KERNEL );
387
+ if (!pd -> chip ) {
388
+ ret = - ENOMEM ;
389
+ goto out_free ;
390
+ }
391
+
392
+ chip_index = 0 ;
393
+ for_each_available_child_of_node (np , child ) {
394
+ cd = & pd -> chip [chip_index ];
395
+
396
+ cd -> mii_bus = & mdio_bus -> dev ;
397
+
398
+ sw_addr = of_get_property (child , "reg" , NULL );
399
+ if (!sw_addr )
400
+ continue ;
401
+
402
+ cd -> sw_addr = be32_to_cpup (sw_addr );
403
+ if (cd -> sw_addr > PHY_MAX_ADDR )
404
+ continue ;
405
+
406
+ for_each_available_child_of_node (child , port ) {
407
+ port_reg = of_get_property (port , "reg" , NULL );
408
+ if (!port_reg )
409
+ continue ;
410
+
411
+ port_index = be32_to_cpup (port_reg );
412
+
413
+ port_name = of_get_property (port , "label" , NULL );
414
+ if (!port_name )
415
+ continue ;
416
+
417
+ cd -> port_names [port_index ] = kstrdup (port_name ,
418
+ GFP_KERNEL );
419
+ if (!cd -> port_names [port_index ]) {
420
+ ret = - ENOMEM ;
421
+ goto out_free_chip ;
422
+ }
423
+
424
+ link = of_parse_phandle (port , "link" , 0 );
425
+
426
+ if (!strcmp (port_name , "dsa" ) && link &&
427
+ pd -> nr_chips > 1 ) {
428
+ ret = dsa_of_setup_routing_table (pd , cd ,
429
+ chip_index , link );
430
+ if (ret )
431
+ goto out_free_chip ;
432
+ }
433
+
434
+ if (port_index == DSA_MAX_PORTS )
435
+ break ;
436
+ }
437
+ }
438
+
439
+ return 0 ;
440
+
441
+ out_free_chip :
442
+ for (i = 0 ; i < pd -> nr_chips ; i ++ ) {
443
+ port_index = 0 ;
444
+ while (pd -> chip [i ].port_names &&
445
+ pd -> chip [i ].port_names [++ port_index ])
446
+ kfree (pd -> chip [i ].port_names [port_index ]);
447
+ kfree (pd -> chip [i ].rtable );
448
+ }
449
+ kfree (pd -> chip );
450
+ out_free :
451
+ kfree (pd );
452
+ pdev -> dev .platform_data = NULL ;
453
+ return ret ;
454
+ }
455
+
456
+ static void dsa_of_remove (struct platform_device * pdev )
457
+ {
458
+ struct dsa_platform_data * pd = pdev -> dev .platform_data ;
459
+ int i ;
460
+ int port_index ;
461
+
462
+ if (!pdev -> dev .of_node )
463
+ return ;
464
+
465
+ for (i = 0 ; i < pd -> nr_chips ; i ++ ) {
466
+ port_index = 0 ;
467
+ while (pd -> chip [i ].port_names &&
468
+ pd -> chip [i ].port_names [++ port_index ])
469
+ kfree (pd -> chip [i ].port_names [port_index ]);
470
+ kfree (pd -> chip [i ].rtable );
471
+ }
472
+
473
+ kfree (pd -> chip );
474
+ kfree (pd );
475
+ }
476
+ #else
477
+ static inline int dsa_of_probe (struct platform_device * pdev )
478
+ {
479
+ return 0 ;
480
+ }
481
+
482
+ static inline void dsa_of_remove (struct platform_device * pdev )
483
+ {
484
+ }
485
+ #endif
486
+
290
487
static int dsa_probe (struct platform_device * pdev )
291
488
{
292
489
static int dsa_version_printed ;
293
490
struct dsa_platform_data * pd = pdev -> dev .platform_data ;
294
491
struct net_device * dev ;
295
492
struct dsa_switch_tree * dst ;
296
- int i ;
493
+ int i , ret ;
297
494
298
495
if (!dsa_version_printed ++ )
299
496
printk (KERN_NOTICE "Distributed Switch Architecture "
300
497
"driver version %s\n" , dsa_driver_version );
301
498
499
+ if (pdev -> dev .of_node ) {
500
+ ret = dsa_of_probe (pdev );
501
+ if (ret )
502
+ return ret ;
503
+
504
+ pd = pdev -> dev .platform_data ;
505
+ }
506
+
302
507
if (pd == NULL || pd -> netdev == NULL )
303
508
return - EINVAL ;
304
509
305
510
dev = dev_to_net_device (pd -> netdev );
306
- if (dev == NULL )
307
- return - EINVAL ;
511
+ if (dev == NULL ) {
512
+ ret = - EINVAL ;
513
+ goto out ;
514
+ }
308
515
309
516
if (dev -> dsa_ptr != NULL ) {
310
517
dev_put (dev );
311
- return - EEXIST ;
518
+ ret = - EEXIST ;
519
+ goto out ;
312
520
}
313
521
314
522
dst = kzalloc (sizeof (* dst ), GFP_KERNEL );
315
523
if (dst == NULL ) {
316
524
dev_put (dev );
317
- return - ENOMEM ;
525
+ ret = - ENOMEM ;
526
+ goto out ;
318
527
}
319
528
320
529
platform_set_drvdata (pdev , dst );
@@ -366,6 +575,11 @@ static int dsa_probe(struct platform_device *pdev)
366
575
}
367
576
368
577
return 0 ;
578
+
579
+ out :
580
+ dsa_of_remove (pdev );
581
+
582
+ return ret ;
369
583
}
370
584
371
585
static int dsa_remove (struct platform_device * pdev )
@@ -385,20 +599,29 @@ static int dsa_remove(struct platform_device *pdev)
385
599
dsa_switch_destroy (ds );
386
600
}
387
601
602
+ dsa_of_remove (pdev );
603
+
388
604
return 0 ;
389
605
}
390
606
391
607
static void dsa_shutdown (struct platform_device * pdev )
392
608
{
393
609
}
394
610
611
+ static const struct of_device_id dsa_of_match_table [] = {
612
+ { .compatible = "marvell,dsa" , },
613
+ {}
614
+ };
615
+ MODULE_DEVICE_TABLE (of , dsa_of_match_table );
616
+
395
617
static struct platform_driver dsa_driver = {
396
618
.probe = dsa_probe ,
397
619
.remove = dsa_remove ,
398
620
.shutdown = dsa_shutdown ,
399
621
.driver = {
400
622
.name = "dsa" ,
401
623
.owner = THIS_MODULE ,
624
+ .of_match_table = dsa_of_match_table ,
402
625
},
403
626
};
404
627
0 commit comments