11/*
22 * net/dsa/dsa.c - Hardware switch handling
33 * Copyright (c) 2008-2009 Marvell Semiconductor
4+ * Copyright (c) 2013 Florian Fainelli <florian@openwrt.org>
45 *
56 * This program is free software; you can redistribute it and/or modify
67 * it under the terms of the GNU General Public License as published by
1415#include <linux/slab.h>
1516#include <linux/module.h>
1617#include <net/dsa.h>
18+ #include <linux/of.h>
19+ #include <linux/of_mdio.h>
20+ #include <linux/of_platform.h>
1721#include "dsa_priv.h"
1822
1923char dsa_driver_version [] = "0.1" ;
@@ -287,34 +291,239 @@ static struct net_device *dev_to_net_device(struct device *dev)
287291 return NULL ;
288292}
289293
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+
290487static int dsa_probe (struct platform_device * pdev )
291488{
292489 static int dsa_version_printed ;
293490 struct dsa_platform_data * pd = pdev -> dev .platform_data ;
294491 struct net_device * dev ;
295492 struct dsa_switch_tree * dst ;
296- int i ;
493+ int i , ret ;
297494
298495 if (!dsa_version_printed ++ )
299496 printk (KERN_NOTICE "Distributed Switch Architecture "
300497 "driver version %s\n" , dsa_driver_version );
301498
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+
302507 if (pd == NULL || pd -> netdev == NULL )
303508 return - EINVAL ;
304509
305510 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+ }
308515
309516 if (dev -> dsa_ptr != NULL ) {
310517 dev_put (dev );
311- return - EEXIST ;
518+ ret = - EEXIST ;
519+ goto out ;
312520 }
313521
314522 dst = kzalloc (sizeof (* dst ), GFP_KERNEL );
315523 if (dst == NULL ) {
316524 dev_put (dev );
317- return - ENOMEM ;
525+ ret = - ENOMEM ;
526+ goto out ;
318527 }
319528
320529 platform_set_drvdata (pdev , dst );
@@ -366,6 +575,11 @@ static int dsa_probe(struct platform_device *pdev)
366575 }
367576
368577 return 0 ;
578+
579+ out :
580+ dsa_of_remove (pdev );
581+
582+ return ret ;
369583}
370584
371585static int dsa_remove (struct platform_device * pdev )
@@ -385,20 +599,29 @@ static int dsa_remove(struct platform_device *pdev)
385599 dsa_switch_destroy (ds );
386600 }
387601
602+ dsa_of_remove (pdev );
603+
388604 return 0 ;
389605}
390606
391607static void dsa_shutdown (struct platform_device * pdev )
392608{
393609}
394610
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+
395617static struct platform_driver dsa_driver = {
396618 .probe = dsa_probe ,
397619 .remove = dsa_remove ,
398620 .shutdown = dsa_shutdown ,
399621 .driver = {
400622 .name = "dsa" ,
401623 .owner = THIS_MODULE ,
624+ .of_match_table = dsa_of_match_table ,
402625 },
403626};
404627
0 commit comments