1 /*
2  * This file is subject to the terms and conditions of the GNU General Public
3  * License.  See the file "COPYING" in the main directory of this archive
4  * for more details.
5  *
6  * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
7  */
8 
9 #include <linux/init.h>
10 #include <linux/kernel.h>
11 #include <linux/platform_device.h>
12 #include <bcm63xx_dev_enet.h>
13 #include <bcm63xx_io.h>
14 #include <bcm63xx_regs.h>
15 
16 static struct resource shared_res[] = {
17 	{
18 		.start		= -1, /* filled at runtime */
19 		.end		= -1, /* filled at runtime */
20 		.flags		= IORESOURCE_MEM,
21 	},
22 };
23 
24 static struct platform_device bcm63xx_enet_shared_device = {
25 	.name		= "bcm63xx_enet_shared",
26 	.id		= 0,
27 	.num_resources	= ARRAY_SIZE(shared_res),
28 	.resource	= shared_res,
29 };
30 
31 static int shared_device_registered;
32 
33 static struct resource enet0_res[] = {
34 	{
35 		.start		= -1, /* filled at runtime */
36 		.end		= -1, /* filled at runtime */
37 		.flags		= IORESOURCE_MEM,
38 	},
39 	{
40 		.start		= -1, /* filled at runtime */
41 		.flags		= IORESOURCE_IRQ,
42 	},
43 	{
44 		.start		= -1, /* filled at runtime */
45 		.flags		= IORESOURCE_IRQ,
46 	},
47 	{
48 		.start		= -1, /* filled at runtime */
49 		.flags		= IORESOURCE_IRQ,
50 	},
51 };
52 
53 static struct bcm63xx_enet_platform_data enet0_pd;
54 
55 static struct platform_device bcm63xx_enet0_device = {
56 	.name		= "bcm63xx_enet",
57 	.id		= 0,
58 	.num_resources	= ARRAY_SIZE(enet0_res),
59 	.resource	= enet0_res,
60 	.dev		= {
61 		.platform_data = &enet0_pd,
62 	},
63 };
64 
65 static struct resource enet1_res[] = {
66 	{
67 		.start		= -1, /* filled at runtime */
68 		.end		= -1, /* filled at runtime */
69 		.flags		= IORESOURCE_MEM,
70 	},
71 	{
72 		.start		= -1, /* filled at runtime */
73 		.flags		= IORESOURCE_IRQ,
74 	},
75 	{
76 		.start		= -1, /* filled at runtime */
77 		.flags		= IORESOURCE_IRQ,
78 	},
79 	{
80 		.start		= -1, /* filled at runtime */
81 		.flags		= IORESOURCE_IRQ,
82 	},
83 };
84 
85 static struct bcm63xx_enet_platform_data enet1_pd;
86 
87 static struct platform_device bcm63xx_enet1_device = {
88 	.name		= "bcm63xx_enet",
89 	.id		= 1,
90 	.num_resources	= ARRAY_SIZE(enet1_res),
91 	.resource	= enet1_res,
92 	.dev		= {
93 		.platform_data = &enet1_pd,
94 	},
95 };
96 
bcm63xx_enet_register(int unit,const struct bcm63xx_enet_platform_data * pd)97 int __init bcm63xx_enet_register(int unit,
98 				 const struct bcm63xx_enet_platform_data *pd)
99 {
100 	struct platform_device *pdev;
101 	struct bcm63xx_enet_platform_data *dpd;
102 	int ret;
103 
104 	if (unit > 1)
105 		return -ENODEV;
106 
107 	if (unit == 1 && BCMCPU_IS_6338())
108 		return -ENODEV;
109 
110 	if (!shared_device_registered) {
111 		shared_res[0].start = bcm63xx_regset_address(RSET_ENETDMA);
112 		shared_res[0].end = shared_res[0].start;
113 		if (BCMCPU_IS_6338())
114 			shared_res[0].end += (RSET_ENETDMA_SIZE / 2)  - 1;
115 		else
116 			shared_res[0].end += (RSET_ENETDMA_SIZE)  - 1;
117 
118 		ret = platform_device_register(&bcm63xx_enet_shared_device);
119 		if (ret)
120 			return ret;
121 		shared_device_registered = 1;
122 	}
123 
124 	if (unit == 0) {
125 		enet0_res[0].start = bcm63xx_regset_address(RSET_ENET0);
126 		enet0_res[0].end = enet0_res[0].start;
127 		enet0_res[0].end += RSET_ENET_SIZE - 1;
128 		enet0_res[1].start = bcm63xx_get_irq_number(IRQ_ENET0);
129 		enet0_res[2].start = bcm63xx_get_irq_number(IRQ_ENET0_RXDMA);
130 		enet0_res[3].start = bcm63xx_get_irq_number(IRQ_ENET0_TXDMA);
131 		pdev = &bcm63xx_enet0_device;
132 	} else {
133 		enet1_res[0].start = bcm63xx_regset_address(RSET_ENET1);
134 		enet1_res[0].end = enet1_res[0].start;
135 		enet1_res[0].end += RSET_ENET_SIZE - 1;
136 		enet1_res[1].start = bcm63xx_get_irq_number(IRQ_ENET1);
137 		enet1_res[2].start = bcm63xx_get_irq_number(IRQ_ENET1_RXDMA);
138 		enet1_res[3].start = bcm63xx_get_irq_number(IRQ_ENET1_TXDMA);
139 		pdev = &bcm63xx_enet1_device;
140 	}
141 
142 	/* copy given platform data */
143 	dpd = pdev->dev.platform_data;
144 	memcpy(dpd, pd, sizeof(*pd));
145 
146 	/* adjust them in case internal phy is used */
147 	if (dpd->use_internal_phy) {
148 
149 		/* internal phy only exists for enet0 */
150 		if (unit == 1)
151 			return -ENODEV;
152 
153 		dpd->phy_id = 1;
154 		dpd->has_phy_interrupt = 1;
155 		dpd->phy_interrupt = bcm63xx_get_irq_number(IRQ_ENET_PHY);
156 	}
157 
158 	ret = platform_device_register(pdev);
159 	if (ret)
160 		return ret;
161 	return 0;
162 }
163