1 | /* |
2 | * Copyright (C) 2004 Florian Schirmer <jolt@tuxbox.org> |
3 | * Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org> |
4 | * Copyright (C) 2006 Michael Buesch <m@bues.ch> |
5 | * Copyright (C) 2010 Waldemar Brodkorb <wbx@openadk.org> |
6 | * Copyright (C) 2010-2012 Hauke Mehrtens <hauke@hauke-m.de> |
7 | * |
8 | * This program is free software; you can redistribute it and/or modify it |
9 | * under the terms of the GNU General Public License as published by the |
10 | * Free Software Foundation; either version 2 of the License, or (at your |
11 | * option) any later version. |
12 | * |
13 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED |
14 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
15 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN |
16 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
17 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
18 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
19 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
20 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
23 | * |
24 | * You should have received a copy of the GNU General Public License along |
25 | * with this program; if not, write to the Free Software Foundation, Inc., |
26 | * 675 Mass Ave, Cambridge, MA 02139, USA. |
27 | */ |
28 | |
29 | #include "bcm47xx_private.h" |
30 | |
31 | #include <linux/bcm47xx_sprom.h> |
32 | #include <linux/export.h> |
33 | #include <linux/types.h> |
34 | #include <linux/ethtool.h> |
35 | #include <linux/phy.h> |
36 | #include <linux/phy_fixed.h> |
37 | #include <linux/ssb/ssb.h> |
38 | #include <linux/ssb/ssb_embedded.h> |
39 | #include <linux/bcma/bcma_soc.h> |
40 | #include <asm/bootinfo.h> |
41 | #include <asm/idle.h> |
42 | #include <asm/prom.h> |
43 | #include <asm/reboot.h> |
44 | #include <asm/time.h> |
45 | #include <bcm47xx.h> |
46 | #include <bcm47xx_board.h> |
47 | |
48 | union bcm47xx_bus bcm47xx_bus; |
49 | EXPORT_SYMBOL(bcm47xx_bus); |
50 | |
51 | enum bcm47xx_bus_type bcm47xx_bus_type; |
52 | EXPORT_SYMBOL(bcm47xx_bus_type); |
53 | |
54 | static void bcm47xx_machine_restart(char *command) |
55 | { |
56 | pr_alert("Please stand by while rebooting the system...\n" ); |
57 | local_irq_disable(); |
58 | /* Set the watchdog timer to reset immediately */ |
59 | switch (bcm47xx_bus_type) { |
60 | #ifdef CONFIG_BCM47XX_SSB |
61 | case BCM47XX_BUS_TYPE_SSB: |
62 | if (bcm47xx_bus.ssb.chip_id == 0x4785) |
63 | write_c0_diag4(1 << 22); |
64 | ssb_watchdog_timer_set(&bcm47xx_bus.ssb, 1); |
65 | if (bcm47xx_bus.ssb.chip_id == 0x4785) { |
66 | __asm__ __volatile__( |
67 | ".set\tmips3\n\t" |
68 | "sync\n\t" |
69 | "wait\n\t" |
70 | ".set\tmips0" ); |
71 | } |
72 | break; |
73 | #endif |
74 | #ifdef CONFIG_BCM47XX_BCMA |
75 | case BCM47XX_BUS_TYPE_BCMA: |
76 | bcma_chipco_watchdog_timer_set(&bcm47xx_bus.bcma.bus.drv_cc, 1); |
77 | break; |
78 | #endif |
79 | } |
80 | while (1) |
81 | cpu_relax(); |
82 | } |
83 | |
84 | static void bcm47xx_machine_halt(void) |
85 | { |
86 | /* Disable interrupts and watchdog and spin forever */ |
87 | local_irq_disable(); |
88 | switch (bcm47xx_bus_type) { |
89 | #ifdef CONFIG_BCM47XX_SSB |
90 | case BCM47XX_BUS_TYPE_SSB: |
91 | ssb_watchdog_timer_set(&bcm47xx_bus.ssb, 0); |
92 | break; |
93 | #endif |
94 | #ifdef CONFIG_BCM47XX_BCMA |
95 | case BCM47XX_BUS_TYPE_BCMA: |
96 | bcma_chipco_watchdog_timer_set(&bcm47xx_bus.bcma.bus.drv_cc, 0); |
97 | break; |
98 | #endif |
99 | } |
100 | while (1) |
101 | cpu_relax(); |
102 | } |
103 | |
104 | #ifdef CONFIG_BCM47XX_SSB |
105 | static void __init bcm47xx_register_ssb(void) |
106 | { |
107 | int err; |
108 | char buf[100]; |
109 | struct ssb_mipscore *mcore; |
110 | |
111 | err = ssb_bus_host_soc_register(&bcm47xx_bus.ssb, SSB_ENUM_BASE); |
112 | if (err) |
113 | panic("Failed to initialize SSB bus (err %d)" , err); |
114 | |
115 | mcore = &bcm47xx_bus.ssb.mipscore; |
116 | if (bcm47xx_nvram_getenv("kernel_args" , buf, sizeof(buf)) >= 0) { |
117 | if (strstr(buf, "console=ttyS1" )) { |
118 | struct ssb_serial_port port; |
119 | |
120 | pr_debug("Swapping serial ports!\n" ); |
121 | /* swap serial ports */ |
122 | memcpy(&port, &mcore->serial_ports[0], sizeof(port)); |
123 | memcpy(&mcore->serial_ports[0], &mcore->serial_ports[1], |
124 | sizeof(port)); |
125 | memcpy(&mcore->serial_ports[1], &port, sizeof(port)); |
126 | } |
127 | } |
128 | } |
129 | #endif |
130 | |
131 | #ifdef CONFIG_BCM47XX_BCMA |
132 | static void __init bcm47xx_register_bcma(void) |
133 | { |
134 | int err; |
135 | |
136 | err = bcma_host_soc_register(&bcm47xx_bus.bcma); |
137 | if (err) |
138 | panic("Failed to register BCMA bus (err %d)" , err); |
139 | } |
140 | #endif |
141 | |
142 | /* |
143 | * Memory setup is done in the early part of MIPS's arch_mem_init. It's supposed |
144 | * to detect memory and record it with memblock_add. |
145 | * Any extra initializaion performed here must not use kmalloc or bootmem. |
146 | */ |
147 | void __init plat_mem_setup(void) |
148 | { |
149 | struct cpuinfo_mips *c = ¤t_cpu_data; |
150 | |
151 | if (c->cputype == CPU_74K) { |
152 | pr_info("Using bcma bus\n" ); |
153 | #ifdef CONFIG_BCM47XX_BCMA |
154 | bcm47xx_bus_type = BCM47XX_BUS_TYPE_BCMA; |
155 | bcm47xx_register_bcma(); |
156 | bcm47xx_set_system_type(bcm47xx_bus.bcma.bus.chipinfo.id); |
157 | #ifdef CONFIG_HIGHMEM |
158 | bcm47xx_prom_highmem_init(); |
159 | #endif |
160 | #endif |
161 | } else { |
162 | pr_info("Using ssb bus\n" ); |
163 | #ifdef CONFIG_BCM47XX_SSB |
164 | bcm47xx_bus_type = BCM47XX_BUS_TYPE_SSB; |
165 | bcm47xx_sprom_register_fallbacks(); |
166 | bcm47xx_register_ssb(); |
167 | bcm47xx_set_system_type(bcm47xx_bus.ssb.chip_id); |
168 | #endif |
169 | } |
170 | |
171 | _machine_restart = bcm47xx_machine_restart; |
172 | _machine_halt = bcm47xx_machine_halt; |
173 | pm_power_off = bcm47xx_machine_halt; |
174 | } |
175 | |
176 | #ifdef CONFIG_BCM47XX_BCMA |
177 | static struct device * __init bcm47xx_setup_device(void) |
178 | { |
179 | struct device *dev; |
180 | int err; |
181 | |
182 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); |
183 | if (!dev) |
184 | return NULL; |
185 | |
186 | err = dev_set_name(dev, "bcm47xx_soc" ); |
187 | if (err) { |
188 | pr_err("Failed to set SoC device name: %d\n" , err); |
189 | kfree(dev); |
190 | return NULL; |
191 | } |
192 | |
193 | err = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32)); |
194 | if (err) |
195 | pr_err("Failed to set SoC DMA mask: %d\n" , err); |
196 | |
197 | return dev; |
198 | } |
199 | #endif |
200 | |
201 | /* |
202 | * This finishes bus initialization doing things that were not possible without |
203 | * kmalloc. Make sure to call it late enough (after mm_init). |
204 | */ |
205 | void __init bcm47xx_bus_setup(void) |
206 | { |
207 | #ifdef CONFIG_BCM47XX_BCMA |
208 | if (bcm47xx_bus_type == BCM47XX_BUS_TYPE_BCMA) { |
209 | int err; |
210 | |
211 | bcm47xx_bus.bcma.dev = bcm47xx_setup_device(); |
212 | if (!bcm47xx_bus.bcma.dev) |
213 | panic("Failed to setup SoC device\n" ); |
214 | |
215 | err = bcma_host_soc_init(&bcm47xx_bus.bcma); |
216 | if (err) |
217 | panic("Failed to initialize BCMA bus (err %d)" , err); |
218 | } |
219 | #endif |
220 | |
221 | /* With bus initialized we can access NVRAM and detect the board */ |
222 | bcm47xx_board_detect(); |
223 | mips_set_machine_name(bcm47xx_board_get_name()); |
224 | } |
225 | |
226 | static int __init bcm47xx_cpu_fixes(void) |
227 | { |
228 | switch (bcm47xx_bus_type) { |
229 | #ifdef CONFIG_BCM47XX_SSB |
230 | case BCM47XX_BUS_TYPE_SSB: |
231 | /* Nothing to do */ |
232 | break; |
233 | #endif |
234 | #ifdef CONFIG_BCM47XX_BCMA |
235 | case BCM47XX_BUS_TYPE_BCMA: |
236 | /* The BCM4706 has a problem with the CPU wait instruction. |
237 | * When r4k_wait or r4k_wait_irqoff is used will just hang and |
238 | * not return from a msleep(). Removing the cpu_wait |
239 | * functionality is a workaround for this problem. The BCM4716 |
240 | * does not have this problem. |
241 | */ |
242 | if (bcm47xx_bus.bcma.bus.chipinfo.id == BCMA_CHIP_ID_BCM4706) |
243 | cpu_wait = NULL; |
244 | break; |
245 | #endif |
246 | } |
247 | return 0; |
248 | } |
249 | arch_initcall(bcm47xx_cpu_fixes); |
250 | |
251 | static struct fixed_phy_status bcm47xx_fixed_phy_status __initdata = { |
252 | .link = 1, |
253 | .speed = SPEED_100, |
254 | .duplex = DUPLEX_FULL, |
255 | }; |
256 | |
257 | static int __init bcm47xx_register_bus_complete(void) |
258 | { |
259 | switch (bcm47xx_bus_type) { |
260 | #ifdef CONFIG_BCM47XX_SSB |
261 | case BCM47XX_BUS_TYPE_SSB: |
262 | /* Nothing to do */ |
263 | break; |
264 | #endif |
265 | #ifdef CONFIG_BCM47XX_BCMA |
266 | case BCM47XX_BUS_TYPE_BCMA: |
267 | if (device_register(bcm47xx_bus.bcma.dev)) |
268 | pr_err("Failed to register SoC device\n" ); |
269 | bcma_bus_register(&bcm47xx_bus.bcma.bus); |
270 | break; |
271 | #endif |
272 | } |
273 | bcm47xx_buttons_register(); |
274 | bcm47xx_leds_register(); |
275 | bcm47xx_workarounds(); |
276 | |
277 | fixed_phy_add(PHY_POLL, phy_id: 0, status: &bcm47xx_fixed_phy_status); |
278 | return 0; |
279 | } |
280 | device_initcall(bcm47xx_register_bus_complete); |
281 | |