diff options
author | 2025-03-08 22:04:20 +0800 | |
---|---|---|
committer | 2025-03-08 22:04:20 +0800 | |
commit | a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a (patch) | |
tree | 84f21bd0bf7071bc5fc7dd989e77d7ceb5476682 /arch/mips/alchemy | |
download | ohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.tar.gz ohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.zip |
Initial commit: OpenHarmony-v4.0-ReleaseOpenHarmony-v4.0-Release
Diffstat (limited to 'arch/mips/alchemy')
30 files changed, 10241 insertions, 0 deletions
diff --git a/arch/mips/alchemy/Kconfig b/arch/mips/alchemy/Kconfig new file mode 100644 index 000000000..69734120a --- /dev/null +++ b/arch/mips/alchemy/Kconfig | |||
@@ -0,0 +1,36 @@ | |||
1 | # SPDX-License-Identifier: GPL-2.0 | ||
2 | choice | ||
3 | prompt "Machine type" | ||
4 | depends on MIPS_ALCHEMY | ||
5 | default MIPS_DB1XXX | ||
6 | |||
7 | config MIPS_MTX1 | ||
8 | bool "4G Systems MTX-1 board" | ||
9 | select HAVE_PCI | ||
10 | select SYS_SUPPORTS_LITTLE_ENDIAN | ||
11 | select SYS_HAS_EARLY_PRINTK | ||
12 | |||
13 | config MIPS_DB1XXX | ||
14 | bool "Alchemy DB1XXX / PB1XXX boards" | ||
15 | select GPIOLIB | ||
16 | select HAVE_PCI | ||
17 | select SYS_SUPPORTS_LITTLE_ENDIAN | ||
18 | select SYS_HAS_EARLY_PRINTK | ||
19 | help | ||
20 | Select this option if you have one of the following Alchemy | ||
21 | development boards: DB1000 DB1500 DB1100 DB1550 DB1200 DB1300 | ||
22 | PB1500 PB1100 PB1550 PB1200 | ||
23 | Board type is autodetected during boot. | ||
24 | |||
25 | config MIPS_XXS1500 | ||
26 | bool "MyCable XXS1500 board" | ||
27 | select SYS_SUPPORTS_LITTLE_ENDIAN | ||
28 | select SYS_HAS_EARLY_PRINTK | ||
29 | |||
30 | config MIPS_GPR | ||
31 | bool "Trapeze ITS GPR board" | ||
32 | select HAVE_PCI | ||
33 | select SYS_SUPPORTS_LITTLE_ENDIAN | ||
34 | select SYS_HAS_EARLY_PRINTK | ||
35 | |||
36 | endchoice | ||
diff --git a/arch/mips/alchemy/Makefile b/arch/mips/alchemy/Makefile new file mode 100644 index 000000000..fabbc7019 --- /dev/null +++ b/arch/mips/alchemy/Makefile | |||
@@ -0,0 +1,4 @@ | |||
1 | # SPDX-License-Identifier: GPL-2.0-only | ||
2 | obj-$(CONFIG_MIPS_GPR) += board-gpr.o | ||
3 | obj-$(CONFIG_MIPS_MTX1) += board-mtx1.o | ||
4 | obj-$(CONFIG_MIPS_XXS1500) += board-xxs1500.o | ||
diff --git a/arch/mips/alchemy/Platform b/arch/mips/alchemy/Platform new file mode 100644 index 000000000..c8cff50b0 --- /dev/null +++ b/arch/mips/alchemy/Platform | |||
@@ -0,0 +1,35 @@ | |||
1 | # | ||
2 | # Core Alchemy code | ||
3 | # | ||
4 | platform-$(CONFIG_MIPS_ALCHEMY) += alchemy/common/ | ||
5 | |||
6 | |||
7 | # | ||
8 | # AMD Alchemy Db1000/Db1500/Pb1500/Db1100/Pb1100 | ||
9 | # Db1550/Pb1550/Db1200/Pb1200/Db1300 | ||
10 | # | ||
11 | platform-$(CONFIG_MIPS_DB1XXX) += alchemy/devboards/ | ||
12 | cflags-$(CONFIG_MIPS_DB1XXX) += -I$(srctree)/arch/mips/include/asm/mach-db1x00 | ||
13 | load-$(CONFIG_MIPS_DB1XXX) += 0xffffffff80100000 | ||
14 | |||
15 | # | ||
16 | # 4G-Systems MTX-1 "MeshCube" wireless router | ||
17 | # | ||
18 | load-$(CONFIG_MIPS_MTX1) += 0xffffffff80100000 | ||
19 | |||
20 | # | ||
21 | # MyCable eval board | ||
22 | # | ||
23 | load-$(CONFIG_MIPS_XXS1500) += 0xffffffff80100000 | ||
24 | |||
25 | # | ||
26 | # Trapeze ITS GRP board | ||
27 | # | ||
28 | load-$(CONFIG_MIPS_GPR) += 0xffffffff80100000 | ||
29 | |||
30 | # boards can specify their own <gpio.h> in one of their include dirs. | ||
31 | # If they do, placing this line here at the end will make sure the | ||
32 | # compiler picks the board one. If they don't, it will make sure | ||
33 | # the alchemy generic gpio header is picked up. | ||
34 | |||
35 | cflags-$(CONFIG_MIPS_ALCHEMY) += -I$(srctree)/arch/mips/include/asm/mach-au1x00 | ||
diff --git a/arch/mips/alchemy/board-gpr.c b/arch/mips/alchemy/board-gpr.c new file mode 100644 index 000000000..f587c40b6 --- /dev/null +++ b/arch/mips/alchemy/board-gpr.c | |||
@@ -0,0 +1,292 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
2 | /* | ||
3 | * GPR board platform device registration (Au1550) | ||
4 | * | ||
5 | * Copyright (C) 2010 Wolfgang Grandegger <wg@denx.de> | ||
6 | */ | ||
7 | |||
8 | #include <linux/delay.h> | ||
9 | #include <linux/init.h> | ||
10 | #include <linux/interrupt.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/platform_device.h> | ||
13 | #include <linux/pm.h> | ||
14 | #include <linux/mtd/partitions.h> | ||
15 | #include <linux/mtd/physmap.h> | ||
16 | #include <linux/leds.h> | ||
17 | #include <linux/gpio.h> | ||
18 | #include <linux/i2c.h> | ||
19 | #include <linux/platform_data/i2c-gpio.h> | ||
20 | #include <linux/gpio/machine.h> | ||
21 | #include <asm/bootinfo.h> | ||
22 | #include <asm/idle.h> | ||
23 | #include <asm/reboot.h> | ||
24 | #include <asm/setup.h> | ||
25 | #include <asm/mach-au1x00/au1000.h> | ||
26 | #include <asm/mach-au1x00/gpio-au1000.h> | ||
27 | #include <prom.h> | ||
28 | |||
29 | const char *get_system_type(void) | ||
30 | { | ||
31 | return "GPR"; | ||
32 | } | ||
33 | |||
34 | void prom_putchar(char c) | ||
35 | { | ||
36 | alchemy_uart_putchar(AU1000_UART0_PHYS_ADDR, c); | ||
37 | } | ||
38 | |||
39 | static void gpr_reset(char *c) | ||
40 | { | ||
41 | /* switch System-LED to orange (red# and green# on) */ | ||
42 | alchemy_gpio_direction_output(4, 0); | ||
43 | alchemy_gpio_direction_output(5, 0); | ||
44 | |||
45 | /* trigger watchdog to reset board in 200ms */ | ||
46 | printk(KERN_EMERG "Triggering watchdog soft reset...\n"); | ||
47 | raw_local_irq_disable(); | ||
48 | alchemy_gpio_direction_output(1, 0); | ||
49 | udelay(1); | ||
50 | alchemy_gpio_set_value(1, 1); | ||
51 | while (1) | ||
52 | cpu_wait(); | ||
53 | } | ||
54 | |||
55 | static void gpr_power_off(void) | ||
56 | { | ||
57 | while (1) | ||
58 | cpu_wait(); | ||
59 | } | ||
60 | |||
61 | void __init board_setup(void) | ||
62 | { | ||
63 | printk(KERN_INFO "Trapeze ITS GPR board\n"); | ||
64 | |||
65 | pm_power_off = gpr_power_off; | ||
66 | _machine_halt = gpr_power_off; | ||
67 | _machine_restart = gpr_reset; | ||
68 | |||
69 | /* Enable UART1/3 */ | ||
70 | alchemy_uart_enable(AU1000_UART3_PHYS_ADDR); | ||
71 | alchemy_uart_enable(AU1000_UART1_PHYS_ADDR); | ||
72 | |||
73 | /* Take away Reset of UMTS-card */ | ||
74 | alchemy_gpio_direction_output(215, 1); | ||
75 | } | ||
76 | |||
77 | /* | ||
78 | * Watchdog | ||
79 | */ | ||
80 | static struct resource gpr_wdt_resource[] = { | ||
81 | [0] = { | ||
82 | .start = 1, | ||
83 | .end = 1, | ||
84 | .name = "gpr-adm6320-wdt", | ||
85 | .flags = IORESOURCE_IRQ, | ||
86 | } | ||
87 | }; | ||
88 | |||
89 | static struct platform_device gpr_wdt_device = { | ||
90 | .name = "adm6320-wdt", | ||
91 | .id = 0, | ||
92 | .num_resources = ARRAY_SIZE(gpr_wdt_resource), | ||
93 | .resource = gpr_wdt_resource, | ||
94 | }; | ||
95 | |||
96 | /* | ||
97 | * FLASH | ||
98 | * | ||
99 | * 0x00000000-0x00200000 : "kernel" | ||
100 | * 0x00200000-0x00a00000 : "rootfs" | ||
101 | * 0x01d00000-0x01f00000 : "config" | ||
102 | * 0x01c00000-0x01d00000 : "yamon" | ||
103 | * 0x01d00000-0x01d40000 : "yamon env vars" | ||
104 | * 0x00000000-0x00a00000 : "kernel+rootfs" | ||
105 | */ | ||
106 | static struct mtd_partition gpr_mtd_partitions[] = { | ||
107 | { | ||
108 | .name = "kernel", | ||
109 | .size = 0x00200000, | ||
110 | .offset = 0, | ||
111 | }, | ||
112 | { | ||
113 | .name = "rootfs", | ||
114 | .size = 0x00800000, | ||
115 | .offset = MTDPART_OFS_APPEND, | ||
116 | .mask_flags = MTD_WRITEABLE, | ||
117 | }, | ||
118 | { | ||
119 | .name = "config", | ||
120 | .size = 0x00200000, | ||
121 | .offset = 0x01d00000, | ||
122 | }, | ||
123 | { | ||
124 | .name = "yamon", | ||
125 | .size = 0x00100000, | ||
126 | .offset = 0x01c00000, | ||
127 | }, | ||
128 | { | ||
129 | .name = "yamon env vars", | ||
130 | .size = 0x00040000, | ||
131 | .offset = MTDPART_OFS_APPEND, | ||
132 | }, | ||
133 | { | ||
134 | .name = "kernel+rootfs", | ||
135 | .size = 0x00a00000, | ||
136 | .offset = 0, | ||
137 | }, | ||
138 | }; | ||
139 | |||
140 | static struct physmap_flash_data gpr_flash_data = { | ||
141 | .width = 4, | ||
142 | .nr_parts = ARRAY_SIZE(gpr_mtd_partitions), | ||
143 | .parts = gpr_mtd_partitions, | ||
144 | }; | ||
145 | |||
146 | static struct resource gpr_mtd_resource = { | ||
147 | .start = 0x1e000000, | ||
148 | .end = 0x1fffffff, | ||
149 | .flags = IORESOURCE_MEM, | ||
150 | }; | ||
151 | |||
152 | static struct platform_device gpr_mtd_device = { | ||
153 | .name = "physmap-flash", | ||
154 | .dev = { | ||
155 | .platform_data = &gpr_flash_data, | ||
156 | }, | ||
157 | .num_resources = 1, | ||
158 | .resource = &gpr_mtd_resource, | ||
159 | }; | ||
160 | |||
161 | /* | ||
162 | * LEDs | ||
163 | */ | ||
164 | static const struct gpio_led gpr_gpio_leds[] = { | ||
165 | { /* green */ | ||
166 | .name = "gpr:green", | ||
167 | .gpio = 4, | ||
168 | .active_low = 1, | ||
169 | }, | ||
170 | { /* red */ | ||
171 | .name = "gpr:red", | ||
172 | .gpio = 5, | ||
173 | .active_low = 1, | ||
174 | } | ||
175 | }; | ||
176 | |||
177 | static struct gpio_led_platform_data gpr_led_data = { | ||
178 | .num_leds = ARRAY_SIZE(gpr_gpio_leds), | ||
179 | .leds = gpr_gpio_leds, | ||
180 | }; | ||
181 | |||
182 | static struct platform_device gpr_led_devices = { | ||
183 | .name = "leds-gpio", | ||
184 | .id = -1, | ||
185 | .dev = { | ||
186 | .platform_data = &gpr_led_data, | ||
187 | } | ||
188 | }; | ||
189 | |||
190 | /* | ||
191 | * I2C | ||
192 | */ | ||
193 | static struct gpiod_lookup_table gpr_i2c_gpiod_table = { | ||
194 | .dev_id = "i2c-gpio", | ||
195 | .table = { | ||
196 | /* | ||
197 | * This should be on "GPIO2" which has base at 200 so | ||
198 | * the global numbers 209 and 210 should correspond to | ||
199 | * local offsets 9 and 10. | ||
200 | */ | ||
201 | GPIO_LOOKUP_IDX("alchemy-gpio2", 9, NULL, 0, | ||
202 | GPIO_ACTIVE_HIGH), | ||
203 | GPIO_LOOKUP_IDX("alchemy-gpio2", 10, NULL, 1, | ||
204 | GPIO_ACTIVE_HIGH), | ||
205 | }, | ||
206 | }; | ||
207 | |||
208 | static struct i2c_gpio_platform_data gpr_i2c_data = { | ||
209 | /* | ||
210 | * The open drain mode is hardwired somewhere or an electrical | ||
211 | * property of the alchemy GPIO controller. | ||
212 | */ | ||
213 | .sda_is_open_drain = 1, | ||
214 | .scl_is_open_drain = 1, | ||
215 | .udelay = 2, /* ~100 kHz */ | ||
216 | .timeout = HZ, | ||
217 | }; | ||
218 | |||
219 | static struct platform_device gpr_i2c_device = { | ||
220 | .name = "i2c-gpio", | ||
221 | .id = -1, | ||
222 | .dev.platform_data = &gpr_i2c_data, | ||
223 | }; | ||
224 | |||
225 | static struct i2c_board_info gpr_i2c_info[] __initdata = { | ||
226 | { | ||
227 | I2C_BOARD_INFO("lm83", 0x18), | ||
228 | } | ||
229 | }; | ||
230 | |||
231 | |||
232 | |||
233 | static struct resource alchemy_pci_host_res[] = { | ||
234 | [0] = { | ||
235 | .start = AU1500_PCI_PHYS_ADDR, | ||
236 | .end = AU1500_PCI_PHYS_ADDR + 0xfff, | ||
237 | .flags = IORESOURCE_MEM, | ||
238 | }, | ||
239 | }; | ||
240 | |||
241 | static int gpr_map_pci_irq(const struct pci_dev *d, u8 slot, u8 pin) | ||
242 | { | ||
243 | if ((slot == 0) && (pin == 1)) | ||
244 | return AU1550_PCI_INTA; | ||
245 | else if ((slot == 0) && (pin == 2)) | ||
246 | return AU1550_PCI_INTB; | ||
247 | |||
248 | return 0xff; | ||
249 | } | ||
250 | |||
251 | static struct alchemy_pci_platdata gpr_pci_pd = { | ||
252 | .board_map_irq = gpr_map_pci_irq, | ||
253 | .pci_cfg_set = PCI_CONFIG_AEN | PCI_CONFIG_R2H | PCI_CONFIG_R1H | | ||
254 | PCI_CONFIG_CH | | ||
255 | #if defined(__MIPSEB__) | ||
256 | PCI_CONFIG_SIC_HWA_DAT | PCI_CONFIG_SM, | ||
257 | #else | ||
258 | 0, | ||
259 | #endif | ||
260 | }; | ||
261 | |||
262 | static struct platform_device gpr_pci_host_dev = { | ||
263 | .dev.platform_data = &gpr_pci_pd, | ||
264 | .name = "alchemy-pci", | ||
265 | .id = 0, | ||
266 | .num_resources = ARRAY_SIZE(alchemy_pci_host_res), | ||
267 | .resource = alchemy_pci_host_res, | ||
268 | }; | ||
269 | |||
270 | static struct platform_device *gpr_devices[] __initdata = { | ||
271 | &gpr_wdt_device, | ||
272 | &gpr_mtd_device, | ||
273 | &gpr_i2c_device, | ||
274 | &gpr_led_devices, | ||
275 | }; | ||
276 | |||
277 | static int __init gpr_pci_init(void) | ||
278 | { | ||
279 | return platform_device_register(&gpr_pci_host_dev); | ||
280 | } | ||
281 | /* must be arch_initcall; MIPS PCI scans busses in a subsys_initcall */ | ||
282 | arch_initcall(gpr_pci_init); | ||
283 | |||
284 | |||
285 | static int __init gpr_dev_init(void) | ||
286 | { | ||
287 | gpiod_add_lookup_table(&gpr_i2c_gpiod_table); | ||
288 | i2c_register_board_info(0, gpr_i2c_info, ARRAY_SIZE(gpr_i2c_info)); | ||
289 | |||
290 | return platform_add_devices(gpr_devices, ARRAY_SIZE(gpr_devices)); | ||
291 | } | ||
292 | device_initcall(gpr_dev_init); | ||
diff --git a/arch/mips/alchemy/board-mtx1.c b/arch/mips/alchemy/board-mtx1.c new file mode 100644 index 000000000..68ea57511 --- /dev/null +++ b/arch/mips/alchemy/board-mtx1.c | |||
@@ -0,0 +1,285 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
2 | /* | ||
3 | * MTX-1 platform devices registration (Au1500) | ||
4 | * | ||
5 | * Copyright (C) 2007-2009, Florian Fainelli <florian@openwrt.org> | ||
6 | */ | ||
7 | |||
8 | #include <linux/init.h> | ||
9 | #include <linux/interrupt.h> | ||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/platform_device.h> | ||
12 | #include <linux/leds.h> | ||
13 | #include <linux/gpio.h> | ||
14 | #include <linux/gpio/machine.h> | ||
15 | #include <linux/gpio_keys.h> | ||
16 | #include <linux/input.h> | ||
17 | #include <linux/mtd/partitions.h> | ||
18 | #include <linux/mtd/physmap.h> | ||
19 | #include <mtd/mtd-abi.h> | ||
20 | #include <asm/bootinfo.h> | ||
21 | #include <asm/reboot.h> | ||
22 | #include <asm/setup.h> | ||
23 | #include <asm/mach-au1x00/au1000.h> | ||
24 | #include <asm/mach-au1x00/gpio-au1000.h> | ||
25 | #include <asm/mach-au1x00/au1xxx_eth.h> | ||
26 | #include <prom.h> | ||
27 | |||
28 | const char *get_system_type(void) | ||
29 | { | ||
30 | return "MTX-1"; | ||
31 | } | ||
32 | |||
33 | void prom_putchar(char c) | ||
34 | { | ||
35 | alchemy_uart_putchar(AU1000_UART0_PHYS_ADDR, c); | ||
36 | } | ||
37 | |||
38 | static void mtx1_reset(char *c) | ||
39 | { | ||
40 | /* Jump to the reset vector */ | ||
41 | __asm__ __volatile__("jr\t%0" : : "r"(0xbfc00000)); | ||
42 | } | ||
43 | |||
44 | static void mtx1_power_off(void) | ||
45 | { | ||
46 | while (1) | ||
47 | asm volatile ( | ||
48 | " .set mips32 \n" | ||
49 | " wait \n" | ||
50 | " .set mips0 \n"); | ||
51 | } | ||
52 | |||
53 | void __init board_setup(void) | ||
54 | { | ||
55 | #if IS_ENABLED(CONFIG_USB_OHCI_HCD) | ||
56 | /* Enable USB power switch */ | ||
57 | alchemy_gpio_direction_output(204, 0); | ||
58 | #endif /* IS_ENABLED(CONFIG_USB_OHCI_HCD) */ | ||
59 | |||
60 | /* Initialize sys_pinfunc */ | ||
61 | alchemy_wrsys(SYS_PF_NI2, AU1000_SYS_PINFUNC); | ||
62 | |||
63 | /* Initialize GPIO */ | ||
64 | alchemy_wrsys(~0, AU1000_SYS_TRIOUTCLR); | ||
65 | alchemy_gpio_direction_output(0, 0); /* Disable M66EN (PCI 66MHz) */ | ||
66 | alchemy_gpio_direction_output(3, 1); /* Disable PCI CLKRUN# */ | ||
67 | alchemy_gpio_direction_output(1, 1); /* Enable EXT_IO3 */ | ||
68 | alchemy_gpio_direction_output(5, 0); /* Disable eth PHY TX_ER */ | ||
69 | |||
70 | /* Enable LED and set it to green */ | ||
71 | alchemy_gpio_direction_output(211, 1); /* green on */ | ||
72 | alchemy_gpio_direction_output(212, 0); /* red off */ | ||
73 | |||
74 | pm_power_off = mtx1_power_off; | ||
75 | _machine_halt = mtx1_power_off; | ||
76 | _machine_restart = mtx1_reset; | ||
77 | |||
78 | printk(KERN_INFO "4G Systems MTX-1 Board\n"); | ||
79 | } | ||
80 | |||
81 | /******************************************************************************/ | ||
82 | |||
83 | static struct gpio_keys_button mtx1_gpio_button[] = { | ||
84 | { | ||
85 | .gpio = 207, | ||
86 | .code = BTN_0, | ||
87 | .desc = "System button", | ||
88 | } | ||
89 | }; | ||
90 | |||
91 | static struct gpio_keys_platform_data mtx1_buttons_data = { | ||
92 | .buttons = mtx1_gpio_button, | ||
93 | .nbuttons = ARRAY_SIZE(mtx1_gpio_button), | ||
94 | }; | ||
95 | |||
96 | static struct platform_device mtx1_button = { | ||
97 | .name = "gpio-keys", | ||
98 | .id = -1, | ||
99 | .dev = { | ||
100 | .platform_data = &mtx1_buttons_data, | ||
101 | } | ||
102 | }; | ||
103 | |||
104 | static struct gpiod_lookup_table mtx1_wdt_gpio_table = { | ||
105 | .dev_id = "mtx1-wdt.0", | ||
106 | .table = { | ||
107 | /* Global number 215 is offset 15 on Alchemy GPIO 2 */ | ||
108 | GPIO_LOOKUP("alchemy-gpio2", 15, NULL, GPIO_ACTIVE_HIGH), | ||
109 | { }, | ||
110 | }, | ||
111 | }; | ||
112 | |||
113 | static struct platform_device mtx1_wdt = { | ||
114 | .name = "mtx1-wdt", | ||
115 | .id = 0, | ||
116 | }; | ||
117 | |||
118 | static const struct gpio_led default_leds[] = { | ||
119 | { | ||
120 | .name = "mtx1:green", | ||
121 | .gpio = 211, | ||
122 | }, { | ||
123 | .name = "mtx1:red", | ||
124 | .gpio = 212, | ||
125 | }, | ||
126 | }; | ||
127 | |||
128 | static struct gpio_led_platform_data mtx1_led_data = { | ||
129 | .num_leds = ARRAY_SIZE(default_leds), | ||
130 | .leds = default_leds, | ||
131 | }; | ||
132 | |||
133 | static struct platform_device mtx1_gpio_leds = { | ||
134 | .name = "leds-gpio", | ||
135 | .id = -1, | ||
136 | .dev = { | ||
137 | .platform_data = &mtx1_led_data, | ||
138 | } | ||
139 | }; | ||
140 | |||
141 | static struct mtd_partition mtx1_mtd_partitions[] = { | ||
142 | { | ||
143 | .name = "filesystem", | ||
144 | .size = 0x01C00000, | ||
145 | .offset = 0, | ||
146 | }, | ||
147 | { | ||
148 | .name = "yamon", | ||
149 | .size = 0x00100000, | ||
150 | .offset = MTDPART_OFS_APPEND, | ||
151 | .mask_flags = MTD_WRITEABLE, | ||
152 | }, | ||
153 | { | ||
154 | .name = "kernel", | ||
155 | .size = 0x002c0000, | ||
156 | .offset = MTDPART_OFS_APPEND, | ||
157 | }, | ||
158 | { | ||
159 | .name = "yamon env", | ||
160 | .size = 0x00040000, | ||
161 | .offset = MTDPART_OFS_APPEND, | ||
162 | }, | ||
163 | }; | ||
164 | |||
165 | static struct physmap_flash_data mtx1_flash_data = { | ||
166 | .width = 4, | ||
167 | .nr_parts = 4, | ||
168 | .parts = mtx1_mtd_partitions, | ||
169 | }; | ||
170 | |||
171 | static struct resource mtx1_mtd_resource = { | ||
172 | .start = 0x1e000000, | ||
173 | .end = 0x1fffffff, | ||
174 | .flags = IORESOURCE_MEM, | ||
175 | }; | ||
176 | |||
177 | static struct platform_device mtx1_mtd = { | ||
178 | .name = "physmap-flash", | ||
179 | .dev = { | ||
180 | .platform_data = &mtx1_flash_data, | ||
181 | }, | ||
182 | .num_resources = 1, | ||
183 | .resource = &mtx1_mtd_resource, | ||
184 | }; | ||
185 | |||
186 | static struct resource alchemy_pci_host_res[] = { | ||
187 | [0] = { | ||
188 | .start = AU1500_PCI_PHYS_ADDR, | ||
189 | .end = AU1500_PCI_PHYS_ADDR + 0xfff, | ||
190 | .flags = IORESOURCE_MEM, | ||
191 | }, | ||
192 | }; | ||
193 | |||
194 | static int mtx1_pci_idsel(unsigned int devsel, int assert) | ||
195 | { | ||
196 | /* This function is only necessary to support a proprietary Cardbus | ||
197 | * adapter on the mtx-1 "singleboard" variant. It triggers a custom | ||
198 | * logic chip connected to EXT_IO3 (GPIO1) to suppress IDSEL signals. | ||
199 | */ | ||
200 | udelay(1); | ||
201 | |||
202 | if (assert && devsel != 0) | ||
203 | /* Suppress signal to Cardbus */ | ||
204 | alchemy_gpio_set_value(1, 0); /* set EXT_IO3 OFF */ | ||
205 | else | ||
206 | alchemy_gpio_set_value(1, 1); /* set EXT_IO3 ON */ | ||
207 | |||
208 | udelay(1); | ||
209 | return 1; | ||
210 | } | ||
211 | |||
212 | static const char mtx1_irqtab[][5] = { | ||
213 | [0] = { -1, AU1500_PCI_INTA, AU1500_PCI_INTA, 0xff, 0xff }, /* IDSEL 00 - AdapterA-Slot0 (top) */ | ||
214 | [1] = { -1, AU1500_PCI_INTB, AU1500_PCI_INTA, 0xff, 0xff }, /* IDSEL 01 - AdapterA-Slot1 (bottom) */ | ||
215 | [2] = { -1, AU1500_PCI_INTC, AU1500_PCI_INTD, 0xff, 0xff }, /* IDSEL 02 - AdapterB-Slot0 (top) */ | ||
216 | [3] = { -1, AU1500_PCI_INTD, AU1500_PCI_INTC, 0xff, 0xff }, /* IDSEL 03 - AdapterB-Slot1 (bottom) */ | ||
217 | [4] = { -1, AU1500_PCI_INTA, AU1500_PCI_INTB, 0xff, 0xff }, /* IDSEL 04 - AdapterC-Slot0 (top) */ | ||
218 | [5] = { -1, AU1500_PCI_INTB, AU1500_PCI_INTA, 0xff, 0xff }, /* IDSEL 05 - AdapterC-Slot1 (bottom) */ | ||
219 | [6] = { -1, AU1500_PCI_INTC, AU1500_PCI_INTD, 0xff, 0xff }, /* IDSEL 06 - AdapterD-Slot0 (top) */ | ||
220 | [7] = { -1, AU1500_PCI_INTD, AU1500_PCI_INTC, 0xff, 0xff }, /* IDSEL 07 - AdapterD-Slot1 (bottom) */ | ||
221 | }; | ||
222 | |||
223 | static int mtx1_map_pci_irq(const struct pci_dev *d, u8 slot, u8 pin) | ||
224 | { | ||
225 | return mtx1_irqtab[slot][pin]; | ||
226 | } | ||
227 | |||
228 | static struct alchemy_pci_platdata mtx1_pci_pd = { | ||
229 | .board_map_irq = mtx1_map_pci_irq, | ||
230 | .board_pci_idsel = mtx1_pci_idsel, | ||
231 | .pci_cfg_set = PCI_CONFIG_AEN | PCI_CONFIG_R2H | PCI_CONFIG_R1H | | ||
232 | PCI_CONFIG_CH | | ||
233 | #if defined(__MIPSEB__) | ||
234 | PCI_CONFIG_SIC_HWA_DAT | PCI_CONFIG_SM, | ||
235 | #else | ||
236 | 0, | ||
237 | #endif | ||
238 | }; | ||
239 | |||
240 | static struct platform_device mtx1_pci_host = { | ||
241 | .dev.platform_data = &mtx1_pci_pd, | ||
242 | .name = "alchemy-pci", | ||
243 | .id = 0, | ||
244 | .num_resources = ARRAY_SIZE(alchemy_pci_host_res), | ||
245 | .resource = alchemy_pci_host_res, | ||
246 | }; | ||
247 | |||
248 | static struct platform_device *mtx1_devs[] __initdata = { | ||
249 | &mtx1_pci_host, | ||
250 | &mtx1_gpio_leds, | ||
251 | &mtx1_wdt, | ||
252 | &mtx1_button, | ||
253 | &mtx1_mtd, | ||
254 | }; | ||
255 | |||
256 | static struct au1000_eth_platform_data mtx1_au1000_eth0_pdata = { | ||
257 | .phy_search_highest_addr = 1, | ||
258 | .phy1_search_mac0 = 1, | ||
259 | }; | ||
260 | |||
261 | static int __init mtx1_register_devices(void) | ||
262 | { | ||
263 | int rc; | ||
264 | |||
265 | irq_set_irq_type(AU1500_GPIO204_INT, IRQ_TYPE_LEVEL_HIGH); | ||
266 | irq_set_irq_type(AU1500_GPIO201_INT, IRQ_TYPE_LEVEL_LOW); | ||
267 | irq_set_irq_type(AU1500_GPIO202_INT, IRQ_TYPE_LEVEL_LOW); | ||
268 | irq_set_irq_type(AU1500_GPIO203_INT, IRQ_TYPE_LEVEL_LOW); | ||
269 | irq_set_irq_type(AU1500_GPIO205_INT, IRQ_TYPE_LEVEL_LOW); | ||
270 | |||
271 | au1xxx_override_eth_cfg(0, &mtx1_au1000_eth0_pdata); | ||
272 | |||
273 | rc = gpio_request(mtx1_gpio_button[0].gpio, | ||
274 | mtx1_gpio_button[0].desc); | ||
275 | if (rc < 0) { | ||
276 | printk(KERN_INFO "mtx1: failed to request %d\n", | ||
277 | mtx1_gpio_button[0].gpio); | ||
278 | goto out; | ||
279 | } | ||
280 | gpio_direction_input(mtx1_gpio_button[0].gpio); | ||
281 | out: | ||
282 | gpiod_add_lookup_table(&mtx1_wdt_gpio_table); | ||
283 | return platform_add_devices(mtx1_devs, ARRAY_SIZE(mtx1_devs)); | ||
284 | } | ||
285 | arch_initcall(mtx1_register_devices); | ||
diff --git a/arch/mips/alchemy/board-xxs1500.c b/arch/mips/alchemy/board-xxs1500.c new file mode 100644 index 000000000..f175bce29 --- /dev/null +++ b/arch/mips/alchemy/board-xxs1500.c | |||
@@ -0,0 +1,125 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
2 | /* | ||
3 | * BRIEF MODULE DESCRIPTION | ||
4 | * MyCable XXS1500 board support | ||
5 | * | ||
6 | * Copyright 2003, 2008 MontaVista Software Inc. | ||
7 | * Author: MontaVista Software, Inc. <source@mvista.com> | ||
8 | */ | ||
9 | |||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/interrupt.h> | ||
13 | #include <linux/platform_device.h> | ||
14 | #include <linux/gpio.h> | ||
15 | #include <linux/delay.h> | ||
16 | #include <linux/pm.h> | ||
17 | #include <asm/bootinfo.h> | ||
18 | #include <asm/reboot.h> | ||
19 | #include <asm/setup.h> | ||
20 | #include <asm/mach-au1x00/au1000.h> | ||
21 | #include <asm/mach-au1x00/gpio-au1000.h> | ||
22 | #include <prom.h> | ||
23 | |||
24 | const char *get_system_type(void) | ||
25 | { | ||
26 | return "XXS1500"; | ||
27 | } | ||
28 | |||
29 | void prom_putchar(char c) | ||
30 | { | ||
31 | alchemy_uart_putchar(AU1000_UART0_PHYS_ADDR, c); | ||
32 | } | ||
33 | |||
34 | static void xxs1500_reset(char *c) | ||
35 | { | ||
36 | /* Jump to the reset vector */ | ||
37 | __asm__ __volatile__("jr\t%0" : : "r"(0xbfc00000)); | ||
38 | } | ||
39 | |||
40 | static void xxs1500_power_off(void) | ||
41 | { | ||
42 | while (1) | ||
43 | asm volatile ( | ||
44 | " .set mips32 \n" | ||
45 | " wait \n" | ||
46 | " .set mips0 \n"); | ||
47 | } | ||
48 | |||
49 | void __init board_setup(void) | ||
50 | { | ||
51 | u32 pin_func; | ||
52 | |||
53 | pm_power_off = xxs1500_power_off; | ||
54 | _machine_halt = xxs1500_power_off; | ||
55 | _machine_restart = xxs1500_reset; | ||
56 | |||
57 | alchemy_gpio1_input_enable(); | ||
58 | alchemy_gpio2_enable(); | ||
59 | |||
60 | /* Set multiple use pins (UART3/GPIO) to UART (it's used as UART too) */ | ||
61 | pin_func = alchemy_rdsys(AU1000_SYS_PINFUNC) & ~SYS_PF_UR3; | ||
62 | pin_func |= SYS_PF_UR3; | ||
63 | alchemy_wrsys(pin_func, AU1000_SYS_PINFUNC); | ||
64 | |||
65 | /* Enable UART */ | ||
66 | alchemy_uart_enable(AU1000_UART3_PHYS_ADDR); | ||
67 | /* Enable DTR (MCR bit 0) = USB power up */ | ||
68 | __raw_writel(1, (void __iomem *)KSEG1ADDR(AU1000_UART3_PHYS_ADDR + 0x18)); | ||
69 | wmb(); | ||
70 | } | ||
71 | |||
72 | /******************************************************************************/ | ||
73 | |||
74 | static struct resource xxs1500_pcmcia_res[] = { | ||
75 | { | ||
76 | .name = "pcmcia-io", | ||
77 | .flags = IORESOURCE_MEM, | ||
78 | .start = AU1000_PCMCIA_IO_PHYS_ADDR, | ||
79 | .end = AU1000_PCMCIA_IO_PHYS_ADDR + 0x000400000 - 1, | ||
80 | }, | ||
81 | { | ||
82 | .name = "pcmcia-attr", | ||
83 | .flags = IORESOURCE_MEM, | ||
84 | .start = AU1000_PCMCIA_ATTR_PHYS_ADDR, | ||
85 | .end = AU1000_PCMCIA_ATTR_PHYS_ADDR + 0x000400000 - 1, | ||
86 | }, | ||
87 | { | ||
88 | .name = "pcmcia-mem", | ||
89 | .flags = IORESOURCE_MEM, | ||
90 | .start = AU1000_PCMCIA_MEM_PHYS_ADDR, | ||
91 | .end = AU1000_PCMCIA_MEM_PHYS_ADDR + 0x000400000 - 1, | ||
92 | }, | ||
93 | }; | ||
94 | |||
95 | static struct platform_device xxs1500_pcmcia_dev = { | ||
96 | .name = "xxs1500_pcmcia", | ||
97 | .id = -1, | ||
98 | .num_resources = ARRAY_SIZE(xxs1500_pcmcia_res), | ||
99 | .resource = xxs1500_pcmcia_res, | ||
100 | }; | ||
101 | |||
102 | static struct platform_device *xxs1500_devs[] __initdata = { | ||
103 | &xxs1500_pcmcia_dev, | ||
104 | }; | ||
105 | |||
106 | static int __init xxs1500_dev_init(void) | ||
107 | { | ||
108 | irq_set_irq_type(AU1500_GPIO204_INT, IRQ_TYPE_LEVEL_HIGH); | ||
109 | irq_set_irq_type(AU1500_GPIO201_INT, IRQ_TYPE_LEVEL_LOW); | ||
110 | irq_set_irq_type(AU1500_GPIO202_INT, IRQ_TYPE_LEVEL_LOW); | ||
111 | irq_set_irq_type(AU1500_GPIO203_INT, IRQ_TYPE_LEVEL_LOW); | ||
112 | irq_set_irq_type(AU1500_GPIO205_INT, IRQ_TYPE_LEVEL_LOW); | ||
113 | irq_set_irq_type(AU1500_GPIO207_INT, IRQ_TYPE_LEVEL_LOW); | ||
114 | |||
115 | irq_set_irq_type(AU1500_GPIO0_INT, IRQ_TYPE_LEVEL_LOW); | ||
116 | irq_set_irq_type(AU1500_GPIO1_INT, IRQ_TYPE_LEVEL_LOW); | ||
117 | irq_set_irq_type(AU1500_GPIO2_INT, IRQ_TYPE_LEVEL_LOW); | ||
118 | irq_set_irq_type(AU1500_GPIO3_INT, IRQ_TYPE_LEVEL_LOW); | ||
119 | irq_set_irq_type(AU1500_GPIO4_INT, IRQ_TYPE_LEVEL_LOW); /* CF irq */ | ||
120 | irq_set_irq_type(AU1500_GPIO5_INT, IRQ_TYPE_LEVEL_LOW); | ||
121 | |||
122 | return platform_add_devices(xxs1500_devs, | ||
123 | ARRAY_SIZE(xxs1500_devs)); | ||
124 | } | ||
125 | device_initcall(xxs1500_dev_init); | ||
diff --git a/arch/mips/alchemy/common/Makefile b/arch/mips/alchemy/common/Makefile new file mode 100644 index 000000000..a0e94388d --- /dev/null +++ b/arch/mips/alchemy/common/Makefile | |||
@@ -0,0 +1,10 @@ | |||
1 | # SPDX-License-Identifier: GPL-2.0-only | ||
2 | # | ||
3 | # Copyright 2000, 2008 MontaVista Software Inc. | ||
4 | # Author: MontaVista Software, Inc. <source@mvista.com> | ||
5 | # | ||
6 | # Makefile for the Alchemy Au1xx0 CPUs, generic files. | ||
7 | # | ||
8 | |||
9 | obj-y += prom.o time.o clock.o platform.o power.o gpiolib.o \ | ||
10 | setup.o sleeper.o dma.o dbdma.o vss.o irq.o usb.o | ||
diff --git a/arch/mips/alchemy/common/clock.c b/arch/mips/alchemy/common/clock.c new file mode 100644 index 000000000..f0c830337 --- /dev/null +++ b/arch/mips/alchemy/common/clock.c | |||
@@ -0,0 +1,1117 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Alchemy clocks. | ||
4 | * | ||
5 | * Exposes all configurable internal clock sources to the clk framework. | ||
6 | * | ||
7 | * We have: | ||
8 | * - Root source, usually 12MHz supplied by an external crystal | ||
9 | * - 3 PLLs which generate multiples of root rate [AUX, CPU, AUX2] | ||
10 | * | ||
11 | * Dividers: | ||
12 | * - 6 clock dividers with: | ||
13 | * * selectable source [one of the PLLs], | ||
14 | * * output divided between [2 .. 512 in steps of 2] (!Au1300) | ||
15 | * or [1 .. 256 in steps of 1] (Au1300), | ||
16 | * * can be enabled individually. | ||
17 | * | ||
18 | * - up to 6 "internal" (fixed) consumers which: | ||
19 | * * take either AUXPLL or one of the above 6 dividers as input, | ||
20 | * * divide this input by 1, 2, or 4 (and 3 on Au1300). | ||
21 | * * can be disabled separately. | ||
22 | * | ||
23 | * Misc clocks: | ||
24 | * - sysbus clock: CPU core clock (CPUPLL) divided by 2, 3 or 4. | ||
25 | * depends on board design and should be set by bootloader, read-only. | ||
26 | * - peripheral clock: half the rate of sysbus clock, source for a lot | ||
27 | * of peripheral blocks, read-only. | ||
28 | * - memory clock: clk rate to main memory chips, depends on board | ||
29 | * design and is read-only, | ||
30 | * - lrclk: the static bus clock signal for synchronous operation. | ||
31 | * depends on board design, must be set by bootloader, | ||
32 | * but may be required to correctly configure devices attached to | ||
33 | * the static bus. The Au1000/1500/1100 manuals call it LCLK, on | ||
34 | * later models it's called RCLK. | ||
35 | */ | ||
36 | |||
37 | #include <linux/init.h> | ||
38 | #include <linux/io.h> | ||
39 | #include <linux/clk.h> | ||
40 | #include <linux/clk-provider.h> | ||
41 | #include <linux/clkdev.h> | ||
42 | #include <linux/slab.h> | ||
43 | #include <linux/spinlock.h> | ||
44 | #include <linux/types.h> | ||
45 | #include <asm/mach-au1x00/au1000.h> | ||
46 | |||
47 | /* Base clock: 12MHz is the default in all databooks, and I haven't | ||
48 | * found any board yet which uses a different rate. | ||
49 | */ | ||
50 | #define ALCHEMY_ROOTCLK_RATE 12000000 | ||
51 | |||
52 | /* | ||
53 | * the internal sources which can be driven by the PLLs and dividers. | ||
54 | * Names taken from the databooks, refer to them for more information, | ||
55 | * especially which ones are share a clock line. | ||
56 | */ | ||
57 | static const char * const alchemy_au1300_intclknames[] = { | ||
58 | "lcd_intclk", "gpemgp_clk", "maempe_clk", "maebsa_clk", | ||
59 | "EXTCLK0", "EXTCLK1" | ||
60 | }; | ||
61 | |||
62 | static const char * const alchemy_au1200_intclknames[] = { | ||
63 | "lcd_intclk", NULL, NULL, NULL, "EXTCLK0", "EXTCLK1" | ||
64 | }; | ||
65 | |||
66 | static const char * const alchemy_au1550_intclknames[] = { | ||
67 | "usb_clk", "psc0_intclk", "psc1_intclk", "pci_clko", | ||
68 | "EXTCLK0", "EXTCLK1" | ||
69 | }; | ||
70 | |||
71 | static const char * const alchemy_au1100_intclknames[] = { | ||
72 | "usb_clk", "lcd_intclk", NULL, "i2s_clk", "EXTCLK0", "EXTCLK1" | ||
73 | }; | ||
74 | |||
75 | static const char * const alchemy_au1500_intclknames[] = { | ||
76 | NULL, "usbd_clk", "usbh_clk", "pci_clko", "EXTCLK0", "EXTCLK1" | ||
77 | }; | ||
78 | |||
79 | static const char * const alchemy_au1000_intclknames[] = { | ||
80 | "irda_clk", "usbd_clk", "usbh_clk", "i2s_clk", "EXTCLK0", | ||
81 | "EXTCLK1" | ||
82 | }; | ||
83 | |||
84 | /* aliases for a few on-chip sources which are either shared | ||
85 | * or have gone through name changes. | ||
86 | */ | ||
87 | static struct clk_aliastable { | ||
88 | char *alias; | ||
89 | char *base; | ||
90 | int cputype; | ||
91 | } alchemy_clk_aliases[] __initdata = { | ||
92 | { "usbh_clk", "usb_clk", ALCHEMY_CPU_AU1100 }, | ||
93 | { "usbd_clk", "usb_clk", ALCHEMY_CPU_AU1100 }, | ||
94 | { "irda_clk", "usb_clk", ALCHEMY_CPU_AU1100 }, | ||
95 | { "usbh_clk", "usb_clk", ALCHEMY_CPU_AU1550 }, | ||
96 | { "usbd_clk", "usb_clk", ALCHEMY_CPU_AU1550 }, | ||
97 | { "psc2_intclk", "usb_clk", ALCHEMY_CPU_AU1550 }, | ||
98 | { "psc3_intclk", "EXTCLK0", ALCHEMY_CPU_AU1550 }, | ||
99 | { "psc0_intclk", "EXTCLK0", ALCHEMY_CPU_AU1200 }, | ||
100 | { "psc1_intclk", "EXTCLK1", ALCHEMY_CPU_AU1200 }, | ||
101 | { "psc0_intclk", "EXTCLK0", ALCHEMY_CPU_AU1300 }, | ||
102 | { "psc2_intclk", "EXTCLK0", ALCHEMY_CPU_AU1300 }, | ||
103 | { "psc1_intclk", "EXTCLK1", ALCHEMY_CPU_AU1300 }, | ||
104 | { "psc3_intclk", "EXTCLK1", ALCHEMY_CPU_AU1300 }, | ||
105 | |||
106 | { NULL, NULL, 0 }, | ||
107 | }; | ||
108 | |||
109 | #define IOMEM(x) ((void __iomem *)(KSEG1ADDR(CPHYSADDR(x)))) | ||
110 | |||
111 | /* access locks to SYS_FREQCTRL0/1 and SYS_CLKSRC registers */ | ||
112 | static spinlock_t alchemy_clk_fg0_lock; | ||
113 | static spinlock_t alchemy_clk_fg1_lock; | ||
114 | static spinlock_t alchemy_clk_csrc_lock; | ||
115 | |||
116 | /* CPU Core clock *****************************************************/ | ||
117 | |||
118 | static unsigned long alchemy_clk_cpu_recalc(struct clk_hw *hw, | ||
119 | unsigned long parent_rate) | ||
120 | { | ||
121 | unsigned long t; | ||
122 | |||
123 | /* | ||
124 | * On early Au1000, sys_cpupll was write-only. Since these | ||
125 | * silicon versions of Au1000 are not sold, we don't bend | ||
126 | * over backwards trying to determine the frequency. | ||
127 | */ | ||
128 | if (unlikely(au1xxx_cpu_has_pll_wo())) | ||
129 | t = 396000000; | ||
130 | else { | ||
131 | t = alchemy_rdsys(AU1000_SYS_CPUPLL) & 0x7f; | ||
132 | if (alchemy_get_cputype() < ALCHEMY_CPU_AU1300) | ||
133 | t &= 0x3f; | ||
134 | t *= parent_rate; | ||
135 | } | ||
136 | |||
137 | return t; | ||
138 | } | ||
139 | |||
140 | void __init alchemy_set_lpj(void) | ||
141 | { | ||
142 | preset_lpj = alchemy_clk_cpu_recalc(NULL, ALCHEMY_ROOTCLK_RATE); | ||
143 | preset_lpj /= 2 * HZ; | ||
144 | } | ||
145 | |||
146 | static const struct clk_ops alchemy_clkops_cpu = { | ||
147 | .recalc_rate = alchemy_clk_cpu_recalc, | ||
148 | }; | ||
149 | |||
150 | static struct clk __init *alchemy_clk_setup_cpu(const char *parent_name, | ||
151 | int ctype) | ||
152 | { | ||
153 | struct clk_init_data id; | ||
154 | struct clk_hw *h; | ||
155 | struct clk *clk; | ||
156 | |||
157 | h = kzalloc(sizeof(*h), GFP_KERNEL); | ||
158 | if (!h) | ||
159 | return ERR_PTR(-ENOMEM); | ||
160 | |||
161 | id.name = ALCHEMY_CPU_CLK; | ||
162 | id.parent_names = &parent_name; | ||
163 | id.num_parents = 1; | ||
164 | id.flags = 0; | ||
165 | id.ops = &alchemy_clkops_cpu; | ||
166 | h->init = &id; | ||
167 | |||
168 | clk = clk_register(NULL, h); | ||
169 | if (IS_ERR(clk)) { | ||
170 | pr_err("failed to register clock\n"); | ||
171 | kfree(h); | ||
172 | } | ||
173 | |||
174 | return clk; | ||
175 | } | ||
176 | |||
177 | /* AUXPLLs ************************************************************/ | ||
178 | |||
179 | struct alchemy_auxpll_clk { | ||
180 | struct clk_hw hw; | ||
181 | unsigned long reg; /* au1300 has also AUXPLL2 */ | ||
182 | int maxmult; /* max multiplier */ | ||
183 | }; | ||
184 | #define to_auxpll_clk(x) container_of(x, struct alchemy_auxpll_clk, hw) | ||
185 | |||
186 | static unsigned long alchemy_clk_aux_recalc(struct clk_hw *hw, | ||
187 | unsigned long parent_rate) | ||
188 | { | ||
189 | struct alchemy_auxpll_clk *a = to_auxpll_clk(hw); | ||
190 | |||
191 | return (alchemy_rdsys(a->reg) & 0xff) * parent_rate; | ||
192 | } | ||
193 | |||
194 | static int alchemy_clk_aux_setr(struct clk_hw *hw, | ||
195 | unsigned long rate, | ||
196 | unsigned long parent_rate) | ||
197 | { | ||
198 | struct alchemy_auxpll_clk *a = to_auxpll_clk(hw); | ||
199 | unsigned long d = rate; | ||
200 | |||
201 | if (rate) | ||
202 | d /= parent_rate; | ||
203 | else | ||
204 | d = 0; | ||
205 | |||
206 | /* minimum is 84MHz, max is 756-1032 depending on variant */ | ||
207 | if (((d < 7) && (d != 0)) || (d > a->maxmult)) | ||
208 | return -EINVAL; | ||
209 | |||
210 | alchemy_wrsys(d, a->reg); | ||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | static long alchemy_clk_aux_roundr(struct clk_hw *hw, | ||
215 | unsigned long rate, | ||
216 | unsigned long *parent_rate) | ||
217 | { | ||
218 | struct alchemy_auxpll_clk *a = to_auxpll_clk(hw); | ||
219 | unsigned long mult; | ||
220 | |||
221 | if (!rate || !*parent_rate) | ||
222 | return 0; | ||
223 | |||
224 | mult = rate / (*parent_rate); | ||
225 | |||
226 | if (mult && (mult < 7)) | ||
227 | mult = 7; | ||
228 | if (mult > a->maxmult) | ||
229 | mult = a->maxmult; | ||
230 | |||
231 | return (*parent_rate) * mult; | ||
232 | } | ||
233 | |||
234 | static const struct clk_ops alchemy_clkops_aux = { | ||
235 | .recalc_rate = alchemy_clk_aux_recalc, | ||
236 | .set_rate = alchemy_clk_aux_setr, | ||
237 | .round_rate = alchemy_clk_aux_roundr, | ||
238 | }; | ||
239 | |||
240 | static struct clk __init *alchemy_clk_setup_aux(const char *parent_name, | ||
241 | char *name, int maxmult, | ||
242 | unsigned long reg) | ||
243 | { | ||
244 | struct clk_init_data id; | ||
245 | struct clk *c; | ||
246 | struct alchemy_auxpll_clk *a; | ||
247 | |||
248 | a = kzalloc(sizeof(*a), GFP_KERNEL); | ||
249 | if (!a) | ||
250 | return ERR_PTR(-ENOMEM); | ||
251 | |||
252 | id.name = name; | ||
253 | id.parent_names = &parent_name; | ||
254 | id.num_parents = 1; | ||
255 | id.flags = CLK_GET_RATE_NOCACHE; | ||
256 | id.ops = &alchemy_clkops_aux; | ||
257 | |||
258 | a->reg = reg; | ||
259 | a->maxmult = maxmult; | ||
260 | a->hw.init = &id; | ||
261 | |||
262 | c = clk_register(NULL, &a->hw); | ||
263 | if (!IS_ERR(c)) | ||
264 | clk_register_clkdev(c, name, NULL); | ||
265 | else | ||
266 | kfree(a); | ||
267 | |||
268 | return c; | ||
269 | } | ||
270 | |||
271 | /* sysbus_clk *********************************************************/ | ||
272 | |||
273 | static struct clk __init *alchemy_clk_setup_sysbus(const char *pn) | ||
274 | { | ||
275 | unsigned long v = (alchemy_rdsys(AU1000_SYS_POWERCTRL) & 3) + 2; | ||
276 | struct clk *c; | ||
277 | |||
278 | c = clk_register_fixed_factor(NULL, ALCHEMY_SYSBUS_CLK, | ||
279 | pn, 0, 1, v); | ||
280 | if (!IS_ERR(c)) | ||
281 | clk_register_clkdev(c, ALCHEMY_SYSBUS_CLK, NULL); | ||
282 | return c; | ||
283 | } | ||
284 | |||
285 | /* Peripheral Clock ***************************************************/ | ||
286 | |||
287 | static struct clk __init *alchemy_clk_setup_periph(const char *pn) | ||
288 | { | ||
289 | /* Peripheral clock runs at half the rate of sysbus clk */ | ||
290 | struct clk *c; | ||
291 | |||
292 | c = clk_register_fixed_factor(NULL, ALCHEMY_PERIPH_CLK, | ||
293 | pn, 0, 1, 2); | ||
294 | if (!IS_ERR(c)) | ||
295 | clk_register_clkdev(c, ALCHEMY_PERIPH_CLK, NULL); | ||
296 | return c; | ||
297 | } | ||
298 | |||
299 | /* mem clock **********************************************************/ | ||
300 | |||
301 | static struct clk __init *alchemy_clk_setup_mem(const char *pn, int ct) | ||
302 | { | ||
303 | void __iomem *addr = IOMEM(AU1000_MEM_PHYS_ADDR); | ||
304 | unsigned long v; | ||
305 | struct clk *c; | ||
306 | int div; | ||
307 | |||
308 | switch (ct) { | ||
309 | case ALCHEMY_CPU_AU1550: | ||
310 | case ALCHEMY_CPU_AU1200: | ||
311 | v = __raw_readl(addr + AU1550_MEM_SDCONFIGB); | ||
312 | div = (v & (1 << 15)) ? 1 : 2; | ||
313 | break; | ||
314 | case ALCHEMY_CPU_AU1300: | ||
315 | v = __raw_readl(addr + AU1550_MEM_SDCONFIGB); | ||
316 | div = (v & (1 << 31)) ? 1 : 2; | ||
317 | break; | ||
318 | case ALCHEMY_CPU_AU1000: | ||
319 | case ALCHEMY_CPU_AU1500: | ||
320 | case ALCHEMY_CPU_AU1100: | ||
321 | default: | ||
322 | div = 2; | ||
323 | break; | ||
324 | } | ||
325 | |||
326 | c = clk_register_fixed_factor(NULL, ALCHEMY_MEM_CLK, pn, | ||
327 | 0, 1, div); | ||
328 | if (!IS_ERR(c)) | ||
329 | clk_register_clkdev(c, ALCHEMY_MEM_CLK, NULL); | ||
330 | return c; | ||
331 | } | ||
332 | |||
333 | /* lrclk: external synchronous static bus clock ***********************/ | ||
334 | |||
335 | static struct clk __init *alchemy_clk_setup_lrclk(const char *pn, int t) | ||
336 | { | ||
337 | /* Au1000, Au1500: MEM_STCFG0[11]: If bit is set, lrclk=pclk/5, | ||
338 | * otherwise lrclk=pclk/4. | ||
339 | * All other variants: MEM_STCFG0[15:13] = divisor. | ||
340 | * L/RCLK = periph_clk / (divisor + 1) | ||
341 | * On Au1000, Au1500, Au1100 it's called LCLK, | ||
342 | * on later models it's called RCLK, but it's the same thing. | ||
343 | */ | ||
344 | struct clk *c; | ||
345 | unsigned long v = alchemy_rdsmem(AU1000_MEM_STCFG0); | ||
346 | |||
347 | switch (t) { | ||
348 | case ALCHEMY_CPU_AU1000: | ||
349 | case ALCHEMY_CPU_AU1500: | ||
350 | v = 4 + ((v >> 11) & 1); | ||
351 | break; | ||
352 | default: /* all other models */ | ||
353 | v = ((v >> 13) & 7) + 1; | ||
354 | } | ||
355 | c = clk_register_fixed_factor(NULL, ALCHEMY_LR_CLK, | ||
356 | pn, 0, 1, v); | ||
357 | if (!IS_ERR(c)) | ||
358 | clk_register_clkdev(c, ALCHEMY_LR_CLK, NULL); | ||
359 | return c; | ||
360 | } | ||
361 | |||
362 | /* Clock dividers and muxes *******************************************/ | ||
363 | |||
364 | /* data for fgen and csrc mux-dividers */ | ||
365 | struct alchemy_fgcs_clk { | ||
366 | struct clk_hw hw; | ||
367 | spinlock_t *reglock; /* register lock */ | ||
368 | unsigned long reg; /* SYS_FREQCTRL0/1 */ | ||
369 | int shift; /* offset in register */ | ||
370 | int parent; /* parent before disable [Au1300] */ | ||
371 | int isen; /* is it enabled? */ | ||
372 | int *dt; /* dividertable for csrc */ | ||
373 | }; | ||
374 | #define to_fgcs_clk(x) container_of(x, struct alchemy_fgcs_clk, hw) | ||
375 | |||
376 | static long alchemy_calc_div(unsigned long rate, unsigned long prate, | ||
377 | int scale, int maxdiv, unsigned long *rv) | ||
378 | { | ||
379 | long div1, div2; | ||
380 | |||
381 | div1 = prate / rate; | ||
382 | if ((prate / div1) > rate) | ||
383 | div1++; | ||
384 | |||
385 | if (scale == 2) { /* only div-by-multiple-of-2 possible */ | ||
386 | if (div1 & 1) | ||
387 | div1++; /* stay <=prate */ | ||
388 | } | ||
389 | |||
390 | div2 = (div1 / scale) - 1; /* value to write to register */ | ||
391 | |||
392 | if (div2 > maxdiv) | ||
393 | div2 = maxdiv; | ||
394 | if (rv) | ||
395 | *rv = div2; | ||
396 | |||
397 | div1 = ((div2 + 1) * scale); | ||
398 | return div1; | ||
399 | } | ||
400 | |||
401 | static int alchemy_clk_fgcs_detr(struct clk_hw *hw, | ||
402 | struct clk_rate_request *req, | ||
403 | int scale, int maxdiv) | ||
404 | { | ||
405 | struct clk_hw *pc, *bpc, *free; | ||
406 | long tdv, tpr, pr, nr, br, bpr, diff, lastdiff; | ||
407 | int j; | ||
408 | |||
409 | lastdiff = INT_MAX; | ||
410 | bpr = 0; | ||
411 | bpc = NULL; | ||
412 | br = -EINVAL; | ||
413 | free = NULL; | ||
414 | |||
415 | /* look at the rates each enabled parent supplies and select | ||
416 | * the one that gets closest to but not over the requested rate. | ||
417 | */ | ||
418 | for (j = 0; j < 7; j++) { | ||
419 | pc = clk_hw_get_parent_by_index(hw, j); | ||
420 | if (!pc) | ||
421 | break; | ||
422 | |||
423 | /* if this parent is currently unused, remember it. | ||
424 | * XXX: we would actually want clk_has_active_children() | ||
425 | * but this is a good-enough approximation for now. | ||
426 | */ | ||
427 | if (!clk_hw_is_prepared(pc)) { | ||
428 | if (!free) | ||
429 | free = pc; | ||
430 | } | ||
431 | |||
432 | pr = clk_hw_get_rate(pc); | ||
433 | if (pr < req->rate) | ||
434 | continue; | ||
435 | |||
436 | /* what can hardware actually provide */ | ||
437 | tdv = alchemy_calc_div(req->rate, pr, scale, maxdiv, NULL); | ||
438 | nr = pr / tdv; | ||
439 | diff = req->rate - nr; | ||
440 | if (nr > req->rate) | ||
441 | continue; | ||
442 | |||
443 | if (diff < lastdiff) { | ||
444 | lastdiff = diff; | ||
445 | bpr = pr; | ||
446 | bpc = pc; | ||
447 | br = nr; | ||
448 | } | ||
449 | if (diff == 0) | ||
450 | break; | ||
451 | } | ||
452 | |||
453 | /* if we couldn't get the exact rate we wanted from the enabled | ||
454 | * parents, maybe we can tell an available disabled/inactive one | ||
455 | * to give us a rate we can divide down to the requested rate. | ||
456 | */ | ||
457 | if (lastdiff && free) { | ||
458 | for (j = (maxdiv == 4) ? 1 : scale; j <= maxdiv; j += scale) { | ||
459 | tpr = req->rate * j; | ||
460 | if (tpr < 0) | ||
461 | break; | ||
462 | pr = clk_hw_round_rate(free, tpr); | ||
463 | |||
464 | tdv = alchemy_calc_div(req->rate, pr, scale, maxdiv, | ||
465 | NULL); | ||
466 | nr = pr / tdv; | ||
467 | diff = req->rate - nr; | ||
468 | if (nr > req->rate) | ||
469 | continue; | ||
470 | if (diff < lastdiff) { | ||
471 | lastdiff = diff; | ||
472 | bpr = pr; | ||
473 | bpc = free; | ||
474 | br = nr; | ||
475 | } | ||
476 | if (diff == 0) | ||
477 | break; | ||
478 | } | ||
479 | } | ||
480 | |||
481 | if (br < 0) | ||
482 | return br; | ||
483 | |||
484 | req->best_parent_rate = bpr; | ||
485 | req->best_parent_hw = bpc; | ||
486 | req->rate = br; | ||
487 | |||
488 | return 0; | ||
489 | } | ||
490 | |||
491 | static int alchemy_clk_fgv1_en(struct clk_hw *hw) | ||
492 | { | ||
493 | struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); | ||
494 | unsigned long v, flags; | ||
495 | |||
496 | spin_lock_irqsave(c->reglock, flags); | ||
497 | v = alchemy_rdsys(c->reg); | ||
498 | v |= (1 << 1) << c->shift; | ||
499 | alchemy_wrsys(v, c->reg); | ||
500 | spin_unlock_irqrestore(c->reglock, flags); | ||
501 | |||
502 | return 0; | ||
503 | } | ||
504 | |||
505 | static int alchemy_clk_fgv1_isen(struct clk_hw *hw) | ||
506 | { | ||
507 | struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); | ||
508 | unsigned long v = alchemy_rdsys(c->reg) >> (c->shift + 1); | ||
509 | |||
510 | return v & 1; | ||
511 | } | ||
512 | |||
513 | static void alchemy_clk_fgv1_dis(struct clk_hw *hw) | ||
514 | { | ||
515 | struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); | ||
516 | unsigned long v, flags; | ||
517 | |||
518 | spin_lock_irqsave(c->reglock, flags); | ||
519 | v = alchemy_rdsys(c->reg); | ||
520 | v &= ~((1 << 1) << c->shift); | ||
521 | alchemy_wrsys(v, c->reg); | ||
522 | spin_unlock_irqrestore(c->reglock, flags); | ||
523 | } | ||
524 | |||
525 | static int alchemy_clk_fgv1_setp(struct clk_hw *hw, u8 index) | ||
526 | { | ||
527 | struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); | ||
528 | unsigned long v, flags; | ||
529 | |||
530 | spin_lock_irqsave(c->reglock, flags); | ||
531 | v = alchemy_rdsys(c->reg); | ||
532 | if (index) | ||
533 | v |= (1 << c->shift); | ||
534 | else | ||
535 | v &= ~(1 << c->shift); | ||
536 | alchemy_wrsys(v, c->reg); | ||
537 | spin_unlock_irqrestore(c->reglock, flags); | ||
538 | |||
539 | return 0; | ||
540 | } | ||
541 | |||
542 | static u8 alchemy_clk_fgv1_getp(struct clk_hw *hw) | ||
543 | { | ||
544 | struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); | ||
545 | |||
546 | return (alchemy_rdsys(c->reg) >> c->shift) & 1; | ||
547 | } | ||
548 | |||
549 | static int alchemy_clk_fgv1_setr(struct clk_hw *hw, unsigned long rate, | ||
550 | unsigned long parent_rate) | ||
551 | { | ||
552 | struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); | ||
553 | unsigned long div, v, flags, ret; | ||
554 | int sh = c->shift + 2; | ||
555 | |||
556 | if (!rate || !parent_rate || rate > (parent_rate / 2)) | ||
557 | return -EINVAL; | ||
558 | ret = alchemy_calc_div(rate, parent_rate, 2, 512, &div); | ||
559 | spin_lock_irqsave(c->reglock, flags); | ||
560 | v = alchemy_rdsys(c->reg); | ||
561 | v &= ~(0xff << sh); | ||
562 | v |= div << sh; | ||
563 | alchemy_wrsys(v, c->reg); | ||
564 | spin_unlock_irqrestore(c->reglock, flags); | ||
565 | |||
566 | return 0; | ||
567 | } | ||
568 | |||
569 | static unsigned long alchemy_clk_fgv1_recalc(struct clk_hw *hw, | ||
570 | unsigned long parent_rate) | ||
571 | { | ||
572 | struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); | ||
573 | unsigned long v = alchemy_rdsys(c->reg) >> (c->shift + 2); | ||
574 | |||
575 | v = ((v & 0xff) + 1) * 2; | ||
576 | return parent_rate / v; | ||
577 | } | ||
578 | |||
579 | static int alchemy_clk_fgv1_detr(struct clk_hw *hw, | ||
580 | struct clk_rate_request *req) | ||
581 | { | ||
582 | return alchemy_clk_fgcs_detr(hw, req, 2, 512); | ||
583 | } | ||
584 | |||
585 | /* Au1000, Au1100, Au15x0, Au12x0 */ | ||
586 | static const struct clk_ops alchemy_clkops_fgenv1 = { | ||
587 | .recalc_rate = alchemy_clk_fgv1_recalc, | ||
588 | .determine_rate = alchemy_clk_fgv1_detr, | ||
589 | .set_rate = alchemy_clk_fgv1_setr, | ||
590 | .set_parent = alchemy_clk_fgv1_setp, | ||
591 | .get_parent = alchemy_clk_fgv1_getp, | ||
592 | .enable = alchemy_clk_fgv1_en, | ||
593 | .disable = alchemy_clk_fgv1_dis, | ||
594 | .is_enabled = alchemy_clk_fgv1_isen, | ||
595 | }; | ||
596 | |||
597 | static void __alchemy_clk_fgv2_en(struct alchemy_fgcs_clk *c) | ||
598 | { | ||
599 | unsigned long v = alchemy_rdsys(c->reg); | ||
600 | |||
601 | v &= ~(3 << c->shift); | ||
602 | v |= (c->parent & 3) << c->shift; | ||
603 | alchemy_wrsys(v, c->reg); | ||
604 | c->isen = 1; | ||
605 | } | ||
606 | |||
607 | static int alchemy_clk_fgv2_en(struct clk_hw *hw) | ||
608 | { | ||
609 | struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); | ||
610 | unsigned long flags; | ||
611 | |||
612 | /* enable by setting the previous parent clock */ | ||
613 | spin_lock_irqsave(c->reglock, flags); | ||
614 | __alchemy_clk_fgv2_en(c); | ||
615 | spin_unlock_irqrestore(c->reglock, flags); | ||
616 | |||
617 | return 0; | ||
618 | } | ||
619 | |||
620 | static int alchemy_clk_fgv2_isen(struct clk_hw *hw) | ||
621 | { | ||
622 | struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); | ||
623 | |||
624 | return ((alchemy_rdsys(c->reg) >> c->shift) & 3) != 0; | ||
625 | } | ||
626 | |||
627 | static void alchemy_clk_fgv2_dis(struct clk_hw *hw) | ||
628 | { | ||
629 | struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); | ||
630 | unsigned long v, flags; | ||
631 | |||
632 | spin_lock_irqsave(c->reglock, flags); | ||
633 | v = alchemy_rdsys(c->reg); | ||
634 | v &= ~(3 << c->shift); /* set input mux to "disabled" state */ | ||
635 | alchemy_wrsys(v, c->reg); | ||
636 | c->isen = 0; | ||
637 | spin_unlock_irqrestore(c->reglock, flags); | ||
638 | } | ||
639 | |||
640 | static int alchemy_clk_fgv2_setp(struct clk_hw *hw, u8 index) | ||
641 | { | ||
642 | struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); | ||
643 | unsigned long flags; | ||
644 | |||
645 | spin_lock_irqsave(c->reglock, flags); | ||
646 | c->parent = index + 1; /* value to write to register */ | ||
647 | if (c->isen) | ||
648 | __alchemy_clk_fgv2_en(c); | ||
649 | spin_unlock_irqrestore(c->reglock, flags); | ||
650 | |||
651 | return 0; | ||
652 | } | ||
653 | |||
654 | static u8 alchemy_clk_fgv2_getp(struct clk_hw *hw) | ||
655 | { | ||
656 | struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); | ||
657 | unsigned long flags, v; | ||
658 | |||
659 | spin_lock_irqsave(c->reglock, flags); | ||
660 | v = c->parent - 1; | ||
661 | spin_unlock_irqrestore(c->reglock, flags); | ||
662 | return v; | ||
663 | } | ||
664 | |||
665 | /* fg0-2 and fg4-6 share a "scale"-bit. With this bit cleared, the | ||
666 | * dividers behave exactly as on previous models (dividers are multiples | ||
667 | * of 2); with the bit set, dividers are multiples of 1, halving their | ||
668 | * range, but making them also much more flexible. | ||
669 | */ | ||
670 | static int alchemy_clk_fgv2_setr(struct clk_hw *hw, unsigned long rate, | ||
671 | unsigned long parent_rate) | ||
672 | { | ||
673 | struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); | ||
674 | int sh = c->shift + 2; | ||
675 | unsigned long div, v, flags, ret; | ||
676 | |||
677 | if (!rate || !parent_rate || rate > parent_rate) | ||
678 | return -EINVAL; | ||
679 | |||
680 | v = alchemy_rdsys(c->reg) & (1 << 30); /* test "scale" bit */ | ||
681 | ret = alchemy_calc_div(rate, parent_rate, v ? 1 : 2, | ||
682 | v ? 256 : 512, &div); | ||
683 | |||
684 | spin_lock_irqsave(c->reglock, flags); | ||
685 | v = alchemy_rdsys(c->reg); | ||
686 | v &= ~(0xff << sh); | ||
687 | v |= (div & 0xff) << sh; | ||
688 | alchemy_wrsys(v, c->reg); | ||
689 | spin_unlock_irqrestore(c->reglock, flags); | ||
690 | |||
691 | return 0; | ||
692 | } | ||
693 | |||
694 | static unsigned long alchemy_clk_fgv2_recalc(struct clk_hw *hw, | ||
695 | unsigned long parent_rate) | ||
696 | { | ||
697 | struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); | ||
698 | int sh = c->shift + 2; | ||
699 | unsigned long v, t; | ||
700 | |||
701 | v = alchemy_rdsys(c->reg); | ||
702 | t = parent_rate / (((v >> sh) & 0xff) + 1); | ||
703 | if ((v & (1 << 30)) == 0) /* test scale bit */ | ||
704 | t /= 2; | ||
705 | |||
706 | return t; | ||
707 | } | ||
708 | |||
709 | static int alchemy_clk_fgv2_detr(struct clk_hw *hw, | ||
710 | struct clk_rate_request *req) | ||
711 | { | ||
712 | struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); | ||
713 | int scale, maxdiv; | ||
714 | |||
715 | if (alchemy_rdsys(c->reg) & (1 << 30)) { | ||
716 | scale = 1; | ||
717 | maxdiv = 256; | ||
718 | } else { | ||
719 | scale = 2; | ||
720 | maxdiv = 512; | ||
721 | } | ||
722 | |||
723 | return alchemy_clk_fgcs_detr(hw, req, scale, maxdiv); | ||
724 | } | ||
725 | |||
726 | /* Au1300 larger input mux, no separate disable bit, flexible divider */ | ||
727 | static const struct clk_ops alchemy_clkops_fgenv2 = { | ||
728 | .recalc_rate = alchemy_clk_fgv2_recalc, | ||
729 | .determine_rate = alchemy_clk_fgv2_detr, | ||
730 | .set_rate = alchemy_clk_fgv2_setr, | ||
731 | .set_parent = alchemy_clk_fgv2_setp, | ||
732 | .get_parent = alchemy_clk_fgv2_getp, | ||
733 | .enable = alchemy_clk_fgv2_en, | ||
734 | .disable = alchemy_clk_fgv2_dis, | ||
735 | .is_enabled = alchemy_clk_fgv2_isen, | ||
736 | }; | ||
737 | |||
738 | static const char * const alchemy_clk_fgv1_parents[] = { | ||
739 | ALCHEMY_CPU_CLK, ALCHEMY_AUXPLL_CLK | ||
740 | }; | ||
741 | |||
742 | static const char * const alchemy_clk_fgv2_parents[] = { | ||
743 | ALCHEMY_AUXPLL2_CLK, ALCHEMY_CPU_CLK, ALCHEMY_AUXPLL_CLK | ||
744 | }; | ||
745 | |||
746 | static const char * const alchemy_clk_fgen_names[] = { | ||
747 | ALCHEMY_FG0_CLK, ALCHEMY_FG1_CLK, ALCHEMY_FG2_CLK, | ||
748 | ALCHEMY_FG3_CLK, ALCHEMY_FG4_CLK, ALCHEMY_FG5_CLK }; | ||
749 | |||
750 | static int __init alchemy_clk_init_fgens(int ctype) | ||
751 | { | ||
752 | struct clk *c; | ||
753 | struct clk_init_data id; | ||
754 | struct alchemy_fgcs_clk *a; | ||
755 | unsigned long v; | ||
756 | int i, ret; | ||
757 | |||
758 | switch (ctype) { | ||
759 | case ALCHEMY_CPU_AU1000...ALCHEMY_CPU_AU1200: | ||
760 | id.ops = &alchemy_clkops_fgenv1; | ||
761 | id.parent_names = alchemy_clk_fgv1_parents; | ||
762 | id.num_parents = 2; | ||
763 | break; | ||
764 | case ALCHEMY_CPU_AU1300: | ||
765 | id.ops = &alchemy_clkops_fgenv2; | ||
766 | id.parent_names = alchemy_clk_fgv2_parents; | ||
767 | id.num_parents = 3; | ||
768 | break; | ||
769 | default: | ||
770 | return -ENODEV; | ||
771 | } | ||
772 | id.flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE; | ||
773 | |||
774 | a = kzalloc((sizeof(*a)) * 6, GFP_KERNEL); | ||
775 | if (!a) | ||
776 | return -ENOMEM; | ||
777 | |||
778 | spin_lock_init(&alchemy_clk_fg0_lock); | ||
779 | spin_lock_init(&alchemy_clk_fg1_lock); | ||
780 | ret = 0; | ||
781 | for (i = 0; i < 6; i++) { | ||
782 | id.name = alchemy_clk_fgen_names[i]; | ||
783 | a->shift = 10 * (i < 3 ? i : i - 3); | ||
784 | if (i > 2) { | ||
785 | a->reg = AU1000_SYS_FREQCTRL1; | ||
786 | a->reglock = &alchemy_clk_fg1_lock; | ||
787 | } else { | ||
788 | a->reg = AU1000_SYS_FREQCTRL0; | ||
789 | a->reglock = &alchemy_clk_fg0_lock; | ||
790 | } | ||
791 | |||
792 | /* default to first parent if bootloader has set | ||
793 | * the mux to disabled state. | ||
794 | */ | ||
795 | if (ctype == ALCHEMY_CPU_AU1300) { | ||
796 | v = alchemy_rdsys(a->reg); | ||
797 | a->parent = (v >> a->shift) & 3; | ||
798 | if (!a->parent) { | ||
799 | a->parent = 1; | ||
800 | a->isen = 0; | ||
801 | } else | ||
802 | a->isen = 1; | ||
803 | } | ||
804 | |||
805 | a->hw.init = &id; | ||
806 | c = clk_register(NULL, &a->hw); | ||
807 | if (IS_ERR(c)) | ||
808 | ret++; | ||
809 | else | ||
810 | clk_register_clkdev(c, id.name, NULL); | ||
811 | a++; | ||
812 | } | ||
813 | |||
814 | return ret; | ||
815 | } | ||
816 | |||
817 | /* internal sources muxes *********************************************/ | ||
818 | |||
819 | static int alchemy_clk_csrc_isen(struct clk_hw *hw) | ||
820 | { | ||
821 | struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); | ||
822 | unsigned long v = alchemy_rdsys(c->reg); | ||
823 | |||
824 | return (((v >> c->shift) >> 2) & 7) != 0; | ||
825 | } | ||
826 | |||
827 | static void __alchemy_clk_csrc_en(struct alchemy_fgcs_clk *c) | ||
828 | { | ||
829 | unsigned long v = alchemy_rdsys(c->reg); | ||
830 | |||
831 | v &= ~((7 << 2) << c->shift); | ||
832 | v |= ((c->parent & 7) << 2) << c->shift; | ||
833 | alchemy_wrsys(v, c->reg); | ||
834 | c->isen = 1; | ||
835 | } | ||
836 | |||
837 | static int alchemy_clk_csrc_en(struct clk_hw *hw) | ||
838 | { | ||
839 | struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); | ||
840 | unsigned long flags; | ||
841 | |||
842 | /* enable by setting the previous parent clock */ | ||
843 | spin_lock_irqsave(c->reglock, flags); | ||
844 | __alchemy_clk_csrc_en(c); | ||
845 | spin_unlock_irqrestore(c->reglock, flags); | ||
846 | |||
847 | return 0; | ||
848 | } | ||
849 | |||
850 | static void alchemy_clk_csrc_dis(struct clk_hw *hw) | ||
851 | { | ||
852 | struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); | ||
853 | unsigned long v, flags; | ||
854 | |||
855 | spin_lock_irqsave(c->reglock, flags); | ||
856 | v = alchemy_rdsys(c->reg); | ||
857 | v &= ~((3 << 2) << c->shift); /* mux to "disabled" state */ | ||
858 | alchemy_wrsys(v, c->reg); | ||
859 | c->isen = 0; | ||
860 | spin_unlock_irqrestore(c->reglock, flags); | ||
861 | } | ||
862 | |||
863 | static int alchemy_clk_csrc_setp(struct clk_hw *hw, u8 index) | ||
864 | { | ||
865 | struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); | ||
866 | unsigned long flags; | ||
867 | |||
868 | spin_lock_irqsave(c->reglock, flags); | ||
869 | c->parent = index + 1; /* value to write to register */ | ||
870 | if (c->isen) | ||
871 | __alchemy_clk_csrc_en(c); | ||
872 | spin_unlock_irqrestore(c->reglock, flags); | ||
873 | |||
874 | return 0; | ||
875 | } | ||
876 | |||
877 | static u8 alchemy_clk_csrc_getp(struct clk_hw *hw) | ||
878 | { | ||
879 | struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); | ||
880 | |||
881 | return c->parent - 1; | ||
882 | } | ||
883 | |||
884 | static unsigned long alchemy_clk_csrc_recalc(struct clk_hw *hw, | ||
885 | unsigned long parent_rate) | ||
886 | { | ||
887 | struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); | ||
888 | unsigned long v = (alchemy_rdsys(c->reg) >> c->shift) & 3; | ||
889 | |||
890 | return parent_rate / c->dt[v]; | ||
891 | } | ||
892 | |||
893 | static int alchemy_clk_csrc_setr(struct clk_hw *hw, unsigned long rate, | ||
894 | unsigned long parent_rate) | ||
895 | { | ||
896 | struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); | ||
897 | unsigned long d, v, flags; | ||
898 | int i; | ||
899 | |||
900 | if (!rate || !parent_rate || rate > parent_rate) | ||
901 | return -EINVAL; | ||
902 | |||
903 | d = (parent_rate + (rate / 2)) / rate; | ||
904 | if (d > 4) | ||
905 | return -EINVAL; | ||
906 | if ((d == 3) && (c->dt[2] != 3)) | ||
907 | d = 4; | ||
908 | |||
909 | for (i = 0; i < 4; i++) | ||
910 | if (c->dt[i] == d) | ||
911 | break; | ||
912 | |||
913 | if (i >= 4) | ||
914 | return -EINVAL; /* oops */ | ||
915 | |||
916 | spin_lock_irqsave(c->reglock, flags); | ||
917 | v = alchemy_rdsys(c->reg); | ||
918 | v &= ~(3 << c->shift); | ||
919 | v |= (i & 3) << c->shift; | ||
920 | alchemy_wrsys(v, c->reg); | ||
921 | spin_unlock_irqrestore(c->reglock, flags); | ||
922 | |||
923 | return 0; | ||
924 | } | ||
925 | |||
926 | static int alchemy_clk_csrc_detr(struct clk_hw *hw, | ||
927 | struct clk_rate_request *req) | ||
928 | { | ||
929 | struct alchemy_fgcs_clk *c = to_fgcs_clk(hw); | ||
930 | int scale = c->dt[2] == 3 ? 1 : 2; /* au1300 check */ | ||
931 | |||
932 | return alchemy_clk_fgcs_detr(hw, req, scale, 4); | ||
933 | } | ||
934 | |||
935 | static const struct clk_ops alchemy_clkops_csrc = { | ||
936 | .recalc_rate = alchemy_clk_csrc_recalc, | ||
937 | .determine_rate = alchemy_clk_csrc_detr, | ||
938 | .set_rate = alchemy_clk_csrc_setr, | ||
939 | .set_parent = alchemy_clk_csrc_setp, | ||
940 | .get_parent = alchemy_clk_csrc_getp, | ||
941 | .enable = alchemy_clk_csrc_en, | ||
942 | .disable = alchemy_clk_csrc_dis, | ||
943 | .is_enabled = alchemy_clk_csrc_isen, | ||
944 | }; | ||
945 | |||
946 | static const char * const alchemy_clk_csrc_parents[] = { | ||
947 | /* disabled at index 0 */ ALCHEMY_AUXPLL_CLK, | ||
948 | ALCHEMY_FG0_CLK, ALCHEMY_FG1_CLK, ALCHEMY_FG2_CLK, | ||
949 | ALCHEMY_FG3_CLK, ALCHEMY_FG4_CLK, ALCHEMY_FG5_CLK | ||
950 | }; | ||
951 | |||
952 | /* divider tables */ | ||
953 | static int alchemy_csrc_dt1[] = { 1, 4, 1, 2 }; /* rest */ | ||
954 | static int alchemy_csrc_dt2[] = { 1, 4, 3, 2 }; /* Au1300 */ | ||
955 | |||
956 | static int __init alchemy_clk_setup_imux(int ctype) | ||
957 | { | ||
958 | struct alchemy_fgcs_clk *a; | ||
959 | const char * const *names; | ||
960 | struct clk_init_data id; | ||
961 | unsigned long v; | ||
962 | int i, ret, *dt; | ||
963 | struct clk *c; | ||
964 | |||
965 | id.ops = &alchemy_clkops_csrc; | ||
966 | id.parent_names = alchemy_clk_csrc_parents; | ||
967 | id.num_parents = 7; | ||
968 | id.flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE; | ||
969 | |||
970 | dt = alchemy_csrc_dt1; | ||
971 | switch (ctype) { | ||
972 | case ALCHEMY_CPU_AU1000: | ||
973 | names = alchemy_au1000_intclknames; | ||
974 | break; | ||
975 | case ALCHEMY_CPU_AU1500: | ||
976 | names = alchemy_au1500_intclknames; | ||
977 | break; | ||
978 | case ALCHEMY_CPU_AU1100: | ||
979 | names = alchemy_au1100_intclknames; | ||
980 | break; | ||
981 | case ALCHEMY_CPU_AU1550: | ||
982 | names = alchemy_au1550_intclknames; | ||
983 | break; | ||
984 | case ALCHEMY_CPU_AU1200: | ||
985 | names = alchemy_au1200_intclknames; | ||
986 | break; | ||
987 | case ALCHEMY_CPU_AU1300: | ||
988 | dt = alchemy_csrc_dt2; | ||
989 | names = alchemy_au1300_intclknames; | ||
990 | break; | ||
991 | default: | ||
992 | return -ENODEV; | ||
993 | } | ||
994 | |||
995 | a = kcalloc(6, sizeof(*a), GFP_KERNEL); | ||
996 | if (!a) | ||
997 | return -ENOMEM; | ||
998 | |||
999 | spin_lock_init(&alchemy_clk_csrc_lock); | ||
1000 | ret = 0; | ||
1001 | |||
1002 | for (i = 0; i < 6; i++) { | ||
1003 | id.name = names[i]; | ||
1004 | if (!id.name) | ||
1005 | goto next; | ||
1006 | |||
1007 | a->shift = i * 5; | ||
1008 | a->reg = AU1000_SYS_CLKSRC; | ||
1009 | a->reglock = &alchemy_clk_csrc_lock; | ||
1010 | a->dt = dt; | ||
1011 | |||
1012 | /* default to first parent clock if mux is initially | ||
1013 | * set to disabled state. | ||
1014 | */ | ||
1015 | v = alchemy_rdsys(a->reg); | ||
1016 | a->parent = ((v >> a->shift) >> 2) & 7; | ||
1017 | if (!a->parent) { | ||
1018 | a->parent = 1; | ||
1019 | a->isen = 0; | ||
1020 | } else | ||
1021 | a->isen = 1; | ||
1022 | |||
1023 | a->hw.init = &id; | ||
1024 | c = clk_register(NULL, &a->hw); | ||
1025 | if (IS_ERR(c)) | ||
1026 | ret++; | ||
1027 | else | ||
1028 | clk_register_clkdev(c, id.name, NULL); | ||
1029 | next: | ||
1030 | a++; | ||
1031 | } | ||
1032 | |||
1033 | return ret; | ||
1034 | } | ||
1035 | |||
1036 | |||
1037 | /**********************************************************************/ | ||
1038 | |||
1039 | |||
1040 | #define ERRCK(x) \ | ||
1041 | if (IS_ERR(x)) { \ | ||
1042 | ret = PTR_ERR(x); \ | ||
1043 | goto out; \ | ||
1044 | } | ||
1045 | |||
1046 | static int __init alchemy_clk_init(void) | ||
1047 | { | ||
1048 | int ctype = alchemy_get_cputype(), ret, i; | ||
1049 | struct clk_aliastable *t = alchemy_clk_aliases; | ||
1050 | struct clk *c; | ||
1051 | |||
1052 | /* Root of the Alchemy clock tree: external 12MHz crystal osc */ | ||
1053 | c = clk_register_fixed_rate(NULL, ALCHEMY_ROOT_CLK, NULL, | ||
1054 | 0, ALCHEMY_ROOTCLK_RATE); | ||
1055 | ERRCK(c) | ||
1056 | |||
1057 | /* CPU core clock */ | ||
1058 | c = alchemy_clk_setup_cpu(ALCHEMY_ROOT_CLK, ctype); | ||
1059 | ERRCK(c) | ||
1060 | |||
1061 | /* AUXPLLs: max 1GHz on Au1300, 748MHz on older models */ | ||
1062 | i = (ctype == ALCHEMY_CPU_AU1300) ? 84 : 63; | ||
1063 | c = alchemy_clk_setup_aux(ALCHEMY_ROOT_CLK, ALCHEMY_AUXPLL_CLK, | ||
1064 | i, AU1000_SYS_AUXPLL); | ||
1065 | ERRCK(c) | ||
1066 | |||
1067 | if (ctype == ALCHEMY_CPU_AU1300) { | ||
1068 | c = alchemy_clk_setup_aux(ALCHEMY_ROOT_CLK, | ||
1069 | ALCHEMY_AUXPLL2_CLK, i, | ||
1070 | AU1300_SYS_AUXPLL2); | ||
1071 | ERRCK(c) | ||
1072 | } | ||
1073 | |||
1074 | /* sysbus clock: cpu core clock divided by 2, 3 or 4 */ | ||
1075 | c = alchemy_clk_setup_sysbus(ALCHEMY_CPU_CLK); | ||
1076 | ERRCK(c) | ||
1077 | |||
1078 | /* peripheral clock: runs at half rate of sysbus clk */ | ||
1079 | c = alchemy_clk_setup_periph(ALCHEMY_SYSBUS_CLK); | ||
1080 | ERRCK(c) | ||
1081 | |||
1082 | /* SDR/DDR memory clock */ | ||
1083 | c = alchemy_clk_setup_mem(ALCHEMY_SYSBUS_CLK, ctype); | ||
1084 | ERRCK(c) | ||
1085 | |||
1086 | /* L/RCLK: external static bus clock for synchronous mode */ | ||
1087 | c = alchemy_clk_setup_lrclk(ALCHEMY_PERIPH_CLK, ctype); | ||
1088 | ERRCK(c) | ||
1089 | |||
1090 | /* Frequency dividers 0-5 */ | ||
1091 | ret = alchemy_clk_init_fgens(ctype); | ||
1092 | if (ret) { | ||
1093 | ret = -ENODEV; | ||
1094 | goto out; | ||
1095 | } | ||
1096 | |||
1097 | /* diving muxes for internal sources */ | ||
1098 | ret = alchemy_clk_setup_imux(ctype); | ||
1099 | if (ret) { | ||
1100 | ret = -ENODEV; | ||
1101 | goto out; | ||
1102 | } | ||
1103 | |||
1104 | /* set up aliases drivers might look for */ | ||
1105 | while (t->base) { | ||
1106 | if (t->cputype == ctype) | ||
1107 | clk_add_alias(t->alias, NULL, t->base, NULL); | ||
1108 | t++; | ||
1109 | } | ||
1110 | |||
1111 | pr_info("Alchemy clocktree installed\n"); | ||
1112 | return 0; | ||
1113 | |||
1114 | out: | ||
1115 | return ret; | ||
1116 | } | ||
1117 | postcore_initcall(alchemy_clk_init); | ||
diff --git a/arch/mips/alchemy/common/dbdma.c b/arch/mips/alchemy/common/dbdma.c new file mode 100644 index 000000000..4ca2c2887 --- /dev/null +++ b/arch/mips/alchemy/common/dbdma.c | |||
@@ -0,0 +1,1089 @@ | |||
1 | /* | ||
2 | * | ||
3 | * BRIEF MODULE DESCRIPTION | ||
4 | * The Descriptor Based DMA channel manager that first appeared | ||
5 | * on the Au1550. I started with dma.c, but I think all that is | ||
6 | * left is this initial comment :-) | ||
7 | * | ||
8 | * Copyright 2004 Embedded Edge, LLC | ||
9 | * dan@embeddededge.com | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify it | ||
12 | * under the terms of the GNU General Public License as published by the | ||
13 | * Free Software Foundation; either version 2 of the License, or (at your | ||
14 | * option) any later version. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
17 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
18 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
19 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
21 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
22 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
23 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
25 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | * | ||
27 | * You should have received a copy of the GNU General Public License along | ||
28 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
29 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
30 | * | ||
31 | */ | ||
32 | |||
33 | #include <linux/init.h> | ||
34 | #include <linux/kernel.h> | ||
35 | #include <linux/slab.h> | ||
36 | #include <linux/spinlock.h> | ||
37 | #include <linux/interrupt.h> | ||
38 | #include <linux/export.h> | ||
39 | #include <linux/syscore_ops.h> | ||
40 | #include <asm/mach-au1x00/au1000.h> | ||
41 | #include <asm/mach-au1x00/au1xxx_dbdma.h> | ||
42 | |||
43 | /* | ||
44 | * The Descriptor Based DMA supports up to 16 channels. | ||
45 | * | ||
46 | * There are 32 devices defined. We keep an internal structure | ||
47 | * of devices using these channels, along with additional | ||
48 | * information. | ||
49 | * | ||
50 | * We allocate the descriptors and allow access to them through various | ||
51 | * functions. The drivers allocate the data buffers and assign them | ||
52 | * to the descriptors. | ||
53 | */ | ||
54 | static DEFINE_SPINLOCK(au1xxx_dbdma_spin_lock); | ||
55 | |||
56 | /* I couldn't find a macro that did this... */ | ||
57 | #define ALIGN_ADDR(x, a) ((((u32)(x)) + (a-1)) & ~(a-1)) | ||
58 | |||
59 | static dbdma_global_t *dbdma_gptr = | ||
60 | (dbdma_global_t *)KSEG1ADDR(AU1550_DBDMA_CONF_PHYS_ADDR); | ||
61 | static int dbdma_initialized; | ||
62 | |||
63 | static dbdev_tab_t *dbdev_tab; | ||
64 | |||
65 | static dbdev_tab_t au1550_dbdev_tab[] __initdata = { | ||
66 | /* UARTS */ | ||
67 | { AU1550_DSCR_CMD0_UART0_TX, DEV_FLAGS_OUT, 0, 8, 0x11100004, 0, 0 }, | ||
68 | { AU1550_DSCR_CMD0_UART0_RX, DEV_FLAGS_IN, 0, 8, 0x11100000, 0, 0 }, | ||
69 | { AU1550_DSCR_CMD0_UART3_TX, DEV_FLAGS_OUT, 0, 8, 0x11400004, 0, 0 }, | ||
70 | { AU1550_DSCR_CMD0_UART3_RX, DEV_FLAGS_IN, 0, 8, 0x11400000, 0, 0 }, | ||
71 | |||
72 | /* EXT DMA */ | ||
73 | { AU1550_DSCR_CMD0_DMA_REQ0, 0, 0, 0, 0x00000000, 0, 0 }, | ||
74 | { AU1550_DSCR_CMD0_DMA_REQ1, 0, 0, 0, 0x00000000, 0, 0 }, | ||
75 | { AU1550_DSCR_CMD0_DMA_REQ2, 0, 0, 0, 0x00000000, 0, 0 }, | ||
76 | { AU1550_DSCR_CMD0_DMA_REQ3, 0, 0, 0, 0x00000000, 0, 0 }, | ||
77 | |||
78 | /* USB DEV */ | ||
79 | { AU1550_DSCR_CMD0_USBDEV_RX0, DEV_FLAGS_IN, 4, 8, 0x10200000, 0, 0 }, | ||
80 | { AU1550_DSCR_CMD0_USBDEV_TX0, DEV_FLAGS_OUT, 4, 8, 0x10200004, 0, 0 }, | ||
81 | { AU1550_DSCR_CMD0_USBDEV_TX1, DEV_FLAGS_OUT, 4, 8, 0x10200008, 0, 0 }, | ||
82 | { AU1550_DSCR_CMD0_USBDEV_TX2, DEV_FLAGS_OUT, 4, 8, 0x1020000c, 0, 0 }, | ||
83 | { AU1550_DSCR_CMD0_USBDEV_RX3, DEV_FLAGS_IN, 4, 8, 0x10200010, 0, 0 }, | ||
84 | { AU1550_DSCR_CMD0_USBDEV_RX4, DEV_FLAGS_IN, 4, 8, 0x10200014, 0, 0 }, | ||
85 | |||
86 | /* PSCs */ | ||
87 | { AU1550_DSCR_CMD0_PSC0_TX, DEV_FLAGS_OUT, 0, 0, 0x11a0001c, 0, 0 }, | ||
88 | { AU1550_DSCR_CMD0_PSC0_RX, DEV_FLAGS_IN, 0, 0, 0x11a0001c, 0, 0 }, | ||
89 | { AU1550_DSCR_CMD0_PSC1_TX, DEV_FLAGS_OUT, 0, 0, 0x11b0001c, 0, 0 }, | ||
90 | { AU1550_DSCR_CMD0_PSC1_RX, DEV_FLAGS_IN, 0, 0, 0x11b0001c, 0, 0 }, | ||
91 | { AU1550_DSCR_CMD0_PSC2_TX, DEV_FLAGS_OUT, 0, 0, 0x10a0001c, 0, 0 }, | ||
92 | { AU1550_DSCR_CMD0_PSC2_RX, DEV_FLAGS_IN, 0, 0, 0x10a0001c, 0, 0 }, | ||
93 | { AU1550_DSCR_CMD0_PSC3_TX, DEV_FLAGS_OUT, 0, 0, 0x10b0001c, 0, 0 }, | ||
94 | { AU1550_DSCR_CMD0_PSC3_RX, DEV_FLAGS_IN, 0, 0, 0x10b0001c, 0, 0 }, | ||
95 | |||
96 | { AU1550_DSCR_CMD0_PCI_WRITE, 0, 0, 0, 0x00000000, 0, 0 }, /* PCI */ | ||
97 | { AU1550_DSCR_CMD0_NAND_FLASH, 0, 0, 0, 0x00000000, 0, 0 }, /* NAND */ | ||
98 | |||
99 | /* MAC 0 */ | ||
100 | { AU1550_DSCR_CMD0_MAC0_RX, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 }, | ||
101 | { AU1550_DSCR_CMD0_MAC0_TX, DEV_FLAGS_OUT, 0, 0, 0x00000000, 0, 0 }, | ||
102 | |||
103 | /* MAC 1 */ | ||
104 | { AU1550_DSCR_CMD0_MAC1_RX, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 }, | ||
105 | { AU1550_DSCR_CMD0_MAC1_TX, DEV_FLAGS_OUT, 0, 0, 0x00000000, 0, 0 }, | ||
106 | |||
107 | { DSCR_CMD0_THROTTLE, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, | ||
108 | { DSCR_CMD0_ALWAYS, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, | ||
109 | }; | ||
110 | |||
111 | static dbdev_tab_t au1200_dbdev_tab[] __initdata = { | ||
112 | { AU1200_DSCR_CMD0_UART0_TX, DEV_FLAGS_OUT, 0, 8, 0x11100004, 0, 0 }, | ||
113 | { AU1200_DSCR_CMD0_UART0_RX, DEV_FLAGS_IN, 0, 8, 0x11100000, 0, 0 }, | ||
114 | { AU1200_DSCR_CMD0_UART1_TX, DEV_FLAGS_OUT, 0, 8, 0x11200004, 0, 0 }, | ||
115 | { AU1200_DSCR_CMD0_UART1_RX, DEV_FLAGS_IN, 0, 8, 0x11200000, 0, 0 }, | ||
116 | |||
117 | { AU1200_DSCR_CMD0_DMA_REQ0, 0, 0, 0, 0x00000000, 0, 0 }, | ||
118 | { AU1200_DSCR_CMD0_DMA_REQ1, 0, 0, 0, 0x00000000, 0, 0 }, | ||
119 | |||
120 | { AU1200_DSCR_CMD0_MAE_BE, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, | ||
121 | { AU1200_DSCR_CMD0_MAE_FE, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, | ||
122 | { AU1200_DSCR_CMD0_MAE_BOTH, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, | ||
123 | { AU1200_DSCR_CMD0_LCD, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, | ||
124 | |||
125 | { AU1200_DSCR_CMD0_SDMS_TX0, DEV_FLAGS_OUT, 4, 8, 0x10600000, 0, 0 }, | ||
126 | { AU1200_DSCR_CMD0_SDMS_RX0, DEV_FLAGS_IN, 4, 8, 0x10600004, 0, 0 }, | ||
127 | { AU1200_DSCR_CMD0_SDMS_TX1, DEV_FLAGS_OUT, 4, 8, 0x10680000, 0, 0 }, | ||
128 | { AU1200_DSCR_CMD0_SDMS_RX1, DEV_FLAGS_IN, 4, 8, 0x10680004, 0, 0 }, | ||
129 | |||
130 | { AU1200_DSCR_CMD0_AES_RX, DEV_FLAGS_IN , 4, 32, 0x10300008, 0, 0 }, | ||
131 | { AU1200_DSCR_CMD0_AES_TX, DEV_FLAGS_OUT, 4, 32, 0x10300004, 0, 0 }, | ||
132 | |||
133 | { AU1200_DSCR_CMD0_PSC0_TX, DEV_FLAGS_OUT, 0, 16, 0x11a0001c, 0, 0 }, | ||
134 | { AU1200_DSCR_CMD0_PSC0_RX, DEV_FLAGS_IN, 0, 16, 0x11a0001c, 0, 0 }, | ||
135 | { AU1200_DSCR_CMD0_PSC0_SYNC, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, | ||
136 | { AU1200_DSCR_CMD0_PSC1_TX, DEV_FLAGS_OUT, 0, 16, 0x11b0001c, 0, 0 }, | ||
137 | { AU1200_DSCR_CMD0_PSC1_RX, DEV_FLAGS_IN, 0, 16, 0x11b0001c, 0, 0 }, | ||
138 | { AU1200_DSCR_CMD0_PSC1_SYNC, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, | ||
139 | |||
140 | { AU1200_DSCR_CMD0_CIM_RXA, DEV_FLAGS_IN, 0, 32, 0x14004020, 0, 0 }, | ||
141 | { AU1200_DSCR_CMD0_CIM_RXB, DEV_FLAGS_IN, 0, 32, 0x14004040, 0, 0 }, | ||
142 | { AU1200_DSCR_CMD0_CIM_RXC, DEV_FLAGS_IN, 0, 32, 0x14004060, 0, 0 }, | ||
143 | { AU1200_DSCR_CMD0_CIM_SYNC, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, | ||
144 | |||
145 | { AU1200_DSCR_CMD0_NAND_FLASH, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 }, | ||
146 | |||
147 | { DSCR_CMD0_THROTTLE, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, | ||
148 | { DSCR_CMD0_ALWAYS, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, | ||
149 | }; | ||
150 | |||
151 | static dbdev_tab_t au1300_dbdev_tab[] __initdata = { | ||
152 | { AU1300_DSCR_CMD0_UART0_TX, DEV_FLAGS_OUT, 0, 8, 0x10100004, 0, 0 }, | ||
153 | { AU1300_DSCR_CMD0_UART0_RX, DEV_FLAGS_IN, 0, 8, 0x10100000, 0, 0 }, | ||
154 | { AU1300_DSCR_CMD0_UART1_TX, DEV_FLAGS_OUT, 0, 8, 0x10101004, 0, 0 }, | ||
155 | { AU1300_DSCR_CMD0_UART1_RX, DEV_FLAGS_IN, 0, 8, 0x10101000, 0, 0 }, | ||
156 | { AU1300_DSCR_CMD0_UART2_TX, DEV_FLAGS_OUT, 0, 8, 0x10102004, 0, 0 }, | ||
157 | { AU1300_DSCR_CMD0_UART2_RX, DEV_FLAGS_IN, 0, 8, 0x10102000, 0, 0 }, | ||
158 | { AU1300_DSCR_CMD0_UART3_TX, DEV_FLAGS_OUT, 0, 8, 0x10103004, 0, 0 }, | ||
159 | { AU1300_DSCR_CMD0_UART3_RX, DEV_FLAGS_IN, 0, 8, 0x10103000, 0, 0 }, | ||
160 | |||
161 | { AU1300_DSCR_CMD0_SDMS_TX0, DEV_FLAGS_OUT, 4, 8, 0x10600000, 0, 0 }, | ||
162 | { AU1300_DSCR_CMD0_SDMS_RX0, DEV_FLAGS_IN, 4, 8, 0x10600004, 0, 0 }, | ||
163 | { AU1300_DSCR_CMD0_SDMS_TX1, DEV_FLAGS_OUT, 8, 8, 0x10601000, 0, 0 }, | ||
164 | { AU1300_DSCR_CMD0_SDMS_RX1, DEV_FLAGS_IN, 8, 8, 0x10601004, 0, 0 }, | ||
165 | |||
166 | { AU1300_DSCR_CMD0_AES_RX, DEV_FLAGS_IN , 4, 32, 0x10300008, 0, 0 }, | ||
167 | { AU1300_DSCR_CMD0_AES_TX, DEV_FLAGS_OUT, 4, 32, 0x10300004, 0, 0 }, | ||
168 | |||
169 | { AU1300_DSCR_CMD0_PSC0_TX, DEV_FLAGS_OUT, 0, 16, 0x10a0001c, 0, 0 }, | ||
170 | { AU1300_DSCR_CMD0_PSC0_RX, DEV_FLAGS_IN, 0, 16, 0x10a0001c, 0, 0 }, | ||
171 | { AU1300_DSCR_CMD0_PSC1_TX, DEV_FLAGS_OUT, 0, 16, 0x10a0101c, 0, 0 }, | ||
172 | { AU1300_DSCR_CMD0_PSC1_RX, DEV_FLAGS_IN, 0, 16, 0x10a0101c, 0, 0 }, | ||
173 | { AU1300_DSCR_CMD0_PSC2_TX, DEV_FLAGS_OUT, 0, 16, 0x10a0201c, 0, 0 }, | ||
174 | { AU1300_DSCR_CMD0_PSC2_RX, DEV_FLAGS_IN, 0, 16, 0x10a0201c, 0, 0 }, | ||
175 | { AU1300_DSCR_CMD0_PSC3_TX, DEV_FLAGS_OUT, 0, 16, 0x10a0301c, 0, 0 }, | ||
176 | { AU1300_DSCR_CMD0_PSC3_RX, DEV_FLAGS_IN, 0, 16, 0x10a0301c, 0, 0 }, | ||
177 | |||
178 | { AU1300_DSCR_CMD0_LCD, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, | ||
179 | { AU1300_DSCR_CMD0_NAND_FLASH, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 }, | ||
180 | |||
181 | { AU1300_DSCR_CMD0_SDMS_TX2, DEV_FLAGS_OUT, 4, 8, 0x10602000, 0, 0 }, | ||
182 | { AU1300_DSCR_CMD0_SDMS_RX2, DEV_FLAGS_IN, 4, 8, 0x10602004, 0, 0 }, | ||
183 | |||
184 | { AU1300_DSCR_CMD0_CIM_SYNC, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, | ||
185 | |||
186 | { AU1300_DSCR_CMD0_UDMA, DEV_FLAGS_ANYUSE, 0, 32, 0x14001810, 0, 0 }, | ||
187 | |||
188 | { AU1300_DSCR_CMD0_DMA_REQ0, 0, 0, 0, 0x00000000, 0, 0 }, | ||
189 | { AU1300_DSCR_CMD0_DMA_REQ1, 0, 0, 0, 0x00000000, 0, 0 }, | ||
190 | |||
191 | { DSCR_CMD0_THROTTLE, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, | ||
192 | { DSCR_CMD0_ALWAYS, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 }, | ||
193 | }; | ||
194 | |||
195 | /* 32 predefined plus 32 custom */ | ||
196 | #define DBDEV_TAB_SIZE 64 | ||
197 | |||
198 | static chan_tab_t *chan_tab_ptr[NUM_DBDMA_CHANS]; | ||
199 | |||
200 | static dbdev_tab_t *find_dbdev_id(u32 id) | ||
201 | { | ||
202 | int i; | ||
203 | dbdev_tab_t *p; | ||
204 | for (i = 0; i < DBDEV_TAB_SIZE; ++i) { | ||
205 | p = &dbdev_tab[i]; | ||
206 | if (p->dev_id == id) | ||
207 | return p; | ||
208 | } | ||
209 | return NULL; | ||
210 | } | ||
211 | |||
212 | void *au1xxx_ddma_get_nextptr_virt(au1x_ddma_desc_t *dp) | ||
213 | { | ||
214 | return phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr)); | ||
215 | } | ||
216 | EXPORT_SYMBOL(au1xxx_ddma_get_nextptr_virt); | ||
217 | |||
218 | u32 au1xxx_ddma_add_device(dbdev_tab_t *dev) | ||
219 | { | ||
220 | u32 ret = 0; | ||
221 | dbdev_tab_t *p; | ||
222 | static u16 new_id = 0x1000; | ||
223 | |||
224 | p = find_dbdev_id(~0); | ||
225 | if (NULL != p) { | ||
226 | memcpy(p, dev, sizeof(dbdev_tab_t)); | ||
227 | p->dev_id = DSCR_DEV2CUSTOM_ID(new_id, dev->dev_id); | ||
228 | ret = p->dev_id; | ||
229 | new_id++; | ||
230 | #if 0 | ||
231 | printk(KERN_DEBUG "add_device: id:%x flags:%x padd:%x\n", | ||
232 | p->dev_id, p->dev_flags, p->dev_physaddr); | ||
233 | #endif | ||
234 | } | ||
235 | |||
236 | return ret; | ||
237 | } | ||
238 | EXPORT_SYMBOL(au1xxx_ddma_add_device); | ||
239 | |||
240 | void au1xxx_ddma_del_device(u32 devid) | ||
241 | { | ||
242 | dbdev_tab_t *p = find_dbdev_id(devid); | ||
243 | |||
244 | if (p != NULL) { | ||
245 | memset(p, 0, sizeof(dbdev_tab_t)); | ||
246 | p->dev_id = ~0; | ||
247 | } | ||
248 | } | ||
249 | EXPORT_SYMBOL(au1xxx_ddma_del_device); | ||
250 | |||
251 | /* Allocate a channel and return a non-zero descriptor if successful. */ | ||
252 | u32 au1xxx_dbdma_chan_alloc(u32 srcid, u32 destid, | ||
253 | void (*callback)(int, void *), void *callparam) | ||
254 | { | ||
255 | unsigned long flags; | ||
256 | u32 used, chan; | ||
257 | u32 dcp; | ||
258 | int i; | ||
259 | dbdev_tab_t *stp, *dtp; | ||
260 | chan_tab_t *ctp; | ||
261 | au1x_dma_chan_t *cp; | ||
262 | |||
263 | /* | ||
264 | * We do the initialization on the first channel allocation. | ||
265 | * We have to wait because of the interrupt handler initialization | ||
266 | * which can't be done successfully during board set up. | ||
267 | */ | ||
268 | if (!dbdma_initialized) | ||
269 | return 0; | ||
270 | |||
271 | stp = find_dbdev_id(srcid); | ||
272 | if (stp == NULL) | ||
273 | return 0; | ||
274 | dtp = find_dbdev_id(destid); | ||
275 | if (dtp == NULL) | ||
276 | return 0; | ||
277 | |||
278 | used = 0; | ||
279 | |||
280 | /* Check to see if we can get both channels. */ | ||
281 | spin_lock_irqsave(&au1xxx_dbdma_spin_lock, flags); | ||
282 | if (!(stp->dev_flags & DEV_FLAGS_INUSE) || | ||
283 | (stp->dev_flags & DEV_FLAGS_ANYUSE)) { | ||
284 | /* Got source */ | ||
285 | stp->dev_flags |= DEV_FLAGS_INUSE; | ||
286 | if (!(dtp->dev_flags & DEV_FLAGS_INUSE) || | ||
287 | (dtp->dev_flags & DEV_FLAGS_ANYUSE)) { | ||
288 | /* Got destination */ | ||
289 | dtp->dev_flags |= DEV_FLAGS_INUSE; | ||
290 | } else { | ||
291 | /* Can't get dest. Release src. */ | ||
292 | stp->dev_flags &= ~DEV_FLAGS_INUSE; | ||
293 | used++; | ||
294 | } | ||
295 | } else | ||
296 | used++; | ||
297 | spin_unlock_irqrestore(&au1xxx_dbdma_spin_lock, flags); | ||
298 | |||
299 | if (used) | ||
300 | return 0; | ||
301 | |||
302 | /* Let's see if we can allocate a channel for it. */ | ||
303 | ctp = NULL; | ||
304 | chan = 0; | ||
305 | spin_lock_irqsave(&au1xxx_dbdma_spin_lock, flags); | ||
306 | for (i = 0; i < NUM_DBDMA_CHANS; i++) | ||
307 | if (chan_tab_ptr[i] == NULL) { | ||
308 | /* | ||
309 | * If kmalloc fails, it is caught below same | ||
310 | * as a channel not available. | ||
311 | */ | ||
312 | ctp = kmalloc(sizeof(chan_tab_t), GFP_ATOMIC); | ||
313 | chan_tab_ptr[i] = ctp; | ||
314 | break; | ||
315 | } | ||
316 | spin_unlock_irqrestore(&au1xxx_dbdma_spin_lock, flags); | ||
317 | |||
318 | if (ctp != NULL) { | ||
319 | memset(ctp, 0, sizeof(chan_tab_t)); | ||
320 | ctp->chan_index = chan = i; | ||
321 | dcp = KSEG1ADDR(AU1550_DBDMA_PHYS_ADDR); | ||
322 | dcp += (0x0100 * chan); | ||
323 | ctp->chan_ptr = (au1x_dma_chan_t *)dcp; | ||
324 | cp = (au1x_dma_chan_t *)dcp; | ||
325 | ctp->chan_src = stp; | ||
326 | ctp->chan_dest = dtp; | ||
327 | ctp->chan_callback = callback; | ||
328 | ctp->chan_callparam = callparam; | ||
329 | |||
330 | /* Initialize channel configuration. */ | ||
331 | i = 0; | ||
332 | if (stp->dev_intlevel) | ||
333 | i |= DDMA_CFG_SED; | ||
334 | if (stp->dev_intpolarity) | ||
335 | i |= DDMA_CFG_SP; | ||
336 | if (dtp->dev_intlevel) | ||
337 | i |= DDMA_CFG_DED; | ||
338 | if (dtp->dev_intpolarity) | ||
339 | i |= DDMA_CFG_DP; | ||
340 | if ((stp->dev_flags & DEV_FLAGS_SYNC) || | ||
341 | (dtp->dev_flags & DEV_FLAGS_SYNC)) | ||
342 | i |= DDMA_CFG_SYNC; | ||
343 | cp->ddma_cfg = i; | ||
344 | wmb(); /* drain writebuffer */ | ||
345 | |||
346 | /* | ||
347 | * Return a non-zero value that can be used to find the channel | ||
348 | * information in subsequent operations. | ||
349 | */ | ||
350 | return (u32)(&chan_tab_ptr[chan]); | ||
351 | } | ||
352 | |||
353 | /* Release devices */ | ||
354 | stp->dev_flags &= ~DEV_FLAGS_INUSE; | ||
355 | dtp->dev_flags &= ~DEV_FLAGS_INUSE; | ||
356 | |||
357 | return 0; | ||
358 | } | ||
359 | EXPORT_SYMBOL(au1xxx_dbdma_chan_alloc); | ||
360 | |||
361 | /* | ||
362 | * Set the device width if source or destination is a FIFO. | ||
363 | * Should be 8, 16, or 32 bits. | ||
364 | */ | ||
365 | u32 au1xxx_dbdma_set_devwidth(u32 chanid, int bits) | ||
366 | { | ||
367 | u32 rv; | ||
368 | chan_tab_t *ctp; | ||
369 | dbdev_tab_t *stp, *dtp; | ||
370 | |||
371 | ctp = *((chan_tab_t **)chanid); | ||
372 | stp = ctp->chan_src; | ||
373 | dtp = ctp->chan_dest; | ||
374 | rv = 0; | ||
375 | |||
376 | if (stp->dev_flags & DEV_FLAGS_IN) { /* Source in fifo */ | ||
377 | rv = stp->dev_devwidth; | ||
378 | stp->dev_devwidth = bits; | ||
379 | } | ||
380 | if (dtp->dev_flags & DEV_FLAGS_OUT) { /* Destination out fifo */ | ||
381 | rv = dtp->dev_devwidth; | ||
382 | dtp->dev_devwidth = bits; | ||
383 | } | ||
384 | |||
385 | return rv; | ||
386 | } | ||
387 | EXPORT_SYMBOL(au1xxx_dbdma_set_devwidth); | ||
388 | |||
389 | /* Allocate a descriptor ring, initializing as much as possible. */ | ||
390 | u32 au1xxx_dbdma_ring_alloc(u32 chanid, int entries) | ||
391 | { | ||
392 | int i; | ||
393 | u32 desc_base, srcid, destid; | ||
394 | u32 cmd0, cmd1, src1, dest1; | ||
395 | u32 src0, dest0; | ||
396 | chan_tab_t *ctp; | ||
397 | dbdev_tab_t *stp, *dtp; | ||
398 | au1x_ddma_desc_t *dp; | ||
399 | |||
400 | /* | ||
401 | * I guess we could check this to be within the | ||
402 | * range of the table...... | ||
403 | */ | ||
404 | ctp = *((chan_tab_t **)chanid); | ||
405 | stp = ctp->chan_src; | ||
406 | dtp = ctp->chan_dest; | ||
407 | |||
408 | /* | ||
409 | * The descriptors must be 32-byte aligned. There is a | ||
410 | * possibility the allocation will give us such an address, | ||
411 | * and if we try that first we are likely to not waste larger | ||
412 | * slabs of memory. | ||
413 | */ | ||
414 | desc_base = (u32)kmalloc_array(entries, sizeof(au1x_ddma_desc_t), | ||
415 | GFP_KERNEL|GFP_DMA); | ||
416 | if (desc_base == 0) | ||
417 | return 0; | ||
418 | |||
419 | if (desc_base & 0x1f) { | ||
420 | /* | ||
421 | * Lost....do it again, allocate extra, and round | ||
422 | * the address base. | ||
423 | */ | ||
424 | kfree((const void *)desc_base); | ||
425 | i = entries * sizeof(au1x_ddma_desc_t); | ||
426 | i += (sizeof(au1x_ddma_desc_t) - 1); | ||
427 | desc_base = (u32)kmalloc(i, GFP_KERNEL|GFP_DMA); | ||
428 | if (desc_base == 0) | ||
429 | return 0; | ||
430 | |||
431 | ctp->cdb_membase = desc_base; | ||
432 | desc_base = ALIGN_ADDR(desc_base, sizeof(au1x_ddma_desc_t)); | ||
433 | } else | ||
434 | ctp->cdb_membase = desc_base; | ||
435 | |||
436 | dp = (au1x_ddma_desc_t *)desc_base; | ||
437 | |||
438 | /* Keep track of the base descriptor. */ | ||
439 | ctp->chan_desc_base = dp; | ||
440 | |||
441 | /* Initialize the rings with as much information as we know. */ | ||
442 | srcid = stp->dev_id; | ||
443 | destid = dtp->dev_id; | ||
444 | |||
445 | cmd0 = cmd1 = src1 = dest1 = 0; | ||
446 | src0 = dest0 = 0; | ||
447 | |||
448 | cmd0 |= DSCR_CMD0_SID(srcid); | ||
449 | cmd0 |= DSCR_CMD0_DID(destid); | ||
450 | cmd0 |= DSCR_CMD0_IE | DSCR_CMD0_CV; | ||
451 | cmd0 |= DSCR_CMD0_ST(DSCR_CMD0_ST_NOCHANGE); | ||
452 | |||
453 | /* Is it mem to mem transfer? */ | ||
454 | if (((DSCR_CUSTOM2DEV_ID(srcid) == DSCR_CMD0_THROTTLE) || | ||
455 | (DSCR_CUSTOM2DEV_ID(srcid) == DSCR_CMD0_ALWAYS)) && | ||
456 | ((DSCR_CUSTOM2DEV_ID(destid) == DSCR_CMD0_THROTTLE) || | ||
457 | (DSCR_CUSTOM2DEV_ID(destid) == DSCR_CMD0_ALWAYS))) | ||
458 | cmd0 |= DSCR_CMD0_MEM; | ||
459 | |||
460 | switch (stp->dev_devwidth) { | ||
461 | case 8: | ||
462 | cmd0 |= DSCR_CMD0_SW(DSCR_CMD0_BYTE); | ||
463 | break; | ||
464 | case 16: | ||
465 | cmd0 |= DSCR_CMD0_SW(DSCR_CMD0_HALFWORD); | ||
466 | break; | ||
467 | case 32: | ||
468 | default: | ||
469 | cmd0 |= DSCR_CMD0_SW(DSCR_CMD0_WORD); | ||
470 | break; | ||
471 | } | ||
472 | |||
473 | switch (dtp->dev_devwidth) { | ||
474 | case 8: | ||
475 | cmd0 |= DSCR_CMD0_DW(DSCR_CMD0_BYTE); | ||
476 | break; | ||
477 | case 16: | ||
478 | cmd0 |= DSCR_CMD0_DW(DSCR_CMD0_HALFWORD); | ||
479 | break; | ||
480 | case 32: | ||
481 | default: | ||
482 | cmd0 |= DSCR_CMD0_DW(DSCR_CMD0_WORD); | ||
483 | break; | ||
484 | } | ||
485 | |||
486 | /* | ||
487 | * If the device is marked as an in/out FIFO, ensure it is | ||
488 | * set non-coherent. | ||
489 | */ | ||
490 | if (stp->dev_flags & DEV_FLAGS_IN) | ||
491 | cmd0 |= DSCR_CMD0_SN; /* Source in FIFO */ | ||
492 | if (dtp->dev_flags & DEV_FLAGS_OUT) | ||
493 | cmd0 |= DSCR_CMD0_DN; /* Destination out FIFO */ | ||
494 | |||
495 | /* | ||
496 | * Set up source1. For now, assume no stride and increment. | ||
497 | * A channel attribute update can change this later. | ||
498 | */ | ||
499 | switch (stp->dev_tsize) { | ||
500 | case 1: | ||
501 | src1 |= DSCR_SRC1_STS(DSCR_xTS_SIZE1); | ||
502 | break; | ||
503 | case 2: | ||
504 | src1 |= DSCR_SRC1_STS(DSCR_xTS_SIZE2); | ||
505 | break; | ||
506 | case 4: | ||
507 | src1 |= DSCR_SRC1_STS(DSCR_xTS_SIZE4); | ||
508 | break; | ||
509 | case 8: | ||
510 | default: | ||
511 | src1 |= DSCR_SRC1_STS(DSCR_xTS_SIZE8); | ||
512 | break; | ||
513 | } | ||
514 | |||
515 | /* If source input is FIFO, set static address. */ | ||
516 | if (stp->dev_flags & DEV_FLAGS_IN) { | ||
517 | if (stp->dev_flags & DEV_FLAGS_BURSTABLE) | ||
518 | src1 |= DSCR_SRC1_SAM(DSCR_xAM_BURST); | ||
519 | else | ||
520 | src1 |= DSCR_SRC1_SAM(DSCR_xAM_STATIC); | ||
521 | } | ||
522 | |||
523 | if (stp->dev_physaddr) | ||
524 | src0 = stp->dev_physaddr; | ||
525 | |||
526 | /* | ||
527 | * Set up dest1. For now, assume no stride and increment. | ||
528 | * A channel attribute update can change this later. | ||
529 | */ | ||
530 | switch (dtp->dev_tsize) { | ||
531 | case 1: | ||
532 | dest1 |= DSCR_DEST1_DTS(DSCR_xTS_SIZE1); | ||
533 | break; | ||
534 | case 2: | ||
535 | dest1 |= DSCR_DEST1_DTS(DSCR_xTS_SIZE2); | ||
536 | break; | ||
537 | case 4: | ||
538 | dest1 |= DSCR_DEST1_DTS(DSCR_xTS_SIZE4); | ||
539 | break; | ||
540 | case 8: | ||
541 | default: | ||
542 | dest1 |= DSCR_DEST1_DTS(DSCR_xTS_SIZE8); | ||
543 | break; | ||
544 | } | ||
545 | |||
546 | /* If destination output is FIFO, set static address. */ | ||
547 | if (dtp->dev_flags & DEV_FLAGS_OUT) { | ||
548 | if (dtp->dev_flags & DEV_FLAGS_BURSTABLE) | ||
549 | dest1 |= DSCR_DEST1_DAM(DSCR_xAM_BURST); | ||
550 | else | ||
551 | dest1 |= DSCR_DEST1_DAM(DSCR_xAM_STATIC); | ||
552 | } | ||
553 | |||
554 | if (dtp->dev_physaddr) | ||
555 | dest0 = dtp->dev_physaddr; | ||
556 | |||
557 | #if 0 | ||
558 | printk(KERN_DEBUG "did:%x sid:%x cmd0:%x cmd1:%x source0:%x " | ||
559 | "source1:%x dest0:%x dest1:%x\n", | ||
560 | dtp->dev_id, stp->dev_id, cmd0, cmd1, src0, | ||
561 | src1, dest0, dest1); | ||
562 | #endif | ||
563 | for (i = 0; i < entries; i++) { | ||
564 | dp->dscr_cmd0 = cmd0; | ||
565 | dp->dscr_cmd1 = cmd1; | ||
566 | dp->dscr_source0 = src0; | ||
567 | dp->dscr_source1 = src1; | ||
568 | dp->dscr_dest0 = dest0; | ||
569 | dp->dscr_dest1 = dest1; | ||
570 | dp->dscr_stat = 0; | ||
571 | dp->sw_context = 0; | ||
572 | dp->sw_status = 0; | ||
573 | dp->dscr_nxtptr = DSCR_NXTPTR(virt_to_phys(dp + 1)); | ||
574 | dp++; | ||
575 | } | ||
576 | |||
577 | /* Make last descrptor point to the first. */ | ||
578 | dp--; | ||
579 | dp->dscr_nxtptr = DSCR_NXTPTR(virt_to_phys(ctp->chan_desc_base)); | ||
580 | ctp->get_ptr = ctp->put_ptr = ctp->cur_ptr = ctp->chan_desc_base; | ||
581 | |||
582 | return (u32)ctp->chan_desc_base; | ||
583 | } | ||
584 | EXPORT_SYMBOL(au1xxx_dbdma_ring_alloc); | ||
585 | |||
586 | /* | ||
587 | * Put a source buffer into the DMA ring. | ||
588 | * This updates the source pointer and byte count. Normally used | ||
589 | * for memory to fifo transfers. | ||
590 | */ | ||
591 | u32 au1xxx_dbdma_put_source(u32 chanid, dma_addr_t buf, int nbytes, u32 flags) | ||
592 | { | ||
593 | chan_tab_t *ctp; | ||
594 | au1x_ddma_desc_t *dp; | ||
595 | |||
596 | /* | ||
597 | * I guess we could check this to be within the | ||
598 | * range of the table...... | ||
599 | */ | ||
600 | ctp = *(chan_tab_t **)chanid; | ||
601 | |||
602 | /* | ||
603 | * We should have multiple callers for a particular channel, | ||
604 | * an interrupt doesn't affect this pointer nor the descriptor, | ||
605 | * so no locking should be needed. | ||
606 | */ | ||
607 | dp = ctp->put_ptr; | ||
608 | |||
609 | /* | ||
610 | * If the descriptor is valid, we are way ahead of the DMA | ||
611 | * engine, so just return an error condition. | ||
612 | */ | ||
613 | if (dp->dscr_cmd0 & DSCR_CMD0_V) | ||
614 | return 0; | ||
615 | |||
616 | /* Load up buffer address and byte count. */ | ||
617 | dp->dscr_source0 = buf & ~0UL; | ||
618 | dp->dscr_cmd1 = nbytes; | ||
619 | /* Check flags */ | ||
620 | if (flags & DDMA_FLAGS_IE) | ||
621 | dp->dscr_cmd0 |= DSCR_CMD0_IE; | ||
622 | if (flags & DDMA_FLAGS_NOIE) | ||
623 | dp->dscr_cmd0 &= ~DSCR_CMD0_IE; | ||
624 | |||
625 | /* | ||
626 | * There is an errata on the Au1200/Au1550 parts that could result | ||
627 | * in "stale" data being DMA'ed. It has to do with the snoop logic on | ||
628 | * the cache eviction buffer. DMA_NONCOHERENT is on by default for | ||
629 | * these parts. If it is fixed in the future, these dma_cache_inv will | ||
630 | * just be nothing more than empty macros. See io.h. | ||
631 | */ | ||
632 | dma_cache_wback_inv((unsigned long)buf, nbytes); | ||
633 | dp->dscr_cmd0 |= DSCR_CMD0_V; /* Let it rip */ | ||
634 | wmb(); /* drain writebuffer */ | ||
635 | dma_cache_wback_inv((unsigned long)dp, sizeof(*dp)); | ||
636 | ctp->chan_ptr->ddma_dbell = 0; | ||
637 | |||
638 | /* Get next descriptor pointer. */ | ||
639 | ctp->put_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr)); | ||
640 | |||
641 | /* Return something non-zero. */ | ||
642 | return nbytes; | ||
643 | } | ||
644 | EXPORT_SYMBOL(au1xxx_dbdma_put_source); | ||
645 | |||
646 | /* Put a destination buffer into the DMA ring. | ||
647 | * This updates the destination pointer and byte count. Normally used | ||
648 | * to place an empty buffer into the ring for fifo to memory transfers. | ||
649 | */ | ||
650 | u32 au1xxx_dbdma_put_dest(u32 chanid, dma_addr_t buf, int nbytes, u32 flags) | ||
651 | { | ||
652 | chan_tab_t *ctp; | ||
653 | au1x_ddma_desc_t *dp; | ||
654 | |||
655 | /* I guess we could check this to be within the | ||
656 | * range of the table...... | ||
657 | */ | ||
658 | ctp = *((chan_tab_t **)chanid); | ||
659 | |||
660 | /* We should have multiple callers for a particular channel, | ||
661 | * an interrupt doesn't affect this pointer nor the descriptor, | ||
662 | * so no locking should be needed. | ||
663 | */ | ||
664 | dp = ctp->put_ptr; | ||
665 | |||
666 | /* If the descriptor is valid, we are way ahead of the DMA | ||
667 | * engine, so just return an error condition. | ||
668 | */ | ||
669 | if (dp->dscr_cmd0 & DSCR_CMD0_V) | ||
670 | return 0; | ||
671 | |||
672 | /* Load up buffer address and byte count */ | ||
673 | |||
674 | /* Check flags */ | ||
675 | if (flags & DDMA_FLAGS_IE) | ||
676 | dp->dscr_cmd0 |= DSCR_CMD0_IE; | ||
677 | if (flags & DDMA_FLAGS_NOIE) | ||
678 | dp->dscr_cmd0 &= ~DSCR_CMD0_IE; | ||
679 | |||
680 | dp->dscr_dest0 = buf & ~0UL; | ||
681 | dp->dscr_cmd1 = nbytes; | ||
682 | #if 0 | ||
683 | printk(KERN_DEBUG "cmd0:%x cmd1:%x source0:%x source1:%x dest0:%x dest1:%x\n", | ||
684 | dp->dscr_cmd0, dp->dscr_cmd1, dp->dscr_source0, | ||
685 | dp->dscr_source1, dp->dscr_dest0, dp->dscr_dest1); | ||
686 | #endif | ||
687 | /* | ||
688 | * There is an errata on the Au1200/Au1550 parts that could result in | ||
689 | * "stale" data being DMA'ed. It has to do with the snoop logic on the | ||
690 | * cache eviction buffer. DMA_NONCOHERENT is on by default for these | ||
691 | * parts. If it is fixed in the future, these dma_cache_inv will just | ||
692 | * be nothing more than empty macros. See io.h. | ||
693 | */ | ||
694 | dma_cache_inv((unsigned long)buf, nbytes); | ||
695 | dp->dscr_cmd0 |= DSCR_CMD0_V; /* Let it rip */ | ||
696 | wmb(); /* drain writebuffer */ | ||
697 | dma_cache_wback_inv((unsigned long)dp, sizeof(*dp)); | ||
698 | ctp->chan_ptr->ddma_dbell = 0; | ||
699 | |||
700 | /* Get next descriptor pointer. */ | ||
701 | ctp->put_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr)); | ||
702 | |||
703 | /* Return something non-zero. */ | ||
704 | return nbytes; | ||
705 | } | ||
706 | EXPORT_SYMBOL(au1xxx_dbdma_put_dest); | ||
707 | |||
708 | /* | ||
709 | * Get a destination buffer into the DMA ring. | ||
710 | * Normally used to get a full buffer from the ring during fifo | ||
711 | * to memory transfers. This does not set the valid bit, you will | ||
712 | * have to put another destination buffer to keep the DMA going. | ||
713 | */ | ||
714 | u32 au1xxx_dbdma_get_dest(u32 chanid, void **buf, int *nbytes) | ||
715 | { | ||
716 | chan_tab_t *ctp; | ||
717 | au1x_ddma_desc_t *dp; | ||
718 | u32 rv; | ||
719 | |||
720 | /* | ||
721 | * I guess we could check this to be within the | ||
722 | * range of the table...... | ||
723 | */ | ||
724 | ctp = *((chan_tab_t **)chanid); | ||
725 | |||
726 | /* | ||
727 | * We should have multiple callers for a particular channel, | ||
728 | * an interrupt doesn't affect this pointer nor the descriptor, | ||
729 | * so no locking should be needed. | ||
730 | */ | ||
731 | dp = ctp->get_ptr; | ||
732 | |||
733 | /* | ||
734 | * If the descriptor is valid, we are way ahead of the DMA | ||
735 | * engine, so just return an error condition. | ||
736 | */ | ||
737 | if (dp->dscr_cmd0 & DSCR_CMD0_V) | ||
738 | return 0; | ||
739 | |||
740 | /* Return buffer address and byte count. */ | ||
741 | *buf = (void *)(phys_to_virt(dp->dscr_dest0)); | ||
742 | *nbytes = dp->dscr_cmd1; | ||
743 | rv = dp->dscr_stat; | ||
744 | |||
745 | /* Get next descriptor pointer. */ | ||
746 | ctp->get_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr)); | ||
747 | |||
748 | /* Return something non-zero. */ | ||
749 | return rv; | ||
750 | } | ||
751 | EXPORT_SYMBOL_GPL(au1xxx_dbdma_get_dest); | ||
752 | |||
753 | void au1xxx_dbdma_stop(u32 chanid) | ||
754 | { | ||
755 | chan_tab_t *ctp; | ||
756 | au1x_dma_chan_t *cp; | ||
757 | int halt_timeout = 0; | ||
758 | |||
759 | ctp = *((chan_tab_t **)chanid); | ||
760 | |||
761 | cp = ctp->chan_ptr; | ||
762 | cp->ddma_cfg &= ~DDMA_CFG_EN; /* Disable channel */ | ||
763 | wmb(); /* drain writebuffer */ | ||
764 | while (!(cp->ddma_stat & DDMA_STAT_H)) { | ||
765 | udelay(1); | ||
766 | halt_timeout++; | ||
767 | if (halt_timeout > 100) { | ||
768 | printk(KERN_WARNING "warning: DMA channel won't halt\n"); | ||
769 | break; | ||
770 | } | ||
771 | } | ||
772 | /* clear current desc valid and doorbell */ | ||
773 | cp->ddma_stat |= (DDMA_STAT_DB | DDMA_STAT_V); | ||
774 | wmb(); /* drain writebuffer */ | ||
775 | } | ||
776 | EXPORT_SYMBOL(au1xxx_dbdma_stop); | ||
777 | |||
778 | /* | ||
779 | * Start using the current descriptor pointer. If the DBDMA encounters | ||
780 | * a non-valid descriptor, it will stop. In this case, we can just | ||
781 | * continue by adding a buffer to the list and starting again. | ||
782 | */ | ||
783 | void au1xxx_dbdma_start(u32 chanid) | ||
784 | { | ||
785 | chan_tab_t *ctp; | ||
786 | au1x_dma_chan_t *cp; | ||
787 | |||
788 | ctp = *((chan_tab_t **)chanid); | ||
789 | cp = ctp->chan_ptr; | ||
790 | cp->ddma_desptr = virt_to_phys(ctp->cur_ptr); | ||
791 | cp->ddma_cfg |= DDMA_CFG_EN; /* Enable channel */ | ||
792 | wmb(); /* drain writebuffer */ | ||
793 | cp->ddma_dbell = 0; | ||
794 | wmb(); /* drain writebuffer */ | ||
795 | } | ||
796 | EXPORT_SYMBOL(au1xxx_dbdma_start); | ||
797 | |||
798 | void au1xxx_dbdma_reset(u32 chanid) | ||
799 | { | ||
800 | chan_tab_t *ctp; | ||
801 | au1x_ddma_desc_t *dp; | ||
802 | |||
803 | au1xxx_dbdma_stop(chanid); | ||
804 | |||
805 | ctp = *((chan_tab_t **)chanid); | ||
806 | ctp->get_ptr = ctp->put_ptr = ctp->cur_ptr = ctp->chan_desc_base; | ||
807 | |||
808 | /* Run through the descriptors and reset the valid indicator. */ | ||
809 | dp = ctp->chan_desc_base; | ||
810 | |||
811 | do { | ||
812 | dp->dscr_cmd0 &= ~DSCR_CMD0_V; | ||
813 | /* | ||
814 | * Reset our software status -- this is used to determine | ||
815 | * if a descriptor is in use by upper level software. Since | ||
816 | * posting can reset 'V' bit. | ||
817 | */ | ||
818 | dp->sw_status = 0; | ||
819 | dp = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr)); | ||
820 | } while (dp != ctp->chan_desc_base); | ||
821 | } | ||
822 | EXPORT_SYMBOL(au1xxx_dbdma_reset); | ||
823 | |||
824 | u32 au1xxx_get_dma_residue(u32 chanid) | ||
825 | { | ||
826 | chan_tab_t *ctp; | ||
827 | au1x_dma_chan_t *cp; | ||
828 | u32 rv; | ||
829 | |||
830 | ctp = *((chan_tab_t **)chanid); | ||
831 | cp = ctp->chan_ptr; | ||
832 | |||
833 | /* This is only valid if the channel is stopped. */ | ||
834 | rv = cp->ddma_bytecnt; | ||
835 | wmb(); /* drain writebuffer */ | ||
836 | |||
837 | return rv; | ||
838 | } | ||
839 | EXPORT_SYMBOL_GPL(au1xxx_get_dma_residue); | ||
840 | |||
841 | void au1xxx_dbdma_chan_free(u32 chanid) | ||
842 | { | ||
843 | chan_tab_t *ctp; | ||
844 | dbdev_tab_t *stp, *dtp; | ||
845 | |||
846 | ctp = *((chan_tab_t **)chanid); | ||
847 | stp = ctp->chan_src; | ||
848 | dtp = ctp->chan_dest; | ||
849 | |||
850 | au1xxx_dbdma_stop(chanid); | ||
851 | |||
852 | kfree((void *)ctp->cdb_membase); | ||
853 | |||
854 | stp->dev_flags &= ~DEV_FLAGS_INUSE; | ||
855 | dtp->dev_flags &= ~DEV_FLAGS_INUSE; | ||
856 | chan_tab_ptr[ctp->chan_index] = NULL; | ||
857 | |||
858 | kfree(ctp); | ||
859 | } | ||
860 | EXPORT_SYMBOL(au1xxx_dbdma_chan_free); | ||
861 | |||
862 | static irqreturn_t dbdma_interrupt(int irq, void *dev_id) | ||
863 | { | ||
864 | u32 intstat; | ||
865 | u32 chan_index; | ||
866 | chan_tab_t *ctp; | ||
867 | au1x_ddma_desc_t *dp; | ||
868 | au1x_dma_chan_t *cp; | ||
869 | |||
870 | intstat = dbdma_gptr->ddma_intstat; | ||
871 | wmb(); /* drain writebuffer */ | ||
872 | chan_index = __ffs(intstat); | ||
873 | |||
874 | ctp = chan_tab_ptr[chan_index]; | ||
875 | cp = ctp->chan_ptr; | ||
876 | dp = ctp->cur_ptr; | ||
877 | |||
878 | /* Reset interrupt. */ | ||
879 | cp->ddma_irq = 0; | ||
880 | wmb(); /* drain writebuffer */ | ||
881 | |||
882 | if (ctp->chan_callback) | ||
883 | ctp->chan_callback(irq, ctp->chan_callparam); | ||
884 | |||
885 | ctp->cur_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr)); | ||
886 | return IRQ_RETVAL(1); | ||
887 | } | ||
888 | |||
889 | void au1xxx_dbdma_dump(u32 chanid) | ||
890 | { | ||
891 | chan_tab_t *ctp; | ||
892 | au1x_ddma_desc_t *dp; | ||
893 | dbdev_tab_t *stp, *dtp; | ||
894 | au1x_dma_chan_t *cp; | ||
895 | u32 i = 0; | ||
896 | |||
897 | ctp = *((chan_tab_t **)chanid); | ||
898 | stp = ctp->chan_src; | ||
899 | dtp = ctp->chan_dest; | ||
900 | cp = ctp->chan_ptr; | ||
901 | |||
902 | printk(KERN_DEBUG "Chan %x, stp %x (dev %d) dtp %x (dev %d)\n", | ||
903 | (u32)ctp, (u32)stp, stp - dbdev_tab, (u32)dtp, | ||
904 | dtp - dbdev_tab); | ||
905 | printk(KERN_DEBUG "desc base %x, get %x, put %x, cur %x\n", | ||
906 | (u32)(ctp->chan_desc_base), (u32)(ctp->get_ptr), | ||
907 | (u32)(ctp->put_ptr), (u32)(ctp->cur_ptr)); | ||
908 | |||
909 | printk(KERN_DEBUG "dbdma chan %x\n", (u32)cp); | ||
910 | printk(KERN_DEBUG "cfg %08x, desptr %08x, statptr %08x\n", | ||
911 | cp->ddma_cfg, cp->ddma_desptr, cp->ddma_statptr); | ||
912 | printk(KERN_DEBUG "dbell %08x, irq %08x, stat %08x, bytecnt %08x\n", | ||
913 | cp->ddma_dbell, cp->ddma_irq, cp->ddma_stat, | ||
914 | cp->ddma_bytecnt); | ||
915 | |||
916 | /* Run through the descriptors */ | ||
917 | dp = ctp->chan_desc_base; | ||
918 | |||
919 | do { | ||
920 | printk(KERN_DEBUG "Dp[%d]= %08x, cmd0 %08x, cmd1 %08x\n", | ||
921 | i++, (u32)dp, dp->dscr_cmd0, dp->dscr_cmd1); | ||
922 | printk(KERN_DEBUG "src0 %08x, src1 %08x, dest0 %08x, dest1 %08x\n", | ||
923 | dp->dscr_source0, dp->dscr_source1, | ||
924 | dp->dscr_dest0, dp->dscr_dest1); | ||
925 | printk(KERN_DEBUG "stat %08x, nxtptr %08x\n", | ||
926 | dp->dscr_stat, dp->dscr_nxtptr); | ||
927 | dp = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr)); | ||
928 | } while (dp != ctp->chan_desc_base); | ||
929 | } | ||
930 | |||
931 | /* Put a descriptor into the DMA ring. | ||
932 | * This updates the source/destination pointers and byte count. | ||
933 | */ | ||
934 | u32 au1xxx_dbdma_put_dscr(u32 chanid, au1x_ddma_desc_t *dscr) | ||
935 | { | ||
936 | chan_tab_t *ctp; | ||
937 | au1x_ddma_desc_t *dp; | ||
938 | u32 nbytes = 0; | ||
939 | |||
940 | /* | ||
941 | * I guess we could check this to be within the | ||
942 | * range of the table...... | ||
943 | */ | ||
944 | ctp = *((chan_tab_t **)chanid); | ||
945 | |||
946 | /* | ||
947 | * We should have multiple callers for a particular channel, | ||
948 | * an interrupt doesn't affect this pointer nor the descriptor, | ||
949 | * so no locking should be needed. | ||
950 | */ | ||
951 | dp = ctp->put_ptr; | ||
952 | |||
953 | /* | ||
954 | * If the descriptor is valid, we are way ahead of the DMA | ||
955 | * engine, so just return an error condition. | ||
956 | */ | ||
957 | if (dp->dscr_cmd0 & DSCR_CMD0_V) | ||
958 | return 0; | ||
959 | |||
960 | /* Load up buffer addresses and byte count. */ | ||
961 | dp->dscr_dest0 = dscr->dscr_dest0; | ||
962 | dp->dscr_source0 = dscr->dscr_source0; | ||
963 | dp->dscr_dest1 = dscr->dscr_dest1; | ||
964 | dp->dscr_source1 = dscr->dscr_source1; | ||
965 | dp->dscr_cmd1 = dscr->dscr_cmd1; | ||
966 | nbytes = dscr->dscr_cmd1; | ||
967 | /* Allow the caller to specify if an interrupt is generated */ | ||
968 | dp->dscr_cmd0 &= ~DSCR_CMD0_IE; | ||
969 | dp->dscr_cmd0 |= dscr->dscr_cmd0 | DSCR_CMD0_V; | ||
970 | ctp->chan_ptr->ddma_dbell = 0; | ||
971 | |||
972 | /* Get next descriptor pointer. */ | ||
973 | ctp->put_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr)); | ||
974 | |||
975 | /* Return something non-zero. */ | ||
976 | return nbytes; | ||
977 | } | ||
978 | |||
979 | |||
980 | static unsigned long alchemy_dbdma_pm_data[NUM_DBDMA_CHANS + 1][6]; | ||
981 | |||
982 | static int alchemy_dbdma_suspend(void) | ||
983 | { | ||
984 | int i; | ||
985 | void __iomem *addr; | ||
986 | |||
987 | addr = (void __iomem *)KSEG1ADDR(AU1550_DBDMA_CONF_PHYS_ADDR); | ||
988 | alchemy_dbdma_pm_data[0][0] = __raw_readl(addr + 0x00); | ||
989 | alchemy_dbdma_pm_data[0][1] = __raw_readl(addr + 0x04); | ||
990 | alchemy_dbdma_pm_data[0][2] = __raw_readl(addr + 0x08); | ||
991 | alchemy_dbdma_pm_data[0][3] = __raw_readl(addr + 0x0c); | ||
992 | |||
993 | /* save channel configurations */ | ||
994 | addr = (void __iomem *)KSEG1ADDR(AU1550_DBDMA_PHYS_ADDR); | ||
995 | for (i = 1; i <= NUM_DBDMA_CHANS; i++) { | ||
996 | alchemy_dbdma_pm_data[i][0] = __raw_readl(addr + 0x00); | ||
997 | alchemy_dbdma_pm_data[i][1] = __raw_readl(addr + 0x04); | ||
998 | alchemy_dbdma_pm_data[i][2] = __raw_readl(addr + 0x08); | ||
999 | alchemy_dbdma_pm_data[i][3] = __raw_readl(addr + 0x0c); | ||
1000 | alchemy_dbdma_pm_data[i][4] = __raw_readl(addr + 0x10); | ||
1001 | alchemy_dbdma_pm_data[i][5] = __raw_readl(addr + 0x14); | ||
1002 | |||
1003 | /* halt channel */ | ||
1004 | __raw_writel(alchemy_dbdma_pm_data[i][0] & ~1, addr + 0x00); | ||
1005 | wmb(); | ||
1006 | while (!(__raw_readl(addr + 0x14) & 1)) | ||
1007 | wmb(); | ||
1008 | |||
1009 | addr += 0x100; /* next channel base */ | ||
1010 | } | ||
1011 | /* disable channel interrupts */ | ||
1012 | addr = (void __iomem *)KSEG1ADDR(AU1550_DBDMA_CONF_PHYS_ADDR); | ||
1013 | __raw_writel(0, addr + 0x0c); | ||
1014 | wmb(); | ||
1015 | |||
1016 | return 0; | ||
1017 | } | ||
1018 | |||
1019 | static void alchemy_dbdma_resume(void) | ||
1020 | { | ||
1021 | int i; | ||
1022 | void __iomem *addr; | ||
1023 | |||
1024 | addr = (void __iomem *)KSEG1ADDR(AU1550_DBDMA_CONF_PHYS_ADDR); | ||
1025 | __raw_writel(alchemy_dbdma_pm_data[0][0], addr + 0x00); | ||
1026 | __raw_writel(alchemy_dbdma_pm_data[0][1], addr + 0x04); | ||
1027 | __raw_writel(alchemy_dbdma_pm_data[0][2], addr + 0x08); | ||
1028 | __raw_writel(alchemy_dbdma_pm_data[0][3], addr + 0x0c); | ||
1029 | |||
1030 | /* restore channel configurations */ | ||
1031 | addr = (void __iomem *)KSEG1ADDR(AU1550_DBDMA_PHYS_ADDR); | ||
1032 | for (i = 1; i <= NUM_DBDMA_CHANS; i++) { | ||
1033 | __raw_writel(alchemy_dbdma_pm_data[i][0], addr + 0x00); | ||
1034 | __raw_writel(alchemy_dbdma_pm_data[i][1], addr + 0x04); | ||
1035 | __raw_writel(alchemy_dbdma_pm_data[i][2], addr + 0x08); | ||
1036 | __raw_writel(alchemy_dbdma_pm_data[i][3], addr + 0x0c); | ||
1037 | __raw_writel(alchemy_dbdma_pm_data[i][4], addr + 0x10); | ||
1038 | __raw_writel(alchemy_dbdma_pm_data[i][5], addr + 0x14); | ||
1039 | wmb(); | ||
1040 | addr += 0x100; /* next channel base */ | ||
1041 | } | ||
1042 | } | ||
1043 | |||
1044 | static struct syscore_ops alchemy_dbdma_syscore_ops = { | ||
1045 | .suspend = alchemy_dbdma_suspend, | ||
1046 | .resume = alchemy_dbdma_resume, | ||
1047 | }; | ||
1048 | |||
1049 | static int __init dbdma_setup(unsigned int irq, dbdev_tab_t *idtable) | ||
1050 | { | ||
1051 | int ret; | ||
1052 | |||
1053 | dbdev_tab = kcalloc(DBDEV_TAB_SIZE, sizeof(dbdev_tab_t), GFP_KERNEL); | ||
1054 | if (!dbdev_tab) | ||
1055 | return -ENOMEM; | ||
1056 | |||
1057 | memcpy(dbdev_tab, idtable, 32 * sizeof(dbdev_tab_t)); | ||
1058 | for (ret = 32; ret < DBDEV_TAB_SIZE; ret++) | ||
1059 | dbdev_tab[ret].dev_id = ~0; | ||
1060 | |||
1061 | dbdma_gptr->ddma_config = 0; | ||
1062 | dbdma_gptr->ddma_throttle = 0; | ||
1063 | dbdma_gptr->ddma_inten = 0xffff; | ||
1064 | wmb(); /* drain writebuffer */ | ||
1065 | |||
1066 | ret = request_irq(irq, dbdma_interrupt, 0, "dbdma", (void *)dbdma_gptr); | ||
1067 | if (ret) | ||
1068 | printk(KERN_ERR "Cannot grab DBDMA interrupt!\n"); | ||
1069 | else { | ||
1070 | dbdma_initialized = 1; | ||
1071 | register_syscore_ops(&alchemy_dbdma_syscore_ops); | ||
1072 | } | ||
1073 | |||
1074 | return ret; | ||
1075 | } | ||
1076 | |||
1077 | static int __init alchemy_dbdma_init(void) | ||
1078 | { | ||
1079 | switch (alchemy_get_cputype()) { | ||
1080 | case ALCHEMY_CPU_AU1550: | ||
1081 | return dbdma_setup(AU1550_DDMA_INT, au1550_dbdev_tab); | ||
1082 | case ALCHEMY_CPU_AU1200: | ||
1083 | return dbdma_setup(AU1200_DDMA_INT, au1200_dbdev_tab); | ||
1084 | case ALCHEMY_CPU_AU1300: | ||
1085 | return dbdma_setup(AU1300_DDMA_INT, au1300_dbdev_tab); | ||
1086 | } | ||
1087 | return 0; | ||
1088 | } | ||
1089 | subsys_initcall(alchemy_dbdma_init); | ||
diff --git a/arch/mips/alchemy/common/dma.c b/arch/mips/alchemy/common/dma.c new file mode 100644 index 000000000..973049b5b --- /dev/null +++ b/arch/mips/alchemy/common/dma.c | |||
@@ -0,0 +1,265 @@ | |||
1 | /* | ||
2 | * | ||
3 | * BRIEF MODULE DESCRIPTION | ||
4 | * A DMA channel allocator for Au1x00. API is modeled loosely off of | ||
5 | * linux/kernel/dma.c. | ||
6 | * | ||
7 | * Copyright 2000, 2008 MontaVista Software Inc. | ||
8 | * Author: MontaVista Software, Inc. <source@mvista.com> | ||
9 | * Copyright (C) 2005 Ralf Baechle (ralf@linux-mips.org) | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify it | ||
12 | * under the terms of the GNU General Public License as published by the | ||
13 | * Free Software Foundation; either version 2 of the License, or (at your | ||
14 | * option) any later version. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
17 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
18 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
19 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
21 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
22 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
23 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
25 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | * | ||
27 | * You should have received a copy of the GNU General Public License along | ||
28 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
29 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
30 | * | ||
31 | */ | ||
32 | |||
33 | #include <linux/init.h> | ||
34 | #include <linux/export.h> | ||
35 | #include <linux/kernel.h> | ||
36 | #include <linux/errno.h> | ||
37 | #include <linux/spinlock.h> | ||
38 | #include <linux/interrupt.h> | ||
39 | |||
40 | #include <asm/mach-au1x00/au1000.h> | ||
41 | #include <asm/mach-au1x00/au1000_dma.h> | ||
42 | |||
43 | /* | ||
44 | * A note on resource allocation: | ||
45 | * | ||
46 | * All drivers needing DMA channels, should allocate and release them | ||
47 | * through the public routines `request_dma()' and `free_dma()'. | ||
48 | * | ||
49 | * In order to avoid problems, all processes should allocate resources in | ||
50 | * the same sequence and release them in the reverse order. | ||
51 | * | ||
52 | * So, when allocating DMAs and IRQs, first allocate the DMA, then the IRQ. | ||
53 | * When releasing them, first release the IRQ, then release the DMA. The | ||
54 | * main reason for this order is that, if you are requesting the DMA buffer | ||
55 | * done interrupt, you won't know the irq number until the DMA channel is | ||
56 | * returned from request_dma. | ||
57 | */ | ||
58 | |||
59 | /* DMA Channel register block spacing */ | ||
60 | #define DMA_CHANNEL_LEN 0x00000100 | ||
61 | |||
62 | DEFINE_SPINLOCK(au1000_dma_spin_lock); | ||
63 | |||
64 | struct dma_chan au1000_dma_table[NUM_AU1000_DMA_CHANNELS] = { | ||
65 | {.dev_id = -1,}, | ||
66 | {.dev_id = -1,}, | ||
67 | {.dev_id = -1,}, | ||
68 | {.dev_id = -1,}, | ||
69 | {.dev_id = -1,}, | ||
70 | {.dev_id = -1,}, | ||
71 | {.dev_id = -1,}, | ||
72 | {.dev_id = -1,} | ||
73 | }; | ||
74 | EXPORT_SYMBOL(au1000_dma_table); | ||
75 | |||
76 | /* Device FIFO addresses and default DMA modes */ | ||
77 | static const struct dma_dev { | ||
78 | unsigned int fifo_addr; | ||
79 | unsigned int dma_mode; | ||
80 | } dma_dev_table[DMA_NUM_DEV] = { | ||
81 | { AU1000_UART0_PHYS_ADDR + 0x04, DMA_DW8 }, /* UART0_TX */ | ||
82 | { AU1000_UART0_PHYS_ADDR + 0x00, DMA_DW8 | DMA_DR }, /* UART0_RX */ | ||
83 | { 0, 0 }, /* DMA_REQ0 */ | ||
84 | { 0, 0 }, /* DMA_REQ1 */ | ||
85 | { AU1000_AC97_PHYS_ADDR + 0x08, DMA_DW16 }, /* AC97 TX c */ | ||
86 | { AU1000_AC97_PHYS_ADDR + 0x08, DMA_DW16 | DMA_DR }, /* AC97 RX c */ | ||
87 | { AU1000_UART3_PHYS_ADDR + 0x04, DMA_DW8 | DMA_NC }, /* UART3_TX */ | ||
88 | { AU1000_UART3_PHYS_ADDR + 0x00, DMA_DW8 | DMA_NC | DMA_DR }, /* UART3_RX */ | ||
89 | { AU1000_USB_UDC_PHYS_ADDR + 0x00, DMA_DW8 | DMA_NC | DMA_DR }, /* EP0RD */ | ||
90 | { AU1000_USB_UDC_PHYS_ADDR + 0x04, DMA_DW8 | DMA_NC }, /* EP0WR */ | ||
91 | { AU1000_USB_UDC_PHYS_ADDR + 0x08, DMA_DW8 | DMA_NC }, /* EP2WR */ | ||
92 | { AU1000_USB_UDC_PHYS_ADDR + 0x0c, DMA_DW8 | DMA_NC }, /* EP3WR */ | ||
93 | { AU1000_USB_UDC_PHYS_ADDR + 0x10, DMA_DW8 | DMA_NC | DMA_DR }, /* EP4RD */ | ||
94 | { AU1000_USB_UDC_PHYS_ADDR + 0x14, DMA_DW8 | DMA_NC | DMA_DR }, /* EP5RD */ | ||
95 | /* on Au1500, these 2 are DMA_REQ2/3 (GPIO208/209) instead! */ | ||
96 | { AU1000_I2S_PHYS_ADDR + 0x00, DMA_DW32 | DMA_NC}, /* I2S TX */ | ||
97 | { AU1000_I2S_PHYS_ADDR + 0x00, DMA_DW32 | DMA_NC | DMA_DR}, /* I2S RX */ | ||
98 | }; | ||
99 | |||
100 | int au1000_dma_read_proc(char *buf, char **start, off_t fpos, | ||
101 | int length, int *eof, void *data) | ||
102 | { | ||
103 | int i, len = 0; | ||
104 | struct dma_chan *chan; | ||
105 | |||
106 | for (i = 0; i < NUM_AU1000_DMA_CHANNELS; i++) { | ||
107 | chan = get_dma_chan(i); | ||
108 | if (chan != NULL) | ||
109 | len += sprintf(buf + len, "%2d: %s\n", | ||
110 | i, chan->dev_str); | ||
111 | } | ||
112 | |||
113 | if (fpos >= len) { | ||
114 | *start = buf; | ||
115 | *eof = 1; | ||
116 | return 0; | ||
117 | } | ||
118 | *start = buf + fpos; | ||
119 | len -= fpos; | ||
120 | if (len > length) | ||
121 | return length; | ||
122 | *eof = 1; | ||
123 | return len; | ||
124 | } | ||
125 | |||
126 | /* Device FIFO addresses and default DMA modes - 2nd bank */ | ||
127 | static const struct dma_dev dma_dev_table_bank2[DMA_NUM_DEV_BANK2] = { | ||
128 | { AU1100_SD0_PHYS_ADDR + 0x00, DMA_DS | DMA_DW8 }, /* coherent */ | ||
129 | { AU1100_SD0_PHYS_ADDR + 0x04, DMA_DS | DMA_DW8 | DMA_DR }, /* coherent */ | ||
130 | { AU1100_SD1_PHYS_ADDR + 0x00, DMA_DS | DMA_DW8 }, /* coherent */ | ||
131 | { AU1100_SD1_PHYS_ADDR + 0x04, DMA_DS | DMA_DW8 | DMA_DR } /* coherent */ | ||
132 | }; | ||
133 | |||
134 | void dump_au1000_dma_channel(unsigned int dmanr) | ||
135 | { | ||
136 | struct dma_chan *chan; | ||
137 | |||
138 | if (dmanr >= NUM_AU1000_DMA_CHANNELS) | ||
139 | return; | ||
140 | chan = &au1000_dma_table[dmanr]; | ||
141 | |||
142 | printk(KERN_INFO "Au1000 DMA%d Register Dump:\n", dmanr); | ||
143 | printk(KERN_INFO " mode = 0x%08x\n", | ||
144 | __raw_readl(chan->io + DMA_MODE_SET)); | ||
145 | printk(KERN_INFO " addr = 0x%08x\n", | ||
146 | __raw_readl(chan->io + DMA_PERIPHERAL_ADDR)); | ||
147 | printk(KERN_INFO " start0 = 0x%08x\n", | ||
148 | __raw_readl(chan->io + DMA_BUFFER0_START)); | ||
149 | printk(KERN_INFO " start1 = 0x%08x\n", | ||
150 | __raw_readl(chan->io + DMA_BUFFER1_START)); | ||
151 | printk(KERN_INFO " count0 = 0x%08x\n", | ||
152 | __raw_readl(chan->io + DMA_BUFFER0_COUNT)); | ||
153 | printk(KERN_INFO " count1 = 0x%08x\n", | ||
154 | __raw_readl(chan->io + DMA_BUFFER1_COUNT)); | ||
155 | } | ||
156 | |||
157 | /* | ||
158 | * Finds a free channel, and binds the requested device to it. | ||
159 | * Returns the allocated channel number, or negative on error. | ||
160 | * Requests the DMA done IRQ if irqhandler != NULL. | ||
161 | */ | ||
162 | int request_au1000_dma(int dev_id, const char *dev_str, | ||
163 | irq_handler_t irqhandler, | ||
164 | unsigned long irqflags, | ||
165 | void *irq_dev_id) | ||
166 | { | ||
167 | struct dma_chan *chan; | ||
168 | const struct dma_dev *dev; | ||
169 | int i, ret; | ||
170 | |||
171 | if (alchemy_get_cputype() == ALCHEMY_CPU_AU1100) { | ||
172 | if (dev_id < 0 || dev_id >= (DMA_NUM_DEV + DMA_NUM_DEV_BANK2)) | ||
173 | return -EINVAL; | ||
174 | } else { | ||
175 | if (dev_id < 0 || dev_id >= DMA_NUM_DEV) | ||
176 | return -EINVAL; | ||
177 | } | ||
178 | |||
179 | for (i = 0; i < NUM_AU1000_DMA_CHANNELS; i++) | ||
180 | if (au1000_dma_table[i].dev_id < 0) | ||
181 | break; | ||
182 | |||
183 | if (i == NUM_AU1000_DMA_CHANNELS) | ||
184 | return -ENODEV; | ||
185 | |||
186 | chan = &au1000_dma_table[i]; | ||
187 | |||
188 | if (dev_id >= DMA_NUM_DEV) { | ||
189 | dev_id -= DMA_NUM_DEV; | ||
190 | dev = &dma_dev_table_bank2[dev_id]; | ||
191 | } else | ||
192 | dev = &dma_dev_table[dev_id]; | ||
193 | |||
194 | if (irqhandler) { | ||
195 | chan->irq_dev = irq_dev_id; | ||
196 | ret = request_irq(chan->irq, irqhandler, irqflags, dev_str, | ||
197 | chan->irq_dev); | ||
198 | if (ret) { | ||
199 | chan->irq_dev = NULL; | ||
200 | return ret; | ||
201 | } | ||
202 | } else { | ||
203 | chan->irq_dev = NULL; | ||
204 | } | ||
205 | |||
206 | /* fill it in */ | ||
207 | chan->io = (void __iomem *)(KSEG1ADDR(AU1000_DMA_PHYS_ADDR) + | ||
208 | i * DMA_CHANNEL_LEN); | ||
209 | chan->dev_id = dev_id; | ||
210 | chan->dev_str = dev_str; | ||
211 | chan->fifo_addr = dev->fifo_addr; | ||
212 | chan->mode = dev->dma_mode; | ||
213 | |||
214 | /* initialize the channel before returning */ | ||
215 | init_dma(i); | ||
216 | |||
217 | return i; | ||
218 | } | ||
219 | EXPORT_SYMBOL(request_au1000_dma); | ||
220 | |||
221 | void free_au1000_dma(unsigned int dmanr) | ||
222 | { | ||
223 | struct dma_chan *chan = get_dma_chan(dmanr); | ||
224 | |||
225 | if (!chan) { | ||
226 | printk(KERN_ERR "Error trying to free DMA%d\n", dmanr); | ||
227 | return; | ||
228 | } | ||
229 | |||
230 | disable_dma(dmanr); | ||
231 | if (chan->irq_dev) | ||
232 | free_irq(chan->irq, chan->irq_dev); | ||
233 | |||
234 | chan->irq_dev = NULL; | ||
235 | chan->dev_id = -1; | ||
236 | } | ||
237 | EXPORT_SYMBOL(free_au1000_dma); | ||
238 | |||
239 | static int __init au1000_dma_init(void) | ||
240 | { | ||
241 | int base, i; | ||
242 | |||
243 | switch (alchemy_get_cputype()) { | ||
244 | case ALCHEMY_CPU_AU1000: | ||
245 | base = AU1000_DMA_INT_BASE; | ||
246 | break; | ||
247 | case ALCHEMY_CPU_AU1500: | ||
248 | base = AU1500_DMA_INT_BASE; | ||
249 | break; | ||
250 | case ALCHEMY_CPU_AU1100: | ||
251 | base = AU1100_DMA_INT_BASE; | ||
252 | break; | ||
253 | default: | ||
254 | goto out; | ||
255 | } | ||
256 | |||
257 | for (i = 0; i < NUM_AU1000_DMA_CHANNELS; i++) | ||
258 | au1000_dma_table[i].irq = base + i; | ||
259 | |||
260 | printk(KERN_INFO "Alchemy DMA initialized\n"); | ||
261 | |||
262 | out: | ||
263 | return 0; | ||
264 | } | ||
265 | arch_initcall(au1000_dma_init); | ||
diff --git a/arch/mips/alchemy/common/gpiolib.c b/arch/mips/alchemy/common/gpiolib.c new file mode 100644 index 000000000..7d5da5edd --- /dev/null +++ b/arch/mips/alchemy/common/gpiolib.c | |||
@@ -0,0 +1,174 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2009, OpenWrt.org, Florian Fainelli <florian@openwrt.org> | ||
3 | * GPIOLIB support for Alchemy chips. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | * | ||
10 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
11 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
12 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
13 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
14 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
15 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
16 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
17 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
18 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
19 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License along | ||
22 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
23 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
24 | * | ||
25 | * Notes : | ||
26 | * This file must ONLY be built when CONFIG_GPIOLIB=y and | ||
27 | * CONFIG_ALCHEMY_GPIO_INDIRECT=n, otherwise compilation will fail! | ||
28 | * au1000 SoC have only one GPIO block : GPIO1 | ||
29 | * Au1100, Au15x0, Au12x0 have a second one : GPIO2 | ||
30 | * Au1300 is totally different: 1 block with up to 128 GPIOs | ||
31 | */ | ||
32 | |||
33 | #include <linux/init.h> | ||
34 | #include <linux/kernel.h> | ||
35 | #include <linux/types.h> | ||
36 | #include <linux/gpio.h> | ||
37 | #include <asm/mach-au1x00/gpio-au1000.h> | ||
38 | #include <asm/mach-au1x00/gpio-au1300.h> | ||
39 | |||
40 | static int gpio2_get(struct gpio_chip *chip, unsigned offset) | ||
41 | { | ||
42 | return !!alchemy_gpio2_get_value(offset + ALCHEMY_GPIO2_BASE); | ||
43 | } | ||
44 | |||
45 | static void gpio2_set(struct gpio_chip *chip, unsigned offset, int value) | ||
46 | { | ||
47 | alchemy_gpio2_set_value(offset + ALCHEMY_GPIO2_BASE, value); | ||
48 | } | ||
49 | |||
50 | static int gpio2_direction_input(struct gpio_chip *chip, unsigned offset) | ||
51 | { | ||
52 | return alchemy_gpio2_direction_input(offset + ALCHEMY_GPIO2_BASE); | ||
53 | } | ||
54 | |||
55 | static int gpio2_direction_output(struct gpio_chip *chip, unsigned offset, | ||
56 | int value) | ||
57 | { | ||
58 | return alchemy_gpio2_direction_output(offset + ALCHEMY_GPIO2_BASE, | ||
59 | value); | ||
60 | } | ||
61 | |||
62 | static int gpio2_to_irq(struct gpio_chip *chip, unsigned offset) | ||
63 | { | ||
64 | return alchemy_gpio2_to_irq(offset + ALCHEMY_GPIO2_BASE); | ||
65 | } | ||
66 | |||
67 | |||
68 | static int gpio1_get(struct gpio_chip *chip, unsigned offset) | ||
69 | { | ||
70 | return !!alchemy_gpio1_get_value(offset + ALCHEMY_GPIO1_BASE); | ||
71 | } | ||
72 | |||
73 | static void gpio1_set(struct gpio_chip *chip, | ||
74 | unsigned offset, int value) | ||
75 | { | ||
76 | alchemy_gpio1_set_value(offset + ALCHEMY_GPIO1_BASE, value); | ||
77 | } | ||
78 | |||
79 | static int gpio1_direction_input(struct gpio_chip *chip, unsigned offset) | ||
80 | { | ||
81 | return alchemy_gpio1_direction_input(offset + ALCHEMY_GPIO1_BASE); | ||
82 | } | ||
83 | |||
84 | static int gpio1_direction_output(struct gpio_chip *chip, | ||
85 | unsigned offset, int value) | ||
86 | { | ||
87 | return alchemy_gpio1_direction_output(offset + ALCHEMY_GPIO1_BASE, | ||
88 | value); | ||
89 | } | ||
90 | |||
91 | static int gpio1_to_irq(struct gpio_chip *chip, unsigned offset) | ||
92 | { | ||
93 | return alchemy_gpio1_to_irq(offset + ALCHEMY_GPIO1_BASE); | ||
94 | } | ||
95 | |||
96 | struct gpio_chip alchemy_gpio_chip[] = { | ||
97 | [0] = { | ||
98 | .label = "alchemy-gpio1", | ||
99 | .direction_input = gpio1_direction_input, | ||
100 | .direction_output = gpio1_direction_output, | ||
101 | .get = gpio1_get, | ||
102 | .set = gpio1_set, | ||
103 | .to_irq = gpio1_to_irq, | ||
104 | .base = ALCHEMY_GPIO1_BASE, | ||
105 | .ngpio = ALCHEMY_GPIO1_NUM, | ||
106 | }, | ||
107 | [1] = { | ||
108 | .label = "alchemy-gpio2", | ||
109 | .direction_input = gpio2_direction_input, | ||
110 | .direction_output = gpio2_direction_output, | ||
111 | .get = gpio2_get, | ||
112 | .set = gpio2_set, | ||
113 | .to_irq = gpio2_to_irq, | ||
114 | .base = ALCHEMY_GPIO2_BASE, | ||
115 | .ngpio = ALCHEMY_GPIO2_NUM, | ||
116 | }, | ||
117 | }; | ||
118 | |||
119 | static int alchemy_gpic_get(struct gpio_chip *chip, unsigned int off) | ||
120 | { | ||
121 | return !!au1300_gpio_get_value(off + AU1300_GPIO_BASE); | ||
122 | } | ||
123 | |||
124 | static void alchemy_gpic_set(struct gpio_chip *chip, unsigned int off, int v) | ||
125 | { | ||
126 | au1300_gpio_set_value(off + AU1300_GPIO_BASE, v); | ||
127 | } | ||
128 | |||
129 | static int alchemy_gpic_dir_input(struct gpio_chip *chip, unsigned int off) | ||
130 | { | ||
131 | return au1300_gpio_direction_input(off + AU1300_GPIO_BASE); | ||
132 | } | ||
133 | |||
134 | static int alchemy_gpic_dir_output(struct gpio_chip *chip, unsigned int off, | ||
135 | int v) | ||
136 | { | ||
137 | return au1300_gpio_direction_output(off + AU1300_GPIO_BASE, v); | ||
138 | } | ||
139 | |||
140 | static int alchemy_gpic_gpio_to_irq(struct gpio_chip *chip, unsigned int off) | ||
141 | { | ||
142 | return au1300_gpio_to_irq(off + AU1300_GPIO_BASE); | ||
143 | } | ||
144 | |||
145 | static struct gpio_chip au1300_gpiochip = { | ||
146 | .label = "alchemy-gpic", | ||
147 | .direction_input = alchemy_gpic_dir_input, | ||
148 | .direction_output = alchemy_gpic_dir_output, | ||
149 | .get = alchemy_gpic_get, | ||
150 | .set = alchemy_gpic_set, | ||
151 | .to_irq = alchemy_gpic_gpio_to_irq, | ||
152 | .base = AU1300_GPIO_BASE, | ||
153 | .ngpio = AU1300_GPIO_NUM, | ||
154 | }; | ||
155 | |||
156 | static int __init alchemy_gpiochip_init(void) | ||
157 | { | ||
158 | int ret = 0; | ||
159 | |||
160 | switch (alchemy_get_cputype()) { | ||
161 | case ALCHEMY_CPU_AU1000: | ||
162 | ret = gpiochip_add_data(&alchemy_gpio_chip[0], NULL); | ||
163 | break; | ||
164 | case ALCHEMY_CPU_AU1500...ALCHEMY_CPU_AU1200: | ||
165 | ret = gpiochip_add_data(&alchemy_gpio_chip[0], NULL); | ||
166 | ret |= gpiochip_add_data(&alchemy_gpio_chip[1], NULL); | ||
167 | break; | ||
168 | case ALCHEMY_CPU_AU1300: | ||
169 | ret = gpiochip_add_data(&au1300_gpiochip, NULL); | ||
170 | break; | ||
171 | } | ||
172 | return ret; | ||
173 | } | ||
174 | arch_initcall(alchemy_gpiochip_init); | ||
diff --git a/arch/mips/alchemy/common/irq.c b/arch/mips/alchemy/common/irq.c new file mode 100644 index 000000000..da9f92200 --- /dev/null +++ b/arch/mips/alchemy/common/irq.c | |||
@@ -0,0 +1,996 @@ | |||
1 | /* | ||
2 | * Copyright 2001, 2007-2008 MontaVista Software Inc. | ||
3 | * Author: MontaVista Software, Inc. <source@mvista.com> | ||
4 | * | ||
5 | * Copyright (C) 2007 Ralf Baechle (ralf@linux-mips.org) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License as published by the | ||
9 | * Free Software Foundation; either version 2 of the License, or (at your | ||
10 | * option) any later version. | ||
11 | * | ||
12 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
13 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
14 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
15 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
16 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
17 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
18 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
19 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
20 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
21 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
22 | * | ||
23 | * You should have received a copy of the GNU General Public License along | ||
24 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
25 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
26 | */ | ||
27 | |||
28 | #include <linux/export.h> | ||
29 | #include <linux/init.h> | ||
30 | #include <linux/interrupt.h> | ||
31 | #include <linux/slab.h> | ||
32 | #include <linux/syscore_ops.h> | ||
33 | |||
34 | #include <asm/irq_cpu.h> | ||
35 | #include <asm/mach-au1x00/au1000.h> | ||
36 | #include <asm/mach-au1x00/gpio-au1300.h> | ||
37 | |||
38 | /* Interrupt Controller register offsets */ | ||
39 | #define IC_CFG0RD 0x40 | ||
40 | #define IC_CFG0SET 0x40 | ||
41 | #define IC_CFG0CLR 0x44 | ||
42 | #define IC_CFG1RD 0x48 | ||
43 | #define IC_CFG1SET 0x48 | ||
44 | #define IC_CFG1CLR 0x4C | ||
45 | #define IC_CFG2RD 0x50 | ||
46 | #define IC_CFG2SET 0x50 | ||
47 | #define IC_CFG2CLR 0x54 | ||
48 | #define IC_REQ0INT 0x54 | ||
49 | #define IC_SRCRD 0x58 | ||
50 | #define IC_SRCSET 0x58 | ||
51 | #define IC_SRCCLR 0x5C | ||
52 | #define IC_REQ1INT 0x5C | ||
53 | #define IC_ASSIGNRD 0x60 | ||
54 | #define IC_ASSIGNSET 0x60 | ||
55 | #define IC_ASSIGNCLR 0x64 | ||
56 | #define IC_WAKERD 0x68 | ||
57 | #define IC_WAKESET 0x68 | ||
58 | #define IC_WAKECLR 0x6C | ||
59 | #define IC_MASKRD 0x70 | ||
60 | #define IC_MASKSET 0x70 | ||
61 | #define IC_MASKCLR 0x74 | ||
62 | #define IC_RISINGRD 0x78 | ||
63 | #define IC_RISINGCLR 0x78 | ||
64 | #define IC_FALLINGRD 0x7C | ||
65 | #define IC_FALLINGCLR 0x7C | ||
66 | #define IC_TESTBIT 0x80 | ||
67 | |||
68 | /* per-processor fixed function irqs */ | ||
69 | struct alchemy_irqmap { | ||
70 | int irq; /* linux IRQ number */ | ||
71 | int type; /* IRQ_TYPE_ */ | ||
72 | int prio; /* irq priority, 0 highest, 3 lowest */ | ||
73 | int internal; /* GPIC: internal source (no ext. pin)? */ | ||
74 | }; | ||
75 | |||
76 | static int au1x_ic_settype(struct irq_data *d, unsigned int type); | ||
77 | static int au1300_gpic_settype(struct irq_data *d, unsigned int type); | ||
78 | |||
79 | |||
80 | /* NOTE on interrupt priorities: The original writers of this code said: | ||
81 | * | ||
82 | * Because of the tight timing of SETUP token to reply transactions, | ||
83 | * the USB devices-side packet complete interrupt (USB_DEV_REQ_INT) | ||
84 | * needs the highest priority. | ||
85 | */ | ||
86 | struct alchemy_irqmap au1000_irqmap[] __initdata = { | ||
87 | { AU1000_UART0_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
88 | { AU1000_UART1_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
89 | { AU1000_UART2_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
90 | { AU1000_UART3_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
91 | { AU1000_SSI0_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
92 | { AU1000_SSI1_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
93 | { AU1000_DMA_INT_BASE, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
94 | { AU1000_DMA_INT_BASE+1, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
95 | { AU1000_DMA_INT_BASE+2, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
96 | { AU1000_DMA_INT_BASE+3, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
97 | { AU1000_DMA_INT_BASE+4, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
98 | { AU1000_DMA_INT_BASE+5, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
99 | { AU1000_DMA_INT_BASE+6, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
100 | { AU1000_DMA_INT_BASE+7, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
101 | { AU1000_TOY_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
102 | { AU1000_TOY_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
103 | { AU1000_TOY_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
104 | { AU1000_TOY_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
105 | { AU1000_RTC_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
106 | { AU1000_RTC_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
107 | { AU1000_RTC_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
108 | { AU1000_RTC_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 0, 0 }, | ||
109 | { AU1000_IRDA_TX_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
110 | { AU1000_IRDA_RX_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
111 | { AU1000_USB_DEV_REQ_INT, IRQ_TYPE_LEVEL_HIGH, 0, 0 }, | ||
112 | { AU1000_USB_DEV_SUS_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
113 | { AU1000_USB_HOST_INT, IRQ_TYPE_LEVEL_LOW, 1, 0 }, | ||
114 | { AU1000_ACSYNC_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
115 | { AU1000_MAC0_DMA_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
116 | { AU1000_MAC1_DMA_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
117 | { AU1000_AC97C_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
118 | { -1, }, | ||
119 | }; | ||
120 | |||
121 | struct alchemy_irqmap au1500_irqmap[] __initdata = { | ||
122 | { AU1500_UART0_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
123 | { AU1500_PCI_INTA, IRQ_TYPE_LEVEL_LOW, 1, 0 }, | ||
124 | { AU1500_PCI_INTB, IRQ_TYPE_LEVEL_LOW, 1, 0 }, | ||
125 | { AU1500_UART3_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
126 | { AU1500_PCI_INTC, IRQ_TYPE_LEVEL_LOW, 1, 0 }, | ||
127 | { AU1500_PCI_INTD, IRQ_TYPE_LEVEL_LOW, 1, 0 }, | ||
128 | { AU1500_DMA_INT_BASE, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
129 | { AU1500_DMA_INT_BASE+1, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
130 | { AU1500_DMA_INT_BASE+2, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
131 | { AU1500_DMA_INT_BASE+3, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
132 | { AU1500_DMA_INT_BASE+4, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
133 | { AU1500_DMA_INT_BASE+5, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
134 | { AU1500_DMA_INT_BASE+6, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
135 | { AU1500_DMA_INT_BASE+7, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
136 | { AU1500_TOY_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
137 | { AU1500_TOY_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
138 | { AU1500_TOY_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
139 | { AU1500_TOY_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
140 | { AU1500_RTC_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
141 | { AU1500_RTC_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
142 | { AU1500_RTC_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
143 | { AU1500_RTC_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 0, 0 }, | ||
144 | { AU1500_USB_DEV_REQ_INT, IRQ_TYPE_LEVEL_HIGH, 0, 0 }, | ||
145 | { AU1500_USB_DEV_SUS_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
146 | { AU1500_USB_HOST_INT, IRQ_TYPE_LEVEL_LOW, 1, 0 }, | ||
147 | { AU1500_ACSYNC_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
148 | { AU1500_MAC0_DMA_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
149 | { AU1500_MAC1_DMA_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
150 | { AU1500_AC97C_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
151 | { -1, }, | ||
152 | }; | ||
153 | |||
154 | struct alchemy_irqmap au1100_irqmap[] __initdata = { | ||
155 | { AU1100_UART0_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
156 | { AU1100_UART1_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
157 | { AU1100_SD_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
158 | { AU1100_UART3_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
159 | { AU1100_SSI0_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
160 | { AU1100_SSI1_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
161 | { AU1100_DMA_INT_BASE, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
162 | { AU1100_DMA_INT_BASE+1, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
163 | { AU1100_DMA_INT_BASE+2, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
164 | { AU1100_DMA_INT_BASE+3, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
165 | { AU1100_DMA_INT_BASE+4, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
166 | { AU1100_DMA_INT_BASE+5, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
167 | { AU1100_DMA_INT_BASE+6, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
168 | { AU1100_DMA_INT_BASE+7, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
169 | { AU1100_TOY_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
170 | { AU1100_TOY_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
171 | { AU1100_TOY_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
172 | { AU1100_TOY_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
173 | { AU1100_RTC_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
174 | { AU1100_RTC_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
175 | { AU1100_RTC_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
176 | { AU1100_RTC_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 0, 0 }, | ||
177 | { AU1100_IRDA_TX_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
178 | { AU1100_IRDA_RX_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
179 | { AU1100_USB_DEV_REQ_INT, IRQ_TYPE_LEVEL_HIGH, 0, 0 }, | ||
180 | { AU1100_USB_DEV_SUS_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
181 | { AU1100_USB_HOST_INT, IRQ_TYPE_LEVEL_LOW, 1, 0 }, | ||
182 | { AU1100_ACSYNC_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
183 | { AU1100_MAC0_DMA_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
184 | { AU1100_LCD_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
185 | { AU1100_AC97C_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
186 | { -1, }, | ||
187 | }; | ||
188 | |||
189 | struct alchemy_irqmap au1550_irqmap[] __initdata = { | ||
190 | { AU1550_UART0_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
191 | { AU1550_PCI_INTA, IRQ_TYPE_LEVEL_LOW, 1, 0 }, | ||
192 | { AU1550_PCI_INTB, IRQ_TYPE_LEVEL_LOW, 1, 0 }, | ||
193 | { AU1550_DDMA_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
194 | { AU1550_CRYPTO_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
195 | { AU1550_PCI_INTC, IRQ_TYPE_LEVEL_LOW, 1, 0 }, | ||
196 | { AU1550_PCI_INTD, IRQ_TYPE_LEVEL_LOW, 1, 0 }, | ||
197 | { AU1550_PCI_RST_INT, IRQ_TYPE_LEVEL_LOW, 1, 0 }, | ||
198 | { AU1550_UART1_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
199 | { AU1550_UART3_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
200 | { AU1550_PSC0_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
201 | { AU1550_PSC1_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
202 | { AU1550_PSC2_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
203 | { AU1550_PSC3_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
204 | { AU1550_TOY_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
205 | { AU1550_TOY_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
206 | { AU1550_TOY_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
207 | { AU1550_TOY_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
208 | { AU1550_RTC_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
209 | { AU1550_RTC_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
210 | { AU1550_RTC_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
211 | { AU1550_RTC_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 0, 0 }, | ||
212 | { AU1550_NAND_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
213 | { AU1550_USB_DEV_REQ_INT, IRQ_TYPE_LEVEL_HIGH, 0, 0 }, | ||
214 | { AU1550_USB_DEV_SUS_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
215 | { AU1550_USB_HOST_INT, IRQ_TYPE_LEVEL_LOW, 1, 0 }, | ||
216 | { AU1550_MAC0_DMA_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
217 | { AU1550_MAC1_DMA_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
218 | { -1, }, | ||
219 | }; | ||
220 | |||
221 | struct alchemy_irqmap au1200_irqmap[] __initdata = { | ||
222 | { AU1200_UART0_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
223 | { AU1200_SWT_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
224 | { AU1200_SD_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
225 | { AU1200_DDMA_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
226 | { AU1200_MAE_BE_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
227 | { AU1200_UART1_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
228 | { AU1200_MAE_FE_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
229 | { AU1200_PSC0_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
230 | { AU1200_PSC1_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
231 | { AU1200_AES_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
232 | { AU1200_CAMERA_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
233 | { AU1200_TOY_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
234 | { AU1200_TOY_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
235 | { AU1200_TOY_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
236 | { AU1200_TOY_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
237 | { AU1200_RTC_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
238 | { AU1200_RTC_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
239 | { AU1200_RTC_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
240 | { AU1200_RTC_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 0, 0 }, | ||
241 | { AU1200_NAND_INT, IRQ_TYPE_EDGE_RISING, 1, 0 }, | ||
242 | { AU1200_USB_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
243 | { AU1200_LCD_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
244 | { AU1200_MAE_BOTH_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0 }, | ||
245 | { -1, }, | ||
246 | }; | ||
247 | |||
248 | static struct alchemy_irqmap au1300_irqmap[] __initdata = { | ||
249 | /* multifunction: gpio pin or device */ | ||
250 | { AU1300_UART1_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0, }, | ||
251 | { AU1300_UART2_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0, }, | ||
252 | { AU1300_UART3_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0, }, | ||
253 | { AU1300_SD1_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0, }, | ||
254 | { AU1300_SD2_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0, }, | ||
255 | { AU1300_PSC0_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0, }, | ||
256 | { AU1300_PSC1_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0, }, | ||
257 | { AU1300_PSC2_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0, }, | ||
258 | { AU1300_PSC3_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0, }, | ||
259 | { AU1300_NAND_INT, IRQ_TYPE_LEVEL_HIGH, 1, 0, }, | ||
260 | /* au1300 internal */ | ||
261 | { AU1300_DDMA_INT, IRQ_TYPE_LEVEL_HIGH, 1, 1, }, | ||
262 | { AU1300_MMU_INT, IRQ_TYPE_LEVEL_HIGH, 1, 1, }, | ||
263 | { AU1300_MPU_INT, IRQ_TYPE_LEVEL_HIGH, 1, 1, }, | ||
264 | { AU1300_GPU_INT, IRQ_TYPE_LEVEL_HIGH, 1, 1, }, | ||
265 | { AU1300_UDMA_INT, IRQ_TYPE_LEVEL_HIGH, 1, 1, }, | ||
266 | { AU1300_TOY_INT, IRQ_TYPE_EDGE_RISING, 1, 1, }, | ||
267 | { AU1300_TOY_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 1, 1, }, | ||
268 | { AU1300_TOY_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 1, 1, }, | ||
269 | { AU1300_TOY_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 1, 1, }, | ||
270 | { AU1300_RTC_INT, IRQ_TYPE_EDGE_RISING, 1, 1, }, | ||
271 | { AU1300_RTC_MATCH0_INT, IRQ_TYPE_EDGE_RISING, 1, 1, }, | ||
272 | { AU1300_RTC_MATCH1_INT, IRQ_TYPE_EDGE_RISING, 1, 1, }, | ||
273 | { AU1300_RTC_MATCH2_INT, IRQ_TYPE_EDGE_RISING, 0, 1, }, | ||
274 | { AU1300_UART0_INT, IRQ_TYPE_LEVEL_HIGH, 1, 1, }, | ||
275 | { AU1300_SD0_INT, IRQ_TYPE_LEVEL_HIGH, 1, 1, }, | ||
276 | { AU1300_USB_INT, IRQ_TYPE_LEVEL_HIGH, 1, 1, }, | ||
277 | { AU1300_LCD_INT, IRQ_TYPE_LEVEL_HIGH, 1, 1, }, | ||
278 | { AU1300_BSA_INT, IRQ_TYPE_LEVEL_HIGH, 1, 1, }, | ||
279 | { AU1300_MPE_INT, IRQ_TYPE_EDGE_RISING, 1, 1, }, | ||
280 | { AU1300_ITE_INT, IRQ_TYPE_LEVEL_HIGH, 1, 1, }, | ||
281 | { AU1300_AES_INT, IRQ_TYPE_LEVEL_HIGH, 1, 1, }, | ||
282 | { AU1300_CIM_INT, IRQ_TYPE_LEVEL_HIGH, 1, 1, }, | ||
283 | { -1, }, /* terminator */ | ||
284 | }; | ||
285 | |||
286 | /******************************************************************************/ | ||
287 | |||
288 | static void au1x_ic0_unmask(struct irq_data *d) | ||
289 | { | ||
290 | unsigned int bit = d->irq - AU1000_INTC0_INT_BASE; | ||
291 | void __iomem *base = (void __iomem *)KSEG1ADDR(AU1000_IC0_PHYS_ADDR); | ||
292 | |||
293 | __raw_writel(1 << bit, base + IC_MASKSET); | ||
294 | __raw_writel(1 << bit, base + IC_WAKESET); | ||
295 | wmb(); | ||
296 | } | ||
297 | |||
298 | static void au1x_ic1_unmask(struct irq_data *d) | ||
299 | { | ||
300 | unsigned int bit = d->irq - AU1000_INTC1_INT_BASE; | ||
301 | void __iomem *base = (void __iomem *)KSEG1ADDR(AU1000_IC1_PHYS_ADDR); | ||
302 | |||
303 | __raw_writel(1 << bit, base + IC_MASKSET); | ||
304 | __raw_writel(1 << bit, base + IC_WAKESET); | ||
305 | wmb(); | ||
306 | } | ||
307 | |||
308 | static void au1x_ic0_mask(struct irq_data *d) | ||
309 | { | ||
310 | unsigned int bit = d->irq - AU1000_INTC0_INT_BASE; | ||
311 | void __iomem *base = (void __iomem *)KSEG1ADDR(AU1000_IC0_PHYS_ADDR); | ||
312 | |||
313 | __raw_writel(1 << bit, base + IC_MASKCLR); | ||
314 | __raw_writel(1 << bit, base + IC_WAKECLR); | ||
315 | wmb(); | ||
316 | } | ||
317 | |||
318 | static void au1x_ic1_mask(struct irq_data *d) | ||
319 | { | ||
320 | unsigned int bit = d->irq - AU1000_INTC1_INT_BASE; | ||
321 | void __iomem *base = (void __iomem *)KSEG1ADDR(AU1000_IC1_PHYS_ADDR); | ||
322 | |||
323 | __raw_writel(1 << bit, base + IC_MASKCLR); | ||
324 | __raw_writel(1 << bit, base + IC_WAKECLR); | ||
325 | wmb(); | ||
326 | } | ||
327 | |||
328 | static void au1x_ic0_ack(struct irq_data *d) | ||
329 | { | ||
330 | unsigned int bit = d->irq - AU1000_INTC0_INT_BASE; | ||
331 | void __iomem *base = (void __iomem *)KSEG1ADDR(AU1000_IC0_PHYS_ADDR); | ||
332 | |||
333 | /* | ||
334 | * This may assume that we don't get interrupts from | ||
335 | * both edges at once, or if we do, that we don't care. | ||
336 | */ | ||
337 | __raw_writel(1 << bit, base + IC_FALLINGCLR); | ||
338 | __raw_writel(1 << bit, base + IC_RISINGCLR); | ||
339 | wmb(); | ||
340 | } | ||
341 | |||
342 | static void au1x_ic1_ack(struct irq_data *d) | ||
343 | { | ||
344 | unsigned int bit = d->irq - AU1000_INTC1_INT_BASE; | ||
345 | void __iomem *base = (void __iomem *)KSEG1ADDR(AU1000_IC1_PHYS_ADDR); | ||
346 | |||
347 | /* | ||
348 | * This may assume that we don't get interrupts from | ||
349 | * both edges at once, or if we do, that we don't care. | ||
350 | */ | ||
351 | __raw_writel(1 << bit, base + IC_FALLINGCLR); | ||
352 | __raw_writel(1 << bit, base + IC_RISINGCLR); | ||
353 | wmb(); | ||
354 | } | ||
355 | |||
356 | static void au1x_ic0_maskack(struct irq_data *d) | ||
357 | { | ||
358 | unsigned int bit = d->irq - AU1000_INTC0_INT_BASE; | ||
359 | void __iomem *base = (void __iomem *)KSEG1ADDR(AU1000_IC0_PHYS_ADDR); | ||
360 | |||
361 | __raw_writel(1 << bit, base + IC_WAKECLR); | ||
362 | __raw_writel(1 << bit, base + IC_MASKCLR); | ||
363 | __raw_writel(1 << bit, base + IC_RISINGCLR); | ||
364 | __raw_writel(1 << bit, base + IC_FALLINGCLR); | ||
365 | wmb(); | ||
366 | } | ||
367 | |||
368 | static void au1x_ic1_maskack(struct irq_data *d) | ||
369 | { | ||
370 | unsigned int bit = d->irq - AU1000_INTC1_INT_BASE; | ||
371 | void __iomem *base = (void __iomem *)KSEG1ADDR(AU1000_IC1_PHYS_ADDR); | ||
372 | |||
373 | __raw_writel(1 << bit, base + IC_WAKECLR); | ||
374 | __raw_writel(1 << bit, base + IC_MASKCLR); | ||
375 | __raw_writel(1 << bit, base + IC_RISINGCLR); | ||
376 | __raw_writel(1 << bit, base + IC_FALLINGCLR); | ||
377 | wmb(); | ||
378 | } | ||
379 | |||
380 | static int au1x_ic1_setwake(struct irq_data *d, unsigned int on) | ||
381 | { | ||
382 | int bit = d->irq - AU1000_INTC1_INT_BASE; | ||
383 | unsigned long wakemsk, flags; | ||
384 | |||
385 | /* only GPIO 0-7 can act as wakeup source. Fortunately these | ||
386 | * are wired up identically on all supported variants. | ||
387 | */ | ||
388 | if ((bit < 0) || (bit > 7)) | ||
389 | return -EINVAL; | ||
390 | |||
391 | local_irq_save(flags); | ||
392 | wakemsk = alchemy_rdsys(AU1000_SYS_WAKEMSK); | ||
393 | if (on) | ||
394 | wakemsk |= 1 << bit; | ||
395 | else | ||
396 | wakemsk &= ~(1 << bit); | ||
397 | alchemy_wrsys(wakemsk, AU1000_SYS_WAKEMSK); | ||
398 | local_irq_restore(flags); | ||
399 | |||
400 | return 0; | ||
401 | } | ||
402 | |||
403 | /* | ||
404 | * irq_chips for both ICs; this way the mask handlers can be | ||
405 | * as short as possible. | ||
406 | */ | ||
407 | static struct irq_chip au1x_ic0_chip = { | ||
408 | .name = "Alchemy-IC0", | ||
409 | .irq_ack = au1x_ic0_ack, | ||
410 | .irq_mask = au1x_ic0_mask, | ||
411 | .irq_mask_ack = au1x_ic0_maskack, | ||
412 | .irq_unmask = au1x_ic0_unmask, | ||
413 | .irq_set_type = au1x_ic_settype, | ||
414 | }; | ||
415 | |||
416 | static struct irq_chip au1x_ic1_chip = { | ||
417 | .name = "Alchemy-IC1", | ||
418 | .irq_ack = au1x_ic1_ack, | ||
419 | .irq_mask = au1x_ic1_mask, | ||
420 | .irq_mask_ack = au1x_ic1_maskack, | ||
421 | .irq_unmask = au1x_ic1_unmask, | ||
422 | .irq_set_type = au1x_ic_settype, | ||
423 | .irq_set_wake = au1x_ic1_setwake, | ||
424 | }; | ||
425 | |||
426 | static int au1x_ic_settype(struct irq_data *d, unsigned int flow_type) | ||
427 | { | ||
428 | struct irq_chip *chip; | ||
429 | unsigned int bit, irq = d->irq; | ||
430 | irq_flow_handler_t handler = NULL; | ||
431 | unsigned char *name = NULL; | ||
432 | void __iomem *base; | ||
433 | int ret; | ||
434 | |||
435 | if (irq >= AU1000_INTC1_INT_BASE) { | ||
436 | bit = irq - AU1000_INTC1_INT_BASE; | ||
437 | chip = &au1x_ic1_chip; | ||
438 | base = (void __iomem *)KSEG1ADDR(AU1000_IC1_PHYS_ADDR); | ||
439 | } else { | ||
440 | bit = irq - AU1000_INTC0_INT_BASE; | ||
441 | chip = &au1x_ic0_chip; | ||
442 | base = (void __iomem *)KSEG1ADDR(AU1000_IC0_PHYS_ADDR); | ||
443 | } | ||
444 | |||
445 | if (bit > 31) | ||
446 | return -EINVAL; | ||
447 | |||
448 | ret = 0; | ||
449 | |||
450 | switch (flow_type) { /* cfgregs 2:1:0 */ | ||
451 | case IRQ_TYPE_EDGE_RISING: /* 0:0:1 */ | ||
452 | __raw_writel(1 << bit, base + IC_CFG2CLR); | ||
453 | __raw_writel(1 << bit, base + IC_CFG1CLR); | ||
454 | __raw_writel(1 << bit, base + IC_CFG0SET); | ||
455 | handler = handle_edge_irq; | ||
456 | name = "riseedge"; | ||
457 | break; | ||
458 | case IRQ_TYPE_EDGE_FALLING: /* 0:1:0 */ | ||
459 | __raw_writel(1 << bit, base + IC_CFG2CLR); | ||
460 | __raw_writel(1 << bit, base + IC_CFG1SET); | ||
461 | __raw_writel(1 << bit, base + IC_CFG0CLR); | ||
462 | handler = handle_edge_irq; | ||
463 | name = "falledge"; | ||
464 | break; | ||
465 | case IRQ_TYPE_EDGE_BOTH: /* 0:1:1 */ | ||
466 | __raw_writel(1 << bit, base + IC_CFG2CLR); | ||
467 | __raw_writel(1 << bit, base + IC_CFG1SET); | ||
468 | __raw_writel(1 << bit, base + IC_CFG0SET); | ||
469 | handler = handle_edge_irq; | ||
470 | name = "bothedge"; | ||
471 | break; | ||
472 | case IRQ_TYPE_LEVEL_HIGH: /* 1:0:1 */ | ||
473 | __raw_writel(1 << bit, base + IC_CFG2SET); | ||
474 | __raw_writel(1 << bit, base + IC_CFG1CLR); | ||
475 | __raw_writel(1 << bit, base + IC_CFG0SET); | ||
476 | handler = handle_level_irq; | ||
477 | name = "hilevel"; | ||
478 | break; | ||
479 | case IRQ_TYPE_LEVEL_LOW: /* 1:1:0 */ | ||
480 | __raw_writel(1 << bit, base + IC_CFG2SET); | ||
481 | __raw_writel(1 << bit, base + IC_CFG1SET); | ||
482 | __raw_writel(1 << bit, base + IC_CFG0CLR); | ||
483 | handler = handle_level_irq; | ||
484 | name = "lowlevel"; | ||
485 | break; | ||
486 | case IRQ_TYPE_NONE: /* 0:0:0 */ | ||
487 | __raw_writel(1 << bit, base + IC_CFG2CLR); | ||
488 | __raw_writel(1 << bit, base + IC_CFG1CLR); | ||
489 | __raw_writel(1 << bit, base + IC_CFG0CLR); | ||
490 | break; | ||
491 | default: | ||
492 | ret = -EINVAL; | ||
493 | } | ||
494 | irq_set_chip_handler_name_locked(d, chip, handler, name); | ||
495 | |||
496 | wmb(); | ||
497 | |||
498 | return ret; | ||
499 | } | ||
500 | |||
501 | /******************************************************************************/ | ||
502 | |||
503 | /* | ||
504 | * au1300_gpic_chgcfg - change PIN configuration. | ||
505 | * @gpio: pin to change (0-based GPIO number from datasheet). | ||
506 | * @clr: clear all bits set in 'clr'. | ||
507 | * @set: set these bits. | ||
508 | * | ||
509 | * modifies a pins' configuration register, bits set in @clr will | ||
510 | * be cleared in the register, bits in @set will be set. | ||
511 | */ | ||
512 | static inline void au1300_gpic_chgcfg(unsigned int gpio, | ||
513 | unsigned long clr, | ||
514 | unsigned long set) | ||
515 | { | ||
516 | void __iomem *r = AU1300_GPIC_ADDR; | ||
517 | unsigned long l; | ||
518 | |||
519 | r += gpio * 4; /* offset into pin config array */ | ||
520 | l = __raw_readl(r + AU1300_GPIC_PINCFG); | ||
521 | l &= ~clr; | ||
522 | l |= set; | ||
523 | __raw_writel(l, r + AU1300_GPIC_PINCFG); | ||
524 | wmb(); | ||
525 | } | ||
526 | |||
527 | /* | ||
528 | * au1300_pinfunc_to_gpio - assign a pin as GPIO input (GPIO ctrl). | ||
529 | * @pin: pin (0-based GPIO number from datasheet). | ||
530 | * | ||
531 | * Assigns a GPIO pin to the GPIO controller, so its level can either | ||
532 | * be read or set through the generic GPIO functions. | ||
533 | * If you need a GPOUT, use au1300_gpio_set_value(pin, 0/1). | ||
534 | * REVISIT: is this function really necessary? | ||
535 | */ | ||
536 | void au1300_pinfunc_to_gpio(enum au1300_multifunc_pins gpio) | ||
537 | { | ||
538 | au1300_gpio_direction_input(gpio + AU1300_GPIO_BASE); | ||
539 | } | ||
540 | EXPORT_SYMBOL_GPL(au1300_pinfunc_to_gpio); | ||
541 | |||
542 | /* | ||
543 | * au1300_pinfunc_to_dev - assign a pin to the device function. | ||
544 | * @pin: pin (0-based GPIO number from datasheet). | ||
545 | * | ||
546 | * Assigns a GPIO pin to its associated device function; the pin will be | ||
547 | * driven by the device and not through GPIO functions. | ||
548 | */ | ||
549 | void au1300_pinfunc_to_dev(enum au1300_multifunc_pins gpio) | ||
550 | { | ||
551 | void __iomem *r = AU1300_GPIC_ADDR; | ||
552 | unsigned long bit; | ||
553 | |||
554 | r += GPIC_GPIO_BANKOFF(gpio); | ||
555 | bit = GPIC_GPIO_TO_BIT(gpio); | ||
556 | __raw_writel(bit, r + AU1300_GPIC_DEVSEL); | ||
557 | wmb(); | ||
558 | } | ||
559 | EXPORT_SYMBOL_GPL(au1300_pinfunc_to_dev); | ||
560 | |||
561 | /* | ||
562 | * au1300_set_irq_priority - set internal priority of IRQ. | ||
563 | * @irq: irq to set priority (linux irq number). | ||
564 | * @p: priority (0 = highest, 3 = lowest). | ||
565 | */ | ||
566 | void au1300_set_irq_priority(unsigned int irq, int p) | ||
567 | { | ||
568 | irq -= ALCHEMY_GPIC_INT_BASE; | ||
569 | au1300_gpic_chgcfg(irq, GPIC_CFG_IL_MASK, GPIC_CFG_IL_SET(p)); | ||
570 | } | ||
571 | EXPORT_SYMBOL_GPL(au1300_set_irq_priority); | ||
572 | |||
573 | /* | ||
574 | * au1300_set_dbdma_gpio - assign a gpio to one of the DBDMA triggers. | ||
575 | * @dchan: dbdma trigger select (0, 1). | ||
576 | * @gpio: pin to assign as trigger. | ||
577 | * | ||
578 | * DBDMA controller has 2 external trigger sources; this function | ||
579 | * assigns a GPIO to the selected trigger. | ||
580 | */ | ||
581 | void au1300_set_dbdma_gpio(int dchan, unsigned int gpio) | ||
582 | { | ||
583 | unsigned long r; | ||
584 | |||
585 | if ((dchan >= 0) && (dchan <= 1)) { | ||
586 | r = __raw_readl(AU1300_GPIC_ADDR + AU1300_GPIC_DMASEL); | ||
587 | r &= ~(0xff << (8 * dchan)); | ||
588 | r |= (gpio & 0x7f) << (8 * dchan); | ||
589 | __raw_writel(r, AU1300_GPIC_ADDR + AU1300_GPIC_DMASEL); | ||
590 | wmb(); | ||
591 | } | ||
592 | } | ||
593 | |||
594 | static inline void gpic_pin_set_idlewake(unsigned int gpio, int allow) | ||
595 | { | ||
596 | au1300_gpic_chgcfg(gpio, GPIC_CFG_IDLEWAKE, | ||
597 | allow ? GPIC_CFG_IDLEWAKE : 0); | ||
598 | } | ||
599 | |||
600 | static void au1300_gpic_mask(struct irq_data *d) | ||
601 | { | ||
602 | void __iomem *r = AU1300_GPIC_ADDR; | ||
603 | unsigned long bit, irq = d->irq; | ||
604 | |||
605 | irq -= ALCHEMY_GPIC_INT_BASE; | ||
606 | r += GPIC_GPIO_BANKOFF(irq); | ||
607 | bit = GPIC_GPIO_TO_BIT(irq); | ||
608 | __raw_writel(bit, r + AU1300_GPIC_IDIS); | ||
609 | wmb(); | ||
610 | |||
611 | gpic_pin_set_idlewake(irq, 0); | ||
612 | } | ||
613 | |||
614 | static void au1300_gpic_unmask(struct irq_data *d) | ||
615 | { | ||
616 | void __iomem *r = AU1300_GPIC_ADDR; | ||
617 | unsigned long bit, irq = d->irq; | ||
618 | |||
619 | irq -= ALCHEMY_GPIC_INT_BASE; | ||
620 | |||
621 | gpic_pin_set_idlewake(irq, 1); | ||
622 | |||
623 | r += GPIC_GPIO_BANKOFF(irq); | ||
624 | bit = GPIC_GPIO_TO_BIT(irq); | ||
625 | __raw_writel(bit, r + AU1300_GPIC_IEN); | ||
626 | wmb(); | ||
627 | } | ||
628 | |||
629 | static void au1300_gpic_maskack(struct irq_data *d) | ||
630 | { | ||
631 | void __iomem *r = AU1300_GPIC_ADDR; | ||
632 | unsigned long bit, irq = d->irq; | ||
633 | |||
634 | irq -= ALCHEMY_GPIC_INT_BASE; | ||
635 | r += GPIC_GPIO_BANKOFF(irq); | ||
636 | bit = GPIC_GPIO_TO_BIT(irq); | ||
637 | __raw_writel(bit, r + AU1300_GPIC_IPEND); /* ack */ | ||
638 | __raw_writel(bit, r + AU1300_GPIC_IDIS); /* mask */ | ||
639 | wmb(); | ||
640 | |||
641 | gpic_pin_set_idlewake(irq, 0); | ||
642 | } | ||
643 | |||
644 | static void au1300_gpic_ack(struct irq_data *d) | ||
645 | { | ||
646 | void __iomem *r = AU1300_GPIC_ADDR; | ||
647 | unsigned long bit, irq = d->irq; | ||
648 | |||
649 | irq -= ALCHEMY_GPIC_INT_BASE; | ||
650 | r += GPIC_GPIO_BANKOFF(irq); | ||
651 | bit = GPIC_GPIO_TO_BIT(irq); | ||
652 | __raw_writel(bit, r + AU1300_GPIC_IPEND); /* ack */ | ||
653 | wmb(); | ||
654 | } | ||
655 | |||
656 | static struct irq_chip au1300_gpic = { | ||
657 | .name = "GPIOINT", | ||
658 | .irq_ack = au1300_gpic_ack, | ||
659 | .irq_mask = au1300_gpic_mask, | ||
660 | .irq_mask_ack = au1300_gpic_maskack, | ||
661 | .irq_unmask = au1300_gpic_unmask, | ||
662 | .irq_set_type = au1300_gpic_settype, | ||
663 | }; | ||
664 | |||
665 | static int au1300_gpic_settype(struct irq_data *d, unsigned int type) | ||
666 | { | ||
667 | unsigned long s; | ||
668 | unsigned char *name = NULL; | ||
669 | irq_flow_handler_t hdl = NULL; | ||
670 | |||
671 | switch (type) { | ||
672 | case IRQ_TYPE_LEVEL_HIGH: | ||
673 | s = GPIC_CFG_IC_LEVEL_HIGH; | ||
674 | name = "high"; | ||
675 | hdl = handle_level_irq; | ||
676 | break; | ||
677 | case IRQ_TYPE_LEVEL_LOW: | ||
678 | s = GPIC_CFG_IC_LEVEL_LOW; | ||
679 | name = "low"; | ||
680 | hdl = handle_level_irq; | ||
681 | break; | ||
682 | case IRQ_TYPE_EDGE_RISING: | ||
683 | s = GPIC_CFG_IC_EDGE_RISE; | ||
684 | name = "posedge"; | ||
685 | hdl = handle_edge_irq; | ||
686 | break; | ||
687 | case IRQ_TYPE_EDGE_FALLING: | ||
688 | s = GPIC_CFG_IC_EDGE_FALL; | ||
689 | name = "negedge"; | ||
690 | hdl = handle_edge_irq; | ||
691 | break; | ||
692 | case IRQ_TYPE_EDGE_BOTH: | ||
693 | s = GPIC_CFG_IC_EDGE_BOTH; | ||
694 | name = "bothedge"; | ||
695 | hdl = handle_edge_irq; | ||
696 | break; | ||
697 | case IRQ_TYPE_NONE: | ||
698 | s = GPIC_CFG_IC_OFF; | ||
699 | name = "disabled"; | ||
700 | hdl = handle_level_irq; | ||
701 | break; | ||
702 | default: | ||
703 | return -EINVAL; | ||
704 | } | ||
705 | |||
706 | irq_set_chip_handler_name_locked(d, &au1300_gpic, hdl, name); | ||
707 | |||
708 | au1300_gpic_chgcfg(d->irq - ALCHEMY_GPIC_INT_BASE, GPIC_CFG_IC_MASK, s); | ||
709 | |||
710 | return 0; | ||
711 | } | ||
712 | |||
713 | /******************************************************************************/ | ||
714 | |||
715 | static inline void ic_init(void __iomem *base) | ||
716 | { | ||
717 | /* initialize interrupt controller to a safe state */ | ||
718 | __raw_writel(0xffffffff, base + IC_CFG0CLR); | ||
719 | __raw_writel(0xffffffff, base + IC_CFG1CLR); | ||
720 | __raw_writel(0xffffffff, base + IC_CFG2CLR); | ||
721 | __raw_writel(0xffffffff, base + IC_MASKCLR); | ||
722 | __raw_writel(0xffffffff, base + IC_ASSIGNCLR); | ||
723 | __raw_writel(0xffffffff, base + IC_WAKECLR); | ||
724 | __raw_writel(0xffffffff, base + IC_SRCSET); | ||
725 | __raw_writel(0xffffffff, base + IC_FALLINGCLR); | ||
726 | __raw_writel(0xffffffff, base + IC_RISINGCLR); | ||
727 | __raw_writel(0x00000000, base + IC_TESTBIT); | ||
728 | wmb(); | ||
729 | } | ||
730 | |||
731 | static unsigned long alchemy_gpic_pmdata[ALCHEMY_GPIC_INT_NUM + 6]; | ||
732 | |||
733 | static inline void alchemy_ic_suspend_one(void __iomem *base, unsigned long *d) | ||
734 | { | ||
735 | d[0] = __raw_readl(base + IC_CFG0RD); | ||
736 | d[1] = __raw_readl(base + IC_CFG1RD); | ||
737 | d[2] = __raw_readl(base + IC_CFG2RD); | ||
738 | d[3] = __raw_readl(base + IC_SRCRD); | ||
739 | d[4] = __raw_readl(base + IC_ASSIGNRD); | ||
740 | d[5] = __raw_readl(base + IC_WAKERD); | ||
741 | d[6] = __raw_readl(base + IC_MASKRD); | ||
742 | ic_init(base); /* shut it up too while at it */ | ||
743 | } | ||
744 | |||
745 | static inline void alchemy_ic_resume_one(void __iomem *base, unsigned long *d) | ||
746 | { | ||
747 | ic_init(base); | ||
748 | |||
749 | __raw_writel(d[0], base + IC_CFG0SET); | ||
750 | __raw_writel(d[1], base + IC_CFG1SET); | ||
751 | __raw_writel(d[2], base + IC_CFG2SET); | ||
752 | __raw_writel(d[3], base + IC_SRCSET); | ||
753 | __raw_writel(d[4], base + IC_ASSIGNSET); | ||
754 | __raw_writel(d[5], base + IC_WAKESET); | ||
755 | wmb(); | ||
756 | |||
757 | __raw_writel(d[6], base + IC_MASKSET); | ||
758 | wmb(); | ||
759 | } | ||
760 | |||
761 | static int alchemy_ic_suspend(void) | ||
762 | { | ||
763 | alchemy_ic_suspend_one((void __iomem *)KSEG1ADDR(AU1000_IC0_PHYS_ADDR), | ||
764 | alchemy_gpic_pmdata); | ||
765 | alchemy_ic_suspend_one((void __iomem *)KSEG1ADDR(AU1000_IC1_PHYS_ADDR), | ||
766 | &alchemy_gpic_pmdata[7]); | ||
767 | return 0; | ||
768 | } | ||
769 | |||
770 | static void alchemy_ic_resume(void) | ||
771 | { | ||
772 | alchemy_ic_resume_one((void __iomem *)KSEG1ADDR(AU1000_IC1_PHYS_ADDR), | ||
773 | &alchemy_gpic_pmdata[7]); | ||
774 | alchemy_ic_resume_one((void __iomem *)KSEG1ADDR(AU1000_IC0_PHYS_ADDR), | ||
775 | alchemy_gpic_pmdata); | ||
776 | } | ||
777 | |||
778 | static int alchemy_gpic_suspend(void) | ||
779 | { | ||
780 | void __iomem *base = (void __iomem *)KSEG1ADDR(AU1300_GPIC_PHYS_ADDR); | ||
781 | int i; | ||
782 | |||
783 | /* save 4 interrupt mask status registers */ | ||
784 | alchemy_gpic_pmdata[0] = __raw_readl(base + AU1300_GPIC_IEN + 0x0); | ||
785 | alchemy_gpic_pmdata[1] = __raw_readl(base + AU1300_GPIC_IEN + 0x4); | ||
786 | alchemy_gpic_pmdata[2] = __raw_readl(base + AU1300_GPIC_IEN + 0x8); | ||
787 | alchemy_gpic_pmdata[3] = __raw_readl(base + AU1300_GPIC_IEN + 0xc); | ||
788 | |||
789 | /* save misc register(s) */ | ||
790 | alchemy_gpic_pmdata[4] = __raw_readl(base + AU1300_GPIC_DMASEL); | ||
791 | |||
792 | /* molto silenzioso */ | ||
793 | __raw_writel(~0UL, base + AU1300_GPIC_IDIS + 0x0); | ||
794 | __raw_writel(~0UL, base + AU1300_GPIC_IDIS + 0x4); | ||
795 | __raw_writel(~0UL, base + AU1300_GPIC_IDIS + 0x8); | ||
796 | __raw_writel(~0UL, base + AU1300_GPIC_IDIS + 0xc); | ||
797 | wmb(); | ||
798 | |||
799 | /* save pin/int-type configuration */ | ||
800 | base += AU1300_GPIC_PINCFG; | ||
801 | for (i = 0; i < ALCHEMY_GPIC_INT_NUM; i++) | ||
802 | alchemy_gpic_pmdata[i + 5] = __raw_readl(base + (i << 2)); | ||
803 | |||
804 | wmb(); | ||
805 | |||
806 | return 0; | ||
807 | } | ||
808 | |||
809 | static void alchemy_gpic_resume(void) | ||
810 | { | ||
811 | void __iomem *base = (void __iomem *)KSEG1ADDR(AU1300_GPIC_PHYS_ADDR); | ||
812 | int i; | ||
813 | |||
814 | /* disable all first */ | ||
815 | __raw_writel(~0UL, base + AU1300_GPIC_IDIS + 0x0); | ||
816 | __raw_writel(~0UL, base + AU1300_GPIC_IDIS + 0x4); | ||
817 | __raw_writel(~0UL, base + AU1300_GPIC_IDIS + 0x8); | ||
818 | __raw_writel(~0UL, base + AU1300_GPIC_IDIS + 0xc); | ||
819 | wmb(); | ||
820 | |||
821 | /* restore pin/int-type configurations */ | ||
822 | base += AU1300_GPIC_PINCFG; | ||
823 | for (i = 0; i < ALCHEMY_GPIC_INT_NUM; i++) | ||
824 | __raw_writel(alchemy_gpic_pmdata[i + 5], base + (i << 2)); | ||
825 | wmb(); | ||
826 | |||
827 | /* restore misc register(s) */ | ||
828 | base = (void __iomem *)KSEG1ADDR(AU1300_GPIC_PHYS_ADDR); | ||
829 | __raw_writel(alchemy_gpic_pmdata[4], base + AU1300_GPIC_DMASEL); | ||
830 | wmb(); | ||
831 | |||
832 | /* finally restore masks */ | ||
833 | __raw_writel(alchemy_gpic_pmdata[0], base + AU1300_GPIC_IEN + 0x0); | ||
834 | __raw_writel(alchemy_gpic_pmdata[1], base + AU1300_GPIC_IEN + 0x4); | ||
835 | __raw_writel(alchemy_gpic_pmdata[2], base + AU1300_GPIC_IEN + 0x8); | ||
836 | __raw_writel(alchemy_gpic_pmdata[3], base + AU1300_GPIC_IEN + 0xc); | ||
837 | wmb(); | ||
838 | } | ||
839 | |||
840 | static struct syscore_ops alchemy_ic_pmops = { | ||
841 | .suspend = alchemy_ic_suspend, | ||
842 | .resume = alchemy_ic_resume, | ||
843 | }; | ||
844 | |||
845 | static struct syscore_ops alchemy_gpic_pmops = { | ||
846 | .suspend = alchemy_gpic_suspend, | ||
847 | .resume = alchemy_gpic_resume, | ||
848 | }; | ||
849 | |||
850 | /******************************************************************************/ | ||
851 | |||
852 | /* create chained handlers for the 4 IC requests to the MIPS IRQ ctrl */ | ||
853 | #define DISP(name, base, addr) \ | ||
854 | static void au1000_##name##_dispatch(struct irq_desc *d) \ | ||
855 | { \ | ||
856 | unsigned long r = __raw_readl((void __iomem *)KSEG1ADDR(addr)); \ | ||
857 | if (likely(r)) \ | ||
858 | generic_handle_irq(base + __ffs(r)); \ | ||
859 | else \ | ||
860 | spurious_interrupt(); \ | ||
861 | } | ||
862 | |||
863 | DISP(ic0r0, AU1000_INTC0_INT_BASE, AU1000_IC0_PHYS_ADDR + IC_REQ0INT) | ||
864 | DISP(ic0r1, AU1000_INTC0_INT_BASE, AU1000_IC0_PHYS_ADDR + IC_REQ1INT) | ||
865 | DISP(ic1r0, AU1000_INTC1_INT_BASE, AU1000_IC1_PHYS_ADDR + IC_REQ0INT) | ||
866 | DISP(ic1r1, AU1000_INTC1_INT_BASE, AU1000_IC1_PHYS_ADDR + IC_REQ1INT) | ||
867 | |||
868 | static void alchemy_gpic_dispatch(struct irq_desc *d) | ||
869 | { | ||
870 | int i = __raw_readl(AU1300_GPIC_ADDR + AU1300_GPIC_PRIENC); | ||
871 | generic_handle_irq(ALCHEMY_GPIC_INT_BASE + i); | ||
872 | } | ||
873 | |||
874 | /******************************************************************************/ | ||
875 | |||
876 | static void __init au1000_init_irq(struct alchemy_irqmap *map) | ||
877 | { | ||
878 | unsigned int bit, irq_nr; | ||
879 | void __iomem *base; | ||
880 | |||
881 | ic_init((void __iomem *)KSEG1ADDR(AU1000_IC0_PHYS_ADDR)); | ||
882 | ic_init((void __iomem *)KSEG1ADDR(AU1000_IC1_PHYS_ADDR)); | ||
883 | register_syscore_ops(&alchemy_ic_pmops); | ||
884 | mips_cpu_irq_init(); | ||
885 | |||
886 | /* register all 64 possible IC0+IC1 irq sources as type "none". | ||
887 | * Use set_irq_type() to set edge/level behaviour at runtime. | ||
888 | */ | ||
889 | for (irq_nr = AU1000_INTC0_INT_BASE; | ||
890 | (irq_nr < AU1000_INTC0_INT_BASE + 32); irq_nr++) | ||
891 | au1x_ic_settype(irq_get_irq_data(irq_nr), IRQ_TYPE_NONE); | ||
892 | |||
893 | for (irq_nr = AU1000_INTC1_INT_BASE; | ||
894 | (irq_nr < AU1000_INTC1_INT_BASE + 32); irq_nr++) | ||
895 | au1x_ic_settype(irq_get_irq_data(irq_nr), IRQ_TYPE_NONE); | ||
896 | |||
897 | /* | ||
898 | * Initialize IC0, which is fixed per processor. | ||
899 | */ | ||
900 | while (map->irq != -1) { | ||
901 | irq_nr = map->irq; | ||
902 | |||
903 | if (irq_nr >= AU1000_INTC1_INT_BASE) { | ||
904 | bit = irq_nr - AU1000_INTC1_INT_BASE; | ||
905 | base = (void __iomem *)KSEG1ADDR(AU1000_IC1_PHYS_ADDR); | ||
906 | } else { | ||
907 | bit = irq_nr - AU1000_INTC0_INT_BASE; | ||
908 | base = (void __iomem *)KSEG1ADDR(AU1000_IC0_PHYS_ADDR); | ||
909 | } | ||
910 | if (map->prio == 0) | ||
911 | __raw_writel(1 << bit, base + IC_ASSIGNSET); | ||
912 | |||
913 | au1x_ic_settype(irq_get_irq_data(irq_nr), map->type); | ||
914 | ++map; | ||
915 | } | ||
916 | |||
917 | irq_set_chained_handler(MIPS_CPU_IRQ_BASE + 2, au1000_ic0r0_dispatch); | ||
918 | irq_set_chained_handler(MIPS_CPU_IRQ_BASE + 3, au1000_ic0r1_dispatch); | ||
919 | irq_set_chained_handler(MIPS_CPU_IRQ_BASE + 4, au1000_ic1r0_dispatch); | ||
920 | irq_set_chained_handler(MIPS_CPU_IRQ_BASE + 5, au1000_ic1r1_dispatch); | ||
921 | } | ||
922 | |||
923 | static void __init alchemy_gpic_init_irq(const struct alchemy_irqmap *dints) | ||
924 | { | ||
925 | int i; | ||
926 | void __iomem *bank_base; | ||
927 | |||
928 | register_syscore_ops(&alchemy_gpic_pmops); | ||
929 | mips_cpu_irq_init(); | ||
930 | |||
931 | /* disable & ack all possible interrupt sources */ | ||
932 | for (i = 0; i < 4; i++) { | ||
933 | bank_base = AU1300_GPIC_ADDR + (i * 4); | ||
934 | __raw_writel(~0UL, bank_base + AU1300_GPIC_IDIS); | ||
935 | wmb(); | ||
936 | __raw_writel(~0UL, bank_base + AU1300_GPIC_IPEND); | ||
937 | wmb(); | ||
938 | } | ||
939 | |||
940 | /* register an irq_chip for them, with 2nd highest priority */ | ||
941 | for (i = ALCHEMY_GPIC_INT_BASE; i <= ALCHEMY_GPIC_INT_LAST; i++) { | ||
942 | au1300_set_irq_priority(i, 1); | ||
943 | au1300_gpic_settype(irq_get_irq_data(i), IRQ_TYPE_NONE); | ||
944 | } | ||
945 | |||
946 | /* setup known on-chip sources */ | ||
947 | while ((i = dints->irq) != -1) { | ||
948 | au1300_gpic_settype(irq_get_irq_data(i), dints->type); | ||
949 | au1300_set_irq_priority(i, dints->prio); | ||
950 | |||
951 | if (dints->internal) | ||
952 | au1300_pinfunc_to_dev(i - ALCHEMY_GPIC_INT_BASE); | ||
953 | |||
954 | dints++; | ||
955 | } | ||
956 | |||
957 | irq_set_chained_handler(MIPS_CPU_IRQ_BASE + 2, alchemy_gpic_dispatch); | ||
958 | irq_set_chained_handler(MIPS_CPU_IRQ_BASE + 3, alchemy_gpic_dispatch); | ||
959 | irq_set_chained_handler(MIPS_CPU_IRQ_BASE + 4, alchemy_gpic_dispatch); | ||
960 | irq_set_chained_handler(MIPS_CPU_IRQ_BASE + 5, alchemy_gpic_dispatch); | ||
961 | } | ||
962 | |||
963 | /******************************************************************************/ | ||
964 | |||
965 | void __init arch_init_irq(void) | ||
966 | { | ||
967 | switch (alchemy_get_cputype()) { | ||
968 | case ALCHEMY_CPU_AU1000: | ||
969 | au1000_init_irq(au1000_irqmap); | ||
970 | break; | ||
971 | case ALCHEMY_CPU_AU1500: | ||
972 | au1000_init_irq(au1500_irqmap); | ||
973 | break; | ||
974 | case ALCHEMY_CPU_AU1100: | ||
975 | au1000_init_irq(au1100_irqmap); | ||
976 | break; | ||
977 | case ALCHEMY_CPU_AU1550: | ||
978 | au1000_init_irq(au1550_irqmap); | ||
979 | break; | ||
980 | case ALCHEMY_CPU_AU1200: | ||
981 | au1000_init_irq(au1200_irqmap); | ||
982 | break; | ||
983 | case ALCHEMY_CPU_AU1300: | ||
984 | alchemy_gpic_init_irq(au1300_irqmap); | ||
985 | break; | ||
986 | default: | ||
987 | pr_err("unknown Alchemy IRQ core\n"); | ||
988 | break; | ||
989 | } | ||
990 | } | ||
991 | |||
992 | asmlinkage void plat_irq_dispatch(void) | ||
993 | { | ||
994 | unsigned long r = (read_c0_status() & read_c0_cause()) >> 8; | ||
995 | do_IRQ(MIPS_CPU_IRQ_BASE + __ffs(r & 0xff)); | ||
996 | } | ||
diff --git a/arch/mips/alchemy/common/platform.c b/arch/mips/alchemy/common/platform.c new file mode 100644 index 000000000..b8f3397c5 --- /dev/null +++ b/arch/mips/alchemy/common/platform.c | |||
@@ -0,0 +1,458 @@ | |||
1 | /* | ||
2 | * Platform device support for Au1x00 SoCs. | ||
3 | * | ||
4 | * Copyright 2004, Matt Porter <mporter@kernel.crashing.org> | ||
5 | * | ||
6 | * (C) Copyright Embedded Alley Solutions, Inc 2005 | ||
7 | * Author: Pantelis Antoniou <pantelis@embeddedalley.com> | ||
8 | * | ||
9 | * This file is licensed under the terms of the GNU General Public | ||
10 | * License version 2. This program is licensed "as is" without any | ||
11 | * warranty of any kind, whether express or implied. | ||
12 | */ | ||
13 | |||
14 | #include <linux/clk.h> | ||
15 | #include <linux/dma-mapping.h> | ||
16 | #include <linux/etherdevice.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/platform_device.h> | ||
19 | #include <linux/serial_8250.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/usb/ehci_pdriver.h> | ||
22 | #include <linux/usb/ohci_pdriver.h> | ||
23 | |||
24 | #include <asm/mach-au1x00/au1000.h> | ||
25 | #include <asm/mach-au1x00/au1xxx_dbdma.h> | ||
26 | #include <asm/mach-au1x00/au1100_mmc.h> | ||
27 | #include <asm/mach-au1x00/au1xxx_eth.h> | ||
28 | |||
29 | #include <prom.h> | ||
30 | |||
31 | static void alchemy_8250_pm(struct uart_port *port, unsigned int state, | ||
32 | unsigned int old_state) | ||
33 | { | ||
34 | #ifdef CONFIG_SERIAL_8250 | ||
35 | switch (state) { | ||
36 | case 0: | ||
37 | alchemy_uart_enable(CPHYSADDR(port->membase)); | ||
38 | serial8250_do_pm(port, state, old_state); | ||
39 | break; | ||
40 | case 3: /* power off */ | ||
41 | serial8250_do_pm(port, state, old_state); | ||
42 | alchemy_uart_disable(CPHYSADDR(port->membase)); | ||
43 | break; | ||
44 | default: | ||
45 | serial8250_do_pm(port, state, old_state); | ||
46 | break; | ||
47 | } | ||
48 | #endif | ||
49 | } | ||
50 | |||
51 | #define PORT(_base, _irq) \ | ||
52 | { \ | ||
53 | .mapbase = _base, \ | ||
54 | .irq = _irq, \ | ||
55 | .regshift = 2, \ | ||
56 | .iotype = UPIO_AU, \ | ||
57 | .flags = UPF_SKIP_TEST | UPF_IOREMAP | \ | ||
58 | UPF_FIXED_TYPE, \ | ||
59 | .type = PORT_16550A, \ | ||
60 | .pm = alchemy_8250_pm, \ | ||
61 | } | ||
62 | |||
63 | static struct plat_serial8250_port au1x00_uart_data[][4] __initdata = { | ||
64 | [ALCHEMY_CPU_AU1000] = { | ||
65 | PORT(AU1000_UART0_PHYS_ADDR, AU1000_UART0_INT), | ||
66 | PORT(AU1000_UART1_PHYS_ADDR, AU1000_UART1_INT), | ||
67 | PORT(AU1000_UART2_PHYS_ADDR, AU1000_UART2_INT), | ||
68 | PORT(AU1000_UART3_PHYS_ADDR, AU1000_UART3_INT), | ||
69 | }, | ||
70 | [ALCHEMY_CPU_AU1500] = { | ||
71 | PORT(AU1000_UART0_PHYS_ADDR, AU1500_UART0_INT), | ||
72 | PORT(AU1000_UART3_PHYS_ADDR, AU1500_UART3_INT), | ||
73 | }, | ||
74 | [ALCHEMY_CPU_AU1100] = { | ||
75 | PORT(AU1000_UART0_PHYS_ADDR, AU1100_UART0_INT), | ||
76 | PORT(AU1000_UART1_PHYS_ADDR, AU1100_UART1_INT), | ||
77 | PORT(AU1000_UART3_PHYS_ADDR, AU1100_UART3_INT), | ||
78 | }, | ||
79 | [ALCHEMY_CPU_AU1550] = { | ||
80 | PORT(AU1000_UART0_PHYS_ADDR, AU1550_UART0_INT), | ||
81 | PORT(AU1000_UART1_PHYS_ADDR, AU1550_UART1_INT), | ||
82 | PORT(AU1000_UART3_PHYS_ADDR, AU1550_UART3_INT), | ||
83 | }, | ||
84 | [ALCHEMY_CPU_AU1200] = { | ||
85 | PORT(AU1000_UART0_PHYS_ADDR, AU1200_UART0_INT), | ||
86 | PORT(AU1000_UART1_PHYS_ADDR, AU1200_UART1_INT), | ||
87 | }, | ||
88 | [ALCHEMY_CPU_AU1300] = { | ||
89 | PORT(AU1300_UART0_PHYS_ADDR, AU1300_UART0_INT), | ||
90 | PORT(AU1300_UART1_PHYS_ADDR, AU1300_UART1_INT), | ||
91 | PORT(AU1300_UART2_PHYS_ADDR, AU1300_UART2_INT), | ||
92 | PORT(AU1300_UART3_PHYS_ADDR, AU1300_UART3_INT), | ||
93 | }, | ||
94 | }; | ||
95 | |||
96 | static struct platform_device au1xx0_uart_device = { | ||
97 | .name = "serial8250", | ||
98 | .id = PLAT8250_DEV_AU1X00, | ||
99 | }; | ||
100 | |||
101 | static void __init alchemy_setup_uarts(int ctype) | ||
102 | { | ||
103 | long uartclk; | ||
104 | int s = sizeof(struct plat_serial8250_port); | ||
105 | int c = alchemy_get_uarts(ctype); | ||
106 | struct plat_serial8250_port *ports; | ||
107 | struct clk *clk = clk_get(NULL, ALCHEMY_PERIPH_CLK); | ||
108 | |||
109 | if (IS_ERR(clk)) | ||
110 | return; | ||
111 | if (clk_prepare_enable(clk)) { | ||
112 | clk_put(clk); | ||
113 | return; | ||
114 | } | ||
115 | uartclk = clk_get_rate(clk); | ||
116 | clk_put(clk); | ||
117 | |||
118 | ports = kcalloc(s, (c + 1), GFP_KERNEL); | ||
119 | if (!ports) { | ||
120 | printk(KERN_INFO "Alchemy: no memory for UART data\n"); | ||
121 | return; | ||
122 | } | ||
123 | memcpy(ports, au1x00_uart_data[ctype], s * c); | ||
124 | au1xx0_uart_device.dev.platform_data = ports; | ||
125 | |||
126 | /* Fill up uartclk. */ | ||
127 | for (s = 0; s < c; s++) | ||
128 | ports[s].uartclk = uartclk; | ||
129 | if (platform_device_register(&au1xx0_uart_device)) | ||
130 | printk(KERN_INFO "Alchemy: failed to register UARTs\n"); | ||
131 | } | ||
132 | |||
133 | |||
134 | static u64 alchemy_all_dmamask = DMA_BIT_MASK(32); | ||
135 | |||
136 | /* Power on callback for the ehci platform driver */ | ||
137 | static int alchemy_ehci_power_on(struct platform_device *pdev) | ||
138 | { | ||
139 | return alchemy_usb_control(ALCHEMY_USB_EHCI0, 1); | ||
140 | } | ||
141 | |||
142 | /* Power off/suspend callback for the ehci platform driver */ | ||
143 | static void alchemy_ehci_power_off(struct platform_device *pdev) | ||
144 | { | ||
145 | alchemy_usb_control(ALCHEMY_USB_EHCI0, 0); | ||
146 | } | ||
147 | |||
148 | static struct usb_ehci_pdata alchemy_ehci_pdata = { | ||
149 | .no_io_watchdog = 1, | ||
150 | .power_on = alchemy_ehci_power_on, | ||
151 | .power_off = alchemy_ehci_power_off, | ||
152 | .power_suspend = alchemy_ehci_power_off, | ||
153 | }; | ||
154 | |||
155 | /* Power on callback for the ohci platform driver */ | ||
156 | static int alchemy_ohci_power_on(struct platform_device *pdev) | ||
157 | { | ||
158 | int unit; | ||
159 | |||
160 | unit = (pdev->id == 1) ? | ||
161 | ALCHEMY_USB_OHCI1 : ALCHEMY_USB_OHCI0; | ||
162 | |||
163 | return alchemy_usb_control(unit, 1); | ||
164 | } | ||
165 | |||
166 | /* Power off/suspend callback for the ohci platform driver */ | ||
167 | static void alchemy_ohci_power_off(struct platform_device *pdev) | ||
168 | { | ||
169 | int unit; | ||
170 | |||
171 | unit = (pdev->id == 1) ? | ||
172 | ALCHEMY_USB_OHCI1 : ALCHEMY_USB_OHCI0; | ||
173 | |||
174 | alchemy_usb_control(unit, 0); | ||
175 | } | ||
176 | |||
177 | static struct usb_ohci_pdata alchemy_ohci_pdata = { | ||
178 | .power_on = alchemy_ohci_power_on, | ||
179 | .power_off = alchemy_ohci_power_off, | ||
180 | .power_suspend = alchemy_ohci_power_off, | ||
181 | }; | ||
182 | |||
183 | static unsigned long alchemy_ohci_data[][2] __initdata = { | ||
184 | [ALCHEMY_CPU_AU1000] = { AU1000_USB_OHCI_PHYS_ADDR, AU1000_USB_HOST_INT }, | ||
185 | [ALCHEMY_CPU_AU1500] = { AU1000_USB_OHCI_PHYS_ADDR, AU1500_USB_HOST_INT }, | ||
186 | [ALCHEMY_CPU_AU1100] = { AU1000_USB_OHCI_PHYS_ADDR, AU1100_USB_HOST_INT }, | ||
187 | [ALCHEMY_CPU_AU1550] = { AU1550_USB_OHCI_PHYS_ADDR, AU1550_USB_HOST_INT }, | ||
188 | [ALCHEMY_CPU_AU1200] = { AU1200_USB_OHCI_PHYS_ADDR, AU1200_USB_INT }, | ||
189 | [ALCHEMY_CPU_AU1300] = { AU1300_USB_OHCI0_PHYS_ADDR, AU1300_USB_INT }, | ||
190 | }; | ||
191 | |||
192 | static unsigned long alchemy_ehci_data[][2] __initdata = { | ||
193 | [ALCHEMY_CPU_AU1200] = { AU1200_USB_EHCI_PHYS_ADDR, AU1200_USB_INT }, | ||
194 | [ALCHEMY_CPU_AU1300] = { AU1300_USB_EHCI_PHYS_ADDR, AU1300_USB_INT }, | ||
195 | }; | ||
196 | |||
197 | static int __init _new_usbres(struct resource **r, struct platform_device **d) | ||
198 | { | ||
199 | *r = kcalloc(2, sizeof(struct resource), GFP_KERNEL); | ||
200 | if (!*r) | ||
201 | return -ENOMEM; | ||
202 | *d = kzalloc(sizeof(struct platform_device), GFP_KERNEL); | ||
203 | if (!*d) { | ||
204 | kfree(*r); | ||
205 | return -ENOMEM; | ||
206 | } | ||
207 | |||
208 | (*d)->dev.coherent_dma_mask = DMA_BIT_MASK(32); | ||
209 | (*d)->num_resources = 2; | ||
210 | (*d)->resource = *r; | ||
211 | |||
212 | return 0; | ||
213 | } | ||
214 | |||
215 | static void __init alchemy_setup_usb(int ctype) | ||
216 | { | ||
217 | struct resource *res; | ||
218 | struct platform_device *pdev; | ||
219 | |||
220 | /* setup OHCI0. Every variant has one */ | ||
221 | if (_new_usbres(&res, &pdev)) | ||
222 | return; | ||
223 | |||
224 | res[0].start = alchemy_ohci_data[ctype][0]; | ||
225 | res[0].end = res[0].start + 0x100 - 1; | ||
226 | res[0].flags = IORESOURCE_MEM; | ||
227 | res[1].start = alchemy_ohci_data[ctype][1]; | ||
228 | res[1].end = res[1].start; | ||
229 | res[1].flags = IORESOURCE_IRQ; | ||
230 | pdev->name = "ohci-platform"; | ||
231 | pdev->id = 0; | ||
232 | pdev->dev.dma_mask = &alchemy_all_dmamask; | ||
233 | pdev->dev.platform_data = &alchemy_ohci_pdata; | ||
234 | |||
235 | if (platform_device_register(pdev)) | ||
236 | printk(KERN_INFO "Alchemy USB: cannot add OHCI0\n"); | ||
237 | |||
238 | |||
239 | /* setup EHCI0: Au1200/Au1300 */ | ||
240 | if ((ctype == ALCHEMY_CPU_AU1200) || (ctype == ALCHEMY_CPU_AU1300)) { | ||
241 | if (_new_usbres(&res, &pdev)) | ||
242 | return; | ||
243 | |||
244 | res[0].start = alchemy_ehci_data[ctype][0]; | ||
245 | res[0].end = res[0].start + 0x100 - 1; | ||
246 | res[0].flags = IORESOURCE_MEM; | ||
247 | res[1].start = alchemy_ehci_data[ctype][1]; | ||
248 | res[1].end = res[1].start; | ||
249 | res[1].flags = IORESOURCE_IRQ; | ||
250 | pdev->name = "ehci-platform"; | ||
251 | pdev->id = 0; | ||
252 | pdev->dev.dma_mask = &alchemy_all_dmamask; | ||
253 | pdev->dev.platform_data = &alchemy_ehci_pdata; | ||
254 | |||
255 | if (platform_device_register(pdev)) | ||
256 | printk(KERN_INFO "Alchemy USB: cannot add EHCI0\n"); | ||
257 | } | ||
258 | |||
259 | /* Au1300: OHCI1 */ | ||
260 | if (ctype == ALCHEMY_CPU_AU1300) { | ||
261 | if (_new_usbres(&res, &pdev)) | ||
262 | return; | ||
263 | |||
264 | res[0].start = AU1300_USB_OHCI1_PHYS_ADDR; | ||
265 | res[0].end = res[0].start + 0x100 - 1; | ||
266 | res[0].flags = IORESOURCE_MEM; | ||
267 | res[1].start = AU1300_USB_INT; | ||
268 | res[1].end = res[1].start; | ||
269 | res[1].flags = IORESOURCE_IRQ; | ||
270 | pdev->name = "ohci-platform"; | ||
271 | pdev->id = 1; | ||
272 | pdev->dev.dma_mask = &alchemy_all_dmamask; | ||
273 | pdev->dev.platform_data = &alchemy_ohci_pdata; | ||
274 | |||
275 | if (platform_device_register(pdev)) | ||
276 | printk(KERN_INFO "Alchemy USB: cannot add OHCI1\n"); | ||
277 | } | ||
278 | } | ||
279 | |||
280 | /* Macro to help defining the Ethernet MAC resources */ | ||
281 | #define MAC_RES_COUNT 4 /* MAC regs, MAC en, MAC INT, MACDMA regs */ | ||
282 | #define MAC_RES(_base, _enable, _irq, _macdma) \ | ||
283 | { \ | ||
284 | .start = _base, \ | ||
285 | .end = _base + 0xffff, \ | ||
286 | .flags = IORESOURCE_MEM, \ | ||
287 | }, \ | ||
288 | { \ | ||
289 | .start = _enable, \ | ||
290 | .end = _enable + 0x3, \ | ||
291 | .flags = IORESOURCE_MEM, \ | ||
292 | }, \ | ||
293 | { \ | ||
294 | .start = _irq, \ | ||
295 | .end = _irq, \ | ||
296 | .flags = IORESOURCE_IRQ \ | ||
297 | }, \ | ||
298 | { \ | ||
299 | .start = _macdma, \ | ||
300 | .end = _macdma + 0x1ff, \ | ||
301 | .flags = IORESOURCE_MEM, \ | ||
302 | } | ||
303 | |||
304 | static struct resource au1xxx_eth0_resources[][MAC_RES_COUNT] __initdata = { | ||
305 | [ALCHEMY_CPU_AU1000] = { | ||
306 | MAC_RES(AU1000_MAC0_PHYS_ADDR, | ||
307 | AU1000_MACEN_PHYS_ADDR, | ||
308 | AU1000_MAC0_DMA_INT, | ||
309 | AU1000_MACDMA0_PHYS_ADDR) | ||
310 | }, | ||
311 | [ALCHEMY_CPU_AU1500] = { | ||
312 | MAC_RES(AU1500_MAC0_PHYS_ADDR, | ||
313 | AU1500_MACEN_PHYS_ADDR, | ||
314 | AU1500_MAC0_DMA_INT, | ||
315 | AU1000_MACDMA0_PHYS_ADDR) | ||
316 | }, | ||
317 | [ALCHEMY_CPU_AU1100] = { | ||
318 | MAC_RES(AU1000_MAC0_PHYS_ADDR, | ||
319 | AU1000_MACEN_PHYS_ADDR, | ||
320 | AU1100_MAC0_DMA_INT, | ||
321 | AU1000_MACDMA0_PHYS_ADDR) | ||
322 | }, | ||
323 | [ALCHEMY_CPU_AU1550] = { | ||
324 | MAC_RES(AU1000_MAC0_PHYS_ADDR, | ||
325 | AU1000_MACEN_PHYS_ADDR, | ||
326 | AU1550_MAC0_DMA_INT, | ||
327 | AU1000_MACDMA0_PHYS_ADDR) | ||
328 | }, | ||
329 | }; | ||
330 | |||
331 | static struct au1000_eth_platform_data au1xxx_eth0_platform_data = { | ||
332 | .phy1_search_mac0 = 1, | ||
333 | }; | ||
334 | |||
335 | static struct platform_device au1xxx_eth0_device = { | ||
336 | .name = "au1000-eth", | ||
337 | .id = 0, | ||
338 | .num_resources = MAC_RES_COUNT, | ||
339 | .dev = { | ||
340 | .dma_mask = &alchemy_all_dmamask, | ||
341 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
342 | .platform_data = &au1xxx_eth0_platform_data, | ||
343 | }, | ||
344 | }; | ||
345 | |||
346 | static struct resource au1xxx_eth1_resources[][MAC_RES_COUNT] __initdata = { | ||
347 | [ALCHEMY_CPU_AU1000] = { | ||
348 | MAC_RES(AU1000_MAC1_PHYS_ADDR, | ||
349 | AU1000_MACEN_PHYS_ADDR + 4, | ||
350 | AU1000_MAC1_DMA_INT, | ||
351 | AU1000_MACDMA1_PHYS_ADDR) | ||
352 | }, | ||
353 | [ALCHEMY_CPU_AU1500] = { | ||
354 | MAC_RES(AU1500_MAC1_PHYS_ADDR, | ||
355 | AU1500_MACEN_PHYS_ADDR + 4, | ||
356 | AU1500_MAC1_DMA_INT, | ||
357 | AU1000_MACDMA1_PHYS_ADDR) | ||
358 | }, | ||
359 | [ALCHEMY_CPU_AU1550] = { | ||
360 | MAC_RES(AU1000_MAC1_PHYS_ADDR, | ||
361 | AU1000_MACEN_PHYS_ADDR + 4, | ||
362 | AU1550_MAC1_DMA_INT, | ||
363 | AU1000_MACDMA1_PHYS_ADDR) | ||
364 | }, | ||
365 | }; | ||
366 | |||
367 | static struct au1000_eth_platform_data au1xxx_eth1_platform_data = { | ||
368 | .phy1_search_mac0 = 1, | ||
369 | }; | ||
370 | |||
371 | static struct platform_device au1xxx_eth1_device = { | ||
372 | .name = "au1000-eth", | ||
373 | .id = 1, | ||
374 | .num_resources = MAC_RES_COUNT, | ||
375 | .dev = { | ||
376 | .dma_mask = &alchemy_all_dmamask, | ||
377 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
378 | .platform_data = &au1xxx_eth1_platform_data, | ||
379 | }, | ||
380 | }; | ||
381 | |||
382 | void __init au1xxx_override_eth_cfg(unsigned int port, | ||
383 | struct au1000_eth_platform_data *eth_data) | ||
384 | { | ||
385 | if (!eth_data || port > 1) | ||
386 | return; | ||
387 | |||
388 | if (port == 0) | ||
389 | memcpy(&au1xxx_eth0_platform_data, eth_data, | ||
390 | sizeof(struct au1000_eth_platform_data)); | ||
391 | else | ||
392 | memcpy(&au1xxx_eth1_platform_data, eth_data, | ||
393 | sizeof(struct au1000_eth_platform_data)); | ||
394 | } | ||
395 | |||
396 | static void __init alchemy_setup_macs(int ctype) | ||
397 | { | ||
398 | int ret, i; | ||
399 | unsigned char ethaddr[6]; | ||
400 | struct resource *macres; | ||
401 | |||
402 | /* Handle 1st MAC */ | ||
403 | if (alchemy_get_macs(ctype) < 1) | ||
404 | return; | ||
405 | |||
406 | macres = kmemdup(au1xxx_eth0_resources[ctype], | ||
407 | sizeof(struct resource) * MAC_RES_COUNT, GFP_KERNEL); | ||
408 | if (!macres) { | ||
409 | printk(KERN_INFO "Alchemy: no memory for MAC0 resources\n"); | ||
410 | return; | ||
411 | } | ||
412 | au1xxx_eth0_device.resource = macres; | ||
413 | |||
414 | i = prom_get_ethernet_addr(ethaddr); | ||
415 | if (!i && !is_valid_ether_addr(au1xxx_eth0_platform_data.mac)) | ||
416 | memcpy(au1xxx_eth0_platform_data.mac, ethaddr, 6); | ||
417 | |||
418 | ret = platform_device_register(&au1xxx_eth0_device); | ||
419 | if (ret) | ||
420 | printk(KERN_INFO "Alchemy: failed to register MAC0\n"); | ||
421 | |||
422 | |||
423 | /* Handle 2nd MAC */ | ||
424 | if (alchemy_get_macs(ctype) < 2) | ||
425 | return; | ||
426 | |||
427 | macres = kmemdup(au1xxx_eth1_resources[ctype], | ||
428 | sizeof(struct resource) * MAC_RES_COUNT, GFP_KERNEL); | ||
429 | if (!macres) { | ||
430 | printk(KERN_INFO "Alchemy: no memory for MAC1 resources\n"); | ||
431 | return; | ||
432 | } | ||
433 | au1xxx_eth1_device.resource = macres; | ||
434 | |||
435 | ethaddr[5] += 1; /* next addr for 2nd MAC */ | ||
436 | if (!i && !is_valid_ether_addr(au1xxx_eth1_platform_data.mac)) | ||
437 | memcpy(au1xxx_eth1_platform_data.mac, ethaddr, 6); | ||
438 | |||
439 | /* Register second MAC if enabled in pinfunc */ | ||
440 | if (!(alchemy_rdsys(AU1000_SYS_PINFUNC) & SYS_PF_NI2)) { | ||
441 | ret = platform_device_register(&au1xxx_eth1_device); | ||
442 | if (ret) | ||
443 | printk(KERN_INFO "Alchemy: failed to register MAC1\n"); | ||
444 | } | ||
445 | } | ||
446 | |||
447 | static int __init au1xxx_platform_init(void) | ||
448 | { | ||
449 | int ctype = alchemy_get_cputype(); | ||
450 | |||
451 | alchemy_setup_uarts(ctype); | ||
452 | alchemy_setup_macs(ctype); | ||
453 | alchemy_setup_usb(ctype); | ||
454 | |||
455 | return 0; | ||
456 | } | ||
457 | |||
458 | arch_initcall(au1xxx_platform_init); | ||
diff --git a/arch/mips/alchemy/common/power.c b/arch/mips/alchemy/common/power.c new file mode 100644 index 000000000..303257b69 --- /dev/null +++ b/arch/mips/alchemy/common/power.c | |||
@@ -0,0 +1,132 @@ | |||
1 | /* | ||
2 | * BRIEF MODULE DESCRIPTION | ||
3 | * Au1xx0 Power Management routines. | ||
4 | * | ||
5 | * Copyright 2001, 2008 MontaVista Software Inc. | ||
6 | * Author: MontaVista Software, Inc. <source@mvista.com> | ||
7 | * | ||
8 | * Some of the routines are right out of init/main.c, whose | ||
9 | * copyrights apply here. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify it | ||
12 | * under the terms of the GNU General Public License as published by the | ||
13 | * Free Software Foundation; either version 2 of the License, or (at your | ||
14 | * option) any later version. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
17 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
18 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
19 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
21 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
22 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
23 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
25 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | * | ||
27 | * You should have received a copy of the GNU General Public License along | ||
28 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
29 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
30 | */ | ||
31 | |||
32 | #include <linux/pm.h> | ||
33 | #include <linux/sysctl.h> | ||
34 | #include <linux/jiffies.h> | ||
35 | |||
36 | #include <linux/uaccess.h> | ||
37 | #include <asm/mach-au1x00/au1000.h> | ||
38 | |||
39 | /* | ||
40 | * We need to save/restore a bunch of core registers that are | ||
41 | * either volatile or reset to some state across a processor sleep. | ||
42 | * If reading a register doesn't provide a proper result for a | ||
43 | * later restore, we have to provide a function for loading that | ||
44 | * register and save a copy. | ||
45 | * | ||
46 | * We only have to save/restore registers that aren't otherwise | ||
47 | * done as part of a driver pm_* function. | ||
48 | */ | ||
49 | static unsigned int sleep_sys_clocks[5]; | ||
50 | static unsigned int sleep_sys_pinfunc; | ||
51 | static unsigned int sleep_static_memctlr[4][3]; | ||
52 | |||
53 | |||
54 | static void save_core_regs(void) | ||
55 | { | ||
56 | /* Clocks and PLLs. */ | ||
57 | sleep_sys_clocks[0] = alchemy_rdsys(AU1000_SYS_FREQCTRL0); | ||
58 | sleep_sys_clocks[1] = alchemy_rdsys(AU1000_SYS_FREQCTRL1); | ||
59 | sleep_sys_clocks[2] = alchemy_rdsys(AU1000_SYS_CLKSRC); | ||
60 | sleep_sys_clocks[3] = alchemy_rdsys(AU1000_SYS_CPUPLL); | ||
61 | sleep_sys_clocks[4] = alchemy_rdsys(AU1000_SYS_AUXPLL); | ||
62 | |||
63 | /* pin mux config */ | ||
64 | sleep_sys_pinfunc = alchemy_rdsys(AU1000_SYS_PINFUNC); | ||
65 | |||
66 | /* Save the static memory controller configuration. */ | ||
67 | sleep_static_memctlr[0][0] = alchemy_rdsmem(AU1000_MEM_STCFG0); | ||
68 | sleep_static_memctlr[0][1] = alchemy_rdsmem(AU1000_MEM_STTIME0); | ||
69 | sleep_static_memctlr[0][2] = alchemy_rdsmem(AU1000_MEM_STADDR0); | ||
70 | sleep_static_memctlr[1][0] = alchemy_rdsmem(AU1000_MEM_STCFG1); | ||
71 | sleep_static_memctlr[1][1] = alchemy_rdsmem(AU1000_MEM_STTIME1); | ||
72 | sleep_static_memctlr[1][2] = alchemy_rdsmem(AU1000_MEM_STADDR1); | ||
73 | sleep_static_memctlr[2][0] = alchemy_rdsmem(AU1000_MEM_STCFG2); | ||
74 | sleep_static_memctlr[2][1] = alchemy_rdsmem(AU1000_MEM_STTIME2); | ||
75 | sleep_static_memctlr[2][2] = alchemy_rdsmem(AU1000_MEM_STADDR2); | ||
76 | sleep_static_memctlr[3][0] = alchemy_rdsmem(AU1000_MEM_STCFG3); | ||
77 | sleep_static_memctlr[3][1] = alchemy_rdsmem(AU1000_MEM_STTIME3); | ||
78 | sleep_static_memctlr[3][2] = alchemy_rdsmem(AU1000_MEM_STADDR3); | ||
79 | } | ||
80 | |||
81 | static void restore_core_regs(void) | ||
82 | { | ||
83 | /* restore clock configuration. Writing CPUPLL last will | ||
84 | * stall a bit and stabilize other clocks (unless this is | ||
85 | * one of those Au1000 with a write-only PLL, where we dont | ||
86 | * have a valid value) | ||
87 | */ | ||
88 | alchemy_wrsys(sleep_sys_clocks[0], AU1000_SYS_FREQCTRL0); | ||
89 | alchemy_wrsys(sleep_sys_clocks[1], AU1000_SYS_FREQCTRL1); | ||
90 | alchemy_wrsys(sleep_sys_clocks[2], AU1000_SYS_CLKSRC); | ||
91 | alchemy_wrsys(sleep_sys_clocks[4], AU1000_SYS_AUXPLL); | ||
92 | if (!au1xxx_cpu_has_pll_wo()) | ||
93 | alchemy_wrsys(sleep_sys_clocks[3], AU1000_SYS_CPUPLL); | ||
94 | |||
95 | alchemy_wrsys(sleep_sys_pinfunc, AU1000_SYS_PINFUNC); | ||
96 | |||
97 | /* Restore the static memory controller configuration. */ | ||
98 | alchemy_wrsmem(sleep_static_memctlr[0][0], AU1000_MEM_STCFG0); | ||
99 | alchemy_wrsmem(sleep_static_memctlr[0][1], AU1000_MEM_STTIME0); | ||
100 | alchemy_wrsmem(sleep_static_memctlr[0][2], AU1000_MEM_STADDR0); | ||
101 | alchemy_wrsmem(sleep_static_memctlr[1][0], AU1000_MEM_STCFG1); | ||
102 | alchemy_wrsmem(sleep_static_memctlr[1][1], AU1000_MEM_STTIME1); | ||
103 | alchemy_wrsmem(sleep_static_memctlr[1][2], AU1000_MEM_STADDR1); | ||
104 | alchemy_wrsmem(sleep_static_memctlr[2][0], AU1000_MEM_STCFG2); | ||
105 | alchemy_wrsmem(sleep_static_memctlr[2][1], AU1000_MEM_STTIME2); | ||
106 | alchemy_wrsmem(sleep_static_memctlr[2][2], AU1000_MEM_STADDR2); | ||
107 | alchemy_wrsmem(sleep_static_memctlr[3][0], AU1000_MEM_STCFG3); | ||
108 | alchemy_wrsmem(sleep_static_memctlr[3][1], AU1000_MEM_STTIME3); | ||
109 | alchemy_wrsmem(sleep_static_memctlr[3][2], AU1000_MEM_STADDR3); | ||
110 | } | ||
111 | |||
112 | void au_sleep(void) | ||
113 | { | ||
114 | save_core_regs(); | ||
115 | |||
116 | switch (alchemy_get_cputype()) { | ||
117 | case ALCHEMY_CPU_AU1000: | ||
118 | case ALCHEMY_CPU_AU1500: | ||
119 | case ALCHEMY_CPU_AU1100: | ||
120 | alchemy_sleep_au1000(); | ||
121 | break; | ||
122 | case ALCHEMY_CPU_AU1550: | ||
123 | case ALCHEMY_CPU_AU1200: | ||
124 | alchemy_sleep_au1550(); | ||
125 | break; | ||
126 | case ALCHEMY_CPU_AU1300: | ||
127 | alchemy_sleep_au1300(); | ||
128 | break; | ||
129 | } | ||
130 | |||
131 | restore_core_regs(); | ||
132 | } | ||
diff --git a/arch/mips/alchemy/common/prom.c b/arch/mips/alchemy/common/prom.c new file mode 100644 index 000000000..d910c0a64 --- /dev/null +++ b/arch/mips/alchemy/common/prom.c | |||
@@ -0,0 +1,149 @@ | |||
1 | /* | ||
2 | * | ||
3 | * BRIEF MODULE DESCRIPTION | ||
4 | * PROM library initialisation code, supports YAMON and U-Boot. | ||
5 | * | ||
6 | * Copyright 2000-2001, 2006, 2008 MontaVista Software Inc. | ||
7 | * Author: MontaVista Software, Inc. <source@mvista.com> | ||
8 | * | ||
9 | * This file was derived from Carsten Langgaard's | ||
10 | * arch/mips/mips-boards/xx files. | ||
11 | * | ||
12 | * Carsten Langgaard, carstenl@mips.com | ||
13 | * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. | ||
14 | * | ||
15 | * This program is free software; you can redistribute it and/or modify it | ||
16 | * under the terms of the GNU General Public License as published by the | ||
17 | * Free Software Foundation; either version 2 of the License, or (at your | ||
18 | * option) any later version. | ||
19 | * | ||
20 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
21 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
22 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
23 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
24 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
25 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
26 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
27 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
29 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
30 | * | ||
31 | * You should have received a copy of the GNU General Public License along | ||
32 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
33 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
34 | */ | ||
35 | |||
36 | #include <linux/init.h> | ||
37 | #include <linux/kernel.h> | ||
38 | #include <linux/memblock.h> | ||
39 | #include <linux/sizes.h> | ||
40 | #include <linux/string.h> | ||
41 | |||
42 | #include <asm/bootinfo.h> | ||
43 | |||
44 | int prom_argc; | ||
45 | char **prom_argv; | ||
46 | char **prom_envp; | ||
47 | |||
48 | void __init prom_init_cmdline(void) | ||
49 | { | ||
50 | int i; | ||
51 | |||
52 | for (i = 1; i < prom_argc; i++) { | ||
53 | strlcat(arcs_cmdline, prom_argv[i], COMMAND_LINE_SIZE); | ||
54 | if (i < (prom_argc - 1)) | ||
55 | strlcat(arcs_cmdline, " ", COMMAND_LINE_SIZE); | ||
56 | } | ||
57 | } | ||
58 | |||
59 | char *prom_getenv(char *envname) | ||
60 | { | ||
61 | /* | ||
62 | * Return a pointer to the given environment variable. | ||
63 | * YAMON uses "name", "value" pairs, while U-Boot uses "name=value". | ||
64 | */ | ||
65 | |||
66 | char **env = prom_envp; | ||
67 | int i = strlen(envname); | ||
68 | int yamon = (*env && strchr(*env, '=') == NULL); | ||
69 | |||
70 | while (*env) { | ||
71 | if (yamon) { | ||
72 | if (strcmp(envname, *env++) == 0) | ||
73 | return *env; | ||
74 | } else if (strncmp(envname, *env, i) == 0 && (*env)[i] == '=') | ||
75 | return *env + i + 1; | ||
76 | env++; | ||
77 | } | ||
78 | |||
79 | return NULL; | ||
80 | } | ||
81 | |||
82 | void __init prom_init(void) | ||
83 | { | ||
84 | unsigned char *memsize_str; | ||
85 | unsigned long memsize; | ||
86 | |||
87 | prom_argc = (int)fw_arg0; | ||
88 | prom_argv = (char **)fw_arg1; | ||
89 | prom_envp = (char **)fw_arg2; | ||
90 | |||
91 | prom_init_cmdline(); | ||
92 | |||
93 | memsize_str = prom_getenv("memsize"); | ||
94 | if (!memsize_str || kstrtoul(memsize_str, 0, &memsize)) | ||
95 | memsize = SZ_64M; /* minimum memsize is 64MB RAM */ | ||
96 | |||
97 | memblock_add(0, memsize); | ||
98 | } | ||
99 | |||
100 | static inline unsigned char str2hexnum(unsigned char c) | ||
101 | { | ||
102 | if (c >= '0' && c <= '9') | ||
103 | return c - '0'; | ||
104 | if (c >= 'a' && c <= 'f') | ||
105 | return c - 'a' + 10; | ||
106 | if (c >= 'A' && c <= 'F') | ||
107 | return c - 'A' + 10; | ||
108 | |||
109 | return 0; /* foo */ | ||
110 | } | ||
111 | |||
112 | static inline void str2eaddr(unsigned char *ea, unsigned char *str) | ||
113 | { | ||
114 | int i; | ||
115 | |||
116 | for (i = 0; i < 6; i++) { | ||
117 | unsigned char num; | ||
118 | |||
119 | if ((*str == '.') || (*str == ':')) | ||
120 | str++; | ||
121 | num = str2hexnum(*str++) << 4; | ||
122 | num |= str2hexnum(*str++); | ||
123 | ea[i] = num; | ||
124 | } | ||
125 | } | ||
126 | |||
127 | int __init prom_get_ethernet_addr(char *ethernet_addr) | ||
128 | { | ||
129 | char *ethaddr_str; | ||
130 | |||
131 | /* Check the environment variables first */ | ||
132 | ethaddr_str = prom_getenv("ethaddr"); | ||
133 | if (!ethaddr_str) { | ||
134 | /* Check command line */ | ||
135 | ethaddr_str = strstr(arcs_cmdline, "ethaddr="); | ||
136 | if (!ethaddr_str) | ||
137 | return -1; | ||
138 | |||
139 | ethaddr_str += strlen("ethaddr="); | ||
140 | } | ||
141 | |||
142 | str2eaddr(ethernet_addr, ethaddr_str); | ||
143 | |||
144 | return 0; | ||
145 | } | ||
146 | |||
147 | void __init prom_free_prom_memory(void) | ||
148 | { | ||
149 | } | ||
diff --git a/arch/mips/alchemy/common/setup.c b/arch/mips/alchemy/common/setup.c new file mode 100644 index 000000000..0f60efe04 --- /dev/null +++ b/arch/mips/alchemy/common/setup.c | |||
@@ -0,0 +1,103 @@ | |||
1 | /* | ||
2 | * Copyright 2000, 2007-2008 MontaVista Software Inc. | ||
3 | * Author: MontaVista Software, Inc. <source@mvista.com | ||
4 | * | ||
5 | * Updates to 2.6, Pete Popov, Embedded Alley Solutions, Inc. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License as published by the | ||
9 | * Free Software Foundation; either version 2 of the License, or (at your | ||
10 | * option) any later version. | ||
11 | * | ||
12 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
13 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
14 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | ||
15 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
16 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
17 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | ||
18 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
19 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
20 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
21 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
22 | * | ||
23 | * You should have received a copy of the GNU General Public License along | ||
24 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
25 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
26 | */ | ||
27 | |||
28 | #include <linux/init.h> | ||
29 | #include <linux/ioport.h> | ||
30 | #include <linux/mm.h> | ||
31 | |||
32 | #include <asm/dma-coherence.h> | ||
33 | #include <asm/mipsregs.h> | ||
34 | |||
35 | #include <au1000.h> | ||
36 | |||
37 | extern void __init board_setup(void); | ||
38 | extern void __init alchemy_set_lpj(void); | ||
39 | |||
40 | void __init plat_mem_setup(void) | ||
41 | { | ||
42 | alchemy_set_lpj(); | ||
43 | |||
44 | if (au1xxx_cpu_needs_config_od()) | ||
45 | /* Various early Au1xx0 errata corrected by this */ | ||
46 | set_c0_config(1 << 19); /* Set Config[OD] */ | ||
47 | else | ||
48 | /* Clear to obtain best system bus performance */ | ||
49 | clear_c0_config(1 << 19); /* Clear Config[OD] */ | ||
50 | |||
51 | hw_coherentio = 0; | ||
52 | coherentio = IO_COHERENCE_ENABLED; | ||
53 | switch (alchemy_get_cputype()) { | ||
54 | case ALCHEMY_CPU_AU1000: | ||
55 | case ALCHEMY_CPU_AU1500: | ||
56 | case ALCHEMY_CPU_AU1100: | ||
57 | coherentio = IO_COHERENCE_DISABLED; | ||
58 | break; | ||
59 | case ALCHEMY_CPU_AU1200: | ||
60 | /* Au1200 AB USB does not support coherent memory */ | ||
61 | if (0 == (read_c0_prid() & PRID_REV_MASK)) | ||
62 | coherentio = IO_COHERENCE_DISABLED; | ||
63 | break; | ||
64 | } | ||
65 | |||
66 | board_setup(); /* board specific setup */ | ||
67 | |||
68 | /* IO/MEM resources. */ | ||
69 | set_io_port_base(0); | ||
70 | ioport_resource.start = IOPORT_RESOURCE_START; | ||
71 | ioport_resource.end = IOPORT_RESOURCE_END; | ||
72 | iomem_resource.start = IOMEM_RESOURCE_START; | ||
73 | iomem_resource.end = IOMEM_RESOURCE_END; | ||
74 | } | ||
75 | |||
76 | #ifdef CONFIG_MIPS_FIXUP_BIGPHYS_ADDR | ||
77 | /* This routine should be valid for all Au1x based boards */ | ||
78 | phys_addr_t fixup_bigphys_addr(phys_addr_t phys_addr, phys_addr_t size) | ||
79 | { | ||
80 | unsigned long start = ALCHEMY_PCI_MEMWIN_START; | ||
81 | unsigned long end = ALCHEMY_PCI_MEMWIN_END; | ||
82 | |||
83 | /* Don't fixup 36-bit addresses */ | ||
84 | if ((phys_addr >> 32) != 0) | ||
85 | return phys_addr; | ||
86 | |||
87 | /* Check for PCI memory window */ | ||
88 | if (phys_addr >= start && (phys_addr + size - 1) <= end) | ||
89 | return (phys_addr_t)(AU1500_PCI_MEM_PHYS_ADDR + phys_addr); | ||
90 | |||
91 | /* default nop */ | ||
92 | return phys_addr; | ||
93 | } | ||
94 | |||
95 | int io_remap_pfn_range(struct vm_area_struct *vma, unsigned long vaddr, | ||
96 | unsigned long pfn, unsigned long size, pgprot_t prot) | ||
97 | { | ||
98 | phys_addr_t phys_addr = fixup_bigphys_addr(pfn << PAGE_SHIFT, size); | ||
99 | |||
100 | return remap_pfn_range(vma, vaddr, phys_addr >> PAGE_SHIFT, size, prot); | ||
101 | } | ||
102 | EXPORT_SYMBOL(io_remap_pfn_range); | ||
103 | #endif /* CONFIG_MIPS_FIXUP_BIGPHYS_ADDR */ | ||
diff --git a/arch/mips/alchemy/common/sleeper.S b/arch/mips/alchemy/common/sleeper.S new file mode 100644 index 000000000..13586d224 --- /dev/null +++ b/arch/mips/alchemy/common/sleeper.S | |||
@@ -0,0 +1,266 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ | ||
2 | /* | ||
3 | * Copyright 2002 Embedded Edge, LLC | ||
4 | * Author: dan@embeddededge.com | ||
5 | * | ||
6 | * Sleep helper for Au1xxx sleep mode. | ||
7 | */ | ||
8 | |||
9 | #include <asm/asm.h> | ||
10 | #include <asm/mipsregs.h> | ||
11 | #include <asm/regdef.h> | ||
12 | #include <asm/stackframe.h> | ||
13 | |||
14 | .extern __flush_cache_all | ||
15 | |||
16 | .text | ||
17 | .set noreorder | ||
18 | .set noat | ||
19 | .align 5 | ||
20 | |||
21 | |||
22 | /* preparatory stuff */ | ||
23 | .macro SETUP_SLEEP | ||
24 | subu sp, PT_SIZE | ||
25 | sw $1, PT_R1(sp) | ||
26 | sw $2, PT_R2(sp) | ||
27 | sw $3, PT_R3(sp) | ||
28 | sw $4, PT_R4(sp) | ||
29 | sw $5, PT_R5(sp) | ||
30 | sw $6, PT_R6(sp) | ||
31 | sw $7, PT_R7(sp) | ||
32 | sw $16, PT_R16(sp) | ||
33 | sw $17, PT_R17(sp) | ||
34 | sw $18, PT_R18(sp) | ||
35 | sw $19, PT_R19(sp) | ||
36 | sw $20, PT_R20(sp) | ||
37 | sw $21, PT_R21(sp) | ||
38 | sw $22, PT_R22(sp) | ||
39 | sw $23, PT_R23(sp) | ||
40 | sw $26, PT_R26(sp) | ||
41 | sw $27, PT_R27(sp) | ||
42 | sw $28, PT_R28(sp) | ||
43 | sw $30, PT_R30(sp) | ||
44 | sw $31, PT_R31(sp) | ||
45 | mfc0 k0, CP0_STATUS | ||
46 | sw k0, 0x20(sp) | ||
47 | mfc0 k0, CP0_CONTEXT | ||
48 | sw k0, 0x1c(sp) | ||
49 | mfc0 k0, CP0_PAGEMASK | ||
50 | sw k0, 0x18(sp) | ||
51 | mfc0 k0, CP0_CONFIG | ||
52 | sw k0, 0x14(sp) | ||
53 | |||
54 | /* flush caches to make sure context is in memory */ | ||
55 | la t1, __flush_cache_all | ||
56 | lw t0, 0(t1) | ||
57 | jalr t0 | ||
58 | nop | ||
59 | |||
60 | /* Now set up the scratch registers so the boot rom will | ||
61 | * return to this point upon wakeup. | ||
62 | * sys_scratch0 : SP | ||
63 | * sys_scratch1 : RA | ||
64 | */ | ||
65 | lui t3, 0xb190 /* sys_xxx */ | ||
66 | sw sp, 0x0018(t3) | ||
67 | la k0, alchemy_sleep_wakeup /* resume path */ | ||
68 | sw k0, 0x001c(t3) | ||
69 | .endm | ||
70 | |||
71 | .macro DO_SLEEP | ||
72 | /* put power supply and processor to sleep */ | ||
73 | sw zero, 0x0078(t3) /* sys_slppwr */ | ||
74 | sync | ||
75 | sw zero, 0x007c(t3) /* sys_sleep */ | ||
76 | sync | ||
77 | nop | ||
78 | nop | ||
79 | nop | ||
80 | nop | ||
81 | nop | ||
82 | nop | ||
83 | nop | ||
84 | nop | ||
85 | .endm | ||
86 | |||
87 | /* sleep code for Au1000/Au1100/Au1500 memory controller type */ | ||
88 | LEAF(alchemy_sleep_au1000) | ||
89 | |||
90 | SETUP_SLEEP | ||
91 | |||
92 | /* cache following instructions, as memory gets put to sleep */ | ||
93 | la t0, 1f | ||
94 | .set arch=r4000 | ||
95 | cache 0x14, 0(t0) | ||
96 | cache 0x14, 32(t0) | ||
97 | cache 0x14, 64(t0) | ||
98 | cache 0x14, 96(t0) | ||
99 | .set mips0 | ||
100 | |||
101 | 1: lui a0, 0xb400 /* mem_xxx */ | ||
102 | sw zero, 0x001c(a0) /* Precharge */ | ||
103 | sync | ||
104 | sw zero, 0x0020(a0) /* Auto Refresh */ | ||
105 | sync | ||
106 | sw zero, 0x0030(a0) /* Sleep */ | ||
107 | sync | ||
108 | |||
109 | DO_SLEEP | ||
110 | |||
111 | END(alchemy_sleep_au1000) | ||
112 | |||
113 | /* sleep code for Au1550/Au1200 memory controller type */ | ||
114 | LEAF(alchemy_sleep_au1550) | ||
115 | |||
116 | SETUP_SLEEP | ||
117 | |||
118 | /* cache following instructions, as memory gets put to sleep */ | ||
119 | la t0, 1f | ||
120 | .set arch=r4000 | ||
121 | cache 0x14, 0(t0) | ||
122 | cache 0x14, 32(t0) | ||
123 | cache 0x14, 64(t0) | ||
124 | cache 0x14, 96(t0) | ||
125 | .set mips0 | ||
126 | |||
127 | 1: lui a0, 0xb400 /* mem_xxx */ | ||
128 | sw zero, 0x08c0(a0) /* Precharge */ | ||
129 | sync | ||
130 | sw zero, 0x08d0(a0) /* Self Refresh */ | ||
131 | sync | ||
132 | |||
133 | /* wait for sdram to enter self-refresh mode */ | ||
134 | lui t0, 0x0100 | ||
135 | 2: lw t1, 0x0850(a0) /* mem_sdstat */ | ||
136 | and t2, t1, t0 | ||
137 | beq t2, zero, 2b | ||
138 | nop | ||
139 | |||
140 | /* disable SDRAM clocks */ | ||
141 | lui t0, 0xcfff | ||
142 | ori t0, t0, 0xffff | ||
143 | lw t1, 0x0840(a0) /* mem_sdconfiga */ | ||
144 | and t1, t0, t1 /* clear CE[1:0] */ | ||
145 | sw t1, 0x0840(a0) /* mem_sdconfiga */ | ||
146 | sync | ||
147 | |||
148 | DO_SLEEP | ||
149 | |||
150 | END(alchemy_sleep_au1550) | ||
151 | |||
152 | /* sleepcode for Au1300 memory controller type */ | ||
153 | LEAF(alchemy_sleep_au1300) | ||
154 | |||
155 | SETUP_SLEEP | ||
156 | |||
157 | /* cache following instructions, as memory gets put to sleep */ | ||
158 | la t0, 2f | ||
159 | la t1, 4f | ||
160 | subu t2, t1, t0 | ||
161 | |||
162 | .set arch=r4000 | ||
163 | |||
164 | 1: cache 0x14, 0(t0) | ||
165 | subu t2, t2, 32 | ||
166 | bgez t2, 1b | ||
167 | addu t0, t0, 32 | ||
168 | |||
169 | .set mips0 | ||
170 | |||
171 | 2: lui a0, 0xb400 /* mem_xxx */ | ||
172 | |||
173 | /* disable all ports in mem_sdportcfga */ | ||
174 | sw zero, 0x868(a0) /* mem_sdportcfga */ | ||
175 | sync | ||
176 | |||
177 | /* disable ODT */ | ||
178 | li t0, 0x03010000 | ||
179 | sw t0, 0x08d8(a0) /* mem_sdcmd0 */ | ||
180 | sw t0, 0x08dc(a0) /* mem_sdcmd1 */ | ||
181 | sync | ||
182 | |||
183 | /* precharge */ | ||
184 | li t0, 0x23000400 | ||
185 | sw t0, 0x08dc(a0) /* mem_sdcmd1 */ | ||
186 | sw t0, 0x08d8(a0) /* mem_sdcmd0 */ | ||
187 | sync | ||
188 | |||
189 | /* auto refresh */ | ||
190 | sw zero, 0x08c8(a0) /* mem_sdautoref */ | ||
191 | sync | ||
192 | |||
193 | /* block access to the DDR */ | ||
194 | lw t0, 0x0848(a0) /* mem_sdconfigb */ | ||
195 | li t1, (1 << 7 | 0x3F) | ||
196 | or t0, t0, t1 | ||
197 | sw t0, 0x0848(a0) /* mem_sdconfigb */ | ||
198 | sync | ||
199 | |||
200 | /* issue the Self Refresh command */ | ||
201 | li t0, 0x10000000 | ||
202 | sw t0, 0x08dc(a0) /* mem_sdcmd1 */ | ||
203 | sw t0, 0x08d8(a0) /* mem_sdcmd0 */ | ||
204 | sync | ||
205 | |||
206 | /* wait for sdram to enter self-refresh mode */ | ||
207 | lui t0, 0x0300 | ||
208 | 3: lw t1, 0x0850(a0) /* mem_sdstat */ | ||
209 | and t2, t1, t0 | ||
210 | bne t2, t0, 3b | ||
211 | nop | ||
212 | |||
213 | /* disable SDRAM clocks */ | ||
214 | li t0, ~(3<<28) | ||
215 | lw t1, 0x0840(a0) /* mem_sdconfiga */ | ||
216 | and t1, t1, t0 /* clear CE[1:0] */ | ||
217 | sw t1, 0x0840(a0) /* mem_sdconfiga */ | ||
218 | sync | ||
219 | |||
220 | DO_SLEEP | ||
221 | 4: | ||
222 | |||
223 | END(alchemy_sleep_au1300) | ||
224 | |||
225 | |||
226 | /* This is where we return upon wakeup. | ||
227 | * Reload all of the registers and return. | ||
228 | */ | ||
229 | LEAF(alchemy_sleep_wakeup) | ||
230 | lw k0, 0x20(sp) | ||
231 | mtc0 k0, CP0_STATUS | ||
232 | lw k0, 0x1c(sp) | ||
233 | mtc0 k0, CP0_CONTEXT | ||
234 | lw k0, 0x18(sp) | ||
235 | mtc0 k0, CP0_PAGEMASK | ||
236 | lw k0, 0x14(sp) | ||
237 | mtc0 k0, CP0_CONFIG | ||
238 | |||
239 | /* We need to catch the early Alchemy SOCs with | ||
240 | * the write-only Config[OD] bit and set it back to one... | ||
241 | */ | ||
242 | jal au1x00_fixup_config_od | ||
243 | nop | ||
244 | lw $1, PT_R1(sp) | ||
245 | lw $2, PT_R2(sp) | ||
246 | lw $3, PT_R3(sp) | ||
247 | lw $4, PT_R4(sp) | ||
248 | lw $5, PT_R5(sp) | ||
249 | lw $6, PT_R6(sp) | ||
250 | lw $7, PT_R7(sp) | ||
251 | lw $16, PT_R16(sp) | ||
252 | lw $17, PT_R17(sp) | ||
253 | lw $18, PT_R18(sp) | ||
254 | lw $19, PT_R19(sp) | ||
255 | lw $20, PT_R20(sp) | ||
256 | lw $21, PT_R21(sp) | ||
257 | lw $22, PT_R22(sp) | ||
258 | lw $23, PT_R23(sp) | ||
259 | lw $26, PT_R26(sp) | ||
260 | lw $27, PT_R27(sp) | ||
261 | lw $28, PT_R28(sp) | ||
262 | lw $30, PT_R30(sp) | ||
263 | lw $31, PT_R31(sp) | ||
264 | jr ra | ||
265 | addiu sp, PT_SIZE | ||
266 | END(alchemy_sleep_wakeup) | ||
diff --git a/arch/mips/alchemy/common/time.c b/arch/mips/alchemy/common/time.c new file mode 100644 index 000000000..d794ffb67 --- /dev/null +++ b/arch/mips/alchemy/common/time.c | |||
@@ -0,0 +1,155 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0-only | ||
2 | /* | ||
3 | * Copyright (C) 2008-2009 Manuel Lauss <manuel.lauss@gmail.com> | ||
4 | * | ||
5 | * Previous incarnations were: | ||
6 | * Copyright (C) 2001, 2006, 2008 MontaVista Software, <source@mvista.com> | ||
7 | * Copied and modified Carsten Langgaard's time.c | ||
8 | * | ||
9 | * Carsten Langgaard, carstenl@mips.com | ||
10 | * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. | ||
11 | * | ||
12 | * ######################################################################## | ||
13 | * | ||
14 | * ######################################################################## | ||
15 | * | ||
16 | * Clocksource/event using the 32.768kHz-clocked Counter1 ('RTC' in the | ||
17 | * databooks). Firmware/Board init code must enable the counters in the | ||
18 | * counter control register, otherwise the CP0 counter clocksource/event | ||
19 | * will be installed instead (and use of 'wait' instruction is prohibited). | ||
20 | */ | ||
21 | |||
22 | #include <linux/clockchips.h> | ||
23 | #include <linux/clocksource.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/spinlock.h> | ||
26 | |||
27 | #include <asm/idle.h> | ||
28 | #include <asm/processor.h> | ||
29 | #include <asm/time.h> | ||
30 | #include <asm/mach-au1x00/au1000.h> | ||
31 | |||
32 | /* 32kHz clock enabled and detected */ | ||
33 | #define CNTR_OK (SYS_CNTRL_E0 | SYS_CNTRL_32S) | ||
34 | |||
35 | static u64 au1x_counter1_read(struct clocksource *cs) | ||
36 | { | ||
37 | return alchemy_rdsys(AU1000_SYS_RTCREAD); | ||
38 | } | ||
39 | |||
40 | static struct clocksource au1x_counter1_clocksource = { | ||
41 | .name = "alchemy-counter1", | ||
42 | .read = au1x_counter1_read, | ||
43 | .mask = CLOCKSOURCE_MASK(32), | ||
44 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
45 | .rating = 1500, | ||
46 | }; | ||
47 | |||
48 | static int au1x_rtcmatch2_set_next_event(unsigned long delta, | ||
49 | struct clock_event_device *cd) | ||
50 | { | ||
51 | delta += alchemy_rdsys(AU1000_SYS_RTCREAD); | ||
52 | /* wait for register access */ | ||
53 | while (alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_M21) | ||
54 | ; | ||
55 | alchemy_wrsys(delta, AU1000_SYS_RTCMATCH2); | ||
56 | |||
57 | return 0; | ||
58 | } | ||
59 | |||
60 | static irqreturn_t au1x_rtcmatch2_irq(int irq, void *dev_id) | ||
61 | { | ||
62 | struct clock_event_device *cd = dev_id; | ||
63 | cd->event_handler(cd); | ||
64 | return IRQ_HANDLED; | ||
65 | } | ||
66 | |||
67 | static struct clock_event_device au1x_rtcmatch2_clockdev = { | ||
68 | .name = "rtcmatch2", | ||
69 | .features = CLOCK_EVT_FEAT_ONESHOT, | ||
70 | .rating = 1500, | ||
71 | .set_next_event = au1x_rtcmatch2_set_next_event, | ||
72 | .cpumask = cpu_possible_mask, | ||
73 | }; | ||
74 | |||
75 | static int __init alchemy_time_init(unsigned int m2int) | ||
76 | { | ||
77 | struct clock_event_device *cd = &au1x_rtcmatch2_clockdev; | ||
78 | unsigned long t; | ||
79 | |||
80 | au1x_rtcmatch2_clockdev.irq = m2int; | ||
81 | |||
82 | /* Check if firmware (YAMON, ...) has enabled 32kHz and clock | ||
83 | * has been detected. If so install the rtcmatch2 clocksource, | ||
84 | * otherwise don't bother. Note that both bits being set is by | ||
85 | * no means a definite guarantee that the counters actually work | ||
86 | * (the 32S bit seems to be stuck set to 1 once a single clock- | ||
87 | * edge is detected, hence the timeouts). | ||
88 | */ | ||
89 | if (CNTR_OK != (alchemy_rdsys(AU1000_SYS_CNTRCTRL) & CNTR_OK)) | ||
90 | goto cntr_err; | ||
91 | |||
92 | /* | ||
93 | * setup counter 1 (RTC) to tick at full speed | ||
94 | */ | ||
95 | t = 0xffffff; | ||
96 | while ((alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_T1S) && --t) | ||
97 | asm volatile ("nop"); | ||
98 | if (!t) | ||
99 | goto cntr_err; | ||
100 | |||
101 | alchemy_wrsys(0, AU1000_SYS_RTCTRIM); /* 32.768 kHz */ | ||
102 | |||
103 | t = 0xffffff; | ||
104 | while ((alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_C1S) && --t) | ||
105 | asm volatile ("nop"); | ||
106 | if (!t) | ||
107 | goto cntr_err; | ||
108 | alchemy_wrsys(0, AU1000_SYS_RTCWRITE); | ||
109 | |||
110 | t = 0xffffff; | ||
111 | while ((alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_C1S) && --t) | ||
112 | asm volatile ("nop"); | ||
113 | if (!t) | ||
114 | goto cntr_err; | ||
115 | |||
116 | /* register counter1 clocksource and event device */ | ||
117 | clocksource_register_hz(&au1x_counter1_clocksource, 32768); | ||
118 | |||
119 | cd->shift = 32; | ||
120 | cd->mult = div_sc(32768, NSEC_PER_SEC, cd->shift); | ||
121 | cd->max_delta_ns = clockevent_delta2ns(0xffffffff, cd); | ||
122 | cd->max_delta_ticks = 0xffffffff; | ||
123 | cd->min_delta_ns = clockevent_delta2ns(9, cd); | ||
124 | cd->min_delta_ticks = 9; /* ~0.28ms */ | ||
125 | clockevents_register_device(cd); | ||
126 | if (request_irq(m2int, au1x_rtcmatch2_irq, IRQF_TIMER, "timer", | ||
127 | &au1x_rtcmatch2_clockdev)) | ||
128 | pr_err("Failed to register timer interrupt\n"); | ||
129 | |||
130 | printk(KERN_INFO "Alchemy clocksource installed\n"); | ||
131 | |||
132 | return 0; | ||
133 | |||
134 | cntr_err: | ||
135 | return -1; | ||
136 | } | ||
137 | |||
138 | static int alchemy_m2inttab[] __initdata = { | ||
139 | AU1000_RTC_MATCH2_INT, | ||
140 | AU1500_RTC_MATCH2_INT, | ||
141 | AU1100_RTC_MATCH2_INT, | ||
142 | AU1550_RTC_MATCH2_INT, | ||
143 | AU1200_RTC_MATCH2_INT, | ||
144 | AU1300_RTC_MATCH2_INT, | ||
145 | }; | ||
146 | |||
147 | void __init plat_time_init(void) | ||
148 | { | ||
149 | int t; | ||
150 | |||
151 | t = alchemy_get_cputype(); | ||
152 | if (t == ALCHEMY_CPU_UNKNOWN || | ||
153 | alchemy_time_init(alchemy_m2inttab[t])) | ||
154 | cpu_wait = NULL; /* wait doesn't work with r4k timer */ | ||
155 | } | ||
diff --git a/arch/mips/alchemy/common/usb.c b/arch/mips/alchemy/common/usb.c new file mode 100644 index 000000000..5d618547e --- /dev/null +++ b/arch/mips/alchemy/common/usb.c | |||
@@ -0,0 +1,627 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0-only | ||
2 | /* | ||
3 | * USB block power/access management abstraction. | ||
4 | * | ||
5 | * Au1000+: The OHCI block control register is at the far end of the OHCI memory | ||
6 | * area. Au1550 has OHCI on different base address. No need to handle | ||
7 | * UDC here. | ||
8 | * Au1200: one register to control access and clocks to O/EHCI, UDC and OTG | ||
9 | * as well as the PHY for EHCI and UDC. | ||
10 | * | ||
11 | */ | ||
12 | |||
13 | #include <linux/clk.h> | ||
14 | #include <linux/export.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/io.h> | ||
17 | #include <linux/spinlock.h> | ||
18 | #include <linux/syscore_ops.h> | ||
19 | #include <asm/cpu.h> | ||
20 | #include <asm/mach-au1x00/au1000.h> | ||
21 | |||
22 | /* control register offsets */ | ||
23 | #define AU1000_OHCICFG 0x7fffc | ||
24 | #define AU1550_OHCICFG 0x07ffc | ||
25 | #define AU1200_USBCFG 0x04 | ||
26 | |||
27 | /* Au1000 USB block config bits */ | ||
28 | #define USBHEN_RD (1 << 4) /* OHCI reset-done indicator */ | ||
29 | #define USBHEN_CE (1 << 3) /* OHCI block clock enable */ | ||
30 | #define USBHEN_E (1 << 2) /* OHCI block enable */ | ||
31 | #define USBHEN_C (1 << 1) /* OHCI block coherency bit */ | ||
32 | #define USBHEN_BE (1 << 0) /* OHCI Big-Endian */ | ||
33 | |||
34 | /* Au1200 USB config bits */ | ||
35 | #define USBCFG_PFEN (1 << 31) /* prefetch enable (undoc) */ | ||
36 | #define USBCFG_RDCOMB (1 << 30) /* read combining (undoc) */ | ||
37 | #define USBCFG_UNKNOWN (5 << 20) /* unknown, leave this way */ | ||
38 | #define USBCFG_SSD (1 << 23) /* serial short detect en */ | ||
39 | #define USBCFG_PPE (1 << 19) /* HS PHY PLL */ | ||
40 | #define USBCFG_UCE (1 << 18) /* UDC clock enable */ | ||
41 | #define USBCFG_ECE (1 << 17) /* EHCI clock enable */ | ||
42 | #define USBCFG_OCE (1 << 16) /* OHCI clock enable */ | ||
43 | #define USBCFG_FLA(x) (((x) & 0x3f) << 8) | ||
44 | #define USBCFG_UCAM (1 << 7) /* coherent access (undoc) */ | ||
45 | #define USBCFG_GME (1 << 6) /* OTG mem access */ | ||
46 | #define USBCFG_DBE (1 << 5) /* UDC busmaster enable */ | ||
47 | #define USBCFG_DME (1 << 4) /* UDC mem enable */ | ||
48 | #define USBCFG_EBE (1 << 3) /* EHCI busmaster enable */ | ||
49 | #define USBCFG_EME (1 << 2) /* EHCI mem enable */ | ||
50 | #define USBCFG_OBE (1 << 1) /* OHCI busmaster enable */ | ||
51 | #define USBCFG_OME (1 << 0) /* OHCI mem enable */ | ||
52 | #define USBCFG_INIT_AU1200 (USBCFG_PFEN | USBCFG_RDCOMB | USBCFG_UNKNOWN |\ | ||
53 | USBCFG_SSD | USBCFG_FLA(0x20) | USBCFG_UCAM | \ | ||
54 | USBCFG_GME | USBCFG_DBE | USBCFG_DME | \ | ||
55 | USBCFG_EBE | USBCFG_EME | USBCFG_OBE | \ | ||
56 | USBCFG_OME) | ||
57 | |||
58 | /* Au1300 USB config registers */ | ||
59 | #define USB_DWC_CTRL1 0x00 | ||
60 | #define USB_DWC_CTRL2 0x04 | ||
61 | #define USB_VBUS_TIMER 0x10 | ||
62 | #define USB_SBUS_CTRL 0x14 | ||
63 | #define USB_MSR_ERR 0x18 | ||
64 | #define USB_DWC_CTRL3 0x1C | ||
65 | #define USB_DWC_CTRL4 0x20 | ||
66 | #define USB_OTG_STATUS 0x28 | ||
67 | #define USB_DWC_CTRL5 0x2C | ||
68 | #define USB_DWC_CTRL6 0x30 | ||
69 | #define USB_DWC_CTRL7 0x34 | ||
70 | #define USB_PHY_STATUS 0xC0 | ||
71 | #define USB_INT_STATUS 0xC4 | ||
72 | #define USB_INT_ENABLE 0xC8 | ||
73 | |||
74 | #define USB_DWC_CTRL1_OTGD 0x04 /* set to DISable OTG */ | ||
75 | #define USB_DWC_CTRL1_HSTRS 0x02 /* set to ENable EHCI */ | ||
76 | #define USB_DWC_CTRL1_DCRS 0x01 /* set to ENable UDC */ | ||
77 | |||
78 | #define USB_DWC_CTRL2_PHY1RS 0x04 /* set to enable PHY1 */ | ||
79 | #define USB_DWC_CTRL2_PHY0RS 0x02 /* set to enable PHY0 */ | ||
80 | #define USB_DWC_CTRL2_PHYRS 0x01 /* set to enable PHY */ | ||
81 | |||
82 | #define USB_DWC_CTRL3_OHCI1_CKEN (1 << 19) | ||
83 | #define USB_DWC_CTRL3_OHCI0_CKEN (1 << 18) | ||
84 | #define USB_DWC_CTRL3_EHCI0_CKEN (1 << 17) | ||
85 | #define USB_DWC_CTRL3_OTG0_CKEN (1 << 16) | ||
86 | |||
87 | #define USB_SBUS_CTRL_SBCA 0x04 /* coherent access */ | ||
88 | |||
89 | #define USB_INTEN_FORCE 0x20 | ||
90 | #define USB_INTEN_PHY 0x10 | ||
91 | #define USB_INTEN_UDC 0x08 | ||
92 | #define USB_INTEN_EHCI 0x04 | ||
93 | #define USB_INTEN_OHCI1 0x02 | ||
94 | #define USB_INTEN_OHCI0 0x01 | ||
95 | |||
96 | static DEFINE_SPINLOCK(alchemy_usb_lock); | ||
97 | |||
98 | static inline void __au1300_usb_phyctl(void __iomem *base, int enable) | ||
99 | { | ||
100 | unsigned long r, s; | ||
101 | |||
102 | r = __raw_readl(base + USB_DWC_CTRL2); | ||
103 | s = __raw_readl(base + USB_DWC_CTRL3); | ||
104 | |||
105 | s &= USB_DWC_CTRL3_OHCI1_CKEN | USB_DWC_CTRL3_OHCI0_CKEN | | ||
106 | USB_DWC_CTRL3_EHCI0_CKEN | USB_DWC_CTRL3_OTG0_CKEN; | ||
107 | |||
108 | if (enable) { | ||
109 | /* simply enable all PHYs */ | ||
110 | r |= USB_DWC_CTRL2_PHY1RS | USB_DWC_CTRL2_PHY0RS | | ||
111 | USB_DWC_CTRL2_PHYRS; | ||
112 | __raw_writel(r, base + USB_DWC_CTRL2); | ||
113 | wmb(); | ||
114 | } else if (!s) { | ||
115 | /* no USB block active, do disable all PHYs */ | ||
116 | r &= ~(USB_DWC_CTRL2_PHY1RS | USB_DWC_CTRL2_PHY0RS | | ||
117 | USB_DWC_CTRL2_PHYRS); | ||
118 | __raw_writel(r, base + USB_DWC_CTRL2); | ||
119 | wmb(); | ||
120 | } | ||
121 | } | ||
122 | |||
123 | static inline void __au1300_ohci_control(void __iomem *base, int enable, int id) | ||
124 | { | ||
125 | unsigned long r; | ||
126 | |||
127 | if (enable) { | ||
128 | __raw_writel(1, base + USB_DWC_CTRL7); /* start OHCI clock */ | ||
129 | wmb(); | ||
130 | |||
131 | r = __raw_readl(base + USB_DWC_CTRL3); /* enable OHCI block */ | ||
132 | r |= (id == 0) ? USB_DWC_CTRL3_OHCI0_CKEN | ||
133 | : USB_DWC_CTRL3_OHCI1_CKEN; | ||
134 | __raw_writel(r, base + USB_DWC_CTRL3); | ||
135 | wmb(); | ||
136 | |||
137 | __au1300_usb_phyctl(base, enable); /* power up the PHYs */ | ||
138 | |||
139 | r = __raw_readl(base + USB_INT_ENABLE); | ||
140 | r |= (id == 0) ? USB_INTEN_OHCI0 : USB_INTEN_OHCI1; | ||
141 | __raw_writel(r, base + USB_INT_ENABLE); | ||
142 | wmb(); | ||
143 | |||
144 | /* reset the OHCI start clock bit */ | ||
145 | __raw_writel(0, base + USB_DWC_CTRL7); | ||
146 | wmb(); | ||
147 | } else { | ||
148 | r = __raw_readl(base + USB_INT_ENABLE); | ||
149 | r &= ~((id == 0) ? USB_INTEN_OHCI0 : USB_INTEN_OHCI1); | ||
150 | __raw_writel(r, base + USB_INT_ENABLE); | ||
151 | wmb(); | ||
152 | |||
153 | r = __raw_readl(base + USB_DWC_CTRL3); | ||
154 | r &= ~((id == 0) ? USB_DWC_CTRL3_OHCI0_CKEN | ||
155 | : USB_DWC_CTRL3_OHCI1_CKEN); | ||
156 | __raw_writel(r, base + USB_DWC_CTRL3); | ||
157 | wmb(); | ||
158 | |||
159 | __au1300_usb_phyctl(base, enable); | ||
160 | } | ||
161 | } | ||
162 | |||
163 | static inline void __au1300_ehci_control(void __iomem *base, int enable) | ||
164 | { | ||
165 | unsigned long r; | ||
166 | |||
167 | if (enable) { | ||
168 | r = __raw_readl(base + USB_DWC_CTRL3); | ||
169 | r |= USB_DWC_CTRL3_EHCI0_CKEN; | ||
170 | __raw_writel(r, base + USB_DWC_CTRL3); | ||
171 | wmb(); | ||
172 | |||
173 | r = __raw_readl(base + USB_DWC_CTRL1); | ||
174 | r |= USB_DWC_CTRL1_HSTRS; | ||
175 | __raw_writel(r, base + USB_DWC_CTRL1); | ||
176 | wmb(); | ||
177 | |||
178 | __au1300_usb_phyctl(base, enable); | ||
179 | |||
180 | r = __raw_readl(base + USB_INT_ENABLE); | ||
181 | r |= USB_INTEN_EHCI; | ||
182 | __raw_writel(r, base + USB_INT_ENABLE); | ||
183 | wmb(); | ||
184 | } else { | ||
185 | r = __raw_readl(base + USB_INT_ENABLE); | ||
186 | r &= ~USB_INTEN_EHCI; | ||
187 | __raw_writel(r, base + USB_INT_ENABLE); | ||
188 | wmb(); | ||
189 | |||
190 | r = __raw_readl(base + USB_DWC_CTRL1); | ||
191 | r &= ~USB_DWC_CTRL1_HSTRS; | ||
192 | __raw_writel(r, base + USB_DWC_CTRL1); | ||
193 | wmb(); | ||
194 | |||
195 | r = __raw_readl(base + USB_DWC_CTRL3); | ||
196 | r &= ~USB_DWC_CTRL3_EHCI0_CKEN; | ||
197 | __raw_writel(r, base + USB_DWC_CTRL3); | ||
198 | wmb(); | ||
199 | |||
200 | __au1300_usb_phyctl(base, enable); | ||
201 | } | ||
202 | } | ||
203 | |||
204 | static inline void __au1300_udc_control(void __iomem *base, int enable) | ||
205 | { | ||
206 | unsigned long r; | ||
207 | |||
208 | if (enable) { | ||
209 | r = __raw_readl(base + USB_DWC_CTRL1); | ||
210 | r |= USB_DWC_CTRL1_DCRS; | ||
211 | __raw_writel(r, base + USB_DWC_CTRL1); | ||
212 | wmb(); | ||
213 | |||
214 | __au1300_usb_phyctl(base, enable); | ||
215 | |||
216 | r = __raw_readl(base + USB_INT_ENABLE); | ||
217 | r |= USB_INTEN_UDC; | ||
218 | __raw_writel(r, base + USB_INT_ENABLE); | ||
219 | wmb(); | ||
220 | } else { | ||
221 | r = __raw_readl(base + USB_INT_ENABLE); | ||
222 | r &= ~USB_INTEN_UDC; | ||
223 | __raw_writel(r, base + USB_INT_ENABLE); | ||
224 | wmb(); | ||
225 | |||
226 | r = __raw_readl(base + USB_DWC_CTRL1); | ||
227 | r &= ~USB_DWC_CTRL1_DCRS; | ||
228 | __raw_writel(r, base + USB_DWC_CTRL1); | ||
229 | wmb(); | ||
230 | |||
231 | __au1300_usb_phyctl(base, enable); | ||
232 | } | ||
233 | } | ||
234 | |||
235 | static inline void __au1300_otg_control(void __iomem *base, int enable) | ||
236 | { | ||
237 | unsigned long r; | ||
238 | if (enable) { | ||
239 | r = __raw_readl(base + USB_DWC_CTRL3); | ||
240 | r |= USB_DWC_CTRL3_OTG0_CKEN; | ||
241 | __raw_writel(r, base + USB_DWC_CTRL3); | ||
242 | wmb(); | ||
243 | |||
244 | r = __raw_readl(base + USB_DWC_CTRL1); | ||
245 | r &= ~USB_DWC_CTRL1_OTGD; | ||
246 | __raw_writel(r, base + USB_DWC_CTRL1); | ||
247 | wmb(); | ||
248 | |||
249 | __au1300_usb_phyctl(base, enable); | ||
250 | } else { | ||
251 | r = __raw_readl(base + USB_DWC_CTRL1); | ||
252 | r |= USB_DWC_CTRL1_OTGD; | ||
253 | __raw_writel(r, base + USB_DWC_CTRL1); | ||
254 | wmb(); | ||
255 | |||
256 | r = __raw_readl(base + USB_DWC_CTRL3); | ||
257 | r &= ~USB_DWC_CTRL3_OTG0_CKEN; | ||
258 | __raw_writel(r, base + USB_DWC_CTRL3); | ||
259 | wmb(); | ||
260 | |||
261 | __au1300_usb_phyctl(base, enable); | ||
262 | } | ||
263 | } | ||
264 | |||
265 | static inline int au1300_usb_control(int block, int enable) | ||
266 | { | ||
267 | void __iomem *base = | ||
268 | (void __iomem *)KSEG1ADDR(AU1300_USB_CTL_PHYS_ADDR); | ||
269 | int ret = 0; | ||
270 | |||
271 | switch (block) { | ||
272 | case ALCHEMY_USB_OHCI0: | ||
273 | __au1300_ohci_control(base, enable, 0); | ||
274 | break; | ||
275 | case ALCHEMY_USB_OHCI1: | ||
276 | __au1300_ohci_control(base, enable, 1); | ||
277 | break; | ||
278 | case ALCHEMY_USB_EHCI0: | ||
279 | __au1300_ehci_control(base, enable); | ||
280 | break; | ||
281 | case ALCHEMY_USB_UDC0: | ||
282 | __au1300_udc_control(base, enable); | ||
283 | break; | ||
284 | case ALCHEMY_USB_OTG0: | ||
285 | __au1300_otg_control(base, enable); | ||
286 | break; | ||
287 | default: | ||
288 | ret = -ENODEV; | ||
289 | } | ||
290 | return ret; | ||
291 | } | ||
292 | |||
293 | static inline void au1300_usb_init(void) | ||
294 | { | ||
295 | void __iomem *base = | ||
296 | (void __iomem *)KSEG1ADDR(AU1300_USB_CTL_PHYS_ADDR); | ||
297 | |||
298 | /* set some sane defaults. Note: we don't fiddle with DWC_CTRL4 | ||
299 | * here at all: Port 2 routing (EHCI or UDC) must be set either | ||
300 | * by boot firmware or platform init code; I can't autodetect | ||
301 | * a sane setting. | ||
302 | */ | ||
303 | __raw_writel(0, base + USB_INT_ENABLE); /* disable all USB irqs */ | ||
304 | wmb(); | ||
305 | __raw_writel(0, base + USB_DWC_CTRL3); /* disable all clocks */ | ||
306 | wmb(); | ||
307 | __raw_writel(~0, base + USB_MSR_ERR); /* clear all errors */ | ||
308 | wmb(); | ||
309 | __raw_writel(~0, base + USB_INT_STATUS); /* clear int status */ | ||
310 | wmb(); | ||
311 | /* set coherent access bit */ | ||
312 | __raw_writel(USB_SBUS_CTRL_SBCA, base + USB_SBUS_CTRL); | ||
313 | wmb(); | ||
314 | } | ||
315 | |||
316 | static inline void __au1200_ohci_control(void __iomem *base, int enable) | ||
317 | { | ||
318 | unsigned long r = __raw_readl(base + AU1200_USBCFG); | ||
319 | if (enable) { | ||
320 | __raw_writel(r | USBCFG_OCE, base + AU1200_USBCFG); | ||
321 | wmb(); | ||
322 | udelay(2000); | ||
323 | } else { | ||
324 | __raw_writel(r & ~USBCFG_OCE, base + AU1200_USBCFG); | ||
325 | wmb(); | ||
326 | udelay(1000); | ||
327 | } | ||
328 | } | ||
329 | |||
330 | static inline void __au1200_ehci_control(void __iomem *base, int enable) | ||
331 | { | ||
332 | unsigned long r = __raw_readl(base + AU1200_USBCFG); | ||
333 | if (enable) { | ||
334 | __raw_writel(r | USBCFG_ECE | USBCFG_PPE, base + AU1200_USBCFG); | ||
335 | wmb(); | ||
336 | udelay(1000); | ||
337 | } else { | ||
338 | if (!(r & USBCFG_UCE)) /* UDC also off? */ | ||
339 | r &= ~USBCFG_PPE; /* yes: disable HS PHY PLL */ | ||
340 | __raw_writel(r & ~USBCFG_ECE, base + AU1200_USBCFG); | ||
341 | wmb(); | ||
342 | udelay(1000); | ||
343 | } | ||
344 | } | ||
345 | |||
346 | static inline void __au1200_udc_control(void __iomem *base, int enable) | ||
347 | { | ||
348 | unsigned long r = __raw_readl(base + AU1200_USBCFG); | ||
349 | if (enable) { | ||
350 | __raw_writel(r | USBCFG_UCE | USBCFG_PPE, base + AU1200_USBCFG); | ||
351 | wmb(); | ||
352 | } else { | ||
353 | if (!(r & USBCFG_ECE)) /* EHCI also off? */ | ||
354 | r &= ~USBCFG_PPE; /* yes: disable HS PHY PLL */ | ||
355 | __raw_writel(r & ~USBCFG_UCE, base + AU1200_USBCFG); | ||
356 | wmb(); | ||
357 | } | ||
358 | } | ||
359 | |||
360 | static inline int au1200_usb_control(int block, int enable) | ||
361 | { | ||
362 | void __iomem *base = | ||
363 | (void __iomem *)KSEG1ADDR(AU1200_USB_CTL_PHYS_ADDR); | ||
364 | |||
365 | switch (block) { | ||
366 | case ALCHEMY_USB_OHCI0: | ||
367 | __au1200_ohci_control(base, enable); | ||
368 | break; | ||
369 | case ALCHEMY_USB_UDC0: | ||
370 | __au1200_udc_control(base, enable); | ||
371 | break; | ||
372 | case ALCHEMY_USB_EHCI0: | ||
373 | __au1200_ehci_control(base, enable); | ||
374 | break; | ||
375 | default: | ||
376 | return -ENODEV; | ||
377 | } | ||
378 | return 0; | ||
379 | } | ||
380 | |||
381 | |||
382 | /* initialize USB block(s) to a known working state */ | ||
383 | static inline void au1200_usb_init(void) | ||
384 | { | ||
385 | void __iomem *base = | ||
386 | (void __iomem *)KSEG1ADDR(AU1200_USB_CTL_PHYS_ADDR); | ||
387 | __raw_writel(USBCFG_INIT_AU1200, base + AU1200_USBCFG); | ||
388 | wmb(); | ||
389 | udelay(1000); | ||
390 | } | ||
391 | |||
392 | static inline int au1000_usb_init(unsigned long rb, int reg) | ||
393 | { | ||
394 | void __iomem *base = (void __iomem *)KSEG1ADDR(rb + reg); | ||
395 | unsigned long r = __raw_readl(base); | ||
396 | struct clk *c; | ||
397 | |||
398 | /* 48MHz check. Don't init if no one can provide it */ | ||
399 | c = clk_get(NULL, "usbh_clk"); | ||
400 | if (IS_ERR(c)) | ||
401 | return -ENODEV; | ||
402 | if (clk_round_rate(c, 48000000) != 48000000) { | ||
403 | clk_put(c); | ||
404 | return -ENODEV; | ||
405 | } | ||
406 | if (clk_set_rate(c, 48000000)) { | ||
407 | clk_put(c); | ||
408 | return -ENODEV; | ||
409 | } | ||
410 | clk_put(c); | ||
411 | |||
412 | #if defined(__BIG_ENDIAN) | ||
413 | r |= USBHEN_BE; | ||
414 | #endif | ||
415 | r |= USBHEN_C; | ||
416 | |||
417 | __raw_writel(r, base); | ||
418 | wmb(); | ||
419 | udelay(1000); | ||
420 | |||
421 | return 0; | ||
422 | } | ||
423 | |||
424 | |||
425 | static inline void __au1xx0_ohci_control(int enable, unsigned long rb, int creg) | ||
426 | { | ||
427 | void __iomem *base = (void __iomem *)KSEG1ADDR(rb); | ||
428 | unsigned long r = __raw_readl(base + creg); | ||
429 | struct clk *c = clk_get(NULL, "usbh_clk"); | ||
430 | |||
431 | if (IS_ERR(c)) | ||
432 | return; | ||
433 | |||
434 | if (enable) { | ||
435 | if (clk_prepare_enable(c)) | ||
436 | goto out; | ||
437 | |||
438 | __raw_writel(r | USBHEN_CE, base + creg); | ||
439 | wmb(); | ||
440 | udelay(1000); | ||
441 | __raw_writel(r | USBHEN_CE | USBHEN_E, base + creg); | ||
442 | wmb(); | ||
443 | udelay(1000); | ||
444 | |||
445 | /* wait for reset complete (read reg twice: au1500 erratum) */ | ||
446 | while (__raw_readl(base + creg), | ||
447 | !(__raw_readl(base + creg) & USBHEN_RD)) | ||
448 | udelay(1000); | ||
449 | } else { | ||
450 | __raw_writel(r & ~(USBHEN_CE | USBHEN_E), base + creg); | ||
451 | wmb(); | ||
452 | clk_disable_unprepare(c); | ||
453 | } | ||
454 | out: | ||
455 | clk_put(c); | ||
456 | } | ||
457 | |||
458 | static inline int au1000_usb_control(int block, int enable, unsigned long rb, | ||
459 | int creg) | ||
460 | { | ||
461 | int ret = 0; | ||
462 | |||
463 | switch (block) { | ||
464 | case ALCHEMY_USB_OHCI0: | ||
465 | __au1xx0_ohci_control(enable, rb, creg); | ||
466 | break; | ||
467 | default: | ||
468 | ret = -ENODEV; | ||
469 | } | ||
470 | return ret; | ||
471 | } | ||
472 | |||
473 | /* | ||
474 | * alchemy_usb_control - control Alchemy on-chip USB blocks | ||
475 | * @block: USB block to target | ||
476 | * @enable: set 1 to enable a block, 0 to disable | ||
477 | */ | ||
478 | int alchemy_usb_control(int block, int enable) | ||
479 | { | ||
480 | unsigned long flags; | ||
481 | int ret; | ||
482 | |||
483 | spin_lock_irqsave(&alchemy_usb_lock, flags); | ||
484 | switch (alchemy_get_cputype()) { | ||
485 | case ALCHEMY_CPU_AU1000: | ||
486 | case ALCHEMY_CPU_AU1500: | ||
487 | case ALCHEMY_CPU_AU1100: | ||
488 | ret = au1000_usb_control(block, enable, | ||
489 | AU1000_USB_OHCI_PHYS_ADDR, AU1000_OHCICFG); | ||
490 | break; | ||
491 | case ALCHEMY_CPU_AU1550: | ||
492 | ret = au1000_usb_control(block, enable, | ||
493 | AU1550_USB_OHCI_PHYS_ADDR, AU1550_OHCICFG); | ||
494 | break; | ||
495 | case ALCHEMY_CPU_AU1200: | ||
496 | ret = au1200_usb_control(block, enable); | ||
497 | break; | ||
498 | case ALCHEMY_CPU_AU1300: | ||
499 | ret = au1300_usb_control(block, enable); | ||
500 | break; | ||
501 | default: | ||
502 | ret = -ENODEV; | ||
503 | } | ||
504 | spin_unlock_irqrestore(&alchemy_usb_lock, flags); | ||
505 | return ret; | ||
506 | } | ||
507 | EXPORT_SYMBOL_GPL(alchemy_usb_control); | ||
508 | |||
509 | |||
510 | static unsigned long alchemy_usb_pmdata[2]; | ||
511 | |||
512 | static void au1000_usb_pm(unsigned long br, int creg, int susp) | ||
513 | { | ||
514 | void __iomem *base = (void __iomem *)KSEG1ADDR(br); | ||
515 | |||
516 | if (susp) { | ||
517 | alchemy_usb_pmdata[0] = __raw_readl(base + creg); | ||
518 | /* There appears to be some undocumented reset register.... */ | ||
519 | __raw_writel(0, base + 0x04); | ||
520 | wmb(); | ||
521 | __raw_writel(0, base + creg); | ||
522 | wmb(); | ||
523 | } else { | ||
524 | __raw_writel(alchemy_usb_pmdata[0], base + creg); | ||
525 | wmb(); | ||
526 | } | ||
527 | } | ||
528 | |||
529 | static void au1200_usb_pm(int susp) | ||
530 | { | ||
531 | void __iomem *base = | ||
532 | (void __iomem *)KSEG1ADDR(AU1200_USB_OTG_PHYS_ADDR); | ||
533 | if (susp) { | ||
534 | /* save OTG_CAP/MUX registers which indicate port routing */ | ||
535 | /* FIXME: write an OTG driver to do that */ | ||
536 | alchemy_usb_pmdata[0] = __raw_readl(base + 0x00); | ||
537 | alchemy_usb_pmdata[1] = __raw_readl(base + 0x04); | ||
538 | } else { | ||
539 | /* restore access to all MMIO areas */ | ||
540 | au1200_usb_init(); | ||
541 | |||
542 | /* restore OTG_CAP/MUX registers */ | ||
543 | __raw_writel(alchemy_usb_pmdata[0], base + 0x00); | ||
544 | __raw_writel(alchemy_usb_pmdata[1], base + 0x04); | ||
545 | wmb(); | ||
546 | } | ||
547 | } | ||
548 | |||
549 | static void au1300_usb_pm(int susp) | ||
550 | { | ||
551 | void __iomem *base = | ||
552 | (void __iomem *)KSEG1ADDR(AU1300_USB_CTL_PHYS_ADDR); | ||
553 | /* remember Port2 routing */ | ||
554 | if (susp) { | ||
555 | alchemy_usb_pmdata[0] = __raw_readl(base + USB_DWC_CTRL4); | ||
556 | } else { | ||
557 | au1300_usb_init(); | ||
558 | __raw_writel(alchemy_usb_pmdata[0], base + USB_DWC_CTRL4); | ||
559 | wmb(); | ||
560 | } | ||
561 | } | ||
562 | |||
563 | static void alchemy_usb_pm(int susp) | ||
564 | { | ||
565 | switch (alchemy_get_cputype()) { | ||
566 | case ALCHEMY_CPU_AU1000: | ||
567 | case ALCHEMY_CPU_AU1500: | ||
568 | case ALCHEMY_CPU_AU1100: | ||
569 | au1000_usb_pm(AU1000_USB_OHCI_PHYS_ADDR, AU1000_OHCICFG, susp); | ||
570 | break; | ||
571 | case ALCHEMY_CPU_AU1550: | ||
572 | au1000_usb_pm(AU1550_USB_OHCI_PHYS_ADDR, AU1550_OHCICFG, susp); | ||
573 | break; | ||
574 | case ALCHEMY_CPU_AU1200: | ||
575 | au1200_usb_pm(susp); | ||
576 | break; | ||
577 | case ALCHEMY_CPU_AU1300: | ||
578 | au1300_usb_pm(susp); | ||
579 | break; | ||
580 | } | ||
581 | } | ||
582 | |||
583 | static int alchemy_usb_suspend(void) | ||
584 | { | ||
585 | alchemy_usb_pm(1); | ||
586 | return 0; | ||
587 | } | ||
588 | |||
589 | static void alchemy_usb_resume(void) | ||
590 | { | ||
591 | alchemy_usb_pm(0); | ||
592 | } | ||
593 | |||
594 | static struct syscore_ops alchemy_usb_pm_ops = { | ||
595 | .suspend = alchemy_usb_suspend, | ||
596 | .resume = alchemy_usb_resume, | ||
597 | }; | ||
598 | |||
599 | static int __init alchemy_usb_init(void) | ||
600 | { | ||
601 | int ret = 0; | ||
602 | |||
603 | switch (alchemy_get_cputype()) { | ||
604 | case ALCHEMY_CPU_AU1000: | ||
605 | case ALCHEMY_CPU_AU1500: | ||
606 | case ALCHEMY_CPU_AU1100: | ||
607 | ret = au1000_usb_init(AU1000_USB_OHCI_PHYS_ADDR, | ||
608 | AU1000_OHCICFG); | ||
609 | break; | ||
610 | case ALCHEMY_CPU_AU1550: | ||
611 | ret = au1000_usb_init(AU1550_USB_OHCI_PHYS_ADDR, | ||
612 | AU1550_OHCICFG); | ||
613 | break; | ||
614 | case ALCHEMY_CPU_AU1200: | ||
615 | au1200_usb_init(); | ||
616 | break; | ||
617 | case ALCHEMY_CPU_AU1300: | ||
618 | au1300_usb_init(); | ||
619 | break; | ||
620 | } | ||
621 | |||
622 | if (!ret) | ||
623 | register_syscore_ops(&alchemy_usb_pm_ops); | ||
624 | |||
625 | return ret; | ||
626 | } | ||
627 | arch_initcall(alchemy_usb_init); | ||
diff --git a/arch/mips/alchemy/common/vss.c b/arch/mips/alchemy/common/vss.c new file mode 100644 index 000000000..3d0d468d9 --- /dev/null +++ b/arch/mips/alchemy/common/vss.c | |||
@@ -0,0 +1,85 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0-only | ||
2 | /* | ||
3 | * Au1300 media block power gating (VSS) | ||
4 | * | ||
5 | * This is a stop-gap solution until I have the clock framework integration | ||
6 | * ready. This stuff here really must be handled transparently when clocks | ||
7 | * for various media blocks are enabled/disabled. | ||
8 | */ | ||
9 | |||
10 | #include <linux/export.h> | ||
11 | #include <linux/spinlock.h> | ||
12 | #include <asm/mach-au1x00/au1000.h> | ||
13 | |||
14 | #define VSS_GATE 0x00 /* gate wait timers */ | ||
15 | #define VSS_CLKRST 0x04 /* clock/block control */ | ||
16 | #define VSS_FTR 0x08 /* footers */ | ||
17 | |||
18 | #define VSS_ADDR(blk) (KSEG1ADDR(AU1300_VSS_PHYS_ADDR) + (blk * 0x0c)) | ||
19 | |||
20 | static DEFINE_SPINLOCK(au1300_vss_lock); | ||
21 | |||
22 | /* enable a block as outlined in the databook */ | ||
23 | static inline void __enable_block(int block) | ||
24 | { | ||
25 | void __iomem *base = (void __iomem *)VSS_ADDR(block); | ||
26 | |||
27 | __raw_writel(3, base + VSS_CLKRST); /* enable clock, assert reset */ | ||
28 | wmb(); | ||
29 | |||
30 | __raw_writel(0x01fffffe, base + VSS_GATE); /* maximum setup time */ | ||
31 | wmb(); | ||
32 | |||
33 | /* enable footers in sequence */ | ||
34 | __raw_writel(0x01, base + VSS_FTR); | ||
35 | wmb(); | ||
36 | __raw_writel(0x03, base + VSS_FTR); | ||
37 | wmb(); | ||
38 | __raw_writel(0x07, base + VSS_FTR); | ||
39 | wmb(); | ||
40 | __raw_writel(0x0f, base + VSS_FTR); | ||
41 | wmb(); | ||
42 | |||
43 | __raw_writel(0x01ffffff, base + VSS_GATE); /* start FSM too */ | ||
44 | wmb(); | ||
45 | |||
46 | __raw_writel(2, base + VSS_CLKRST); /* deassert reset */ | ||
47 | wmb(); | ||
48 | |||
49 | __raw_writel(0x1f, base + VSS_FTR); /* enable isolation cells */ | ||
50 | wmb(); | ||
51 | } | ||
52 | |||
53 | /* disable a block as outlined in the databook */ | ||
54 | static inline void __disable_block(int block) | ||
55 | { | ||
56 | void __iomem *base = (void __iomem *)VSS_ADDR(block); | ||
57 | |||
58 | __raw_writel(0x0f, base + VSS_FTR); /* disable isolation cells */ | ||
59 | wmb(); | ||
60 | __raw_writel(0, base + VSS_GATE); /* disable FSM */ | ||
61 | wmb(); | ||
62 | __raw_writel(3, base + VSS_CLKRST); /* assert reset */ | ||
63 | wmb(); | ||
64 | __raw_writel(1, base + VSS_CLKRST); /* disable clock */ | ||
65 | wmb(); | ||
66 | __raw_writel(0, base + VSS_FTR); /* disable all footers */ | ||
67 | wmb(); | ||
68 | } | ||
69 | |||
70 | void au1300_vss_block_control(int block, int enable) | ||
71 | { | ||
72 | unsigned long flags; | ||
73 | |||
74 | if (alchemy_get_cputype() != ALCHEMY_CPU_AU1300) | ||
75 | return; | ||
76 | |||
77 | /* only one block at a time */ | ||
78 | spin_lock_irqsave(&au1300_vss_lock, flags); | ||
79 | if (enable) | ||
80 | __enable_block(block); | ||
81 | else | ||
82 | __disable_block(block); | ||
83 | spin_unlock_irqrestore(&au1300_vss_lock, flags); | ||
84 | } | ||
85 | EXPORT_SYMBOL_GPL(au1300_vss_block_control); | ||
diff --git a/arch/mips/alchemy/devboards/Makefile b/arch/mips/alchemy/devboards/Makefile new file mode 100644 index 000000000..10a52283f --- /dev/null +++ b/arch/mips/alchemy/devboards/Makefile | |||
@@ -0,0 +1,7 @@ | |||
1 | # SPDX-License-Identifier: GPL-2.0-only | ||
2 | # | ||
3 | # Alchemy Develboards | ||
4 | # | ||
5 | |||
6 | obj-y += bcsr.o platform.o db1000.o db1200.o db1300.o db1550.o db1xxx.o | ||
7 | obj-$(CONFIG_PM) += pm.o | ||
diff --git a/arch/mips/alchemy/devboards/bcsr.c b/arch/mips/alchemy/devboards/bcsr.c new file mode 100644 index 000000000..8df0ccdc9 --- /dev/null +++ b/arch/mips/alchemy/devboards/bcsr.c | |||
@@ -0,0 +1,147 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0-only | ||
2 | /* | ||
3 | * bcsr.h -- Db1xxx/Pb1xxx Devboard CPLD registers ("BCSR") abstraction. | ||
4 | * | ||
5 | * All Alchemy development boards (except, of course, the weird PB1000) | ||
6 | * have a few registers in a CPLD with standardised layout; they mostly | ||
7 | * only differ in base address. | ||
8 | * All registers are 16bits wide with 32bit spacing. | ||
9 | */ | ||
10 | |||
11 | #include <linux/interrupt.h> | ||
12 | #include <linux/irqchip/chained_irq.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/export.h> | ||
15 | #include <linux/spinlock.h> | ||
16 | #include <linux/irq.h> | ||
17 | #include <asm/addrspace.h> | ||
18 | #include <asm/io.h> | ||
19 | #include <asm/mach-db1x00/bcsr.h> | ||
20 | |||
21 | static struct bcsr_reg { | ||
22 | void __iomem *raddr; | ||
23 | spinlock_t lock; | ||
24 | } bcsr_regs[BCSR_CNT]; | ||
25 | |||
26 | static void __iomem *bcsr_virt; /* KSEG1 addr of BCSR base */ | ||
27 | static int bcsr_csc_base; /* linux-irq of first cascaded irq */ | ||
28 | |||
29 | void __init bcsr_init(unsigned long bcsr1_phys, unsigned long bcsr2_phys) | ||
30 | { | ||
31 | int i; | ||
32 | |||
33 | bcsr1_phys = KSEG1ADDR(CPHYSADDR(bcsr1_phys)); | ||
34 | bcsr2_phys = KSEG1ADDR(CPHYSADDR(bcsr2_phys)); | ||
35 | |||
36 | bcsr_virt = (void __iomem *)bcsr1_phys; | ||
37 | |||
38 | for (i = 0; i < BCSR_CNT; i++) { | ||
39 | if (i >= BCSR_HEXLEDS) | ||
40 | bcsr_regs[i].raddr = (void __iomem *)bcsr2_phys + | ||
41 | (0x04 * (i - BCSR_HEXLEDS)); | ||
42 | else | ||
43 | bcsr_regs[i].raddr = (void __iomem *)bcsr1_phys + | ||
44 | (0x04 * i); | ||
45 | |||
46 | spin_lock_init(&bcsr_regs[i].lock); | ||
47 | } | ||
48 | } | ||
49 | |||
50 | unsigned short bcsr_read(enum bcsr_id reg) | ||
51 | { | ||
52 | unsigned short r; | ||
53 | unsigned long flags; | ||
54 | |||
55 | spin_lock_irqsave(&bcsr_regs[reg].lock, flags); | ||
56 | r = __raw_readw(bcsr_regs[reg].raddr); | ||
57 | spin_unlock_irqrestore(&bcsr_regs[reg].lock, flags); | ||
58 | return r; | ||
59 | } | ||
60 | EXPORT_SYMBOL_GPL(bcsr_read); | ||
61 | |||
62 | void bcsr_write(enum bcsr_id reg, unsigned short val) | ||
63 | { | ||
64 | unsigned long flags; | ||
65 | |||
66 | spin_lock_irqsave(&bcsr_regs[reg].lock, flags); | ||
67 | __raw_writew(val, bcsr_regs[reg].raddr); | ||
68 | wmb(); | ||
69 | spin_unlock_irqrestore(&bcsr_regs[reg].lock, flags); | ||
70 | } | ||
71 | EXPORT_SYMBOL_GPL(bcsr_write); | ||
72 | |||
73 | void bcsr_mod(enum bcsr_id reg, unsigned short clr, unsigned short set) | ||
74 | { | ||
75 | unsigned short r; | ||
76 | unsigned long flags; | ||
77 | |||
78 | spin_lock_irqsave(&bcsr_regs[reg].lock, flags); | ||
79 | r = __raw_readw(bcsr_regs[reg].raddr); | ||
80 | r &= ~clr; | ||
81 | r |= set; | ||
82 | __raw_writew(r, bcsr_regs[reg].raddr); | ||
83 | wmb(); | ||
84 | spin_unlock_irqrestore(&bcsr_regs[reg].lock, flags); | ||
85 | } | ||
86 | EXPORT_SYMBOL_GPL(bcsr_mod); | ||
87 | |||
88 | /* | ||
89 | * DB1200/PB1200 CPLD IRQ muxer | ||
90 | */ | ||
91 | static void bcsr_csc_handler(struct irq_desc *d) | ||
92 | { | ||
93 | unsigned short bisr = __raw_readw(bcsr_virt + BCSR_REG_INTSTAT); | ||
94 | struct irq_chip *chip = irq_desc_get_chip(d); | ||
95 | |||
96 | chained_irq_enter(chip, d); | ||
97 | generic_handle_irq(bcsr_csc_base + __ffs(bisr)); | ||
98 | chained_irq_exit(chip, d); | ||
99 | } | ||
100 | |||
101 | static void bcsr_irq_mask(struct irq_data *d) | ||
102 | { | ||
103 | unsigned short v = 1 << (d->irq - bcsr_csc_base); | ||
104 | __raw_writew(v, bcsr_virt + BCSR_REG_MASKCLR); | ||
105 | wmb(); | ||
106 | } | ||
107 | |||
108 | static void bcsr_irq_maskack(struct irq_data *d) | ||
109 | { | ||
110 | unsigned short v = 1 << (d->irq - bcsr_csc_base); | ||
111 | __raw_writew(v, bcsr_virt + BCSR_REG_MASKCLR); | ||
112 | __raw_writew(v, bcsr_virt + BCSR_REG_INTSTAT); /* ack */ | ||
113 | wmb(); | ||
114 | } | ||
115 | |||
116 | static void bcsr_irq_unmask(struct irq_data *d) | ||
117 | { | ||
118 | unsigned short v = 1 << (d->irq - bcsr_csc_base); | ||
119 | __raw_writew(v, bcsr_virt + BCSR_REG_MASKSET); | ||
120 | wmb(); | ||
121 | } | ||
122 | |||
123 | static struct irq_chip bcsr_irq_type = { | ||
124 | .name = "CPLD", | ||
125 | .irq_mask = bcsr_irq_mask, | ||
126 | .irq_mask_ack = bcsr_irq_maskack, | ||
127 | .irq_unmask = bcsr_irq_unmask, | ||
128 | }; | ||
129 | |||
130 | void __init bcsr_init_irq(int csc_start, int csc_end, int hook_irq) | ||
131 | { | ||
132 | unsigned int irq; | ||
133 | |||
134 | /* mask & enable & ack all */ | ||
135 | __raw_writew(0xffff, bcsr_virt + BCSR_REG_MASKCLR); | ||
136 | __raw_writew(0xffff, bcsr_virt + BCSR_REG_INTSET); | ||
137 | __raw_writew(0xffff, bcsr_virt + BCSR_REG_INTSTAT); | ||
138 | wmb(); | ||
139 | |||
140 | bcsr_csc_base = csc_start; | ||
141 | |||
142 | for (irq = csc_start; irq <= csc_end; irq++) | ||
143 | irq_set_chip_and_handler_name(irq, &bcsr_irq_type, | ||
144 | handle_level_irq, "level"); | ||
145 | |||
146 | irq_set_chained_handler(hook_irq, bcsr_csc_handler); | ||
147 | } | ||
diff --git a/arch/mips/alchemy/devboards/db1000.c b/arch/mips/alchemy/devboards/db1000.c new file mode 100644 index 000000000..2c52ee27b --- /dev/null +++ b/arch/mips/alchemy/devboards/db1000.c | |||
@@ -0,0 +1,564 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
2 | /* | ||
3 | * DBAu1000/1500/1100 PBAu1100/1500 board support | ||
4 | * | ||
5 | * Copyright 2000, 2008 MontaVista Software Inc. | ||
6 | * Author: MontaVista Software, Inc. <source@mvista.com> | ||
7 | */ | ||
8 | |||
9 | #include <linux/clk.h> | ||
10 | #include <linux/dma-mapping.h> | ||
11 | #include <linux/gpio.h> | ||
12 | #include <linux/gpio/machine.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/interrupt.h> | ||
15 | #include <linux/leds.h> | ||
16 | #include <linux/mmc/host.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/platform_device.h> | ||
19 | #include <linux/pm.h> | ||
20 | #include <linux/spi/spi.h> | ||
21 | #include <linux/spi/spi_gpio.h> | ||
22 | #include <linux/spi/ads7846.h> | ||
23 | #include <asm/mach-au1x00/au1000.h> | ||
24 | #include <asm/mach-au1x00/gpio-au1000.h> | ||
25 | #include <asm/mach-au1x00/au1000_dma.h> | ||
26 | #include <asm/mach-au1x00/au1100_mmc.h> | ||
27 | #include <asm/mach-db1x00/bcsr.h> | ||
28 | #include <asm/reboot.h> | ||
29 | #include <prom.h> | ||
30 | #include "platform.h" | ||
31 | |||
32 | #define F_SWAPPED (bcsr_read(BCSR_STATUS) & BCSR_STATUS_DB1000_SWAPBOOT) | ||
33 | |||
34 | const char *get_system_type(void); | ||
35 | |||
36 | int __init db1000_board_setup(void) | ||
37 | { | ||
38 | /* initialize board register space */ | ||
39 | bcsr_init(DB1000_BCSR_PHYS_ADDR, | ||
40 | DB1000_BCSR_PHYS_ADDR + DB1000_BCSR_HEXLED_OFS); | ||
41 | |||
42 | switch (BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI))) { | ||
43 | case BCSR_WHOAMI_DB1000: | ||
44 | case BCSR_WHOAMI_DB1500: | ||
45 | case BCSR_WHOAMI_DB1100: | ||
46 | case BCSR_WHOAMI_PB1500: | ||
47 | case BCSR_WHOAMI_PB1500R2: | ||
48 | case BCSR_WHOAMI_PB1100: | ||
49 | pr_info("AMD Alchemy %s Board\n", get_system_type()); | ||
50 | return 0; | ||
51 | } | ||
52 | return -ENODEV; | ||
53 | } | ||
54 | |||
55 | static int db1500_map_pci_irq(const struct pci_dev *d, u8 slot, u8 pin) | ||
56 | { | ||
57 | if ((slot < 12) || (slot > 13) || pin == 0) | ||
58 | return -1; | ||
59 | if (slot == 12) | ||
60 | return (pin == 1) ? AU1500_PCI_INTA : 0xff; | ||
61 | if (slot == 13) { | ||
62 | switch (pin) { | ||
63 | case 1: return AU1500_PCI_INTA; | ||
64 | case 2: return AU1500_PCI_INTB; | ||
65 | case 3: return AU1500_PCI_INTC; | ||
66 | case 4: return AU1500_PCI_INTD; | ||
67 | } | ||
68 | } | ||
69 | return -1; | ||
70 | } | ||
71 | |||
72 | static u64 au1xxx_all_dmamask = DMA_BIT_MASK(32); | ||
73 | |||
74 | static struct resource alchemy_pci_host_res[] = { | ||
75 | [0] = { | ||
76 | .start = AU1500_PCI_PHYS_ADDR, | ||
77 | .end = AU1500_PCI_PHYS_ADDR + 0xfff, | ||
78 | .flags = IORESOURCE_MEM, | ||
79 | }, | ||
80 | }; | ||
81 | |||
82 | static struct alchemy_pci_platdata db1500_pci_pd = { | ||
83 | .board_map_irq = db1500_map_pci_irq, | ||
84 | }; | ||
85 | |||
86 | static struct platform_device db1500_pci_host_dev = { | ||
87 | .dev.platform_data = &db1500_pci_pd, | ||
88 | .name = "alchemy-pci", | ||
89 | .id = 0, | ||
90 | .num_resources = ARRAY_SIZE(alchemy_pci_host_res), | ||
91 | .resource = alchemy_pci_host_res, | ||
92 | }; | ||
93 | |||
94 | int __init db1500_pci_setup(void) | ||
95 | { | ||
96 | return platform_device_register(&db1500_pci_host_dev); | ||
97 | } | ||
98 | |||
99 | static struct resource au1100_lcd_resources[] = { | ||
100 | [0] = { | ||
101 | .start = AU1100_LCD_PHYS_ADDR, | ||
102 | .end = AU1100_LCD_PHYS_ADDR + 0x800 - 1, | ||
103 | .flags = IORESOURCE_MEM, | ||
104 | }, | ||
105 | [1] = { | ||
106 | .start = AU1100_LCD_INT, | ||
107 | .end = AU1100_LCD_INT, | ||
108 | .flags = IORESOURCE_IRQ, | ||
109 | } | ||
110 | }; | ||
111 | |||
112 | static struct platform_device au1100_lcd_device = { | ||
113 | .name = "au1100-lcd", | ||
114 | .id = 0, | ||
115 | .dev = { | ||
116 | .dma_mask = &au1xxx_all_dmamask, | ||
117 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
118 | }, | ||
119 | .num_resources = ARRAY_SIZE(au1100_lcd_resources), | ||
120 | .resource = au1100_lcd_resources, | ||
121 | }; | ||
122 | |||
123 | static struct resource alchemy_ac97c_res[] = { | ||
124 | [0] = { | ||
125 | .start = AU1000_AC97_PHYS_ADDR, | ||
126 | .end = AU1000_AC97_PHYS_ADDR + 0xfff, | ||
127 | .flags = IORESOURCE_MEM, | ||
128 | }, | ||
129 | [1] = { | ||
130 | .start = DMA_ID_AC97C_TX, | ||
131 | .end = DMA_ID_AC97C_TX, | ||
132 | .flags = IORESOURCE_DMA, | ||
133 | }, | ||
134 | [2] = { | ||
135 | .start = DMA_ID_AC97C_RX, | ||
136 | .end = DMA_ID_AC97C_RX, | ||
137 | .flags = IORESOURCE_DMA, | ||
138 | }, | ||
139 | }; | ||
140 | |||
141 | static struct platform_device alchemy_ac97c_dev = { | ||
142 | .name = "alchemy-ac97c", | ||
143 | .id = -1, | ||
144 | .resource = alchemy_ac97c_res, | ||
145 | .num_resources = ARRAY_SIZE(alchemy_ac97c_res), | ||
146 | }; | ||
147 | |||
148 | static struct platform_device alchemy_ac97c_dma_dev = { | ||
149 | .name = "alchemy-pcm-dma", | ||
150 | .id = 0, | ||
151 | }; | ||
152 | |||
153 | static struct platform_device db1x00_codec_dev = { | ||
154 | .name = "ac97-codec", | ||
155 | .id = -1, | ||
156 | }; | ||
157 | |||
158 | static struct platform_device db1x00_audio_dev = { | ||
159 | .name = "db1000-audio", | ||
160 | .dev = { | ||
161 | .dma_mask = &au1xxx_all_dmamask, | ||
162 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
163 | }, | ||
164 | }; | ||
165 | |||
166 | /******************************************************************************/ | ||
167 | |||
168 | static irqreturn_t db1100_mmc_cd(int irq, void *ptr) | ||
169 | { | ||
170 | void (*mmc_cd)(struct mmc_host *, unsigned long); | ||
171 | /* link against CONFIG_MMC=m */ | ||
172 | mmc_cd = symbol_get(mmc_detect_change); | ||
173 | mmc_cd(ptr, msecs_to_jiffies(500)); | ||
174 | symbol_put(mmc_detect_change); | ||
175 | |||
176 | return IRQ_HANDLED; | ||
177 | } | ||
178 | |||
179 | static int db1100_mmc_cd_setup(void *mmc_host, int en) | ||
180 | { | ||
181 | int ret = 0, irq; | ||
182 | |||
183 | if (BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI)) == BCSR_WHOAMI_DB1100) | ||
184 | irq = AU1100_GPIO19_INT; | ||
185 | else | ||
186 | irq = AU1100_GPIO14_INT; /* PB1100 SD0 CD# */ | ||
187 | |||
188 | if (en) { | ||
189 | irq_set_irq_type(irq, IRQ_TYPE_EDGE_BOTH); | ||
190 | ret = request_irq(irq, db1100_mmc_cd, 0, | ||
191 | "sd0_cd", mmc_host); | ||
192 | } else | ||
193 | free_irq(irq, mmc_host); | ||
194 | return ret; | ||
195 | } | ||
196 | |||
197 | static int db1100_mmc1_cd_setup(void *mmc_host, int en) | ||
198 | { | ||
199 | int ret = 0, irq; | ||
200 | |||
201 | if (BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI)) == BCSR_WHOAMI_DB1100) | ||
202 | irq = AU1100_GPIO20_INT; | ||
203 | else | ||
204 | irq = AU1100_GPIO15_INT; /* PB1100 SD1 CD# */ | ||
205 | |||
206 | if (en) { | ||
207 | irq_set_irq_type(irq, IRQ_TYPE_EDGE_BOTH); | ||
208 | ret = request_irq(irq, db1100_mmc_cd, 0, | ||
209 | "sd1_cd", mmc_host); | ||
210 | } else | ||
211 | free_irq(irq, mmc_host); | ||
212 | return ret; | ||
213 | } | ||
214 | |||
215 | static int db1100_mmc_card_readonly(void *mmc_host) | ||
216 | { | ||
217 | /* testing suggests that this bit is inverted */ | ||
218 | return (bcsr_read(BCSR_STATUS) & BCSR_STATUS_SD0WP) ? 0 : 1; | ||
219 | } | ||
220 | |||
221 | static int db1100_mmc_card_inserted(void *mmc_host) | ||
222 | { | ||
223 | return !alchemy_gpio_get_value(19); | ||
224 | } | ||
225 | |||
226 | static void db1100_mmc_set_power(void *mmc_host, int state) | ||
227 | { | ||
228 | int bit; | ||
229 | |||
230 | if (BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI)) == BCSR_WHOAMI_DB1100) | ||
231 | bit = BCSR_BOARD_SD0PWR; | ||
232 | else | ||
233 | bit = BCSR_BOARD_PB1100_SD0PWR; | ||
234 | |||
235 | if (state) { | ||
236 | bcsr_mod(BCSR_BOARD, 0, bit); | ||
237 | msleep(400); /* stabilization time */ | ||
238 | } else | ||
239 | bcsr_mod(BCSR_BOARD, bit, 0); | ||
240 | } | ||
241 | |||
242 | static void db1100_mmcled_set(struct led_classdev *led, enum led_brightness b) | ||
243 | { | ||
244 | if (b != LED_OFF) | ||
245 | bcsr_mod(BCSR_LEDS, BCSR_LEDS_LED0, 0); | ||
246 | else | ||
247 | bcsr_mod(BCSR_LEDS, 0, BCSR_LEDS_LED0); | ||
248 | } | ||
249 | |||
250 | static struct led_classdev db1100_mmc_led = { | ||
251 | .brightness_set = db1100_mmcled_set, | ||
252 | }; | ||
253 | |||
254 | static int db1100_mmc1_card_readonly(void *mmc_host) | ||
255 | { | ||
256 | return (bcsr_read(BCSR_BOARD) & BCSR_BOARD_SD1WP) ? 1 : 0; | ||
257 | } | ||
258 | |||
259 | static int db1100_mmc1_card_inserted(void *mmc_host) | ||
260 | { | ||
261 | return !alchemy_gpio_get_value(20); | ||
262 | } | ||
263 | |||
264 | static void db1100_mmc1_set_power(void *mmc_host, int state) | ||
265 | { | ||
266 | int bit; | ||
267 | |||
268 | if (BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI)) == BCSR_WHOAMI_DB1100) | ||
269 | bit = BCSR_BOARD_SD1PWR; | ||
270 | else | ||
271 | bit = BCSR_BOARD_PB1100_SD1PWR; | ||
272 | |||
273 | if (state) { | ||
274 | bcsr_mod(BCSR_BOARD, 0, bit); | ||
275 | msleep(400); /* stabilization time */ | ||
276 | } else | ||
277 | bcsr_mod(BCSR_BOARD, bit, 0); | ||
278 | } | ||
279 | |||
280 | static void db1100_mmc1led_set(struct led_classdev *led, enum led_brightness b) | ||
281 | { | ||
282 | if (b != LED_OFF) | ||
283 | bcsr_mod(BCSR_LEDS, BCSR_LEDS_LED1, 0); | ||
284 | else | ||
285 | bcsr_mod(BCSR_LEDS, 0, BCSR_LEDS_LED1); | ||
286 | } | ||
287 | |||
288 | static struct led_classdev db1100_mmc1_led = { | ||
289 | .brightness_set = db1100_mmc1led_set, | ||
290 | }; | ||
291 | |||
292 | static struct au1xmmc_platform_data db1100_mmc_platdata[2] = { | ||
293 | [0] = { | ||
294 | .cd_setup = db1100_mmc_cd_setup, | ||
295 | .set_power = db1100_mmc_set_power, | ||
296 | .card_inserted = db1100_mmc_card_inserted, | ||
297 | .card_readonly = db1100_mmc_card_readonly, | ||
298 | .led = &db1100_mmc_led, | ||
299 | }, | ||
300 | [1] = { | ||
301 | .cd_setup = db1100_mmc1_cd_setup, | ||
302 | .set_power = db1100_mmc1_set_power, | ||
303 | .card_inserted = db1100_mmc1_card_inserted, | ||
304 | .card_readonly = db1100_mmc1_card_readonly, | ||
305 | .led = &db1100_mmc1_led, | ||
306 | }, | ||
307 | }; | ||
308 | |||
309 | static struct resource au1100_mmc0_resources[] = { | ||
310 | [0] = { | ||
311 | .start = AU1100_SD0_PHYS_ADDR, | ||
312 | .end = AU1100_SD0_PHYS_ADDR + 0xfff, | ||
313 | .flags = IORESOURCE_MEM, | ||
314 | }, | ||
315 | [1] = { | ||
316 | .start = AU1100_SD_INT, | ||
317 | .end = AU1100_SD_INT, | ||
318 | .flags = IORESOURCE_IRQ, | ||
319 | }, | ||
320 | [2] = { | ||
321 | .start = DMA_ID_SD0_TX, | ||
322 | .end = DMA_ID_SD0_TX, | ||
323 | .flags = IORESOURCE_DMA, | ||
324 | }, | ||
325 | [3] = { | ||
326 | .start = DMA_ID_SD0_RX, | ||
327 | .end = DMA_ID_SD0_RX, | ||
328 | .flags = IORESOURCE_DMA, | ||
329 | } | ||
330 | }; | ||
331 | |||
332 | static struct platform_device db1100_mmc0_dev = { | ||
333 | .name = "au1xxx-mmc", | ||
334 | .id = 0, | ||
335 | .dev = { | ||
336 | .dma_mask = &au1xxx_all_dmamask, | ||
337 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
338 | .platform_data = &db1100_mmc_platdata[0], | ||
339 | }, | ||
340 | .num_resources = ARRAY_SIZE(au1100_mmc0_resources), | ||
341 | .resource = au1100_mmc0_resources, | ||
342 | }; | ||
343 | |||
344 | static struct resource au1100_mmc1_res[] = { | ||
345 | [0] = { | ||
346 | .start = AU1100_SD1_PHYS_ADDR, | ||
347 | .end = AU1100_SD1_PHYS_ADDR + 0xfff, | ||
348 | .flags = IORESOURCE_MEM, | ||
349 | }, | ||
350 | [1] = { | ||
351 | .start = AU1100_SD_INT, | ||
352 | .end = AU1100_SD_INT, | ||
353 | .flags = IORESOURCE_IRQ, | ||
354 | }, | ||
355 | [2] = { | ||
356 | .start = DMA_ID_SD1_TX, | ||
357 | .end = DMA_ID_SD1_TX, | ||
358 | .flags = IORESOURCE_DMA, | ||
359 | }, | ||
360 | [3] = { | ||
361 | .start = DMA_ID_SD1_RX, | ||
362 | .end = DMA_ID_SD1_RX, | ||
363 | .flags = IORESOURCE_DMA, | ||
364 | } | ||
365 | }; | ||
366 | |||
367 | static struct platform_device db1100_mmc1_dev = { | ||
368 | .name = "au1xxx-mmc", | ||
369 | .id = 1, | ||
370 | .dev = { | ||
371 | .dma_mask = &au1xxx_all_dmamask, | ||
372 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
373 | .platform_data = &db1100_mmc_platdata[1], | ||
374 | }, | ||
375 | .num_resources = ARRAY_SIZE(au1100_mmc1_res), | ||
376 | .resource = au1100_mmc1_res, | ||
377 | }; | ||
378 | |||
379 | /******************************************************************************/ | ||
380 | |||
381 | static struct ads7846_platform_data db1100_touch_pd = { | ||
382 | .model = 7846, | ||
383 | .vref_mv = 3300, | ||
384 | .gpio_pendown = 21, | ||
385 | }; | ||
386 | |||
387 | static struct spi_gpio_platform_data db1100_spictl_pd = { | ||
388 | .num_chipselect = 1, | ||
389 | }; | ||
390 | |||
391 | static struct spi_board_info db1100_spi_info[] __initdata = { | ||
392 | [0] = { | ||
393 | .modalias = "ads7846", | ||
394 | .max_speed_hz = 3250000, | ||
395 | .bus_num = 0, | ||
396 | .chip_select = 0, | ||
397 | .mode = 0, | ||
398 | .irq = AU1100_GPIO21_INT, | ||
399 | .platform_data = &db1100_touch_pd, | ||
400 | }, | ||
401 | }; | ||
402 | |||
403 | static struct platform_device db1100_spi_dev = { | ||
404 | .name = "spi_gpio", | ||
405 | .id = 0, | ||
406 | .dev = { | ||
407 | .platform_data = &db1100_spictl_pd, | ||
408 | .dma_mask = &au1xxx_all_dmamask, | ||
409 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
410 | }, | ||
411 | }; | ||
412 | |||
413 | /* | ||
414 | * Alchemy GPIO 2 has its base at 200 so the GPIO lines | ||
415 | * 207 thru 210 are GPIOs at offset 7 thru 10 at this chip. | ||
416 | */ | ||
417 | static struct gpiod_lookup_table db1100_spi_gpiod_table = { | ||
418 | .dev_id = "spi_gpio", | ||
419 | .table = { | ||
420 | GPIO_LOOKUP("alchemy-gpio2", 9, | ||
421 | "sck", GPIO_ACTIVE_HIGH), | ||
422 | GPIO_LOOKUP("alchemy-gpio2", 8, | ||
423 | "mosi", GPIO_ACTIVE_HIGH), | ||
424 | GPIO_LOOKUP("alchemy-gpio2", 7, | ||
425 | "miso", GPIO_ACTIVE_HIGH), | ||
426 | GPIO_LOOKUP("alchemy-gpio2", 10, | ||
427 | "cs", GPIO_ACTIVE_HIGH), | ||
428 | { }, | ||
429 | }, | ||
430 | }; | ||
431 | |||
432 | static struct platform_device *db1x00_devs[] = { | ||
433 | &db1x00_codec_dev, | ||
434 | &alchemy_ac97c_dma_dev, | ||
435 | &alchemy_ac97c_dev, | ||
436 | &db1x00_audio_dev, | ||
437 | }; | ||
438 | |||
439 | static struct platform_device *db1100_devs[] = { | ||
440 | &au1100_lcd_device, | ||
441 | &db1100_mmc0_dev, | ||
442 | &db1100_mmc1_dev, | ||
443 | }; | ||
444 | |||
445 | int __init db1000_dev_setup(void) | ||
446 | { | ||
447 | int board = BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI)); | ||
448 | int c0, c1, d0, d1, s0, s1, flashsize = 32, twosocks = 1; | ||
449 | unsigned long pfc; | ||
450 | struct clk *c, *p; | ||
451 | |||
452 | if (board == BCSR_WHOAMI_DB1500) { | ||
453 | c0 = AU1500_GPIO2_INT; | ||
454 | c1 = AU1500_GPIO5_INT; | ||
455 | d0 = 0; /* GPIO number, NOT irq! */ | ||
456 | d1 = 3; /* GPIO number, NOT irq! */ | ||
457 | s0 = AU1500_GPIO1_INT; | ||
458 | s1 = AU1500_GPIO4_INT; | ||
459 | } else if (board == BCSR_WHOAMI_DB1100) { | ||
460 | c0 = AU1100_GPIO2_INT; | ||
461 | c1 = AU1100_GPIO5_INT; | ||
462 | d0 = 0; /* GPIO number, NOT irq! */ | ||
463 | d1 = 3; /* GPIO number, NOT irq! */ | ||
464 | s0 = AU1100_GPIO1_INT; | ||
465 | s1 = AU1100_GPIO4_INT; | ||
466 | |||
467 | gpio_request(19, "sd0_cd"); | ||
468 | gpio_request(20, "sd1_cd"); | ||
469 | gpio_direction_input(19); /* sd0 cd# */ | ||
470 | gpio_direction_input(20); /* sd1 cd# */ | ||
471 | |||
472 | /* spi_gpio on SSI0 pins */ | ||
473 | pfc = alchemy_rdsys(AU1000_SYS_PINFUNC); | ||
474 | pfc |= (1 << 0); /* SSI0 pins as GPIOs */ | ||
475 | alchemy_wrsys(pfc, AU1000_SYS_PINFUNC); | ||
476 | |||
477 | spi_register_board_info(db1100_spi_info, | ||
478 | ARRAY_SIZE(db1100_spi_info)); | ||
479 | |||
480 | /* link LCD clock to AUXPLL */ | ||
481 | p = clk_get(NULL, "auxpll_clk"); | ||
482 | c = clk_get(NULL, "lcd_intclk"); | ||
483 | if (!IS_ERR(c) && !IS_ERR(p)) { | ||
484 | clk_set_parent(c, p); | ||
485 | clk_set_rate(c, clk_get_rate(p)); | ||
486 | } | ||
487 | if (!IS_ERR(c)) | ||
488 | clk_put(c); | ||
489 | if (!IS_ERR(p)) | ||
490 | clk_put(p); | ||
491 | |||
492 | platform_add_devices(db1100_devs, ARRAY_SIZE(db1100_devs)); | ||
493 | gpiod_add_lookup_table(&db1100_spi_gpiod_table); | ||
494 | platform_device_register(&db1100_spi_dev); | ||
495 | } else if (board == BCSR_WHOAMI_DB1000) { | ||
496 | c0 = AU1000_GPIO2_INT; | ||
497 | c1 = AU1000_GPIO5_INT; | ||
498 | d0 = 0; /* GPIO number, NOT irq! */ | ||
499 | d1 = 3; /* GPIO number, NOT irq! */ | ||
500 | s0 = AU1000_GPIO1_INT; | ||
501 | s1 = AU1000_GPIO4_INT; | ||
502 | } else if ((board == BCSR_WHOAMI_PB1500) || | ||
503 | (board == BCSR_WHOAMI_PB1500R2)) { | ||
504 | c0 = AU1500_GPIO203_INT; | ||
505 | d0 = 1; /* GPIO number, NOT irq! */ | ||
506 | s0 = AU1500_GPIO202_INT; | ||
507 | twosocks = 0; | ||
508 | flashsize = 64; | ||
509 | /* RTC and daughtercard irqs */ | ||
510 | irq_set_irq_type(AU1500_GPIO204_INT, IRQ_TYPE_LEVEL_LOW); | ||
511 | irq_set_irq_type(AU1500_GPIO205_INT, IRQ_TYPE_LEVEL_LOW); | ||
512 | /* EPSON S1D13806 0x1b000000 | ||
513 | * SRAM 1MB/2MB 0x1a000000 | ||
514 | * DS1693 RTC 0x0c000000 | ||
515 | */ | ||
516 | } else if (board == BCSR_WHOAMI_PB1100) { | ||
517 | c0 = AU1100_GPIO11_INT; | ||
518 | d0 = 9; /* GPIO number, NOT irq! */ | ||
519 | s0 = AU1100_GPIO10_INT; | ||
520 | twosocks = 0; | ||
521 | flashsize = 64; | ||
522 | /* pendown, rtc, daughtercard irqs */ | ||
523 | irq_set_irq_type(AU1100_GPIO8_INT, IRQ_TYPE_LEVEL_LOW); | ||
524 | irq_set_irq_type(AU1100_GPIO12_INT, IRQ_TYPE_LEVEL_LOW); | ||
525 | irq_set_irq_type(AU1100_GPIO13_INT, IRQ_TYPE_LEVEL_LOW); | ||
526 | /* EPSON S1D13806 0x1b000000 | ||
527 | * SRAM 1MB/2MB 0x1a000000 | ||
528 | * DiskOnChip 0x0d000000 | ||
529 | * DS1693 RTC 0x0c000000 | ||
530 | */ | ||
531 | platform_add_devices(db1100_devs, ARRAY_SIZE(db1100_devs)); | ||
532 | } else | ||
533 | return 0; /* unknown board, no further dev setup to do */ | ||
534 | |||
535 | irq_set_irq_type(c0, IRQ_TYPE_LEVEL_LOW); | ||
536 | irq_set_irq_type(s0, IRQ_TYPE_LEVEL_LOW); | ||
537 | |||
538 | db1x_register_pcmcia_socket( | ||
539 | AU1000_PCMCIA_ATTR_PHYS_ADDR, | ||
540 | AU1000_PCMCIA_ATTR_PHYS_ADDR + 0x000400000 - 1, | ||
541 | AU1000_PCMCIA_MEM_PHYS_ADDR, | ||
542 | AU1000_PCMCIA_MEM_PHYS_ADDR + 0x000400000 - 1, | ||
543 | AU1000_PCMCIA_IO_PHYS_ADDR, | ||
544 | AU1000_PCMCIA_IO_PHYS_ADDR + 0x000010000 - 1, | ||
545 | c0, d0, /*s0*/0, 0, 0); | ||
546 | |||
547 | if (twosocks) { | ||
548 | irq_set_irq_type(c1, IRQ_TYPE_LEVEL_LOW); | ||
549 | irq_set_irq_type(s1, IRQ_TYPE_LEVEL_LOW); | ||
550 | |||
551 | db1x_register_pcmcia_socket( | ||
552 | AU1000_PCMCIA_ATTR_PHYS_ADDR + 0x004000000, | ||
553 | AU1000_PCMCIA_ATTR_PHYS_ADDR + 0x004400000 - 1, | ||
554 | AU1000_PCMCIA_MEM_PHYS_ADDR + 0x004000000, | ||
555 | AU1000_PCMCIA_MEM_PHYS_ADDR + 0x004400000 - 1, | ||
556 | AU1000_PCMCIA_IO_PHYS_ADDR + 0x004000000, | ||
557 | AU1000_PCMCIA_IO_PHYS_ADDR + 0x004010000 - 1, | ||
558 | c1, d1, /*s1*/0, 0, 1); | ||
559 | } | ||
560 | |||
561 | platform_add_devices(db1x00_devs, ARRAY_SIZE(db1x00_devs)); | ||
562 | db1x_register_norflash(flashsize << 20, 4 /* 32bit */, F_SWAPPED); | ||
563 | return 0; | ||
564 | } | ||
diff --git a/arch/mips/alchemy/devboards/db1200.c b/arch/mips/alchemy/devboards/db1200.c new file mode 100644 index 000000000..421d65143 --- /dev/null +++ b/arch/mips/alchemy/devboards/db1200.c | |||
@@ -0,0 +1,952 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
2 | /* | ||
3 | * DBAu1200/PBAu1200 board platform device registration | ||
4 | * | ||
5 | * Copyright (C) 2008-2011 Manuel Lauss | ||
6 | */ | ||
7 | |||
8 | #include <linux/clk.h> | ||
9 | #include <linux/dma-mapping.h> | ||
10 | #include <linux/gpio.h> | ||
11 | #include <linux/i2c.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/interrupt.h> | ||
15 | #include <linux/io.h> | ||
16 | #include <linux/leds.h> | ||
17 | #include <linux/mmc/host.h> | ||
18 | #include <linux/mtd/mtd.h> | ||
19 | #include <linux/mtd/platnand.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/serial_8250.h> | ||
22 | #include <linux/spi/spi.h> | ||
23 | #include <linux/spi/flash.h> | ||
24 | #include <linux/smc91x.h> | ||
25 | #include <linux/ata_platform.h> | ||
26 | #include <asm/mach-au1x00/au1000.h> | ||
27 | #include <asm/mach-au1x00/au1100_mmc.h> | ||
28 | #include <asm/mach-au1x00/au1xxx_dbdma.h> | ||
29 | #include <asm/mach-au1x00/au1xxx_psc.h> | ||
30 | #include <asm/mach-au1x00/au1200fb.h> | ||
31 | #include <asm/mach-au1x00/au1550_spi.h> | ||
32 | #include <asm/mach-db1x00/bcsr.h> | ||
33 | |||
34 | #include "platform.h" | ||
35 | |||
36 | #define BCSR_INT_IDE 0x0001 | ||
37 | #define BCSR_INT_ETH 0x0002 | ||
38 | #define BCSR_INT_PC0 0x0004 | ||
39 | #define BCSR_INT_PC0STSCHG 0x0008 | ||
40 | #define BCSR_INT_PC1 0x0010 | ||
41 | #define BCSR_INT_PC1STSCHG 0x0020 | ||
42 | #define BCSR_INT_DC 0x0040 | ||
43 | #define BCSR_INT_FLASHBUSY 0x0080 | ||
44 | #define BCSR_INT_PC0INSERT 0x0100 | ||
45 | #define BCSR_INT_PC0EJECT 0x0200 | ||
46 | #define BCSR_INT_PC1INSERT 0x0400 | ||
47 | #define BCSR_INT_PC1EJECT 0x0800 | ||
48 | #define BCSR_INT_SD0INSERT 0x1000 | ||
49 | #define BCSR_INT_SD0EJECT 0x2000 | ||
50 | #define BCSR_INT_SD1INSERT 0x4000 | ||
51 | #define BCSR_INT_SD1EJECT 0x8000 | ||
52 | |||
53 | #define DB1200_IDE_PHYS_ADDR 0x18800000 | ||
54 | #define DB1200_IDE_REG_SHIFT 5 | ||
55 | #define DB1200_IDE_PHYS_LEN (16 << DB1200_IDE_REG_SHIFT) | ||
56 | #define DB1200_ETH_PHYS_ADDR 0x19000300 | ||
57 | #define DB1200_NAND_PHYS_ADDR 0x20000000 | ||
58 | |||
59 | #define PB1200_IDE_PHYS_ADDR 0x0C800000 | ||
60 | #define PB1200_ETH_PHYS_ADDR 0x0D000300 | ||
61 | #define PB1200_NAND_PHYS_ADDR 0x1C000000 | ||
62 | |||
63 | #define DB1200_INT_BEGIN (AU1000_MAX_INTR + 1) | ||
64 | #define DB1200_IDE_INT (DB1200_INT_BEGIN + 0) | ||
65 | #define DB1200_ETH_INT (DB1200_INT_BEGIN + 1) | ||
66 | #define DB1200_PC0_INT (DB1200_INT_BEGIN + 2) | ||
67 | #define DB1200_PC0_STSCHG_INT (DB1200_INT_BEGIN + 3) | ||
68 | #define DB1200_PC1_INT (DB1200_INT_BEGIN + 4) | ||
69 | #define DB1200_PC1_STSCHG_INT (DB1200_INT_BEGIN + 5) | ||
70 | #define DB1200_DC_INT (DB1200_INT_BEGIN + 6) | ||
71 | #define DB1200_FLASHBUSY_INT (DB1200_INT_BEGIN + 7) | ||
72 | #define DB1200_PC0_INSERT_INT (DB1200_INT_BEGIN + 8) | ||
73 | #define DB1200_PC0_EJECT_INT (DB1200_INT_BEGIN + 9) | ||
74 | #define DB1200_PC1_INSERT_INT (DB1200_INT_BEGIN + 10) | ||
75 | #define DB1200_PC1_EJECT_INT (DB1200_INT_BEGIN + 11) | ||
76 | #define DB1200_SD0_INSERT_INT (DB1200_INT_BEGIN + 12) | ||
77 | #define DB1200_SD0_EJECT_INT (DB1200_INT_BEGIN + 13) | ||
78 | #define PB1200_SD1_INSERT_INT (DB1200_INT_BEGIN + 14) | ||
79 | #define PB1200_SD1_EJECT_INT (DB1200_INT_BEGIN + 15) | ||
80 | #define DB1200_INT_END (DB1200_INT_BEGIN + 15) | ||
81 | |||
82 | const char *get_system_type(void); | ||
83 | |||
84 | static int __init db1200_detect_board(void) | ||
85 | { | ||
86 | int bid; | ||
87 | |||
88 | /* try the DB1200 first */ | ||
89 | bcsr_init(DB1200_BCSR_PHYS_ADDR, | ||
90 | DB1200_BCSR_PHYS_ADDR + DB1200_BCSR_HEXLED_OFS); | ||
91 | if (BCSR_WHOAMI_DB1200 == BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI))) { | ||
92 | unsigned short t = bcsr_read(BCSR_HEXLEDS); | ||
93 | bcsr_write(BCSR_HEXLEDS, ~t); | ||
94 | if (bcsr_read(BCSR_HEXLEDS) != t) { | ||
95 | bcsr_write(BCSR_HEXLEDS, t); | ||
96 | return 0; | ||
97 | } | ||
98 | } | ||
99 | |||
100 | /* okay, try the PB1200 then */ | ||
101 | bcsr_init(PB1200_BCSR_PHYS_ADDR, | ||
102 | PB1200_BCSR_PHYS_ADDR + PB1200_BCSR_HEXLED_OFS); | ||
103 | bid = BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI)); | ||
104 | if ((bid == BCSR_WHOAMI_PB1200_DDR1) || | ||
105 | (bid == BCSR_WHOAMI_PB1200_DDR2)) { | ||
106 | unsigned short t = bcsr_read(BCSR_HEXLEDS); | ||
107 | bcsr_write(BCSR_HEXLEDS, ~t); | ||
108 | if (bcsr_read(BCSR_HEXLEDS) != t) { | ||
109 | bcsr_write(BCSR_HEXLEDS, t); | ||
110 | return 0; | ||
111 | } | ||
112 | } | ||
113 | |||
114 | return 1; /* it's neither */ | ||
115 | } | ||
116 | |||
117 | int __init db1200_board_setup(void) | ||
118 | { | ||
119 | unsigned short whoami; | ||
120 | |||
121 | if (db1200_detect_board()) | ||
122 | return -ENODEV; | ||
123 | |||
124 | whoami = bcsr_read(BCSR_WHOAMI); | ||
125 | switch (BCSR_WHOAMI_BOARD(whoami)) { | ||
126 | case BCSR_WHOAMI_PB1200_DDR1: | ||
127 | case BCSR_WHOAMI_PB1200_DDR2: | ||
128 | case BCSR_WHOAMI_DB1200: | ||
129 | break; | ||
130 | default: | ||
131 | return -ENODEV; | ||
132 | } | ||
133 | |||
134 | printk(KERN_INFO "Alchemy/AMD/RMI %s Board, CPLD Rev %d" | ||
135 | " Board-ID %d Daughtercard ID %d\n", get_system_type(), | ||
136 | (whoami >> 4) & 0xf, (whoami >> 8) & 0xf, whoami & 0xf); | ||
137 | |||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | /******************************************************************************/ | ||
142 | |||
143 | static u64 au1200_all_dmamask = DMA_BIT_MASK(32); | ||
144 | |||
145 | static struct mtd_partition db1200_spiflash_parts[] = { | ||
146 | { | ||
147 | .name = "spi_flash", | ||
148 | .offset = 0, | ||
149 | .size = MTDPART_SIZ_FULL, | ||
150 | }, | ||
151 | }; | ||
152 | |||
153 | static struct flash_platform_data db1200_spiflash_data = { | ||
154 | .name = "s25fl001", | ||
155 | .parts = db1200_spiflash_parts, | ||
156 | .nr_parts = ARRAY_SIZE(db1200_spiflash_parts), | ||
157 | .type = "m25p10", | ||
158 | }; | ||
159 | |||
160 | static struct spi_board_info db1200_spi_devs[] __initdata = { | ||
161 | { | ||
162 | /* TI TMP121AIDBVR temp sensor */ | ||
163 | .modalias = "tmp121", | ||
164 | .max_speed_hz = 2000000, | ||
165 | .bus_num = 0, | ||
166 | .chip_select = 0, | ||
167 | .mode = 0, | ||
168 | }, | ||
169 | { | ||
170 | /* Spansion S25FL001D0FMA SPI flash */ | ||
171 | .modalias = "m25p80", | ||
172 | .max_speed_hz = 50000000, | ||
173 | .bus_num = 0, | ||
174 | .chip_select = 1, | ||
175 | .mode = 0, | ||
176 | .platform_data = &db1200_spiflash_data, | ||
177 | }, | ||
178 | }; | ||
179 | |||
180 | static struct i2c_board_info db1200_i2c_devs[] __initdata = { | ||
181 | { I2C_BOARD_INFO("24c04", 0x52), }, /* AT24C04-10 I2C eeprom */ | ||
182 | { I2C_BOARD_INFO("ne1619", 0x2d), }, /* adm1025-compat hwmon */ | ||
183 | { I2C_BOARD_INFO("wm8731", 0x1b), }, /* I2S audio codec WM8731 */ | ||
184 | }; | ||
185 | |||
186 | /**********************************************************************/ | ||
187 | |||
188 | static void au1200_nand_cmd_ctrl(struct nand_chip *this, int cmd, | ||
189 | unsigned int ctrl) | ||
190 | { | ||
191 | unsigned long ioaddr = (unsigned long)this->legacy.IO_ADDR_W; | ||
192 | |||
193 | ioaddr &= 0xffffff00; | ||
194 | |||
195 | if (ctrl & NAND_CLE) { | ||
196 | ioaddr += MEM_STNAND_CMD; | ||
197 | } else if (ctrl & NAND_ALE) { | ||
198 | ioaddr += MEM_STNAND_ADDR; | ||
199 | } else { | ||
200 | /* assume we want to r/w real data by default */ | ||
201 | ioaddr += MEM_STNAND_DATA; | ||
202 | } | ||
203 | this->legacy.IO_ADDR_R = this->legacy.IO_ADDR_W = (void __iomem *)ioaddr; | ||
204 | if (cmd != NAND_CMD_NONE) { | ||
205 | __raw_writeb(cmd, this->legacy.IO_ADDR_W); | ||
206 | wmb(); | ||
207 | } | ||
208 | } | ||
209 | |||
210 | static int au1200_nand_device_ready(struct nand_chip *this) | ||
211 | { | ||
212 | return alchemy_rdsmem(AU1000_MEM_STSTAT) & 1; | ||
213 | } | ||
214 | |||
215 | static struct mtd_partition db1200_nand_parts[] = { | ||
216 | { | ||
217 | .name = "NAND FS 0", | ||
218 | .offset = 0, | ||
219 | .size = 8 * 1024 * 1024, | ||
220 | }, | ||
221 | { | ||
222 | .name = "NAND FS 1", | ||
223 | .offset = MTDPART_OFS_APPEND, | ||
224 | .size = MTDPART_SIZ_FULL | ||
225 | }, | ||
226 | }; | ||
227 | |||
228 | struct platform_nand_data db1200_nand_platdata = { | ||
229 | .chip = { | ||
230 | .nr_chips = 1, | ||
231 | .chip_offset = 0, | ||
232 | .nr_partitions = ARRAY_SIZE(db1200_nand_parts), | ||
233 | .partitions = db1200_nand_parts, | ||
234 | .chip_delay = 20, | ||
235 | }, | ||
236 | .ctrl = { | ||
237 | .dev_ready = au1200_nand_device_ready, | ||
238 | .cmd_ctrl = au1200_nand_cmd_ctrl, | ||
239 | }, | ||
240 | }; | ||
241 | |||
242 | static struct resource db1200_nand_res[] = { | ||
243 | [0] = { | ||
244 | .start = DB1200_NAND_PHYS_ADDR, | ||
245 | .end = DB1200_NAND_PHYS_ADDR + 0xff, | ||
246 | .flags = IORESOURCE_MEM, | ||
247 | }, | ||
248 | }; | ||
249 | |||
250 | static struct platform_device db1200_nand_dev = { | ||
251 | .name = "gen_nand", | ||
252 | .num_resources = ARRAY_SIZE(db1200_nand_res), | ||
253 | .resource = db1200_nand_res, | ||
254 | .id = -1, | ||
255 | .dev = { | ||
256 | .platform_data = &db1200_nand_platdata, | ||
257 | } | ||
258 | }; | ||
259 | |||
260 | /**********************************************************************/ | ||
261 | |||
262 | static struct smc91x_platdata db1200_eth_data = { | ||
263 | .flags = SMC91X_NOWAIT | SMC91X_USE_16BIT, | ||
264 | .leda = RPC_LED_100_10, | ||
265 | .ledb = RPC_LED_TX_RX, | ||
266 | }; | ||
267 | |||
268 | static struct resource db1200_eth_res[] = { | ||
269 | [0] = { | ||
270 | .start = DB1200_ETH_PHYS_ADDR, | ||
271 | .end = DB1200_ETH_PHYS_ADDR + 0xf, | ||
272 | .flags = IORESOURCE_MEM, | ||
273 | }, | ||
274 | [1] = { | ||
275 | .start = DB1200_ETH_INT, | ||
276 | .end = DB1200_ETH_INT, | ||
277 | .flags = IORESOURCE_IRQ, | ||
278 | }, | ||
279 | }; | ||
280 | |||
281 | static struct platform_device db1200_eth_dev = { | ||
282 | .dev = { | ||
283 | .platform_data = &db1200_eth_data, | ||
284 | }, | ||
285 | .name = "smc91x", | ||
286 | .id = -1, | ||
287 | .num_resources = ARRAY_SIZE(db1200_eth_res), | ||
288 | .resource = db1200_eth_res, | ||
289 | }; | ||
290 | |||
291 | /**********************************************************************/ | ||
292 | |||
293 | static struct pata_platform_info db1200_ide_info = { | ||
294 | .ioport_shift = DB1200_IDE_REG_SHIFT, | ||
295 | }; | ||
296 | |||
297 | #define IDE_ALT_START (14 << DB1200_IDE_REG_SHIFT) | ||
298 | static struct resource db1200_ide_res[] = { | ||
299 | [0] = { | ||
300 | .start = DB1200_IDE_PHYS_ADDR, | ||
301 | .end = DB1200_IDE_PHYS_ADDR + IDE_ALT_START - 1, | ||
302 | .flags = IORESOURCE_MEM, | ||
303 | }, | ||
304 | [1] = { | ||
305 | .start = DB1200_IDE_PHYS_ADDR + IDE_ALT_START, | ||
306 | .end = DB1200_IDE_PHYS_ADDR + DB1200_IDE_PHYS_LEN - 1, | ||
307 | .flags = IORESOURCE_MEM, | ||
308 | }, | ||
309 | [2] = { | ||
310 | .start = DB1200_IDE_INT, | ||
311 | .end = DB1200_IDE_INT, | ||
312 | .flags = IORESOURCE_IRQ, | ||
313 | }, | ||
314 | }; | ||
315 | |||
316 | static struct platform_device db1200_ide_dev = { | ||
317 | .name = "pata_platform", | ||
318 | .id = 0, | ||
319 | .dev = { | ||
320 | .dma_mask = &au1200_all_dmamask, | ||
321 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
322 | .platform_data = &db1200_ide_info, | ||
323 | }, | ||
324 | .num_resources = ARRAY_SIZE(db1200_ide_res), | ||
325 | .resource = db1200_ide_res, | ||
326 | }; | ||
327 | |||
328 | /**********************************************************************/ | ||
329 | |||
330 | /* SD carddetects: they're supposed to be edge-triggered, but ack | ||
331 | * doesn't seem to work (CPLD Rev 2). Instead, the screaming one | ||
332 | * is disabled and its counterpart enabled. The 200ms timeout is | ||
333 | * because the carddetect usually triggers twice, after debounce. | ||
334 | */ | ||
335 | static irqreturn_t db1200_mmc_cd(int irq, void *ptr) | ||
336 | { | ||
337 | disable_irq_nosync(irq); | ||
338 | return IRQ_WAKE_THREAD; | ||
339 | } | ||
340 | |||
341 | static irqreturn_t db1200_mmc_cdfn(int irq, void *ptr) | ||
342 | { | ||
343 | void (*mmc_cd)(struct mmc_host *, unsigned long); | ||
344 | |||
345 | /* link against CONFIG_MMC=m */ | ||
346 | mmc_cd = symbol_get(mmc_detect_change); | ||
347 | if (mmc_cd) { | ||
348 | mmc_cd(ptr, msecs_to_jiffies(200)); | ||
349 | symbol_put(mmc_detect_change); | ||
350 | } | ||
351 | |||
352 | msleep(100); /* debounce */ | ||
353 | if (irq == DB1200_SD0_INSERT_INT) | ||
354 | enable_irq(DB1200_SD0_EJECT_INT); | ||
355 | else | ||
356 | enable_irq(DB1200_SD0_INSERT_INT); | ||
357 | |||
358 | return IRQ_HANDLED; | ||
359 | } | ||
360 | |||
361 | static int db1200_mmc_cd_setup(void *mmc_host, int en) | ||
362 | { | ||
363 | int ret; | ||
364 | |||
365 | if (en) { | ||
366 | ret = request_threaded_irq(DB1200_SD0_INSERT_INT, db1200_mmc_cd, | ||
367 | db1200_mmc_cdfn, 0, "sd_insert", mmc_host); | ||
368 | if (ret) | ||
369 | goto out; | ||
370 | |||
371 | ret = request_threaded_irq(DB1200_SD0_EJECT_INT, db1200_mmc_cd, | ||
372 | db1200_mmc_cdfn, 0, "sd_eject", mmc_host); | ||
373 | if (ret) { | ||
374 | free_irq(DB1200_SD0_INSERT_INT, mmc_host); | ||
375 | goto out; | ||
376 | } | ||
377 | |||
378 | if (bcsr_read(BCSR_SIGSTAT) & BCSR_INT_SD0INSERT) | ||
379 | enable_irq(DB1200_SD0_EJECT_INT); | ||
380 | else | ||
381 | enable_irq(DB1200_SD0_INSERT_INT); | ||
382 | |||
383 | } else { | ||
384 | free_irq(DB1200_SD0_INSERT_INT, mmc_host); | ||
385 | free_irq(DB1200_SD0_EJECT_INT, mmc_host); | ||
386 | } | ||
387 | ret = 0; | ||
388 | out: | ||
389 | return ret; | ||
390 | } | ||
391 | |||
392 | static void db1200_mmc_set_power(void *mmc_host, int state) | ||
393 | { | ||
394 | if (state) { | ||
395 | bcsr_mod(BCSR_BOARD, 0, BCSR_BOARD_SD0PWR); | ||
396 | msleep(400); /* stabilization time */ | ||
397 | } else | ||
398 | bcsr_mod(BCSR_BOARD, BCSR_BOARD_SD0PWR, 0); | ||
399 | } | ||
400 | |||
401 | static int db1200_mmc_card_readonly(void *mmc_host) | ||
402 | { | ||
403 | return (bcsr_read(BCSR_STATUS) & BCSR_STATUS_SD0WP) ? 1 : 0; | ||
404 | } | ||
405 | |||
406 | static int db1200_mmc_card_inserted(void *mmc_host) | ||
407 | { | ||
408 | return (bcsr_read(BCSR_SIGSTAT) & BCSR_INT_SD0INSERT) ? 1 : 0; | ||
409 | } | ||
410 | |||
411 | static void db1200_mmcled_set(struct led_classdev *led, | ||
412 | enum led_brightness brightness) | ||
413 | { | ||
414 | if (brightness != LED_OFF) | ||
415 | bcsr_mod(BCSR_LEDS, BCSR_LEDS_LED0, 0); | ||
416 | else | ||
417 | bcsr_mod(BCSR_LEDS, 0, BCSR_LEDS_LED0); | ||
418 | } | ||
419 | |||
420 | static struct led_classdev db1200_mmc_led = { | ||
421 | .brightness_set = db1200_mmcled_set, | ||
422 | }; | ||
423 | |||
424 | /* -- */ | ||
425 | |||
426 | static irqreturn_t pb1200_mmc1_cd(int irq, void *ptr) | ||
427 | { | ||
428 | disable_irq_nosync(irq); | ||
429 | return IRQ_WAKE_THREAD; | ||
430 | } | ||
431 | |||
432 | static irqreturn_t pb1200_mmc1_cdfn(int irq, void *ptr) | ||
433 | { | ||
434 | void (*mmc_cd)(struct mmc_host *, unsigned long); | ||
435 | |||
436 | /* link against CONFIG_MMC=m */ | ||
437 | mmc_cd = symbol_get(mmc_detect_change); | ||
438 | if (mmc_cd) { | ||
439 | mmc_cd(ptr, msecs_to_jiffies(200)); | ||
440 | symbol_put(mmc_detect_change); | ||
441 | } | ||
442 | |||
443 | msleep(100); /* debounce */ | ||
444 | if (irq == PB1200_SD1_INSERT_INT) | ||
445 | enable_irq(PB1200_SD1_EJECT_INT); | ||
446 | else | ||
447 | enable_irq(PB1200_SD1_INSERT_INT); | ||
448 | |||
449 | return IRQ_HANDLED; | ||
450 | } | ||
451 | |||
452 | static int pb1200_mmc1_cd_setup(void *mmc_host, int en) | ||
453 | { | ||
454 | int ret; | ||
455 | |||
456 | if (en) { | ||
457 | ret = request_threaded_irq(PB1200_SD1_INSERT_INT, pb1200_mmc1_cd, | ||
458 | pb1200_mmc1_cdfn, 0, "sd1_insert", mmc_host); | ||
459 | if (ret) | ||
460 | goto out; | ||
461 | |||
462 | ret = request_threaded_irq(PB1200_SD1_EJECT_INT, pb1200_mmc1_cd, | ||
463 | pb1200_mmc1_cdfn, 0, "sd1_eject", mmc_host); | ||
464 | if (ret) { | ||
465 | free_irq(PB1200_SD1_INSERT_INT, mmc_host); | ||
466 | goto out; | ||
467 | } | ||
468 | |||
469 | if (bcsr_read(BCSR_SIGSTAT) & BCSR_INT_SD1INSERT) | ||
470 | enable_irq(PB1200_SD1_EJECT_INT); | ||
471 | else | ||
472 | enable_irq(PB1200_SD1_INSERT_INT); | ||
473 | |||
474 | } else { | ||
475 | free_irq(PB1200_SD1_INSERT_INT, mmc_host); | ||
476 | free_irq(PB1200_SD1_EJECT_INT, mmc_host); | ||
477 | } | ||
478 | ret = 0; | ||
479 | out: | ||
480 | return ret; | ||
481 | } | ||
482 | |||
483 | static void pb1200_mmc1led_set(struct led_classdev *led, | ||
484 | enum led_brightness brightness) | ||
485 | { | ||
486 | if (brightness != LED_OFF) | ||
487 | bcsr_mod(BCSR_LEDS, BCSR_LEDS_LED1, 0); | ||
488 | else | ||
489 | bcsr_mod(BCSR_LEDS, 0, BCSR_LEDS_LED1); | ||
490 | } | ||
491 | |||
492 | static struct led_classdev pb1200_mmc1_led = { | ||
493 | .brightness_set = pb1200_mmc1led_set, | ||
494 | }; | ||
495 | |||
496 | static void pb1200_mmc1_set_power(void *mmc_host, int state) | ||
497 | { | ||
498 | if (state) { | ||
499 | bcsr_mod(BCSR_BOARD, 0, BCSR_BOARD_SD1PWR); | ||
500 | msleep(400); /* stabilization time */ | ||
501 | } else | ||
502 | bcsr_mod(BCSR_BOARD, BCSR_BOARD_SD1PWR, 0); | ||
503 | } | ||
504 | |||
505 | static int pb1200_mmc1_card_readonly(void *mmc_host) | ||
506 | { | ||
507 | return (bcsr_read(BCSR_STATUS) & BCSR_STATUS_SD1WP) ? 1 : 0; | ||
508 | } | ||
509 | |||
510 | static int pb1200_mmc1_card_inserted(void *mmc_host) | ||
511 | { | ||
512 | return (bcsr_read(BCSR_SIGSTAT) & BCSR_INT_SD1INSERT) ? 1 : 0; | ||
513 | } | ||
514 | |||
515 | |||
516 | static struct au1xmmc_platform_data db1200_mmc_platdata[2] = { | ||
517 | [0] = { | ||
518 | .cd_setup = db1200_mmc_cd_setup, | ||
519 | .set_power = db1200_mmc_set_power, | ||
520 | .card_inserted = db1200_mmc_card_inserted, | ||
521 | .card_readonly = db1200_mmc_card_readonly, | ||
522 | .led = &db1200_mmc_led, | ||
523 | }, | ||
524 | [1] = { | ||
525 | .cd_setup = pb1200_mmc1_cd_setup, | ||
526 | .set_power = pb1200_mmc1_set_power, | ||
527 | .card_inserted = pb1200_mmc1_card_inserted, | ||
528 | .card_readonly = pb1200_mmc1_card_readonly, | ||
529 | .led = &pb1200_mmc1_led, | ||
530 | }, | ||
531 | }; | ||
532 | |||
533 | static struct resource au1200_mmc0_resources[] = { | ||
534 | [0] = { | ||
535 | .start = AU1100_SD0_PHYS_ADDR, | ||
536 | .end = AU1100_SD0_PHYS_ADDR + 0xfff, | ||
537 | .flags = IORESOURCE_MEM, | ||
538 | }, | ||
539 | [1] = { | ||
540 | .start = AU1200_SD_INT, | ||
541 | .end = AU1200_SD_INT, | ||
542 | .flags = IORESOURCE_IRQ, | ||
543 | }, | ||
544 | [2] = { | ||
545 | .start = AU1200_DSCR_CMD0_SDMS_TX0, | ||
546 | .end = AU1200_DSCR_CMD0_SDMS_TX0, | ||
547 | .flags = IORESOURCE_DMA, | ||
548 | }, | ||
549 | [3] = { | ||
550 | .start = AU1200_DSCR_CMD0_SDMS_RX0, | ||
551 | .end = AU1200_DSCR_CMD0_SDMS_RX0, | ||
552 | .flags = IORESOURCE_DMA, | ||
553 | } | ||
554 | }; | ||
555 | |||
556 | static struct platform_device db1200_mmc0_dev = { | ||
557 | .name = "au1xxx-mmc", | ||
558 | .id = 0, | ||
559 | .dev = { | ||
560 | .dma_mask = &au1200_all_dmamask, | ||
561 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
562 | .platform_data = &db1200_mmc_platdata[0], | ||
563 | }, | ||
564 | .num_resources = ARRAY_SIZE(au1200_mmc0_resources), | ||
565 | .resource = au1200_mmc0_resources, | ||
566 | }; | ||
567 | |||
568 | static struct resource au1200_mmc1_res[] = { | ||
569 | [0] = { | ||
570 | .start = AU1100_SD1_PHYS_ADDR, | ||
571 | .end = AU1100_SD1_PHYS_ADDR + 0xfff, | ||
572 | .flags = IORESOURCE_MEM, | ||
573 | }, | ||
574 | [1] = { | ||
575 | .start = AU1200_SD_INT, | ||
576 | .end = AU1200_SD_INT, | ||
577 | .flags = IORESOURCE_IRQ, | ||
578 | }, | ||
579 | [2] = { | ||
580 | .start = AU1200_DSCR_CMD0_SDMS_TX1, | ||
581 | .end = AU1200_DSCR_CMD0_SDMS_TX1, | ||
582 | .flags = IORESOURCE_DMA, | ||
583 | }, | ||
584 | [3] = { | ||
585 | .start = AU1200_DSCR_CMD0_SDMS_RX1, | ||
586 | .end = AU1200_DSCR_CMD0_SDMS_RX1, | ||
587 | .flags = IORESOURCE_DMA, | ||
588 | } | ||
589 | }; | ||
590 | |||
591 | static struct platform_device pb1200_mmc1_dev = { | ||
592 | .name = "au1xxx-mmc", | ||
593 | .id = 1, | ||
594 | .dev = { | ||
595 | .dma_mask = &au1200_all_dmamask, | ||
596 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
597 | .platform_data = &db1200_mmc_platdata[1], | ||
598 | }, | ||
599 | .num_resources = ARRAY_SIZE(au1200_mmc1_res), | ||
600 | .resource = au1200_mmc1_res, | ||
601 | }; | ||
602 | |||
603 | /**********************************************************************/ | ||
604 | |||
605 | static int db1200fb_panel_index(void) | ||
606 | { | ||
607 | return (bcsr_read(BCSR_SWITCHES) >> 8) & 0x0f; | ||
608 | } | ||
609 | |||
610 | static int db1200fb_panel_init(void) | ||
611 | { | ||
612 | /* Apply power */ | ||
613 | bcsr_mod(BCSR_BOARD, 0, BCSR_BOARD_LCDVEE | BCSR_BOARD_LCDVDD | | ||
614 | BCSR_BOARD_LCDBL); | ||
615 | return 0; | ||
616 | } | ||
617 | |||
618 | static int db1200fb_panel_shutdown(void) | ||
619 | { | ||
620 | /* Remove power */ | ||
621 | bcsr_mod(BCSR_BOARD, BCSR_BOARD_LCDVEE | BCSR_BOARD_LCDVDD | | ||
622 | BCSR_BOARD_LCDBL, 0); | ||
623 | return 0; | ||
624 | } | ||
625 | |||
626 | static struct au1200fb_platdata db1200fb_pd = { | ||
627 | .panel_index = db1200fb_panel_index, | ||
628 | .panel_init = db1200fb_panel_init, | ||
629 | .panel_shutdown = db1200fb_panel_shutdown, | ||
630 | }; | ||
631 | |||
632 | static struct resource au1200_lcd_res[] = { | ||
633 | [0] = { | ||
634 | .start = AU1200_LCD_PHYS_ADDR, | ||
635 | .end = AU1200_LCD_PHYS_ADDR + 0x800 - 1, | ||
636 | .flags = IORESOURCE_MEM, | ||
637 | }, | ||
638 | [1] = { | ||
639 | .start = AU1200_LCD_INT, | ||
640 | .end = AU1200_LCD_INT, | ||
641 | .flags = IORESOURCE_IRQ, | ||
642 | } | ||
643 | }; | ||
644 | |||
645 | static struct platform_device au1200_lcd_dev = { | ||
646 | .name = "au1200-lcd", | ||
647 | .id = 0, | ||
648 | .dev = { | ||
649 | .dma_mask = &au1200_all_dmamask, | ||
650 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
651 | .platform_data = &db1200fb_pd, | ||
652 | }, | ||
653 | .num_resources = ARRAY_SIZE(au1200_lcd_res), | ||
654 | .resource = au1200_lcd_res, | ||
655 | }; | ||
656 | |||
657 | /**********************************************************************/ | ||
658 | |||
659 | static struct resource au1200_psc0_res[] = { | ||
660 | [0] = { | ||
661 | .start = AU1550_PSC0_PHYS_ADDR, | ||
662 | .end = AU1550_PSC0_PHYS_ADDR + 0xfff, | ||
663 | .flags = IORESOURCE_MEM, | ||
664 | }, | ||
665 | [1] = { | ||
666 | .start = AU1200_PSC0_INT, | ||
667 | .end = AU1200_PSC0_INT, | ||
668 | .flags = IORESOURCE_IRQ, | ||
669 | }, | ||
670 | [2] = { | ||
671 | .start = AU1200_DSCR_CMD0_PSC0_TX, | ||
672 | .end = AU1200_DSCR_CMD0_PSC0_TX, | ||
673 | .flags = IORESOURCE_DMA, | ||
674 | }, | ||
675 | [3] = { | ||
676 | .start = AU1200_DSCR_CMD0_PSC0_RX, | ||
677 | .end = AU1200_DSCR_CMD0_PSC0_RX, | ||
678 | .flags = IORESOURCE_DMA, | ||
679 | }, | ||
680 | }; | ||
681 | |||
682 | static struct platform_device db1200_i2c_dev = { | ||
683 | .name = "au1xpsc_smbus", | ||
684 | .id = 0, /* bus number */ | ||
685 | .num_resources = ARRAY_SIZE(au1200_psc0_res), | ||
686 | .resource = au1200_psc0_res, | ||
687 | }; | ||
688 | |||
689 | static void db1200_spi_cs_en(struct au1550_spi_info *spi, int cs, int pol) | ||
690 | { | ||
691 | if (cs) | ||
692 | bcsr_mod(BCSR_RESETS, 0, BCSR_RESETS_SPISEL); | ||
693 | else | ||
694 | bcsr_mod(BCSR_RESETS, BCSR_RESETS_SPISEL, 0); | ||
695 | } | ||
696 | |||
697 | static struct au1550_spi_info db1200_spi_platdata = { | ||
698 | .mainclk_hz = 50000000, /* PSC0 clock */ | ||
699 | .num_chipselect = 2, | ||
700 | .activate_cs = db1200_spi_cs_en, | ||
701 | }; | ||
702 | |||
703 | static struct platform_device db1200_spi_dev = { | ||
704 | .dev = { | ||
705 | .dma_mask = &au1200_all_dmamask, | ||
706 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
707 | .platform_data = &db1200_spi_platdata, | ||
708 | }, | ||
709 | .name = "au1550-spi", | ||
710 | .id = 0, /* bus number */ | ||
711 | .num_resources = ARRAY_SIZE(au1200_psc0_res), | ||
712 | .resource = au1200_psc0_res, | ||
713 | }; | ||
714 | |||
715 | static struct resource au1200_psc1_res[] = { | ||
716 | [0] = { | ||
717 | .start = AU1550_PSC1_PHYS_ADDR, | ||
718 | .end = AU1550_PSC1_PHYS_ADDR + 0xfff, | ||
719 | .flags = IORESOURCE_MEM, | ||
720 | }, | ||
721 | [1] = { | ||
722 | .start = AU1200_PSC1_INT, | ||
723 | .end = AU1200_PSC1_INT, | ||
724 | .flags = IORESOURCE_IRQ, | ||
725 | }, | ||
726 | [2] = { | ||
727 | .start = AU1200_DSCR_CMD0_PSC1_TX, | ||
728 | .end = AU1200_DSCR_CMD0_PSC1_TX, | ||
729 | .flags = IORESOURCE_DMA, | ||
730 | }, | ||
731 | [3] = { | ||
732 | .start = AU1200_DSCR_CMD0_PSC1_RX, | ||
733 | .end = AU1200_DSCR_CMD0_PSC1_RX, | ||
734 | .flags = IORESOURCE_DMA, | ||
735 | }, | ||
736 | }; | ||
737 | |||
738 | /* AC97 or I2S device */ | ||
739 | static struct platform_device db1200_audio_dev = { | ||
740 | /* name assigned later based on switch setting */ | ||
741 | .id = 1, /* PSC ID */ | ||
742 | .num_resources = ARRAY_SIZE(au1200_psc1_res), | ||
743 | .resource = au1200_psc1_res, | ||
744 | }; | ||
745 | |||
746 | /* DB1200 ASoC card device */ | ||
747 | static struct platform_device db1200_sound_dev = { | ||
748 | /* name assigned later based on switch setting */ | ||
749 | .id = 1, /* PSC ID */ | ||
750 | .dev = { | ||
751 | .dma_mask = &au1200_all_dmamask, | ||
752 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
753 | }, | ||
754 | }; | ||
755 | |||
756 | static struct platform_device db1200_stac_dev = { | ||
757 | .name = "ac97-codec", | ||
758 | .id = 1, /* on PSC1 */ | ||
759 | }; | ||
760 | |||
761 | static struct platform_device db1200_audiodma_dev = { | ||
762 | .name = "au1xpsc-pcm", | ||
763 | .id = 1, /* PSC ID */ | ||
764 | }; | ||
765 | |||
766 | static struct platform_device *db1200_devs[] __initdata = { | ||
767 | NULL, /* PSC0, selected by S6.8 */ | ||
768 | &db1200_ide_dev, | ||
769 | &db1200_mmc0_dev, | ||
770 | &au1200_lcd_dev, | ||
771 | &db1200_eth_dev, | ||
772 | &db1200_nand_dev, | ||
773 | &db1200_audiodma_dev, | ||
774 | &db1200_audio_dev, | ||
775 | &db1200_stac_dev, | ||
776 | &db1200_sound_dev, | ||
777 | }; | ||
778 | |||
779 | static struct platform_device *pb1200_devs[] __initdata = { | ||
780 | &pb1200_mmc1_dev, | ||
781 | }; | ||
782 | |||
783 | /* Some peripheral base addresses differ on the PB1200 */ | ||
784 | static int __init pb1200_res_fixup(void) | ||
785 | { | ||
786 | /* CPLD Revs earlier than 4 cause problems */ | ||
787 | if (BCSR_WHOAMI_CPLD(bcsr_read(BCSR_WHOAMI)) <= 3) { | ||
788 | printk(KERN_ERR "WARNING!!!\n"); | ||
789 | printk(KERN_ERR "WARNING!!!\n"); | ||
790 | printk(KERN_ERR "PB1200 must be at CPLD rev 4. Please have\n"); | ||
791 | printk(KERN_ERR "the board updated to latest revisions.\n"); | ||
792 | printk(KERN_ERR "This software will not work reliably\n"); | ||
793 | printk(KERN_ERR "on anything older than CPLD rev 4.!\n"); | ||
794 | printk(KERN_ERR "WARNING!!!\n"); | ||
795 | printk(KERN_ERR "WARNING!!!\n"); | ||
796 | return 1; | ||
797 | } | ||
798 | |||
799 | db1200_nand_res[0].start = PB1200_NAND_PHYS_ADDR; | ||
800 | db1200_nand_res[0].end = PB1200_NAND_PHYS_ADDR + 0xff; | ||
801 | db1200_ide_res[0].start = PB1200_IDE_PHYS_ADDR; | ||
802 | db1200_ide_res[0].end = PB1200_IDE_PHYS_ADDR + DB1200_IDE_PHYS_LEN - 1; | ||
803 | db1200_eth_res[0].start = PB1200_ETH_PHYS_ADDR; | ||
804 | db1200_eth_res[0].end = PB1200_ETH_PHYS_ADDR + 0xff; | ||
805 | return 0; | ||
806 | } | ||
807 | |||
808 | int __init db1200_dev_setup(void) | ||
809 | { | ||
810 | unsigned long pfc; | ||
811 | unsigned short sw; | ||
812 | int swapped, bid; | ||
813 | struct clk *c; | ||
814 | |||
815 | bid = BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI)); | ||
816 | if ((bid == BCSR_WHOAMI_PB1200_DDR1) || | ||
817 | (bid == BCSR_WHOAMI_PB1200_DDR2)) { | ||
818 | if (pb1200_res_fixup()) | ||
819 | return -ENODEV; | ||
820 | } | ||
821 | |||
822 | /* GPIO7 is low-level triggered CPLD cascade */ | ||
823 | irq_set_irq_type(AU1200_GPIO7_INT, IRQ_TYPE_LEVEL_LOW); | ||
824 | bcsr_init_irq(DB1200_INT_BEGIN, DB1200_INT_END, AU1200_GPIO7_INT); | ||
825 | |||
826 | /* SMBus/SPI on PSC0, Audio on PSC1 */ | ||
827 | pfc = alchemy_rdsys(AU1000_SYS_PINFUNC); | ||
828 | pfc &= ~(SYS_PINFUNC_P0A | SYS_PINFUNC_P0B); | ||
829 | pfc &= ~(SYS_PINFUNC_P1A | SYS_PINFUNC_P1B | SYS_PINFUNC_FS3); | ||
830 | pfc |= SYS_PINFUNC_P1C; /* SPI is configured later */ | ||
831 | alchemy_wrsys(pfc, AU1000_SYS_PINFUNC); | ||
832 | |||
833 | /* get 50MHz for I2C driver on PSC0 */ | ||
834 | c = clk_get(NULL, "psc0_intclk"); | ||
835 | if (!IS_ERR(c)) { | ||
836 | pfc = clk_round_rate(c, 50000000); | ||
837 | if ((pfc < 1) || (abs(50000000 - pfc) > 2500000)) | ||
838 | pr_warn("DB1200: cant get I2C close to 50MHz\n"); | ||
839 | else | ||
840 | clk_set_rate(c, pfc); | ||
841 | clk_prepare_enable(c); | ||
842 | clk_put(c); | ||
843 | } | ||
844 | |||
845 | /* insert/eject pairs: one of both is always screaming. To avoid | ||
846 | * issues they must not be automatically enabled when initially | ||
847 | * requested. | ||
848 | */ | ||
849 | irq_set_status_flags(DB1200_SD0_INSERT_INT, IRQ_NOAUTOEN); | ||
850 | irq_set_status_flags(DB1200_SD0_EJECT_INT, IRQ_NOAUTOEN); | ||
851 | irq_set_status_flags(DB1200_PC0_INSERT_INT, IRQ_NOAUTOEN); | ||
852 | irq_set_status_flags(DB1200_PC0_EJECT_INT, IRQ_NOAUTOEN); | ||
853 | irq_set_status_flags(DB1200_PC1_INSERT_INT, IRQ_NOAUTOEN); | ||
854 | irq_set_status_flags(DB1200_PC1_EJECT_INT, IRQ_NOAUTOEN); | ||
855 | |||
856 | i2c_register_board_info(0, db1200_i2c_devs, | ||
857 | ARRAY_SIZE(db1200_i2c_devs)); | ||
858 | spi_register_board_info(db1200_spi_devs, | ||
859 | ARRAY_SIZE(db1200_i2c_devs)); | ||
860 | |||
861 | /* SWITCHES: S6.8 I2C/SPI selector (OFF=I2C ON=SPI) | ||
862 | * S6.7 AC97/I2S selector (OFF=AC97 ON=I2S) | ||
863 | * or S12 on the PB1200. | ||
864 | */ | ||
865 | |||
866 | /* NOTE: GPIO215 controls OTG VBUS supply. In SPI mode however | ||
867 | * this pin is claimed by PSC0 (unused though, but pinmux doesn't | ||
868 | * allow to free it without crippling the SPI interface). | ||
869 | * As a result, in SPI mode, OTG simply won't work (PSC0 uses | ||
870 | * it as an input pin which is pulled high on the boards). | ||
871 | */ | ||
872 | pfc = alchemy_rdsys(AU1000_SYS_PINFUNC) & ~SYS_PINFUNC_P0A; | ||
873 | |||
874 | /* switch off OTG VBUS supply */ | ||
875 | gpio_request(215, "otg-vbus"); | ||
876 | gpio_direction_output(215, 1); | ||
877 | |||
878 | printk(KERN_INFO "%s device configuration:\n", get_system_type()); | ||
879 | |||
880 | sw = bcsr_read(BCSR_SWITCHES); | ||
881 | if (sw & BCSR_SWITCHES_DIP_8) { | ||
882 | db1200_devs[0] = &db1200_i2c_dev; | ||
883 | bcsr_mod(BCSR_RESETS, BCSR_RESETS_PSC0MUX, 0); | ||
884 | |||
885 | pfc |= (2 << 17); /* GPIO2 block owns GPIO215 */ | ||
886 | |||
887 | printk(KERN_INFO " S6.8 OFF: PSC0 mode I2C\n"); | ||
888 | printk(KERN_INFO " OTG port VBUS supply available!\n"); | ||
889 | } else { | ||
890 | db1200_devs[0] = &db1200_spi_dev; | ||
891 | bcsr_mod(BCSR_RESETS, 0, BCSR_RESETS_PSC0MUX); | ||
892 | |||
893 | pfc |= (1 << 17); /* PSC0 owns GPIO215 */ | ||
894 | |||
895 | printk(KERN_INFO " S6.8 ON : PSC0 mode SPI\n"); | ||
896 | printk(KERN_INFO " OTG port VBUS supply disabled\n"); | ||
897 | } | ||
898 | alchemy_wrsys(pfc, AU1000_SYS_PINFUNC); | ||
899 | |||
900 | /* Audio: DIP7 selects I2S(0)/AC97(1), but need I2C for I2S! | ||
901 | * so: DIP7=1 || DIP8=0 => AC97, DIP7=0 && DIP8=1 => I2S | ||
902 | */ | ||
903 | sw &= BCSR_SWITCHES_DIP_8 | BCSR_SWITCHES_DIP_7; | ||
904 | if (sw == BCSR_SWITCHES_DIP_8) { | ||
905 | bcsr_mod(BCSR_RESETS, 0, BCSR_RESETS_PSC1MUX); | ||
906 | db1200_audio_dev.name = "au1xpsc_i2s"; | ||
907 | db1200_sound_dev.name = "db1200-i2s"; | ||
908 | printk(KERN_INFO " S6.7 ON : PSC1 mode I2S\n"); | ||
909 | } else { | ||
910 | bcsr_mod(BCSR_RESETS, BCSR_RESETS_PSC1MUX, 0); | ||
911 | db1200_audio_dev.name = "au1xpsc_ac97"; | ||
912 | db1200_sound_dev.name = "db1200-ac97"; | ||
913 | printk(KERN_INFO " S6.7 OFF: PSC1 mode AC97\n"); | ||
914 | } | ||
915 | |||
916 | /* Audio PSC clock is supplied externally. (FIXME: platdata!!) */ | ||
917 | __raw_writel(PSC_SEL_CLK_SERCLK, | ||
918 | (void __iomem *)KSEG1ADDR(AU1550_PSC1_PHYS_ADDR) + PSC_SEL_OFFSET); | ||
919 | wmb(); | ||
920 | |||
921 | db1x_register_pcmcia_socket( | ||
922 | AU1000_PCMCIA_ATTR_PHYS_ADDR, | ||
923 | AU1000_PCMCIA_ATTR_PHYS_ADDR + 0x000400000 - 1, | ||
924 | AU1000_PCMCIA_MEM_PHYS_ADDR, | ||
925 | AU1000_PCMCIA_MEM_PHYS_ADDR + 0x000400000 - 1, | ||
926 | AU1000_PCMCIA_IO_PHYS_ADDR, | ||
927 | AU1000_PCMCIA_IO_PHYS_ADDR + 0x000010000 - 1, | ||
928 | DB1200_PC0_INT, DB1200_PC0_INSERT_INT, | ||
929 | /*DB1200_PC0_STSCHG_INT*/0, DB1200_PC0_EJECT_INT, 0); | ||
930 | |||
931 | db1x_register_pcmcia_socket( | ||
932 | AU1000_PCMCIA_ATTR_PHYS_ADDR + 0x004000000, | ||
933 | AU1000_PCMCIA_ATTR_PHYS_ADDR + 0x004400000 - 1, | ||
934 | AU1000_PCMCIA_MEM_PHYS_ADDR + 0x004000000, | ||
935 | AU1000_PCMCIA_MEM_PHYS_ADDR + 0x004400000 - 1, | ||
936 | AU1000_PCMCIA_IO_PHYS_ADDR + 0x004000000, | ||
937 | AU1000_PCMCIA_IO_PHYS_ADDR + 0x004010000 - 1, | ||
938 | DB1200_PC1_INT, DB1200_PC1_INSERT_INT, | ||
939 | /*DB1200_PC1_STSCHG_INT*/0, DB1200_PC1_EJECT_INT, 1); | ||
940 | |||
941 | swapped = bcsr_read(BCSR_STATUS) & BCSR_STATUS_DB1200_SWAPBOOT; | ||
942 | db1x_register_norflash(64 << 20, 2, swapped); | ||
943 | |||
944 | platform_add_devices(db1200_devs, ARRAY_SIZE(db1200_devs)); | ||
945 | |||
946 | /* PB1200 is a DB1200 with a 2nd MMC and Camera connector */ | ||
947 | if ((bid == BCSR_WHOAMI_PB1200_DDR1) || | ||
948 | (bid == BCSR_WHOAMI_PB1200_DDR2)) | ||
949 | platform_add_devices(pb1200_devs, ARRAY_SIZE(pb1200_devs)); | ||
950 | |||
951 | return 0; | ||
952 | } | ||
diff --git a/arch/mips/alchemy/devboards/db1300.c b/arch/mips/alchemy/devboards/db1300.c new file mode 100644 index 000000000..cd72eaa11 --- /dev/null +++ b/arch/mips/alchemy/devboards/db1300.c | |||
@@ -0,0 +1,892 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * DBAu1300 init and platform device setup. | ||
4 | * | ||
5 | * (c) 2009 Manuel Lauss <manuel.lauss@googlemail.com> | ||
6 | */ | ||
7 | |||
8 | #include <linux/clk.h> | ||
9 | #include <linux/dma-mapping.h> | ||
10 | #include <linux/gpio.h> | ||
11 | #include <linux/gpio_keys.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/input.h> /* KEY_* codes */ | ||
14 | #include <linux/i2c.h> | ||
15 | #include <linux/io.h> | ||
16 | #include <linux/leds.h> | ||
17 | #include <linux/interrupt.h> | ||
18 | #include <linux/ata_platform.h> | ||
19 | #include <linux/mmc/host.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/mtd/mtd.h> | ||
22 | #include <linux/mtd/platnand.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | #include <linux/smsc911x.h> | ||
25 | #include <linux/wm97xx.h> | ||
26 | |||
27 | #include <asm/mach-au1x00/au1000.h> | ||
28 | #include <asm/mach-au1x00/gpio-au1300.h> | ||
29 | #include <asm/mach-au1x00/au1100_mmc.h> | ||
30 | #include <asm/mach-au1x00/au1200fb.h> | ||
31 | #include <asm/mach-au1x00/au1xxx_dbdma.h> | ||
32 | #include <asm/mach-au1x00/au1xxx_psc.h> | ||
33 | #include <asm/mach-db1x00/bcsr.h> | ||
34 | #include <asm/mach-au1x00/prom.h> | ||
35 | |||
36 | #include "platform.h" | ||
37 | |||
38 | /* FPGA (external mux) interrupt sources */ | ||
39 | #define DB1300_FIRST_INT (ALCHEMY_GPIC_INT_LAST + 1) | ||
40 | #define DB1300_IDE_INT (DB1300_FIRST_INT + 0) | ||
41 | #define DB1300_ETH_INT (DB1300_FIRST_INT + 1) | ||
42 | #define DB1300_CF_INT (DB1300_FIRST_INT + 2) | ||
43 | #define DB1300_VIDEO_INT (DB1300_FIRST_INT + 4) | ||
44 | #define DB1300_HDMI_INT (DB1300_FIRST_INT + 5) | ||
45 | #define DB1300_DC_INT (DB1300_FIRST_INT + 6) | ||
46 | #define DB1300_FLASH_INT (DB1300_FIRST_INT + 7) | ||
47 | #define DB1300_CF_INSERT_INT (DB1300_FIRST_INT + 8) | ||
48 | #define DB1300_CF_EJECT_INT (DB1300_FIRST_INT + 9) | ||
49 | #define DB1300_AC97_INT (DB1300_FIRST_INT + 10) | ||
50 | #define DB1300_AC97_PEN_INT (DB1300_FIRST_INT + 11) | ||
51 | #define DB1300_SD1_INSERT_INT (DB1300_FIRST_INT + 12) | ||
52 | #define DB1300_SD1_EJECT_INT (DB1300_FIRST_INT + 13) | ||
53 | #define DB1300_OTG_VBUS_OC_INT (DB1300_FIRST_INT + 14) | ||
54 | #define DB1300_HOST_VBUS_OC_INT (DB1300_FIRST_INT + 15) | ||
55 | #define DB1300_LAST_INT (DB1300_FIRST_INT + 15) | ||
56 | |||
57 | /* SMSC9210 CS */ | ||
58 | #define DB1300_ETH_PHYS_ADDR 0x19000000 | ||
59 | #define DB1300_ETH_PHYS_END 0x197fffff | ||
60 | |||
61 | /* ATA CS */ | ||
62 | #define DB1300_IDE_PHYS_ADDR 0x18800000 | ||
63 | #define DB1300_IDE_REG_SHIFT 5 | ||
64 | #define DB1300_IDE_PHYS_LEN (16 << DB1300_IDE_REG_SHIFT) | ||
65 | |||
66 | /* NAND CS */ | ||
67 | #define DB1300_NAND_PHYS_ADDR 0x20000000 | ||
68 | #define DB1300_NAND_PHYS_END 0x20000fff | ||
69 | |||
70 | |||
71 | static struct i2c_board_info db1300_i2c_devs[] __initdata = { | ||
72 | { I2C_BOARD_INFO("wm8731", 0x1b), }, /* I2S audio codec */ | ||
73 | { I2C_BOARD_INFO("ne1619", 0x2d), }, /* adm1025-compat hwmon */ | ||
74 | }; | ||
75 | |||
76 | /* multifunction pins to assign to GPIO controller */ | ||
77 | static int db1300_gpio_pins[] __initdata = { | ||
78 | AU1300_PIN_LCDPWM0, AU1300_PIN_PSC2SYNC1, AU1300_PIN_WAKE1, | ||
79 | AU1300_PIN_WAKE2, AU1300_PIN_WAKE3, AU1300_PIN_FG3AUX, | ||
80 | AU1300_PIN_EXTCLK1, | ||
81 | -1, /* terminator */ | ||
82 | }; | ||
83 | |||
84 | /* multifunction pins to assign to device functions */ | ||
85 | static int db1300_dev_pins[] __initdata = { | ||
86 | /* wake-from-str pins 0-3 */ | ||
87 | AU1300_PIN_WAKE0, | ||
88 | /* external clock sources for PSC0 */ | ||
89 | AU1300_PIN_EXTCLK0, | ||
90 | /* 8bit MMC interface on SD0: 6-9 */ | ||
91 | AU1300_PIN_SD0DAT4, AU1300_PIN_SD0DAT5, AU1300_PIN_SD0DAT6, | ||
92 | AU1300_PIN_SD0DAT7, | ||
93 | /* UART1 pins: 11-18 */ | ||
94 | AU1300_PIN_U1RI, AU1300_PIN_U1DCD, AU1300_PIN_U1DSR, | ||
95 | AU1300_PIN_U1CTS, AU1300_PIN_U1RTS, AU1300_PIN_U1DTR, | ||
96 | AU1300_PIN_U1RX, AU1300_PIN_U1TX, | ||
97 | /* UART0 pins: 19-24 */ | ||
98 | AU1300_PIN_U0RI, AU1300_PIN_U0DCD, AU1300_PIN_U0DSR, | ||
99 | AU1300_PIN_U0CTS, AU1300_PIN_U0RTS, AU1300_PIN_U0DTR, | ||
100 | /* UART2: 25-26 */ | ||
101 | AU1300_PIN_U2RX, AU1300_PIN_U2TX, | ||
102 | /* UART3: 27-28 */ | ||
103 | AU1300_PIN_U3RX, AU1300_PIN_U3TX, | ||
104 | /* LCD controller PWMs, ext pixclock: 30-31 */ | ||
105 | AU1300_PIN_LCDPWM1, AU1300_PIN_LCDCLKIN, | ||
106 | /* SD1 interface: 32-37 */ | ||
107 | AU1300_PIN_SD1DAT0, AU1300_PIN_SD1DAT1, AU1300_PIN_SD1DAT2, | ||
108 | AU1300_PIN_SD1DAT3, AU1300_PIN_SD1CMD, AU1300_PIN_SD1CLK, | ||
109 | /* SD2 interface: 38-43 */ | ||
110 | AU1300_PIN_SD2DAT0, AU1300_PIN_SD2DAT1, AU1300_PIN_SD2DAT2, | ||
111 | AU1300_PIN_SD2DAT3, AU1300_PIN_SD2CMD, AU1300_PIN_SD2CLK, | ||
112 | /* PSC0/1 clocks: 44-45 */ | ||
113 | AU1300_PIN_PSC0CLK, AU1300_PIN_PSC1CLK, | ||
114 | /* PSCs: 46-49/50-53/54-57/58-61 */ | ||
115 | AU1300_PIN_PSC0SYNC0, AU1300_PIN_PSC0SYNC1, AU1300_PIN_PSC0D0, | ||
116 | AU1300_PIN_PSC0D1, | ||
117 | AU1300_PIN_PSC1SYNC0, AU1300_PIN_PSC1SYNC1, AU1300_PIN_PSC1D0, | ||
118 | AU1300_PIN_PSC1D1, | ||
119 | AU1300_PIN_PSC2SYNC0, AU1300_PIN_PSC2D0, | ||
120 | AU1300_PIN_PSC2D1, | ||
121 | AU1300_PIN_PSC3SYNC0, AU1300_PIN_PSC3SYNC1, AU1300_PIN_PSC3D0, | ||
122 | AU1300_PIN_PSC3D1, | ||
123 | /* PCMCIA interface: 62-70 */ | ||
124 | AU1300_PIN_PCE2, AU1300_PIN_PCE1, AU1300_PIN_PIOS16, | ||
125 | AU1300_PIN_PIOR, AU1300_PIN_PWE, AU1300_PIN_PWAIT, | ||
126 | AU1300_PIN_PREG, AU1300_PIN_POE, AU1300_PIN_PIOW, | ||
127 | /* camera interface H/V sync inputs: 71-72 */ | ||
128 | AU1300_PIN_CIMLS, AU1300_PIN_CIMFS, | ||
129 | /* PSC2/3 clocks: 73-74 */ | ||
130 | AU1300_PIN_PSC2CLK, AU1300_PIN_PSC3CLK, | ||
131 | -1, /* terminator */ | ||
132 | }; | ||
133 | |||
134 | static void __init db1300_gpio_config(void) | ||
135 | { | ||
136 | int *i; | ||
137 | |||
138 | i = &db1300_dev_pins[0]; | ||
139 | while (*i != -1) | ||
140 | au1300_pinfunc_to_dev(*i++); | ||
141 | |||
142 | i = &db1300_gpio_pins[0]; | ||
143 | while (*i != -1) | ||
144 | au1300_gpio_direction_input(*i++);/* implies pin_to_gpio */ | ||
145 | |||
146 | au1300_set_dbdma_gpio(1, AU1300_PIN_FG3AUX); | ||
147 | } | ||
148 | |||
149 | /**********************************************************************/ | ||
150 | |||
151 | static u64 au1300_all_dmamask = DMA_BIT_MASK(32); | ||
152 | |||
153 | static void au1300_nand_cmd_ctrl(struct nand_chip *this, int cmd, | ||
154 | unsigned int ctrl) | ||
155 | { | ||
156 | unsigned long ioaddr = (unsigned long)this->legacy.IO_ADDR_W; | ||
157 | |||
158 | ioaddr &= 0xffffff00; | ||
159 | |||
160 | if (ctrl & NAND_CLE) { | ||
161 | ioaddr += MEM_STNAND_CMD; | ||
162 | } else if (ctrl & NAND_ALE) { | ||
163 | ioaddr += MEM_STNAND_ADDR; | ||
164 | } else { | ||
165 | /* assume we want to r/w real data by default */ | ||
166 | ioaddr += MEM_STNAND_DATA; | ||
167 | } | ||
168 | this->legacy.IO_ADDR_R = this->legacy.IO_ADDR_W = (void __iomem *)ioaddr; | ||
169 | if (cmd != NAND_CMD_NONE) { | ||
170 | __raw_writeb(cmd, this->legacy.IO_ADDR_W); | ||
171 | wmb(); | ||
172 | } | ||
173 | } | ||
174 | |||
175 | static int au1300_nand_device_ready(struct nand_chip *this) | ||
176 | { | ||
177 | return alchemy_rdsmem(AU1000_MEM_STSTAT) & 1; | ||
178 | } | ||
179 | |||
180 | static struct mtd_partition db1300_nand_parts[] = { | ||
181 | { | ||
182 | .name = "NAND FS 0", | ||
183 | .offset = 0, | ||
184 | .size = 8 * 1024 * 1024, | ||
185 | }, | ||
186 | { | ||
187 | .name = "NAND FS 1", | ||
188 | .offset = MTDPART_OFS_APPEND, | ||
189 | .size = MTDPART_SIZ_FULL | ||
190 | }, | ||
191 | }; | ||
192 | |||
193 | struct platform_nand_data db1300_nand_platdata = { | ||
194 | .chip = { | ||
195 | .nr_chips = 1, | ||
196 | .chip_offset = 0, | ||
197 | .nr_partitions = ARRAY_SIZE(db1300_nand_parts), | ||
198 | .partitions = db1300_nand_parts, | ||
199 | .chip_delay = 20, | ||
200 | }, | ||
201 | .ctrl = { | ||
202 | .dev_ready = au1300_nand_device_ready, | ||
203 | .cmd_ctrl = au1300_nand_cmd_ctrl, | ||
204 | }, | ||
205 | }; | ||
206 | |||
207 | static struct resource db1300_nand_res[] = { | ||
208 | [0] = { | ||
209 | .start = DB1300_NAND_PHYS_ADDR, | ||
210 | .end = DB1300_NAND_PHYS_ADDR + 0xff, | ||
211 | .flags = IORESOURCE_MEM, | ||
212 | }, | ||
213 | }; | ||
214 | |||
215 | static struct platform_device db1300_nand_dev = { | ||
216 | .name = "gen_nand", | ||
217 | .num_resources = ARRAY_SIZE(db1300_nand_res), | ||
218 | .resource = db1300_nand_res, | ||
219 | .id = -1, | ||
220 | .dev = { | ||
221 | .platform_data = &db1300_nand_platdata, | ||
222 | } | ||
223 | }; | ||
224 | |||
225 | /**********************************************************************/ | ||
226 | |||
227 | static struct resource db1300_eth_res[] = { | ||
228 | [0] = { | ||
229 | .start = DB1300_ETH_PHYS_ADDR, | ||
230 | .end = DB1300_ETH_PHYS_END, | ||
231 | .flags = IORESOURCE_MEM, | ||
232 | }, | ||
233 | [1] = { | ||
234 | .start = DB1300_ETH_INT, | ||
235 | .end = DB1300_ETH_INT, | ||
236 | .flags = IORESOURCE_IRQ, | ||
237 | }, | ||
238 | }; | ||
239 | |||
240 | static struct smsc911x_platform_config db1300_eth_config = { | ||
241 | .phy_interface = PHY_INTERFACE_MODE_MII, | ||
242 | .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, | ||
243 | .irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL, | ||
244 | .flags = SMSC911X_USE_32BIT, | ||
245 | }; | ||
246 | |||
247 | static struct platform_device db1300_eth_dev = { | ||
248 | .name = "smsc911x", | ||
249 | .id = -1, | ||
250 | .num_resources = ARRAY_SIZE(db1300_eth_res), | ||
251 | .resource = db1300_eth_res, | ||
252 | .dev = { | ||
253 | .platform_data = &db1300_eth_config, | ||
254 | }, | ||
255 | }; | ||
256 | |||
257 | /**********************************************************************/ | ||
258 | |||
259 | static struct resource au1300_psc1_res[] = { | ||
260 | [0] = { | ||
261 | .start = AU1300_PSC1_PHYS_ADDR, | ||
262 | .end = AU1300_PSC1_PHYS_ADDR + 0x0fff, | ||
263 | .flags = IORESOURCE_MEM, | ||
264 | }, | ||
265 | [1] = { | ||
266 | .start = AU1300_PSC1_INT, | ||
267 | .end = AU1300_PSC1_INT, | ||
268 | .flags = IORESOURCE_IRQ, | ||
269 | }, | ||
270 | [2] = { | ||
271 | .start = AU1300_DSCR_CMD0_PSC1_TX, | ||
272 | .end = AU1300_DSCR_CMD0_PSC1_TX, | ||
273 | .flags = IORESOURCE_DMA, | ||
274 | }, | ||
275 | [3] = { | ||
276 | .start = AU1300_DSCR_CMD0_PSC1_RX, | ||
277 | .end = AU1300_DSCR_CMD0_PSC1_RX, | ||
278 | .flags = IORESOURCE_DMA, | ||
279 | }, | ||
280 | }; | ||
281 | |||
282 | static struct platform_device db1300_ac97_dev = { | ||
283 | .name = "au1xpsc_ac97", | ||
284 | .id = 1, /* PSC ID. match with AC97 codec ID! */ | ||
285 | .num_resources = ARRAY_SIZE(au1300_psc1_res), | ||
286 | .resource = au1300_psc1_res, | ||
287 | }; | ||
288 | |||
289 | /**********************************************************************/ | ||
290 | |||
291 | static struct resource au1300_psc2_res[] = { | ||
292 | [0] = { | ||
293 | .start = AU1300_PSC2_PHYS_ADDR, | ||
294 | .end = AU1300_PSC2_PHYS_ADDR + 0x0fff, | ||
295 | .flags = IORESOURCE_MEM, | ||
296 | }, | ||
297 | [1] = { | ||
298 | .start = AU1300_PSC2_INT, | ||
299 | .end = AU1300_PSC2_INT, | ||
300 | .flags = IORESOURCE_IRQ, | ||
301 | }, | ||
302 | [2] = { | ||
303 | .start = AU1300_DSCR_CMD0_PSC2_TX, | ||
304 | .end = AU1300_DSCR_CMD0_PSC2_TX, | ||
305 | .flags = IORESOURCE_DMA, | ||
306 | }, | ||
307 | [3] = { | ||
308 | .start = AU1300_DSCR_CMD0_PSC2_RX, | ||
309 | .end = AU1300_DSCR_CMD0_PSC2_RX, | ||
310 | .flags = IORESOURCE_DMA, | ||
311 | }, | ||
312 | }; | ||
313 | |||
314 | static struct platform_device db1300_i2s_dev = { | ||
315 | .name = "au1xpsc_i2s", | ||
316 | .id = 2, /* PSC ID */ | ||
317 | .num_resources = ARRAY_SIZE(au1300_psc2_res), | ||
318 | .resource = au1300_psc2_res, | ||
319 | }; | ||
320 | |||
321 | /**********************************************************************/ | ||
322 | |||
323 | static struct resource au1300_psc3_res[] = { | ||
324 | [0] = { | ||
325 | .start = AU1300_PSC3_PHYS_ADDR, | ||
326 | .end = AU1300_PSC3_PHYS_ADDR + 0x0fff, | ||
327 | .flags = IORESOURCE_MEM, | ||
328 | }, | ||
329 | [1] = { | ||
330 | .start = AU1300_PSC3_INT, | ||
331 | .end = AU1300_PSC3_INT, | ||
332 | .flags = IORESOURCE_IRQ, | ||
333 | }, | ||
334 | [2] = { | ||
335 | .start = AU1300_DSCR_CMD0_PSC3_TX, | ||
336 | .end = AU1300_DSCR_CMD0_PSC3_TX, | ||
337 | .flags = IORESOURCE_DMA, | ||
338 | }, | ||
339 | [3] = { | ||
340 | .start = AU1300_DSCR_CMD0_PSC3_RX, | ||
341 | .end = AU1300_DSCR_CMD0_PSC3_RX, | ||
342 | .flags = IORESOURCE_DMA, | ||
343 | }, | ||
344 | }; | ||
345 | |||
346 | static struct platform_device db1300_i2c_dev = { | ||
347 | .name = "au1xpsc_smbus", | ||
348 | .id = 0, /* bus number */ | ||
349 | .num_resources = ARRAY_SIZE(au1300_psc3_res), | ||
350 | .resource = au1300_psc3_res, | ||
351 | }; | ||
352 | |||
353 | /**********************************************************************/ | ||
354 | |||
355 | /* proper key assignments when facing the LCD panel. For key assignments | ||
356 | * according to the schematics swap up with down and left with right. | ||
357 | * I chose to use it to emulate the arrow keys of a keyboard. | ||
358 | */ | ||
359 | static struct gpio_keys_button db1300_5waysw_arrowkeys[] = { | ||
360 | { | ||
361 | .code = KEY_DOWN, | ||
362 | .gpio = AU1300_PIN_LCDPWM0, | ||
363 | .type = EV_KEY, | ||
364 | .debounce_interval = 1, | ||
365 | .active_low = 1, | ||
366 | .desc = "5waysw-down", | ||
367 | }, | ||
368 | { | ||
369 | .code = KEY_UP, | ||
370 | .gpio = AU1300_PIN_PSC2SYNC1, | ||
371 | .type = EV_KEY, | ||
372 | .debounce_interval = 1, | ||
373 | .active_low = 1, | ||
374 | .desc = "5waysw-up", | ||
375 | }, | ||
376 | { | ||
377 | .code = KEY_RIGHT, | ||
378 | .gpio = AU1300_PIN_WAKE3, | ||
379 | .type = EV_KEY, | ||
380 | .debounce_interval = 1, | ||
381 | .active_low = 1, | ||
382 | .desc = "5waysw-right", | ||
383 | }, | ||
384 | { | ||
385 | .code = KEY_LEFT, | ||
386 | .gpio = AU1300_PIN_WAKE2, | ||
387 | .type = EV_KEY, | ||
388 | .debounce_interval = 1, | ||
389 | .active_low = 1, | ||
390 | .desc = "5waysw-left", | ||
391 | }, | ||
392 | { | ||
393 | .code = KEY_ENTER, | ||
394 | .gpio = AU1300_PIN_WAKE1, | ||
395 | .type = EV_KEY, | ||
396 | .debounce_interval = 1, | ||
397 | .active_low = 1, | ||
398 | .desc = "5waysw-push", | ||
399 | }, | ||
400 | }; | ||
401 | |||
402 | static struct gpio_keys_platform_data db1300_5waysw_data = { | ||
403 | .buttons = db1300_5waysw_arrowkeys, | ||
404 | .nbuttons = ARRAY_SIZE(db1300_5waysw_arrowkeys), | ||
405 | .rep = 1, | ||
406 | .name = "db1300-5wayswitch", | ||
407 | }; | ||
408 | |||
409 | static struct platform_device db1300_5waysw_dev = { | ||
410 | .name = "gpio-keys", | ||
411 | .dev = { | ||
412 | .platform_data = &db1300_5waysw_data, | ||
413 | }, | ||
414 | }; | ||
415 | |||
416 | /**********************************************************************/ | ||
417 | |||
418 | static struct pata_platform_info db1300_ide_info = { | ||
419 | .ioport_shift = DB1300_IDE_REG_SHIFT, | ||
420 | }; | ||
421 | |||
422 | #define IDE_ALT_START (14 << DB1300_IDE_REG_SHIFT) | ||
423 | static struct resource db1300_ide_res[] = { | ||
424 | [0] = { | ||
425 | .start = DB1300_IDE_PHYS_ADDR, | ||
426 | .end = DB1300_IDE_PHYS_ADDR + IDE_ALT_START - 1, | ||
427 | .flags = IORESOURCE_MEM, | ||
428 | }, | ||
429 | [1] = { | ||
430 | .start = DB1300_IDE_PHYS_ADDR + IDE_ALT_START, | ||
431 | .end = DB1300_IDE_PHYS_ADDR + DB1300_IDE_PHYS_LEN - 1, | ||
432 | .flags = IORESOURCE_MEM, | ||
433 | }, | ||
434 | [2] = { | ||
435 | .start = DB1300_IDE_INT, | ||
436 | .end = DB1300_IDE_INT, | ||
437 | .flags = IORESOURCE_IRQ, | ||
438 | }, | ||
439 | }; | ||
440 | |||
441 | static struct platform_device db1300_ide_dev = { | ||
442 | .dev = { | ||
443 | .dma_mask = &au1300_all_dmamask, | ||
444 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
445 | .platform_data = &db1300_ide_info, | ||
446 | }, | ||
447 | .name = "pata_platform", | ||
448 | .resource = db1300_ide_res, | ||
449 | .num_resources = ARRAY_SIZE(db1300_ide_res), | ||
450 | }; | ||
451 | |||
452 | /**********************************************************************/ | ||
453 | |||
454 | static irqreturn_t db1300_mmc_cd(int irq, void *ptr) | ||
455 | { | ||
456 | disable_irq_nosync(irq); | ||
457 | return IRQ_WAKE_THREAD; | ||
458 | } | ||
459 | |||
460 | static irqreturn_t db1300_mmc_cdfn(int irq, void *ptr) | ||
461 | { | ||
462 | void (*mmc_cd)(struct mmc_host *, unsigned long); | ||
463 | |||
464 | /* link against CONFIG_MMC=m. We can only be called once MMC core has | ||
465 | * initialized the controller, so symbol_get() should always succeed. | ||
466 | */ | ||
467 | mmc_cd = symbol_get(mmc_detect_change); | ||
468 | mmc_cd(ptr, msecs_to_jiffies(200)); | ||
469 | symbol_put(mmc_detect_change); | ||
470 | |||
471 | msleep(100); /* debounce */ | ||
472 | if (irq == DB1300_SD1_INSERT_INT) | ||
473 | enable_irq(DB1300_SD1_EJECT_INT); | ||
474 | else | ||
475 | enable_irq(DB1300_SD1_INSERT_INT); | ||
476 | |||
477 | return IRQ_HANDLED; | ||
478 | } | ||
479 | |||
480 | static int db1300_mmc_card_readonly(void *mmc_host) | ||
481 | { | ||
482 | /* it uses SD1 interface, but the DB1200's SD0 bit in the CPLD */ | ||
483 | return bcsr_read(BCSR_STATUS) & BCSR_STATUS_SD0WP; | ||
484 | } | ||
485 | |||
486 | static int db1300_mmc_card_inserted(void *mmc_host) | ||
487 | { | ||
488 | return bcsr_read(BCSR_SIGSTAT) & (1 << 12); /* insertion irq signal */ | ||
489 | } | ||
490 | |||
491 | static int db1300_mmc_cd_setup(void *mmc_host, int en) | ||
492 | { | ||
493 | int ret; | ||
494 | |||
495 | if (en) { | ||
496 | ret = request_threaded_irq(DB1300_SD1_INSERT_INT, db1300_mmc_cd, | ||
497 | db1300_mmc_cdfn, 0, "sd_insert", mmc_host); | ||
498 | if (ret) | ||
499 | goto out; | ||
500 | |||
501 | ret = request_threaded_irq(DB1300_SD1_EJECT_INT, db1300_mmc_cd, | ||
502 | db1300_mmc_cdfn, 0, "sd_eject", mmc_host); | ||
503 | if (ret) { | ||
504 | free_irq(DB1300_SD1_INSERT_INT, mmc_host); | ||
505 | goto out; | ||
506 | } | ||
507 | |||
508 | if (db1300_mmc_card_inserted(mmc_host)) | ||
509 | enable_irq(DB1300_SD1_EJECT_INT); | ||
510 | else | ||
511 | enable_irq(DB1300_SD1_INSERT_INT); | ||
512 | |||
513 | } else { | ||
514 | free_irq(DB1300_SD1_INSERT_INT, mmc_host); | ||
515 | free_irq(DB1300_SD1_EJECT_INT, mmc_host); | ||
516 | } | ||
517 | ret = 0; | ||
518 | out: | ||
519 | return ret; | ||
520 | } | ||
521 | |||
522 | static void db1300_mmcled_set(struct led_classdev *led, | ||
523 | enum led_brightness brightness) | ||
524 | { | ||
525 | if (brightness != LED_OFF) | ||
526 | bcsr_mod(BCSR_LEDS, BCSR_LEDS_LED0, 0); | ||
527 | else | ||
528 | bcsr_mod(BCSR_LEDS, 0, BCSR_LEDS_LED0); | ||
529 | } | ||
530 | |||
531 | static struct led_classdev db1300_mmc_led = { | ||
532 | .brightness_set = db1300_mmcled_set, | ||
533 | }; | ||
534 | |||
535 | struct au1xmmc_platform_data db1300_sd1_platdata = { | ||
536 | .cd_setup = db1300_mmc_cd_setup, | ||
537 | .card_inserted = db1300_mmc_card_inserted, | ||
538 | .card_readonly = db1300_mmc_card_readonly, | ||
539 | .led = &db1300_mmc_led, | ||
540 | }; | ||
541 | |||
542 | static struct resource au1300_sd1_res[] = { | ||
543 | [0] = { | ||
544 | .start = AU1300_SD1_PHYS_ADDR, | ||
545 | .end = AU1300_SD1_PHYS_ADDR, | ||
546 | .flags = IORESOURCE_MEM, | ||
547 | }, | ||
548 | [1] = { | ||
549 | .start = AU1300_SD1_INT, | ||
550 | .end = AU1300_SD1_INT, | ||
551 | .flags = IORESOURCE_IRQ, | ||
552 | }, | ||
553 | [2] = { | ||
554 | .start = AU1300_DSCR_CMD0_SDMS_TX1, | ||
555 | .end = AU1300_DSCR_CMD0_SDMS_TX1, | ||
556 | .flags = IORESOURCE_DMA, | ||
557 | }, | ||
558 | [3] = { | ||
559 | .start = AU1300_DSCR_CMD0_SDMS_RX1, | ||
560 | .end = AU1300_DSCR_CMD0_SDMS_RX1, | ||
561 | .flags = IORESOURCE_DMA, | ||
562 | }, | ||
563 | }; | ||
564 | |||
565 | static struct platform_device db1300_sd1_dev = { | ||
566 | .dev = { | ||
567 | .dma_mask = &au1300_all_dmamask, | ||
568 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
569 | .platform_data = &db1300_sd1_platdata, | ||
570 | }, | ||
571 | .name = "au1xxx-mmc", | ||
572 | .id = 1, | ||
573 | .resource = au1300_sd1_res, | ||
574 | .num_resources = ARRAY_SIZE(au1300_sd1_res), | ||
575 | }; | ||
576 | |||
577 | /**********************************************************************/ | ||
578 | |||
579 | static int db1300_movinand_inserted(void *mmc_host) | ||
580 | { | ||
581 | return 0; /* disable for now, it doesn't work yet */ | ||
582 | } | ||
583 | |||
584 | static int db1300_movinand_readonly(void *mmc_host) | ||
585 | { | ||
586 | return 0; | ||
587 | } | ||
588 | |||
589 | static void db1300_movinand_led_set(struct led_classdev *led, | ||
590 | enum led_brightness brightness) | ||
591 | { | ||
592 | if (brightness != LED_OFF) | ||
593 | bcsr_mod(BCSR_LEDS, BCSR_LEDS_LED1, 0); | ||
594 | else | ||
595 | bcsr_mod(BCSR_LEDS, 0, BCSR_LEDS_LED1); | ||
596 | } | ||
597 | |||
598 | static struct led_classdev db1300_movinand_led = { | ||
599 | .brightness_set = db1300_movinand_led_set, | ||
600 | }; | ||
601 | |||
602 | struct au1xmmc_platform_data db1300_sd0_platdata = { | ||
603 | .card_inserted = db1300_movinand_inserted, | ||
604 | .card_readonly = db1300_movinand_readonly, | ||
605 | .led = &db1300_movinand_led, | ||
606 | .mask_host_caps = MMC_CAP_NEEDS_POLL, | ||
607 | }; | ||
608 | |||
609 | static struct resource au1300_sd0_res[] = { | ||
610 | [0] = { | ||
611 | .start = AU1100_SD0_PHYS_ADDR, | ||
612 | .end = AU1100_SD0_PHYS_ADDR, | ||
613 | .flags = IORESOURCE_MEM, | ||
614 | }, | ||
615 | [1] = { | ||
616 | .start = AU1300_SD0_INT, | ||
617 | .end = AU1300_SD0_INT, | ||
618 | .flags = IORESOURCE_IRQ, | ||
619 | }, | ||
620 | [2] = { | ||
621 | .start = AU1300_DSCR_CMD0_SDMS_TX0, | ||
622 | .end = AU1300_DSCR_CMD0_SDMS_TX0, | ||
623 | .flags = IORESOURCE_DMA, | ||
624 | }, | ||
625 | [3] = { | ||
626 | .start = AU1300_DSCR_CMD0_SDMS_RX0, | ||
627 | .end = AU1300_DSCR_CMD0_SDMS_RX0, | ||
628 | .flags = IORESOURCE_DMA, | ||
629 | }, | ||
630 | }; | ||
631 | |||
632 | static struct platform_device db1300_sd0_dev = { | ||
633 | .dev = { | ||
634 | .dma_mask = &au1300_all_dmamask, | ||
635 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
636 | .platform_data = &db1300_sd0_platdata, | ||
637 | }, | ||
638 | .name = "au1xxx-mmc", | ||
639 | .id = 0, | ||
640 | .resource = au1300_sd0_res, | ||
641 | .num_resources = ARRAY_SIZE(au1300_sd0_res), | ||
642 | }; | ||
643 | |||
644 | /**********************************************************************/ | ||
645 | |||
646 | static struct platform_device db1300_wm9715_dev = { | ||
647 | .name = "wm9712-codec", | ||
648 | .id = 1, /* ID of PSC for AC97 audio, see asoc glue! */ | ||
649 | }; | ||
650 | |||
651 | static struct platform_device db1300_ac97dma_dev = { | ||
652 | .name = "au1xpsc-pcm", | ||
653 | .id = 1, /* PSC ID */ | ||
654 | }; | ||
655 | |||
656 | static struct platform_device db1300_i2sdma_dev = { | ||
657 | .name = "au1xpsc-pcm", | ||
658 | .id = 2, /* PSC ID */ | ||
659 | }; | ||
660 | |||
661 | static struct platform_device db1300_sndac97_dev = { | ||
662 | .name = "db1300-ac97", | ||
663 | .dev = { | ||
664 | .dma_mask = &au1300_all_dmamask, | ||
665 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
666 | }, | ||
667 | }; | ||
668 | |||
669 | static struct platform_device db1300_sndi2s_dev = { | ||
670 | .name = "db1300-i2s", | ||
671 | .dev = { | ||
672 | .dma_mask = &au1300_all_dmamask, | ||
673 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
674 | }, | ||
675 | }; | ||
676 | |||
677 | /**********************************************************************/ | ||
678 | |||
679 | static int db1300fb_panel_index(void) | ||
680 | { | ||
681 | return 9; /* DB1300_800x480 */ | ||
682 | } | ||
683 | |||
684 | static int db1300fb_panel_init(void) | ||
685 | { | ||
686 | /* Apply power (Vee/Vdd logic is inverted on Panel DB1300_800x480) */ | ||
687 | bcsr_mod(BCSR_BOARD, BCSR_BOARD_LCDVEE | BCSR_BOARD_LCDVDD, | ||
688 | BCSR_BOARD_LCDBL); | ||
689 | return 0; | ||
690 | } | ||
691 | |||
692 | static int db1300fb_panel_shutdown(void) | ||
693 | { | ||
694 | /* Remove power (Vee/Vdd logic is inverted on Panel DB1300_800x480) */ | ||
695 | bcsr_mod(BCSR_BOARD, BCSR_BOARD_LCDBL, | ||
696 | BCSR_BOARD_LCDVEE | BCSR_BOARD_LCDVDD); | ||
697 | return 0; | ||
698 | } | ||
699 | |||
700 | static struct au1200fb_platdata db1300fb_pd = { | ||
701 | .panel_index = db1300fb_panel_index, | ||
702 | .panel_init = db1300fb_panel_init, | ||
703 | .panel_shutdown = db1300fb_panel_shutdown, | ||
704 | }; | ||
705 | |||
706 | static struct resource au1300_lcd_res[] = { | ||
707 | [0] = { | ||
708 | .start = AU1200_LCD_PHYS_ADDR, | ||
709 | .end = AU1200_LCD_PHYS_ADDR + 0x800 - 1, | ||
710 | .flags = IORESOURCE_MEM, | ||
711 | }, | ||
712 | [1] = { | ||
713 | .start = AU1300_LCD_INT, | ||
714 | .end = AU1300_LCD_INT, | ||
715 | .flags = IORESOURCE_IRQ, | ||
716 | } | ||
717 | }; | ||
718 | |||
719 | |||
720 | static struct platform_device db1300_lcd_dev = { | ||
721 | .name = "au1200-lcd", | ||
722 | .id = 0, | ||
723 | .dev = { | ||
724 | .dma_mask = &au1300_all_dmamask, | ||
725 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
726 | .platform_data = &db1300fb_pd, | ||
727 | }, | ||
728 | .num_resources = ARRAY_SIZE(au1300_lcd_res), | ||
729 | .resource = au1300_lcd_res, | ||
730 | }; | ||
731 | |||
732 | /**********************************************************************/ | ||
733 | |||
734 | #if IS_ENABLED(CONFIG_TOUCHSCREEN_WM97XX) | ||
735 | static void db1300_wm97xx_irqen(struct wm97xx *wm, int enable) | ||
736 | { | ||
737 | if (enable) | ||
738 | enable_irq(DB1300_AC97_PEN_INT); | ||
739 | else | ||
740 | disable_irq_nosync(DB1300_AC97_PEN_INT); | ||
741 | } | ||
742 | |||
743 | static struct wm97xx_mach_ops db1300_wm97xx_ops = { | ||
744 | .irq_enable = db1300_wm97xx_irqen, | ||
745 | .irq_gpio = WM97XX_GPIO_3, | ||
746 | }; | ||
747 | |||
748 | static int db1300_wm97xx_probe(struct platform_device *pdev) | ||
749 | { | ||
750 | struct wm97xx *wm = platform_get_drvdata(pdev); | ||
751 | |||
752 | /* external pendown indicator */ | ||
753 | wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN, | ||
754 | WM97XX_GPIO_POL_LOW, WM97XX_GPIO_STICKY, | ||
755 | WM97XX_GPIO_WAKE); | ||
756 | |||
757 | /* internal "virtual" pendown gpio */ | ||
758 | wm97xx_config_gpio(wm, WM97XX_GPIO_3, WM97XX_GPIO_OUT, | ||
759 | WM97XX_GPIO_POL_LOW, WM97XX_GPIO_NOTSTICKY, | ||
760 | WM97XX_GPIO_NOWAKE); | ||
761 | |||
762 | wm->pen_irq = DB1300_AC97_PEN_INT; | ||
763 | |||
764 | return wm97xx_register_mach_ops(wm, &db1300_wm97xx_ops); | ||
765 | } | ||
766 | #else | ||
767 | static int db1300_wm97xx_probe(struct platform_device *pdev) | ||
768 | { | ||
769 | return -ENODEV; | ||
770 | } | ||
771 | #endif | ||
772 | |||
773 | static struct platform_driver db1300_wm97xx_driver = { | ||
774 | .driver.name = "wm97xx-touch", | ||
775 | .driver.owner = THIS_MODULE, | ||
776 | .probe = db1300_wm97xx_probe, | ||
777 | }; | ||
778 | |||
779 | /**********************************************************************/ | ||
780 | |||
781 | static struct platform_device *db1300_dev[] __initdata = { | ||
782 | &db1300_eth_dev, | ||
783 | &db1300_i2c_dev, | ||
784 | &db1300_5waysw_dev, | ||
785 | &db1300_nand_dev, | ||
786 | &db1300_ide_dev, | ||
787 | &db1300_sd0_dev, | ||
788 | &db1300_sd1_dev, | ||
789 | &db1300_lcd_dev, | ||
790 | &db1300_ac97_dev, | ||
791 | &db1300_i2s_dev, | ||
792 | &db1300_wm9715_dev, | ||
793 | &db1300_ac97dma_dev, | ||
794 | &db1300_i2sdma_dev, | ||
795 | &db1300_sndac97_dev, | ||
796 | &db1300_sndi2s_dev, | ||
797 | }; | ||
798 | |||
799 | int __init db1300_dev_setup(void) | ||
800 | { | ||
801 | int swapped, cpldirq; | ||
802 | struct clk *c; | ||
803 | |||
804 | /* setup CPLD IRQ muxer */ | ||
805 | cpldirq = au1300_gpio_to_irq(AU1300_PIN_EXTCLK1); | ||
806 | irq_set_irq_type(cpldirq, IRQ_TYPE_LEVEL_HIGH); | ||
807 | bcsr_init_irq(DB1300_FIRST_INT, DB1300_LAST_INT, cpldirq); | ||
808 | |||
809 | /* insert/eject IRQs: one always triggers so don't enable them | ||
810 | * when doing request_irq() on them. DB1200 has this bug too. | ||
811 | */ | ||
812 | irq_set_status_flags(DB1300_SD1_INSERT_INT, IRQ_NOAUTOEN); | ||
813 | irq_set_status_flags(DB1300_SD1_EJECT_INT, IRQ_NOAUTOEN); | ||
814 | irq_set_status_flags(DB1300_CF_INSERT_INT, IRQ_NOAUTOEN); | ||
815 | irq_set_status_flags(DB1300_CF_EJECT_INT, IRQ_NOAUTOEN); | ||
816 | |||
817 | /* | ||
818 | * setup board | ||
819 | */ | ||
820 | prom_get_ethernet_addr(&db1300_eth_config.mac[0]); | ||
821 | |||
822 | i2c_register_board_info(0, db1300_i2c_devs, | ||
823 | ARRAY_SIZE(db1300_i2c_devs)); | ||
824 | |||
825 | if (platform_driver_register(&db1300_wm97xx_driver)) | ||
826 | pr_warn("DB1300: failed to init touch pen irq support!\n"); | ||
827 | |||
828 | /* Audio PSC clock is supplied by codecs (PSC1, 2) */ | ||
829 | __raw_writel(PSC_SEL_CLK_SERCLK, | ||
830 | (void __iomem *)KSEG1ADDR(AU1300_PSC1_PHYS_ADDR) + PSC_SEL_OFFSET); | ||
831 | wmb(); | ||
832 | __raw_writel(PSC_SEL_CLK_SERCLK, | ||
833 | (void __iomem *)KSEG1ADDR(AU1300_PSC2_PHYS_ADDR) + PSC_SEL_OFFSET); | ||
834 | wmb(); | ||
835 | /* I2C driver wants 50MHz, get as close as possible */ | ||
836 | c = clk_get(NULL, "psc3_intclk"); | ||
837 | if (!IS_ERR(c)) { | ||
838 | clk_set_rate(c, 50000000); | ||
839 | clk_prepare_enable(c); | ||
840 | clk_put(c); | ||
841 | } | ||
842 | __raw_writel(PSC_SEL_CLK_INTCLK, | ||
843 | (void __iomem *)KSEG1ADDR(AU1300_PSC3_PHYS_ADDR) + PSC_SEL_OFFSET); | ||
844 | wmb(); | ||
845 | |||
846 | /* enable power to USB ports */ | ||
847 | bcsr_mod(BCSR_RESETS, 0, BCSR_RESETS_USBHPWR | BCSR_RESETS_OTGPWR); | ||
848 | |||
849 | /* although it is socket #0, it uses the CPLD bits which previous boards | ||
850 | * have used for socket #1. | ||
851 | */ | ||
852 | db1x_register_pcmcia_socket( | ||
853 | AU1000_PCMCIA_ATTR_PHYS_ADDR, | ||
854 | AU1000_PCMCIA_ATTR_PHYS_ADDR + 0x00400000 - 1, | ||
855 | AU1000_PCMCIA_MEM_PHYS_ADDR, | ||
856 | AU1000_PCMCIA_MEM_PHYS_ADDR + 0x00400000 - 1, | ||
857 | AU1000_PCMCIA_IO_PHYS_ADDR, | ||
858 | AU1000_PCMCIA_IO_PHYS_ADDR + 0x00010000 - 1, | ||
859 | DB1300_CF_INT, DB1300_CF_INSERT_INT, 0, DB1300_CF_EJECT_INT, 1); | ||
860 | |||
861 | swapped = bcsr_read(BCSR_STATUS) & BCSR_STATUS_DB1200_SWAPBOOT; | ||
862 | db1x_register_norflash(64 << 20, 2, swapped); | ||
863 | |||
864 | return platform_add_devices(db1300_dev, ARRAY_SIZE(db1300_dev)); | ||
865 | } | ||
866 | |||
867 | |||
868 | int __init db1300_board_setup(void) | ||
869 | { | ||
870 | unsigned short whoami; | ||
871 | |||
872 | bcsr_init(DB1300_BCSR_PHYS_ADDR, | ||
873 | DB1300_BCSR_PHYS_ADDR + DB1300_BCSR_HEXLED_OFS); | ||
874 | |||
875 | whoami = bcsr_read(BCSR_WHOAMI); | ||
876 | if (BCSR_WHOAMI_BOARD(whoami) != BCSR_WHOAMI_DB1300) | ||
877 | return -ENODEV; | ||
878 | |||
879 | db1300_gpio_config(); | ||
880 | |||
881 | printk(KERN_INFO "NetLogic DBAu1300 Development Platform.\n\t" | ||
882 | "BoardID %d CPLD Rev %d DaughtercardID %d\n", | ||
883 | BCSR_WHOAMI_BOARD(whoami), BCSR_WHOAMI_CPLD(whoami), | ||
884 | BCSR_WHOAMI_DCID(whoami)); | ||
885 | |||
886 | /* enable UARTs, YAMON only enables #2 */ | ||
887 | alchemy_uart_enable(AU1300_UART0_PHYS_ADDR); | ||
888 | alchemy_uart_enable(AU1300_UART1_PHYS_ADDR); | ||
889 | alchemy_uart_enable(AU1300_UART3_PHYS_ADDR); | ||
890 | |||
891 | return 0; | ||
892 | } | ||
diff --git a/arch/mips/alchemy/devboards/db1550.c b/arch/mips/alchemy/devboards/db1550.c new file mode 100644 index 000000000..752b93d91 --- /dev/null +++ b/arch/mips/alchemy/devboards/db1550.c | |||
@@ -0,0 +1,628 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Alchemy Db1550/Pb1550 board support | ||
4 | * | ||
5 | * (c) 2011 Manuel Lauss <manuel.lauss@googlemail.com> | ||
6 | */ | ||
7 | |||
8 | #include <linux/clk.h> | ||
9 | #include <linux/dma-mapping.h> | ||
10 | #include <linux/gpio.h> | ||
11 | #include <linux/i2c.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/io.h> | ||
14 | #include <linux/interrupt.h> | ||
15 | #include <linux/mtd/mtd.h> | ||
16 | #include <linux/mtd/platnand.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/pm.h> | ||
19 | #include <linux/spi/spi.h> | ||
20 | #include <linux/spi/flash.h> | ||
21 | #include <asm/bootinfo.h> | ||
22 | #include <asm/mach-au1x00/au1000.h> | ||
23 | #include <asm/mach-au1x00/gpio-au1000.h> | ||
24 | #include <asm/mach-au1x00/au1xxx_eth.h> | ||
25 | #include <asm/mach-au1x00/au1xxx_dbdma.h> | ||
26 | #include <asm/mach-au1x00/au1xxx_psc.h> | ||
27 | #include <asm/mach-au1x00/au1550_spi.h> | ||
28 | #include <asm/mach-au1x00/au1550nd.h> | ||
29 | #include <asm/mach-db1x00/bcsr.h> | ||
30 | #include <prom.h> | ||
31 | #include "platform.h" | ||
32 | |||
33 | static void __init db1550_hw_setup(void) | ||
34 | { | ||
35 | void __iomem *base; | ||
36 | unsigned long v; | ||
37 | |||
38 | /* complete pin setup: assign GPIO16 to PSC0_SYNC1 (SPI cs# line) | ||
39 | * as well as PSC1_SYNC for AC97 on PB1550. | ||
40 | */ | ||
41 | v = alchemy_rdsys(AU1000_SYS_PINFUNC); | ||
42 | alchemy_wrsys(v | 1 | SYS_PF_PSC1_S1, AU1000_SYS_PINFUNC); | ||
43 | |||
44 | /* reset the AC97 codec now, the reset time in the psc-ac97 driver | ||
45 | * is apparently too short although it's ridiculous as it is. | ||
46 | */ | ||
47 | base = (void __iomem *)KSEG1ADDR(AU1550_PSC1_PHYS_ADDR); | ||
48 | __raw_writel(PSC_SEL_CLK_SERCLK | PSC_SEL_PS_AC97MODE, | ||
49 | base + PSC_SEL_OFFSET); | ||
50 | __raw_writel(PSC_CTRL_DISABLE, base + PSC_CTRL_OFFSET); | ||
51 | wmb(); | ||
52 | __raw_writel(PSC_AC97RST_RST, base + PSC_AC97RST_OFFSET); | ||
53 | wmb(); | ||
54 | } | ||
55 | |||
56 | int __init db1550_board_setup(void) | ||
57 | { | ||
58 | unsigned short whoami; | ||
59 | |||
60 | bcsr_init(DB1550_BCSR_PHYS_ADDR, | ||
61 | DB1550_BCSR_PHYS_ADDR + DB1550_BCSR_HEXLED_OFS); | ||
62 | |||
63 | whoami = bcsr_read(BCSR_WHOAMI); /* PB1550 hexled offset differs */ | ||
64 | switch (BCSR_WHOAMI_BOARD(whoami)) { | ||
65 | case BCSR_WHOAMI_PB1550_SDR: | ||
66 | case BCSR_WHOAMI_PB1550_DDR: | ||
67 | bcsr_init(PB1550_BCSR_PHYS_ADDR, | ||
68 | PB1550_BCSR_PHYS_ADDR + PB1550_BCSR_HEXLED_OFS); | ||
69 | case BCSR_WHOAMI_DB1550: | ||
70 | break; | ||
71 | default: | ||
72 | return -ENODEV; | ||
73 | } | ||
74 | |||
75 | pr_info("Alchemy/AMD %s Board, CPLD Rev %d Board-ID %d " \ | ||
76 | "Daughtercard ID %d\n", get_system_type(), | ||
77 | (whoami >> 4) & 0xf, (whoami >> 8) & 0xf, whoami & 0xf); | ||
78 | |||
79 | db1550_hw_setup(); | ||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | /*****************************************************************************/ | ||
84 | |||
85 | static u64 au1550_all_dmamask = DMA_BIT_MASK(32); | ||
86 | |||
87 | static struct mtd_partition db1550_spiflash_parts[] = { | ||
88 | { | ||
89 | .name = "spi_flash", | ||
90 | .offset = 0, | ||
91 | .size = MTDPART_SIZ_FULL, | ||
92 | }, | ||
93 | }; | ||
94 | |||
95 | static struct flash_platform_data db1550_spiflash_data = { | ||
96 | .name = "s25fl010", | ||
97 | .parts = db1550_spiflash_parts, | ||
98 | .nr_parts = ARRAY_SIZE(db1550_spiflash_parts), | ||
99 | .type = "m25p10", | ||
100 | }; | ||
101 | |||
102 | static struct spi_board_info db1550_spi_devs[] __initdata = { | ||
103 | { | ||
104 | /* TI TMP121AIDBVR temp sensor */ | ||
105 | .modalias = "tmp121", | ||
106 | .max_speed_hz = 2400000, | ||
107 | .bus_num = 0, | ||
108 | .chip_select = 0, | ||
109 | .mode = SPI_MODE_0, | ||
110 | }, | ||
111 | { | ||
112 | /* Spansion S25FL001D0FMA SPI flash */ | ||
113 | .modalias = "m25p80", | ||
114 | .max_speed_hz = 2400000, | ||
115 | .bus_num = 0, | ||
116 | .chip_select = 1, | ||
117 | .mode = SPI_MODE_0, | ||
118 | .platform_data = &db1550_spiflash_data, | ||
119 | }, | ||
120 | }; | ||
121 | |||
122 | static struct i2c_board_info db1550_i2c_devs[] __initdata = { | ||
123 | { I2C_BOARD_INFO("24c04", 0x52),}, /* AT24C04-10 I2C eeprom */ | ||
124 | { I2C_BOARD_INFO("ne1619", 0x2d),}, /* adm1025-compat hwmon */ | ||
125 | { I2C_BOARD_INFO("wm8731", 0x1b),}, /* I2S audio codec WM8731 */ | ||
126 | }; | ||
127 | |||
128 | /**********************************************************************/ | ||
129 | |||
130 | static void au1550_nand_cmd_ctrl(struct nand_chip *this, int cmd, | ||
131 | unsigned int ctrl) | ||
132 | { | ||
133 | unsigned long ioaddr = (unsigned long)this->legacy.IO_ADDR_W; | ||
134 | |||
135 | ioaddr &= 0xffffff00; | ||
136 | |||
137 | if (ctrl & NAND_CLE) { | ||
138 | ioaddr += MEM_STNAND_CMD; | ||
139 | } else if (ctrl & NAND_ALE) { | ||
140 | ioaddr += MEM_STNAND_ADDR; | ||
141 | } else { | ||
142 | /* assume we want to r/w real data by default */ | ||
143 | ioaddr += MEM_STNAND_DATA; | ||
144 | } | ||
145 | this->legacy.IO_ADDR_R = this->legacy.IO_ADDR_W = (void __iomem *)ioaddr; | ||
146 | if (cmd != NAND_CMD_NONE) { | ||
147 | __raw_writeb(cmd, this->legacy.IO_ADDR_W); | ||
148 | wmb(); | ||
149 | } | ||
150 | } | ||
151 | |||
152 | static int au1550_nand_device_ready(struct nand_chip *this) | ||
153 | { | ||
154 | return alchemy_rdsmem(AU1000_MEM_STSTAT) & 1; | ||
155 | } | ||
156 | |||
157 | static struct mtd_partition db1550_nand_parts[] = { | ||
158 | { | ||
159 | .name = "NAND FS 0", | ||
160 | .offset = 0, | ||
161 | .size = 8 * 1024 * 1024, | ||
162 | }, | ||
163 | { | ||
164 | .name = "NAND FS 1", | ||
165 | .offset = MTDPART_OFS_APPEND, | ||
166 | .size = MTDPART_SIZ_FULL | ||
167 | }, | ||
168 | }; | ||
169 | |||
170 | struct platform_nand_data db1550_nand_platdata = { | ||
171 | .chip = { | ||
172 | .nr_chips = 1, | ||
173 | .chip_offset = 0, | ||
174 | .nr_partitions = ARRAY_SIZE(db1550_nand_parts), | ||
175 | .partitions = db1550_nand_parts, | ||
176 | .chip_delay = 20, | ||
177 | }, | ||
178 | .ctrl = { | ||
179 | .dev_ready = au1550_nand_device_ready, | ||
180 | .cmd_ctrl = au1550_nand_cmd_ctrl, | ||
181 | }, | ||
182 | }; | ||
183 | |||
184 | static struct resource db1550_nand_res[] = { | ||
185 | [0] = { | ||
186 | .start = 0x20000000, | ||
187 | .end = 0x200000ff, | ||
188 | .flags = IORESOURCE_MEM, | ||
189 | }, | ||
190 | }; | ||
191 | |||
192 | static struct platform_device db1550_nand_dev = { | ||
193 | .name = "gen_nand", | ||
194 | .num_resources = ARRAY_SIZE(db1550_nand_res), | ||
195 | .resource = db1550_nand_res, | ||
196 | .id = -1, | ||
197 | .dev = { | ||
198 | .platform_data = &db1550_nand_platdata, | ||
199 | } | ||
200 | }; | ||
201 | |||
202 | static struct au1550nd_platdata pb1550_nand_pd = { | ||
203 | .parts = db1550_nand_parts, | ||
204 | .num_parts = ARRAY_SIZE(db1550_nand_parts), | ||
205 | .devwidth = 0, /* x8 NAND default, needs fixing up */ | ||
206 | }; | ||
207 | |||
208 | static struct platform_device pb1550_nand_dev = { | ||
209 | .name = "au1550-nand", | ||
210 | .id = -1, | ||
211 | .resource = db1550_nand_res, | ||
212 | .num_resources = ARRAY_SIZE(db1550_nand_res), | ||
213 | .dev = { | ||
214 | .platform_data = &pb1550_nand_pd, | ||
215 | }, | ||
216 | }; | ||
217 | |||
218 | static void __init pb1550_nand_setup(void) | ||
219 | { | ||
220 | int boot_swapboot = (alchemy_rdsmem(AU1000_MEM_STSTAT) & (0x7 << 1)) | | ||
221 | ((bcsr_read(BCSR_STATUS) >> 6) & 0x1); | ||
222 | |||
223 | gpio_direction_input(206); /* de-assert NAND CS# */ | ||
224 | switch (boot_swapboot) { | ||
225 | case 0: case 2: case 8: case 0xC: case 0xD: | ||
226 | /* x16 NAND Flash */ | ||
227 | pb1550_nand_pd.devwidth = 1; | ||
228 | fallthrough; | ||
229 | case 1: case 3: case 9: case 0xE: case 0xF: | ||
230 | /* x8 NAND, already set up */ | ||
231 | platform_device_register(&pb1550_nand_dev); | ||
232 | } | ||
233 | } | ||
234 | |||
235 | /**********************************************************************/ | ||
236 | |||
237 | static struct resource au1550_psc0_res[] = { | ||
238 | [0] = { | ||
239 | .start = AU1550_PSC0_PHYS_ADDR, | ||
240 | .end = AU1550_PSC0_PHYS_ADDR + 0xfff, | ||
241 | .flags = IORESOURCE_MEM, | ||
242 | }, | ||
243 | [1] = { | ||
244 | .start = AU1550_PSC0_INT, | ||
245 | .end = AU1550_PSC0_INT, | ||
246 | .flags = IORESOURCE_IRQ, | ||
247 | }, | ||
248 | [2] = { | ||
249 | .start = AU1550_DSCR_CMD0_PSC0_TX, | ||
250 | .end = AU1550_DSCR_CMD0_PSC0_TX, | ||
251 | .flags = IORESOURCE_DMA, | ||
252 | }, | ||
253 | [3] = { | ||
254 | .start = AU1550_DSCR_CMD0_PSC0_RX, | ||
255 | .end = AU1550_DSCR_CMD0_PSC0_RX, | ||
256 | .flags = IORESOURCE_DMA, | ||
257 | }, | ||
258 | }; | ||
259 | |||
260 | static void db1550_spi_cs_en(struct au1550_spi_info *spi, int cs, int pol) | ||
261 | { | ||
262 | if (cs) | ||
263 | bcsr_mod(BCSR_BOARD, 0, BCSR_BOARD_SPISEL); | ||
264 | else | ||
265 | bcsr_mod(BCSR_BOARD, BCSR_BOARD_SPISEL, 0); | ||
266 | } | ||
267 | |||
268 | static struct au1550_spi_info db1550_spi_platdata = { | ||
269 | .mainclk_hz = 48000000, /* PSC0 clock: max. 2.4MHz SPI clk */ | ||
270 | .num_chipselect = 2, | ||
271 | .activate_cs = db1550_spi_cs_en, | ||
272 | }; | ||
273 | |||
274 | |||
275 | static struct platform_device db1550_spi_dev = { | ||
276 | .dev = { | ||
277 | .dma_mask = &au1550_all_dmamask, | ||
278 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
279 | .platform_data = &db1550_spi_platdata, | ||
280 | }, | ||
281 | .name = "au1550-spi", | ||
282 | .id = 0, /* bus number */ | ||
283 | .num_resources = ARRAY_SIZE(au1550_psc0_res), | ||
284 | .resource = au1550_psc0_res, | ||
285 | }; | ||
286 | |||
287 | /**********************************************************************/ | ||
288 | |||
289 | static struct resource au1550_psc1_res[] = { | ||
290 | [0] = { | ||
291 | .start = AU1550_PSC1_PHYS_ADDR, | ||
292 | .end = AU1550_PSC1_PHYS_ADDR + 0xfff, | ||
293 | .flags = IORESOURCE_MEM, | ||
294 | }, | ||
295 | [1] = { | ||
296 | .start = AU1550_PSC1_INT, | ||
297 | .end = AU1550_PSC1_INT, | ||
298 | .flags = IORESOURCE_IRQ, | ||
299 | }, | ||
300 | [2] = { | ||
301 | .start = AU1550_DSCR_CMD0_PSC1_TX, | ||
302 | .end = AU1550_DSCR_CMD0_PSC1_TX, | ||
303 | .flags = IORESOURCE_DMA, | ||
304 | }, | ||
305 | [3] = { | ||
306 | .start = AU1550_DSCR_CMD0_PSC1_RX, | ||
307 | .end = AU1550_DSCR_CMD0_PSC1_RX, | ||
308 | .flags = IORESOURCE_DMA, | ||
309 | }, | ||
310 | }; | ||
311 | |||
312 | static struct platform_device db1550_ac97_dev = { | ||
313 | .name = "au1xpsc_ac97", | ||
314 | .id = 1, /* PSC ID */ | ||
315 | .num_resources = ARRAY_SIZE(au1550_psc1_res), | ||
316 | .resource = au1550_psc1_res, | ||
317 | }; | ||
318 | |||
319 | |||
320 | static struct resource au1550_psc2_res[] = { | ||
321 | [0] = { | ||
322 | .start = AU1550_PSC2_PHYS_ADDR, | ||
323 | .end = AU1550_PSC2_PHYS_ADDR + 0xfff, | ||
324 | .flags = IORESOURCE_MEM, | ||
325 | }, | ||
326 | [1] = { | ||
327 | .start = AU1550_PSC2_INT, | ||
328 | .end = AU1550_PSC2_INT, | ||
329 | .flags = IORESOURCE_IRQ, | ||
330 | }, | ||
331 | [2] = { | ||
332 | .start = AU1550_DSCR_CMD0_PSC2_TX, | ||
333 | .end = AU1550_DSCR_CMD0_PSC2_TX, | ||
334 | .flags = IORESOURCE_DMA, | ||
335 | }, | ||
336 | [3] = { | ||
337 | .start = AU1550_DSCR_CMD0_PSC2_RX, | ||
338 | .end = AU1550_DSCR_CMD0_PSC2_RX, | ||
339 | .flags = IORESOURCE_DMA, | ||
340 | }, | ||
341 | }; | ||
342 | |||
343 | static struct platform_device db1550_i2c_dev = { | ||
344 | .name = "au1xpsc_smbus", | ||
345 | .id = 0, /* bus number */ | ||
346 | .num_resources = ARRAY_SIZE(au1550_psc2_res), | ||
347 | .resource = au1550_psc2_res, | ||
348 | }; | ||
349 | |||
350 | /**********************************************************************/ | ||
351 | |||
352 | static struct resource au1550_psc3_res[] = { | ||
353 | [0] = { | ||
354 | .start = AU1550_PSC3_PHYS_ADDR, | ||
355 | .end = AU1550_PSC3_PHYS_ADDR + 0xfff, | ||
356 | .flags = IORESOURCE_MEM, | ||
357 | }, | ||
358 | [1] = { | ||
359 | .start = AU1550_PSC3_INT, | ||
360 | .end = AU1550_PSC3_INT, | ||
361 | .flags = IORESOURCE_IRQ, | ||
362 | }, | ||
363 | [2] = { | ||
364 | .start = AU1550_DSCR_CMD0_PSC3_TX, | ||
365 | .end = AU1550_DSCR_CMD0_PSC3_TX, | ||
366 | .flags = IORESOURCE_DMA, | ||
367 | }, | ||
368 | [3] = { | ||
369 | .start = AU1550_DSCR_CMD0_PSC3_RX, | ||
370 | .end = AU1550_DSCR_CMD0_PSC3_RX, | ||
371 | .flags = IORESOURCE_DMA, | ||
372 | }, | ||
373 | }; | ||
374 | |||
375 | static struct platform_device db1550_i2s_dev = { | ||
376 | .name = "au1xpsc_i2s", | ||
377 | .id = 3, /* PSC ID */ | ||
378 | .num_resources = ARRAY_SIZE(au1550_psc3_res), | ||
379 | .resource = au1550_psc3_res, | ||
380 | }; | ||
381 | |||
382 | /**********************************************************************/ | ||
383 | |||
384 | static struct platform_device db1550_stac_dev = { | ||
385 | .name = "ac97-codec", | ||
386 | .id = 1, /* on PSC1 */ | ||
387 | }; | ||
388 | |||
389 | static struct platform_device db1550_ac97dma_dev = { | ||
390 | .name = "au1xpsc-pcm", | ||
391 | .id = 1, /* on PSC3 */ | ||
392 | }; | ||
393 | |||
394 | static struct platform_device db1550_i2sdma_dev = { | ||
395 | .name = "au1xpsc-pcm", | ||
396 | .id = 3, /* on PSC3 */ | ||
397 | }; | ||
398 | |||
399 | static struct platform_device db1550_sndac97_dev = { | ||
400 | .name = "db1550-ac97", | ||
401 | .dev = { | ||
402 | .dma_mask = &au1550_all_dmamask, | ||
403 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
404 | }, | ||
405 | }; | ||
406 | |||
407 | static struct platform_device db1550_sndi2s_dev = { | ||
408 | .name = "db1550-i2s", | ||
409 | .dev = { | ||
410 | .dma_mask = &au1550_all_dmamask, | ||
411 | .coherent_dma_mask = DMA_BIT_MASK(32), | ||
412 | }, | ||
413 | }; | ||
414 | |||
415 | /**********************************************************************/ | ||
416 | |||
417 | static int db1550_map_pci_irq(const struct pci_dev *d, u8 slot, u8 pin) | ||
418 | { | ||
419 | if ((slot < 11) || (slot > 13) || pin == 0) | ||
420 | return -1; | ||
421 | if (slot == 11) | ||
422 | return (pin == 1) ? AU1550_PCI_INTC : 0xff; | ||
423 | if (slot == 12) { | ||
424 | switch (pin) { | ||
425 | case 1: return AU1550_PCI_INTB; | ||
426 | case 2: return AU1550_PCI_INTC; | ||
427 | case 3: return AU1550_PCI_INTD; | ||
428 | case 4: return AU1550_PCI_INTA; | ||
429 | } | ||
430 | } | ||
431 | if (slot == 13) { | ||
432 | switch (pin) { | ||
433 | case 1: return AU1550_PCI_INTA; | ||
434 | case 2: return AU1550_PCI_INTB; | ||
435 | case 3: return AU1550_PCI_INTC; | ||
436 | case 4: return AU1550_PCI_INTD; | ||
437 | } | ||
438 | } | ||
439 | return -1; | ||
440 | } | ||
441 | |||
442 | static int pb1550_map_pci_irq(const struct pci_dev *d, u8 slot, u8 pin) | ||
443 | { | ||
444 | if ((slot < 12) || (slot > 13) || pin == 0) | ||
445 | return -1; | ||
446 | if (slot == 12) { | ||
447 | switch (pin) { | ||
448 | case 1: return AU1500_PCI_INTB; | ||
449 | case 2: return AU1500_PCI_INTC; | ||
450 | case 3: return AU1500_PCI_INTD; | ||
451 | case 4: return AU1500_PCI_INTA; | ||
452 | } | ||
453 | } | ||
454 | if (slot == 13) { | ||
455 | switch (pin) { | ||
456 | case 1: return AU1500_PCI_INTA; | ||
457 | case 2: return AU1500_PCI_INTB; | ||
458 | case 3: return AU1500_PCI_INTC; | ||
459 | case 4: return AU1500_PCI_INTD; | ||
460 | } | ||
461 | } | ||
462 | return -1; | ||
463 | } | ||
464 | |||
465 | static struct resource alchemy_pci_host_res[] = { | ||
466 | [0] = { | ||
467 | .start = AU1500_PCI_PHYS_ADDR, | ||
468 | .end = AU1500_PCI_PHYS_ADDR + 0xfff, | ||
469 | .flags = IORESOURCE_MEM, | ||
470 | }, | ||
471 | }; | ||
472 | |||
473 | static struct alchemy_pci_platdata db1550_pci_pd = { | ||
474 | .board_map_irq = db1550_map_pci_irq, | ||
475 | }; | ||
476 | |||
477 | static struct platform_device db1550_pci_host_dev = { | ||
478 | .dev.platform_data = &db1550_pci_pd, | ||
479 | .name = "alchemy-pci", | ||
480 | .id = 0, | ||
481 | .num_resources = ARRAY_SIZE(alchemy_pci_host_res), | ||
482 | .resource = alchemy_pci_host_res, | ||
483 | }; | ||
484 | |||
485 | /**********************************************************************/ | ||
486 | |||
487 | static struct platform_device *db1550_devs[] __initdata = { | ||
488 | &db1550_i2c_dev, | ||
489 | &db1550_ac97_dev, | ||
490 | &db1550_spi_dev, | ||
491 | &db1550_i2s_dev, | ||
492 | &db1550_stac_dev, | ||
493 | &db1550_ac97dma_dev, | ||
494 | &db1550_i2sdma_dev, | ||
495 | &db1550_sndac97_dev, | ||
496 | &db1550_sndi2s_dev, | ||
497 | }; | ||
498 | |||
499 | /* must be arch_initcall; MIPS PCI scans busses in a subsys_initcall */ | ||
500 | int __init db1550_pci_setup(int id) | ||
501 | { | ||
502 | if (id) | ||
503 | db1550_pci_pd.board_map_irq = pb1550_map_pci_irq; | ||
504 | return platform_device_register(&db1550_pci_host_dev); | ||
505 | } | ||
506 | |||
507 | static void __init db1550_devices(void) | ||
508 | { | ||
509 | alchemy_gpio_direction_output(203, 0); /* red led on */ | ||
510 | |||
511 | irq_set_irq_type(AU1550_GPIO0_INT, IRQ_TYPE_EDGE_BOTH); /* CD0# */ | ||
512 | irq_set_irq_type(AU1550_GPIO1_INT, IRQ_TYPE_EDGE_BOTH); /* CD1# */ | ||
513 | irq_set_irq_type(AU1550_GPIO3_INT, IRQ_TYPE_LEVEL_LOW); /* CARD0# */ | ||
514 | irq_set_irq_type(AU1550_GPIO5_INT, IRQ_TYPE_LEVEL_LOW); /* CARD1# */ | ||
515 | irq_set_irq_type(AU1550_GPIO21_INT, IRQ_TYPE_LEVEL_LOW); /* STSCHG0# */ | ||
516 | irq_set_irq_type(AU1550_GPIO22_INT, IRQ_TYPE_LEVEL_LOW); /* STSCHG1# */ | ||
517 | |||
518 | db1x_register_pcmcia_socket( | ||
519 | AU1000_PCMCIA_ATTR_PHYS_ADDR, | ||
520 | AU1000_PCMCIA_ATTR_PHYS_ADDR + 0x000400000 - 1, | ||
521 | AU1000_PCMCIA_MEM_PHYS_ADDR, | ||
522 | AU1000_PCMCIA_MEM_PHYS_ADDR + 0x000400000 - 1, | ||
523 | AU1000_PCMCIA_IO_PHYS_ADDR, | ||
524 | AU1000_PCMCIA_IO_PHYS_ADDR + 0x000010000 - 1, | ||
525 | AU1550_GPIO3_INT, 0, | ||
526 | /*AU1550_GPIO21_INT*/0, 0, 0); | ||
527 | |||
528 | db1x_register_pcmcia_socket( | ||
529 | AU1000_PCMCIA_ATTR_PHYS_ADDR + 0x004000000, | ||
530 | AU1000_PCMCIA_ATTR_PHYS_ADDR + 0x004400000 - 1, | ||
531 | AU1000_PCMCIA_MEM_PHYS_ADDR + 0x004000000, | ||
532 | AU1000_PCMCIA_MEM_PHYS_ADDR + 0x004400000 - 1, | ||
533 | AU1000_PCMCIA_IO_PHYS_ADDR + 0x004000000, | ||
534 | AU1000_PCMCIA_IO_PHYS_ADDR + 0x004010000 - 1, | ||
535 | AU1550_GPIO5_INT, 1, | ||
536 | /*AU1550_GPIO22_INT*/0, 0, 1); | ||
537 | |||
538 | platform_device_register(&db1550_nand_dev); | ||
539 | |||
540 | alchemy_gpio_direction_output(202, 0); /* green led on */ | ||
541 | } | ||
542 | |||
543 | static void __init pb1550_devices(void) | ||
544 | { | ||
545 | irq_set_irq_type(AU1550_GPIO0_INT, IRQ_TYPE_LEVEL_LOW); | ||
546 | irq_set_irq_type(AU1550_GPIO1_INT, IRQ_TYPE_LEVEL_LOW); | ||
547 | irq_set_irq_type(AU1550_GPIO201_205_INT, IRQ_TYPE_LEVEL_HIGH); | ||
548 | |||
549 | /* enable both PCMCIA card irqs in the shared line */ | ||
550 | alchemy_gpio2_enable_int(201); /* socket 0 card irq */ | ||
551 | alchemy_gpio2_enable_int(202); /* socket 1 card irq */ | ||
552 | |||
553 | /* Pb1550, like all others, also has statuschange irqs; however they're | ||
554 | * wired up on one of the Au1550's shared GPIO201_205 line, which also | ||
555 | * services the PCMCIA card interrupts. So we ignore statuschange and | ||
556 | * use the GPIO201_205 exclusively for card interrupts, since a) pcmcia | ||
557 | * drivers are used to shared irqs and b) statuschange isn't really use- | ||
558 | * ful anyway. | ||
559 | */ | ||
560 | db1x_register_pcmcia_socket( | ||
561 | AU1000_PCMCIA_ATTR_PHYS_ADDR, | ||
562 | AU1000_PCMCIA_ATTR_PHYS_ADDR + 0x000400000 - 1, | ||
563 | AU1000_PCMCIA_MEM_PHYS_ADDR, | ||
564 | AU1000_PCMCIA_MEM_PHYS_ADDR + 0x000400000 - 1, | ||
565 | AU1000_PCMCIA_IO_PHYS_ADDR, | ||
566 | AU1000_PCMCIA_IO_PHYS_ADDR + 0x000010000 - 1, | ||
567 | AU1550_GPIO201_205_INT, AU1550_GPIO0_INT, 0, 0, 0); | ||
568 | |||
569 | db1x_register_pcmcia_socket( | ||
570 | AU1000_PCMCIA_ATTR_PHYS_ADDR + 0x008000000, | ||
571 | AU1000_PCMCIA_ATTR_PHYS_ADDR + 0x008400000 - 1, | ||
572 | AU1000_PCMCIA_MEM_PHYS_ADDR + 0x008000000, | ||
573 | AU1000_PCMCIA_MEM_PHYS_ADDR + 0x008400000 - 1, | ||
574 | AU1000_PCMCIA_IO_PHYS_ADDR + 0x008000000, | ||
575 | AU1000_PCMCIA_IO_PHYS_ADDR + 0x008010000 - 1, | ||
576 | AU1550_GPIO201_205_INT, AU1550_GPIO1_INT, 0, 0, 1); | ||
577 | |||
578 | pb1550_nand_setup(); | ||
579 | } | ||
580 | |||
581 | int __init db1550_dev_setup(void) | ||
582 | { | ||
583 | int swapped, id; | ||
584 | struct clk *c; | ||
585 | |||
586 | id = (BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI)) != BCSR_WHOAMI_DB1550); | ||
587 | |||
588 | i2c_register_board_info(0, db1550_i2c_devs, | ||
589 | ARRAY_SIZE(db1550_i2c_devs)); | ||
590 | spi_register_board_info(db1550_spi_devs, | ||
591 | ARRAY_SIZE(db1550_i2c_devs)); | ||
592 | |||
593 | c = clk_get(NULL, "psc0_intclk"); | ||
594 | if (!IS_ERR(c)) { | ||
595 | clk_set_rate(c, 50000000); | ||
596 | clk_prepare_enable(c); | ||
597 | clk_put(c); | ||
598 | } | ||
599 | c = clk_get(NULL, "psc2_intclk"); | ||
600 | if (!IS_ERR(c)) { | ||
601 | clk_set_rate(c, db1550_spi_platdata.mainclk_hz); | ||
602 | clk_prepare_enable(c); | ||
603 | clk_put(c); | ||
604 | } | ||
605 | |||
606 | /* Audio PSC clock is supplied by codecs (PSC1, 3) FIXME: platdata!! */ | ||
607 | __raw_writel(PSC_SEL_CLK_SERCLK, | ||
608 | (void __iomem *)KSEG1ADDR(AU1550_PSC1_PHYS_ADDR) + PSC_SEL_OFFSET); | ||
609 | wmb(); | ||
610 | __raw_writel(PSC_SEL_CLK_SERCLK, | ||
611 | (void __iomem *)KSEG1ADDR(AU1550_PSC3_PHYS_ADDR) + PSC_SEL_OFFSET); | ||
612 | wmb(); | ||
613 | /* SPI/I2C use internally supplied 50MHz source */ | ||
614 | __raw_writel(PSC_SEL_CLK_INTCLK, | ||
615 | (void __iomem *)KSEG1ADDR(AU1550_PSC0_PHYS_ADDR) + PSC_SEL_OFFSET); | ||
616 | wmb(); | ||
617 | __raw_writel(PSC_SEL_CLK_INTCLK, | ||
618 | (void __iomem *)KSEG1ADDR(AU1550_PSC2_PHYS_ADDR) + PSC_SEL_OFFSET); | ||
619 | wmb(); | ||
620 | |||
621 | id ? pb1550_devices() : db1550_devices(); | ||
622 | |||
623 | swapped = bcsr_read(BCSR_STATUS) & | ||
624 | (id ? BCSR_STATUS_PB1550_SWAPBOOT : BCSR_STATUS_DB1000_SWAPBOOT); | ||
625 | db1x_register_norflash(128 << 20, 4, swapped); | ||
626 | |||
627 | return platform_add_devices(db1550_devs, ARRAY_SIZE(db1550_devs)); | ||
628 | } | ||
diff --git a/arch/mips/alchemy/devboards/db1xxx.c b/arch/mips/alchemy/devboards/db1xxx.c new file mode 100644 index 000000000..e6d25aad8 --- /dev/null +++ b/arch/mips/alchemy/devboards/db1xxx.c | |||
@@ -0,0 +1,124 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Alchemy DB/PB1xxx board support. | ||
4 | */ | ||
5 | |||
6 | #include <asm/prom.h> | ||
7 | #include <asm/mach-au1x00/au1000.h> | ||
8 | #include <asm/mach-db1x00/bcsr.h> | ||
9 | |||
10 | int __init db1000_board_setup(void); | ||
11 | int __init db1000_dev_setup(void); | ||
12 | int __init db1500_pci_setup(void); | ||
13 | int __init db1200_board_setup(void); | ||
14 | int __init db1200_dev_setup(void); | ||
15 | int __init db1300_board_setup(void); | ||
16 | int __init db1300_dev_setup(void); | ||
17 | int __init db1550_board_setup(void); | ||
18 | int __init db1550_dev_setup(void); | ||
19 | int __init db1550_pci_setup(int); | ||
20 | |||
21 | static const char *board_type_str(void) | ||
22 | { | ||
23 | switch (BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI))) { | ||
24 | case BCSR_WHOAMI_DB1000: | ||
25 | return "DB1000"; | ||
26 | case BCSR_WHOAMI_DB1500: | ||
27 | return "DB1500"; | ||
28 | case BCSR_WHOAMI_DB1100: | ||
29 | return "DB1100"; | ||
30 | case BCSR_WHOAMI_PB1500: | ||
31 | case BCSR_WHOAMI_PB1500R2: | ||
32 | return "PB1500"; | ||
33 | case BCSR_WHOAMI_PB1100: | ||
34 | return "PB1100"; | ||
35 | case BCSR_WHOAMI_PB1200_DDR1: | ||
36 | case BCSR_WHOAMI_PB1200_DDR2: | ||
37 | return "PB1200"; | ||
38 | case BCSR_WHOAMI_DB1200: | ||
39 | return "DB1200"; | ||
40 | case BCSR_WHOAMI_DB1300: | ||
41 | return "DB1300"; | ||
42 | case BCSR_WHOAMI_DB1550: | ||
43 | return "DB1550"; | ||
44 | case BCSR_WHOAMI_PB1550_SDR: | ||
45 | case BCSR_WHOAMI_PB1550_DDR: | ||
46 | return "PB1550"; | ||
47 | default: | ||
48 | return "(unknown)"; | ||
49 | } | ||
50 | } | ||
51 | |||
52 | const char *get_system_type(void) | ||
53 | { | ||
54 | return board_type_str(); | ||
55 | } | ||
56 | |||
57 | void __init board_setup(void) | ||
58 | { | ||
59 | int ret; | ||
60 | |||
61 | switch (alchemy_get_cputype()) { | ||
62 | case ALCHEMY_CPU_AU1000: | ||
63 | case ALCHEMY_CPU_AU1500: | ||
64 | case ALCHEMY_CPU_AU1100: | ||
65 | ret = db1000_board_setup(); | ||
66 | break; | ||
67 | case ALCHEMY_CPU_AU1550: | ||
68 | ret = db1550_board_setup(); | ||
69 | break; | ||
70 | case ALCHEMY_CPU_AU1200: | ||
71 | ret = db1200_board_setup(); | ||
72 | break; | ||
73 | case ALCHEMY_CPU_AU1300: | ||
74 | ret = db1300_board_setup(); | ||
75 | break; | ||
76 | default: | ||
77 | pr_err("unsupported CPU on board\n"); | ||
78 | ret = -ENODEV; | ||
79 | } | ||
80 | if (ret) | ||
81 | panic("cannot initialize board support"); | ||
82 | } | ||
83 | |||
84 | static int __init db1xxx_arch_init(void) | ||
85 | { | ||
86 | int id = BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI)); | ||
87 | if (id == BCSR_WHOAMI_DB1550) | ||
88 | return db1550_pci_setup(0); | ||
89 | else if ((id == BCSR_WHOAMI_PB1550_SDR) || | ||
90 | (id == BCSR_WHOAMI_PB1550_DDR)) | ||
91 | return db1550_pci_setup(1); | ||
92 | else if ((id == BCSR_WHOAMI_DB1500) || (id == BCSR_WHOAMI_PB1500) || | ||
93 | (id == BCSR_WHOAMI_PB1500R2)) | ||
94 | return db1500_pci_setup(); | ||
95 | |||
96 | return 0; | ||
97 | } | ||
98 | arch_initcall(db1xxx_arch_init); | ||
99 | |||
100 | static int __init db1xxx_dev_init(void) | ||
101 | { | ||
102 | mips_set_machine_name(board_type_str()); | ||
103 | switch (BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI))) { | ||
104 | case BCSR_WHOAMI_DB1000: | ||
105 | case BCSR_WHOAMI_DB1500: | ||
106 | case BCSR_WHOAMI_DB1100: | ||
107 | case BCSR_WHOAMI_PB1500: | ||
108 | case BCSR_WHOAMI_PB1500R2: | ||
109 | case BCSR_WHOAMI_PB1100: | ||
110 | return db1000_dev_setup(); | ||
111 | case BCSR_WHOAMI_PB1200_DDR1: | ||
112 | case BCSR_WHOAMI_PB1200_DDR2: | ||
113 | case BCSR_WHOAMI_DB1200: | ||
114 | return db1200_dev_setup(); | ||
115 | case BCSR_WHOAMI_DB1300: | ||
116 | return db1300_dev_setup(); | ||
117 | case BCSR_WHOAMI_DB1550: | ||
118 | case BCSR_WHOAMI_PB1550_SDR: | ||
119 | case BCSR_WHOAMI_PB1550_DDR: | ||
120 | return db1550_dev_setup(); | ||
121 | } | ||
122 | return 0; | ||
123 | } | ||
124 | device_initcall(db1xxx_dev_init); | ||
diff --git a/arch/mips/alchemy/devboards/platform.c b/arch/mips/alchemy/devboards/platform.c new file mode 100644 index 000000000..754bdd2ca --- /dev/null +++ b/arch/mips/alchemy/devboards/platform.c | |||
@@ -0,0 +1,248 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * devoard misc stuff. | ||
4 | */ | ||
5 | |||
6 | #include <linux/init.h> | ||
7 | #include <linux/mtd/mtd.h> | ||
8 | #include <linux/mtd/map.h> | ||
9 | #include <linux/mtd/physmap.h> | ||
10 | #include <linux/slab.h> | ||
11 | #include <linux/platform_device.h> | ||
12 | #include <linux/pm.h> | ||
13 | |||
14 | #include <asm/bootinfo.h> | ||
15 | #include <asm/idle.h> | ||
16 | #include <asm/reboot.h> | ||
17 | #include <asm/setup.h> | ||
18 | #include <asm/mach-au1x00/au1000.h> | ||
19 | #include <asm/mach-db1x00/bcsr.h> | ||
20 | |||
21 | #include <prom.h> | ||
22 | |||
23 | void prom_putchar(char c) | ||
24 | { | ||
25 | if (alchemy_get_cputype() == ALCHEMY_CPU_AU1300) | ||
26 | alchemy_uart_putchar(AU1300_UART2_PHYS_ADDR, c); | ||
27 | else | ||
28 | alchemy_uart_putchar(AU1000_UART0_PHYS_ADDR, c); | ||
29 | } | ||
30 | |||
31 | |||
32 | static struct platform_device db1x00_rtc_dev = { | ||
33 | .name = "rtc-au1xxx", | ||
34 | .id = -1, | ||
35 | }; | ||
36 | |||
37 | |||
38 | static void db1x_power_off(void) | ||
39 | { | ||
40 | bcsr_write(BCSR_RESETS, 0); | ||
41 | bcsr_write(BCSR_SYSTEM, BCSR_SYSTEM_PWROFF | BCSR_SYSTEM_RESET); | ||
42 | while (1) /* sit and spin */ | ||
43 | cpu_wait(); | ||
44 | } | ||
45 | |||
46 | static void db1x_reset(char *c) | ||
47 | { | ||
48 | bcsr_write(BCSR_RESETS, 0); | ||
49 | bcsr_write(BCSR_SYSTEM, 0); | ||
50 | } | ||
51 | |||
52 | static int __init db1x_late_setup(void) | ||
53 | { | ||
54 | if (!pm_power_off) | ||
55 | pm_power_off = db1x_power_off; | ||
56 | if (!_machine_halt) | ||
57 | _machine_halt = db1x_power_off; | ||
58 | if (!_machine_restart) | ||
59 | _machine_restart = db1x_reset; | ||
60 | |||
61 | platform_device_register(&db1x00_rtc_dev); | ||
62 | |||
63 | return 0; | ||
64 | } | ||
65 | device_initcall(db1x_late_setup); | ||
66 | |||
67 | /* register a pcmcia socket */ | ||
68 | int __init db1x_register_pcmcia_socket(phys_addr_t pcmcia_attr_start, | ||
69 | phys_addr_t pcmcia_attr_end, | ||
70 | phys_addr_t pcmcia_mem_start, | ||
71 | phys_addr_t pcmcia_mem_end, | ||
72 | phys_addr_t pcmcia_io_start, | ||
73 | phys_addr_t pcmcia_io_end, | ||
74 | int card_irq, | ||
75 | int cd_irq, | ||
76 | int stschg_irq, | ||
77 | int eject_irq, | ||
78 | int id) | ||
79 | { | ||
80 | int cnt, i, ret; | ||
81 | struct resource *sr; | ||
82 | struct platform_device *pd; | ||
83 | |||
84 | cnt = 5; | ||
85 | if (eject_irq) | ||
86 | cnt++; | ||
87 | if (stschg_irq) | ||
88 | cnt++; | ||
89 | |||
90 | sr = kcalloc(cnt, sizeof(struct resource), GFP_KERNEL); | ||
91 | if (!sr) | ||
92 | return -ENOMEM; | ||
93 | |||
94 | pd = platform_device_alloc("db1xxx_pcmcia", id); | ||
95 | if (!pd) { | ||
96 | ret = -ENOMEM; | ||
97 | goto out; | ||
98 | } | ||
99 | |||
100 | sr[0].name = "pcmcia-attr"; | ||
101 | sr[0].flags = IORESOURCE_MEM; | ||
102 | sr[0].start = pcmcia_attr_start; | ||
103 | sr[0].end = pcmcia_attr_end; | ||
104 | |||
105 | sr[1].name = "pcmcia-mem"; | ||
106 | sr[1].flags = IORESOURCE_MEM; | ||
107 | sr[1].start = pcmcia_mem_start; | ||
108 | sr[1].end = pcmcia_mem_end; | ||
109 | |||
110 | sr[2].name = "pcmcia-io"; | ||
111 | sr[2].flags = IORESOURCE_MEM; | ||
112 | sr[2].start = pcmcia_io_start; | ||
113 | sr[2].end = pcmcia_io_end; | ||
114 | |||
115 | sr[3].name = "insert"; | ||
116 | sr[3].flags = IORESOURCE_IRQ; | ||
117 | sr[3].start = sr[3].end = cd_irq; | ||
118 | |||
119 | sr[4].name = "card"; | ||
120 | sr[4].flags = IORESOURCE_IRQ; | ||
121 | sr[4].start = sr[4].end = card_irq; | ||
122 | |||
123 | i = 5; | ||
124 | if (stschg_irq) { | ||
125 | sr[i].name = "stschg"; | ||
126 | sr[i].flags = IORESOURCE_IRQ; | ||
127 | sr[i].start = sr[i].end = stschg_irq; | ||
128 | i++; | ||
129 | } | ||
130 | if (eject_irq) { | ||
131 | sr[i].name = "eject"; | ||
132 | sr[i].flags = IORESOURCE_IRQ; | ||
133 | sr[i].start = sr[i].end = eject_irq; | ||
134 | } | ||
135 | |||
136 | pd->resource = sr; | ||
137 | pd->num_resources = cnt; | ||
138 | |||
139 | ret = platform_device_add(pd); | ||
140 | if (!ret) | ||
141 | return 0; | ||
142 | |||
143 | platform_device_put(pd); | ||
144 | out: | ||
145 | kfree(sr); | ||
146 | return ret; | ||
147 | } | ||
148 | |||
149 | #define YAMON_SIZE 0x00100000 | ||
150 | #define YAMON_ENV_SIZE 0x00040000 | ||
151 | |||
152 | int __init db1x_register_norflash(unsigned long size, int width, | ||
153 | int swapped) | ||
154 | { | ||
155 | struct physmap_flash_data *pfd; | ||
156 | struct platform_device *pd; | ||
157 | struct mtd_partition *parts; | ||
158 | struct resource *res; | ||
159 | int ret, i; | ||
160 | |||
161 | if (size < (8 * 1024 * 1024)) | ||
162 | return -EINVAL; | ||
163 | |||
164 | ret = -ENOMEM; | ||
165 | parts = kcalloc(5, sizeof(struct mtd_partition), GFP_KERNEL); | ||
166 | if (!parts) | ||
167 | goto out; | ||
168 | |||
169 | res = kzalloc(sizeof(struct resource), GFP_KERNEL); | ||
170 | if (!res) | ||
171 | goto out1; | ||
172 | |||
173 | pfd = kzalloc(sizeof(struct physmap_flash_data), GFP_KERNEL); | ||
174 | if (!pfd) | ||
175 | goto out2; | ||
176 | |||
177 | pd = platform_device_alloc("physmap-flash", 0); | ||
178 | if (!pd) | ||
179 | goto out3; | ||
180 | |||
181 | /* NOR flash ends at 0x20000000, regardless of size */ | ||
182 | res->start = 0x20000000 - size; | ||
183 | res->end = 0x20000000 - 1; | ||
184 | res->flags = IORESOURCE_MEM; | ||
185 | |||
186 | /* partition setup. Most Develboards have a switch which allows | ||
187 | * to swap the physical locations of the 2 NOR flash banks. | ||
188 | */ | ||
189 | i = 0; | ||
190 | if (!swapped) { | ||
191 | /* first NOR chip */ | ||
192 | parts[i].offset = 0; | ||
193 | parts[i].name = "User FS"; | ||
194 | parts[i].size = size / 2; | ||
195 | i++; | ||
196 | } | ||
197 | |||
198 | parts[i].offset = MTDPART_OFS_APPEND; | ||
199 | parts[i].name = "User FS 2"; | ||
200 | parts[i].size = (size / 2) - (0x20000000 - 0x1fc00000); | ||
201 | i++; | ||
202 | |||
203 | parts[i].offset = MTDPART_OFS_APPEND; | ||
204 | parts[i].name = "YAMON"; | ||
205 | parts[i].size = YAMON_SIZE; | ||
206 | parts[i].mask_flags = MTD_WRITEABLE; | ||
207 | i++; | ||
208 | |||
209 | parts[i].offset = MTDPART_OFS_APPEND; | ||
210 | parts[i].name = "raw kernel"; | ||
211 | parts[i].size = 0x00400000 - YAMON_SIZE - YAMON_ENV_SIZE; | ||
212 | i++; | ||
213 | |||
214 | parts[i].offset = MTDPART_OFS_APPEND; | ||
215 | parts[i].name = "YAMON Env"; | ||
216 | parts[i].size = YAMON_ENV_SIZE; | ||
217 | parts[i].mask_flags = MTD_WRITEABLE; | ||
218 | i++; | ||
219 | |||
220 | if (swapped) { | ||
221 | parts[i].offset = MTDPART_OFS_APPEND; | ||
222 | parts[i].name = "User FS"; | ||
223 | parts[i].size = size / 2; | ||
224 | i++; | ||
225 | } | ||
226 | |||
227 | pfd->width = width; | ||
228 | pfd->parts = parts; | ||
229 | pfd->nr_parts = 5; | ||
230 | |||
231 | pd->dev.platform_data = pfd; | ||
232 | pd->resource = res; | ||
233 | pd->num_resources = 1; | ||
234 | |||
235 | ret = platform_device_add(pd); | ||
236 | if (!ret) | ||
237 | return ret; | ||
238 | |||
239 | platform_device_put(pd); | ||
240 | out3: | ||
241 | kfree(pfd); | ||
242 | out2: | ||
243 | kfree(res); | ||
244 | out1: | ||
245 | kfree(parts); | ||
246 | out: | ||
247 | return ret; | ||
248 | } | ||
diff --git a/arch/mips/alchemy/devboards/platform.h b/arch/mips/alchemy/devboards/platform.h new file mode 100644 index 000000000..23d98fc09 --- /dev/null +++ b/arch/mips/alchemy/devboards/platform.h | |||
@@ -0,0 +1,22 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | #ifndef _DEVBOARD_PLATFORM_H_ | ||
3 | #define _DEVBOARD_PLATFORM_H_ | ||
4 | |||
5 | #include <linux/init.h> | ||
6 | |||
7 | int __init db1x_register_pcmcia_socket(phys_addr_t pcmcia_attr_start, | ||
8 | phys_addr_t pcmcia_attr_len, | ||
9 | phys_addr_t pcmcia_mem_start, | ||
10 | phys_addr_t pcmcia_mem_end, | ||
11 | phys_addr_t pcmcia_io_start, | ||
12 | phys_addr_t pcmcia_io_end, | ||
13 | int card_irq, | ||
14 | int cd_irq, | ||
15 | int stschg_irq, | ||
16 | int eject_irq, | ||
17 | int id); | ||
18 | |||
19 | int __init db1x_register_norflash(unsigned long size, int width, | ||
20 | int swapped); | ||
21 | |||
22 | #endif | ||
diff --git a/arch/mips/alchemy/devboards/pm.c b/arch/mips/alchemy/devboards/pm.c new file mode 100644 index 000000000..73c778146 --- /dev/null +++ b/arch/mips/alchemy/devboards/pm.c | |||
@@ -0,0 +1,254 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Alchemy Development Board example suspend userspace interface. | ||
4 | * | ||
5 | * (c) 2008 Manuel Lauss <mano@roarinelk.homelinux.net> | ||
6 | */ | ||
7 | |||
8 | #include <linux/init.h> | ||
9 | #include <linux/kobject.h> | ||
10 | #include <linux/suspend.h> | ||
11 | #include <linux/sysfs.h> | ||
12 | #include <asm/mach-au1x00/au1000.h> | ||
13 | #include <asm/mach-au1x00/gpio-au1000.h> | ||
14 | #include <asm/mach-db1x00/bcsr.h> | ||
15 | |||
16 | /* | ||
17 | * Generic suspend userspace interface for Alchemy development boards. | ||
18 | * This code exports a few sysfs nodes under /sys/power/db1x/ which | ||
19 | * can be used by userspace to en/disable all au1x-provided wakeup | ||
20 | * sources and configure the timeout after which the the TOYMATCH2 irq | ||
21 | * is to trigger a wakeup. | ||
22 | */ | ||
23 | |||
24 | |||
25 | static unsigned long db1x_pm_sleep_secs; | ||
26 | static unsigned long db1x_pm_wakemsk; | ||
27 | static unsigned long db1x_pm_last_wakesrc; | ||
28 | |||
29 | static int db1x_pm_enter(suspend_state_t state) | ||
30 | { | ||
31 | unsigned short bcsrs[16]; | ||
32 | int i, j, hasint; | ||
33 | |||
34 | /* save CPLD regs */ | ||
35 | hasint = bcsr_read(BCSR_WHOAMI); | ||
36 | hasint = BCSR_WHOAMI_BOARD(hasint) >= BCSR_WHOAMI_DB1200; | ||
37 | j = (hasint) ? BCSR_MASKSET : BCSR_SYSTEM; | ||
38 | |||
39 | for (i = BCSR_STATUS; i <= j; i++) | ||
40 | bcsrs[i] = bcsr_read(i); | ||
41 | |||
42 | /* shut off hexleds */ | ||
43 | bcsr_write(BCSR_HEXCLEAR, 3); | ||
44 | |||
45 | /* enable GPIO based wakeup */ | ||
46 | alchemy_gpio1_input_enable(); | ||
47 | |||
48 | /* clear and setup wake cause and source */ | ||
49 | alchemy_wrsys(0, AU1000_SYS_WAKEMSK); | ||
50 | alchemy_wrsys(0, AU1000_SYS_WAKESRC); | ||
51 | |||
52 | alchemy_wrsys(db1x_pm_wakemsk, AU1000_SYS_WAKEMSK); | ||
53 | |||
54 | /* setup 1Hz-timer-based wakeup: wait for reg access */ | ||
55 | while (alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_M20) | ||
56 | asm volatile ("nop"); | ||
57 | |||
58 | alchemy_wrsys(alchemy_rdsys(AU1000_SYS_TOYREAD) + db1x_pm_sleep_secs, | ||
59 | AU1000_SYS_TOYMATCH2); | ||
60 | |||
61 | /* wait for value to really hit the register */ | ||
62 | while (alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_M20) | ||
63 | asm volatile ("nop"); | ||
64 | |||
65 | /* ...and now the sandman can come! */ | ||
66 | au_sleep(); | ||
67 | |||
68 | |||
69 | /* restore CPLD regs */ | ||
70 | for (i = BCSR_STATUS; i <= BCSR_SYSTEM; i++) | ||
71 | bcsr_write(i, bcsrs[i]); | ||
72 | |||
73 | /* restore CPLD int registers */ | ||
74 | if (hasint) { | ||
75 | bcsr_write(BCSR_INTCLR, 0xffff); | ||
76 | bcsr_write(BCSR_MASKCLR, 0xffff); | ||
77 | bcsr_write(BCSR_INTSTAT, 0xffff); | ||
78 | bcsr_write(BCSR_INTSET, bcsrs[BCSR_INTSET]); | ||
79 | bcsr_write(BCSR_MASKSET, bcsrs[BCSR_MASKSET]); | ||
80 | } | ||
81 | |||
82 | /* light up hexleds */ | ||
83 | bcsr_write(BCSR_HEXCLEAR, 0); | ||
84 | |||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | static int db1x_pm_begin(suspend_state_t state) | ||
89 | { | ||
90 | if (!db1x_pm_wakemsk) { | ||
91 | printk(KERN_ERR "db1x: no wakeup source activated!\n"); | ||
92 | return -EINVAL; | ||
93 | } | ||
94 | |||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | static void db1x_pm_end(void) | ||
99 | { | ||
100 | /* read and store wakeup source, the clear the register. To | ||
101 | * be able to clear it, WAKEMSK must be cleared first. | ||
102 | */ | ||
103 | db1x_pm_last_wakesrc = alchemy_rdsys(AU1000_SYS_WAKESRC); | ||
104 | |||
105 | alchemy_wrsys(0, AU1000_SYS_WAKEMSK); | ||
106 | alchemy_wrsys(0, AU1000_SYS_WAKESRC); | ||
107 | } | ||
108 | |||
109 | static const struct platform_suspend_ops db1x_pm_ops = { | ||
110 | .valid = suspend_valid_only_mem, | ||
111 | .begin = db1x_pm_begin, | ||
112 | .enter = db1x_pm_enter, | ||
113 | .end = db1x_pm_end, | ||
114 | }; | ||
115 | |||
116 | #define ATTRCMP(x) (0 == strcmp(attr->attr.name, #x)) | ||
117 | |||
118 | static ssize_t db1x_pmattr_show(struct kobject *kobj, | ||
119 | struct kobj_attribute *attr, | ||
120 | char *buf) | ||
121 | { | ||
122 | int idx; | ||
123 | |||
124 | if (ATTRCMP(timer_timeout)) | ||
125 | return sprintf(buf, "%lu\n", db1x_pm_sleep_secs); | ||
126 | |||
127 | else if (ATTRCMP(timer)) | ||
128 | return sprintf(buf, "%u\n", | ||
129 | !!(db1x_pm_wakemsk & SYS_WAKEMSK_M2)); | ||
130 | |||
131 | else if (ATTRCMP(wakesrc)) | ||
132 | return sprintf(buf, "%lu\n", db1x_pm_last_wakesrc); | ||
133 | |||
134 | else if (ATTRCMP(gpio0) || ATTRCMP(gpio1) || ATTRCMP(gpio2) || | ||
135 | ATTRCMP(gpio3) || ATTRCMP(gpio4) || ATTRCMP(gpio5) || | ||
136 | ATTRCMP(gpio6) || ATTRCMP(gpio7)) { | ||
137 | idx = (attr->attr.name)[4] - '0'; | ||
138 | return sprintf(buf, "%d\n", | ||
139 | !!(db1x_pm_wakemsk & SYS_WAKEMSK_GPIO(idx))); | ||
140 | |||
141 | } else if (ATTRCMP(wakemsk)) { | ||
142 | return sprintf(buf, "%08lx\n", db1x_pm_wakemsk); | ||
143 | } | ||
144 | |||
145 | return -ENOENT; | ||
146 | } | ||
147 | |||
148 | static ssize_t db1x_pmattr_store(struct kobject *kobj, | ||
149 | struct kobj_attribute *attr, | ||
150 | const char *instr, | ||
151 | size_t bytes) | ||
152 | { | ||
153 | unsigned long l; | ||
154 | int tmp; | ||
155 | |||
156 | if (ATTRCMP(timer_timeout)) { | ||
157 | tmp = kstrtoul(instr, 0, &l); | ||
158 | if (tmp) | ||
159 | return tmp; | ||
160 | |||
161 | db1x_pm_sleep_secs = l; | ||
162 | |||
163 | } else if (ATTRCMP(timer)) { | ||
164 | if (instr[0] != '0') | ||
165 | db1x_pm_wakemsk |= SYS_WAKEMSK_M2; | ||
166 | else | ||
167 | db1x_pm_wakemsk &= ~SYS_WAKEMSK_M2; | ||
168 | |||
169 | } else if (ATTRCMP(gpio0) || ATTRCMP(gpio1) || ATTRCMP(gpio2) || | ||
170 | ATTRCMP(gpio3) || ATTRCMP(gpio4) || ATTRCMP(gpio5) || | ||
171 | ATTRCMP(gpio6) || ATTRCMP(gpio7)) { | ||
172 | tmp = (attr->attr.name)[4] - '0'; | ||
173 | if (instr[0] != '0') { | ||
174 | db1x_pm_wakemsk |= SYS_WAKEMSK_GPIO(tmp); | ||
175 | } else { | ||
176 | db1x_pm_wakemsk &= ~SYS_WAKEMSK_GPIO(tmp); | ||
177 | } | ||
178 | |||
179 | } else if (ATTRCMP(wakemsk)) { | ||
180 | tmp = kstrtoul(instr, 0, &l); | ||
181 | if (tmp) | ||
182 | return tmp; | ||
183 | |||
184 | db1x_pm_wakemsk = l & 0x0000003f; | ||
185 | |||
186 | } else | ||
187 | bytes = -ENOENT; | ||
188 | |||
189 | return bytes; | ||
190 | } | ||
191 | |||
192 | #define ATTR(x) \ | ||
193 | static struct kobj_attribute x##_attribute = \ | ||
194 | __ATTR(x, 0664, db1x_pmattr_show, \ | ||
195 | db1x_pmattr_store); | ||
196 | |||
197 | ATTR(gpio0) /* GPIO-based wakeup enable */ | ||
198 | ATTR(gpio1) | ||
199 | ATTR(gpio2) | ||
200 | ATTR(gpio3) | ||
201 | ATTR(gpio4) | ||
202 | ATTR(gpio5) | ||
203 | ATTR(gpio6) | ||
204 | ATTR(gpio7) | ||
205 | ATTR(timer) /* TOYMATCH2-based wakeup enable */ | ||
206 | ATTR(timer_timeout) /* timer-based wakeup timeout value, in seconds */ | ||
207 | ATTR(wakesrc) /* contents of SYS_WAKESRC after last wakeup */ | ||
208 | ATTR(wakemsk) /* direct access to SYS_WAKEMSK */ | ||
209 | |||
210 | #define ATTR_LIST(x) & x ## _attribute.attr | ||
211 | static struct attribute *db1x_pmattrs[] = { | ||
212 | ATTR_LIST(gpio0), | ||
213 | ATTR_LIST(gpio1), | ||
214 | ATTR_LIST(gpio2), | ||
215 | ATTR_LIST(gpio3), | ||
216 | ATTR_LIST(gpio4), | ||
217 | ATTR_LIST(gpio5), | ||
218 | ATTR_LIST(gpio6), | ||
219 | ATTR_LIST(gpio7), | ||
220 | ATTR_LIST(timer), | ||
221 | ATTR_LIST(timer_timeout), | ||
222 | ATTR_LIST(wakesrc), | ||
223 | ATTR_LIST(wakemsk), | ||
224 | NULL, /* terminator */ | ||
225 | }; | ||
226 | |||
227 | static struct attribute_group db1x_pmattr_group = { | ||
228 | .name = "db1x", | ||
229 | .attrs = db1x_pmattrs, | ||
230 | }; | ||
231 | |||
232 | /* | ||
233 | * Initialize suspend interface | ||
234 | */ | ||
235 | static int __init pm_init(void) | ||
236 | { | ||
237 | /* init TOY to tick at 1Hz if not already done. No need to wait | ||
238 | * for confirmation since there's plenty of time from here to | ||
239 | * the next suspend cycle. | ||
240 | */ | ||
241 | if (alchemy_rdsys(AU1000_SYS_TOYTRIM) != 32767) | ||
242 | alchemy_wrsys(32767, AU1000_SYS_TOYTRIM); | ||
243 | |||
244 | db1x_pm_last_wakesrc = alchemy_rdsys(AU1000_SYS_WAKESRC); | ||
245 | |||
246 | alchemy_wrsys(0, AU1000_SYS_WAKESRC); | ||
247 | alchemy_wrsys(0, AU1000_SYS_WAKEMSK); | ||
248 | |||
249 | suspend_set_ops(&db1x_pm_ops); | ||
250 | |||
251 | return sysfs_create_group(power_kobj, &db1x_pmattr_group); | ||
252 | } | ||
253 | |||
254 | late_initcall(pm_init); | ||