1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/init.h> |
3 | #include <linux/types.h> |
4 | #include <linux/kernel.h> |
5 | #include <linux/mm.h> |
6 | #include <linux/tty.h> |
7 | #include <linux/rtc.h> |
8 | #include <linux/vt_kern.h> |
9 | #include <linux/interrupt.h> |
10 | |
11 | #include <asm/setup.h> |
12 | #include <asm/bootinfo.h> |
13 | #include <asm/bootinfo-apollo.h> |
14 | #include <asm/byteorder.h> |
15 | #include <asm/apollohw.h> |
16 | #include <asm/irq.h> |
17 | #include <asm/machdep.h> |
18 | #include <asm/config.h> |
19 | |
20 | #include "apollo.h" |
21 | |
22 | u_long sio01_physaddr; |
23 | u_long sio23_physaddr; |
24 | u_long rtc_physaddr; |
25 | u_long pica_physaddr; |
26 | u_long picb_physaddr; |
27 | u_long cpuctrl_physaddr; |
28 | u_long timer_physaddr; |
29 | u_long apollo_model; |
30 | |
31 | extern void dn_sched_init(void); |
32 | extern int dn_dummy_hwclk(int, struct rtc_time *); |
33 | static void dn_dummy_reset(void); |
34 | #ifdef CONFIG_HEARTBEAT |
35 | static void dn_heartbeat(int on); |
36 | #endif |
37 | static irqreturn_t dn_timer_int(int irq,void *); |
38 | static void dn_get_model(char *model); |
39 | static const char *apollo_models[] = { |
40 | [APOLLO_DN3000-APOLLO_DN3000] = "DN3000 (Otter)" , |
41 | [APOLLO_DN3010-APOLLO_DN3000] = "DN3010 (Otter)" , |
42 | [APOLLO_DN3500-APOLLO_DN3000] = "DN3500 (Cougar II)" , |
43 | [APOLLO_DN4000-APOLLO_DN3000] = "DN4000 (Mink)" , |
44 | [APOLLO_DN4500-APOLLO_DN3000] = "DN4500 (Roadrunner)" |
45 | }; |
46 | |
47 | int __init apollo_parse_bootinfo(const struct bi_record *record) |
48 | { |
49 | int unknown = 0; |
50 | const void *data = record->data; |
51 | |
52 | switch (be16_to_cpu(record->tag)) { |
53 | case BI_APOLLO_MODEL: |
54 | apollo_model = be32_to_cpup(p: data); |
55 | break; |
56 | |
57 | default: |
58 | unknown=1; |
59 | } |
60 | |
61 | return unknown; |
62 | } |
63 | |
64 | static void __init dn_setup_model(void) |
65 | { |
66 | pr_info("Apollo hardware found: [%s]\n" , |
67 | apollo_models[apollo_model - APOLLO_DN3000]); |
68 | |
69 | switch(apollo_model) { |
70 | case APOLLO_UNKNOWN: |
71 | panic(fmt: "Unknown apollo model" ); |
72 | break; |
73 | case APOLLO_DN3000: |
74 | case APOLLO_DN3010: |
75 | sio01_physaddr=SAU8_SIO01_PHYSADDR; |
76 | rtc_physaddr=SAU8_RTC_PHYSADDR; |
77 | pica_physaddr=SAU8_PICA; |
78 | picb_physaddr=SAU8_PICB; |
79 | cpuctrl_physaddr=SAU8_CPUCTRL; |
80 | timer_physaddr=SAU8_TIMER; |
81 | break; |
82 | case APOLLO_DN4000: |
83 | sio01_physaddr=SAU7_SIO01_PHYSADDR; |
84 | sio23_physaddr=SAU7_SIO23_PHYSADDR; |
85 | rtc_physaddr=SAU7_RTC_PHYSADDR; |
86 | pica_physaddr=SAU7_PICA; |
87 | picb_physaddr=SAU7_PICB; |
88 | cpuctrl_physaddr=SAU7_CPUCTRL; |
89 | timer_physaddr=SAU7_TIMER; |
90 | break; |
91 | case APOLLO_DN4500: |
92 | panic(fmt: "Apollo model not yet supported" ); |
93 | break; |
94 | case APOLLO_DN3500: |
95 | sio01_physaddr=SAU7_SIO01_PHYSADDR; |
96 | sio23_physaddr=SAU7_SIO23_PHYSADDR; |
97 | rtc_physaddr=SAU7_RTC_PHYSADDR; |
98 | pica_physaddr=SAU7_PICA; |
99 | picb_physaddr=SAU7_PICB; |
100 | cpuctrl_physaddr=SAU7_CPUCTRL; |
101 | timer_physaddr=SAU7_TIMER; |
102 | break; |
103 | default: |
104 | panic(fmt: "Undefined apollo model" ); |
105 | break; |
106 | } |
107 | |
108 | |
109 | } |
110 | |
111 | static void dn_serial_print(const char *str) |
112 | { |
113 | while (*str) { |
114 | if (*str == '\n') { |
115 | sio01.rhrb_thrb = (unsigned char)'\r'; |
116 | while (!(sio01.srb_csrb & 0x4)) |
117 | ; |
118 | } |
119 | sio01.rhrb_thrb = (unsigned char)*str++; |
120 | while (!(sio01.srb_csrb & 0x4)) |
121 | ; |
122 | } |
123 | } |
124 | |
125 | void __init config_apollo(void) |
126 | { |
127 | int i; |
128 | |
129 | dn_setup_model(); |
130 | |
131 | mach_sched_init=dn_sched_init; /* */ |
132 | mach_init_IRQ=dn_init_IRQ; |
133 | mach_hwclk = dn_dummy_hwclk; /* */ |
134 | mach_reset = dn_dummy_reset; /* */ |
135 | #ifdef CONFIG_HEARTBEAT |
136 | mach_heartbeat = dn_heartbeat; |
137 | #endif |
138 | mach_get_model = dn_get_model; |
139 | |
140 | cpuctrl=0xaa00; |
141 | |
142 | /* clear DMA translation table */ |
143 | for(i=0;i<0x400;i++) |
144 | addr_xlat_map[i]=0; |
145 | |
146 | } |
147 | |
148 | irqreturn_t dn_timer_int(int irq, void *dev_id) |
149 | { |
150 | unsigned char *at = (unsigned char *)apollo_timer; |
151 | |
152 | legacy_timer_tick(ticks: 1); |
153 | timer_heartbeat(); |
154 | |
155 | READ_ONCE(*(at + 3)); |
156 | READ_ONCE(*(at + 5)); |
157 | |
158 | return IRQ_HANDLED; |
159 | } |
160 | |
161 | void dn_sched_init(void) |
162 | { |
163 | /* program timer 1 */ |
164 | *(volatile unsigned char *)(apollo_timer + 3) = 0x01; |
165 | *(volatile unsigned char *)(apollo_timer + 1) = 0x40; |
166 | *(volatile unsigned char *)(apollo_timer + 5) = 0x09; |
167 | *(volatile unsigned char *)(apollo_timer + 7) = 0xc4; |
168 | |
169 | /* enable IRQ of PIC B */ |
170 | *(volatile unsigned char *)(pica+1)&=(~8); |
171 | |
172 | #if 0 |
173 | pr_info("*(0x10803) %02x\n" , |
174 | *(volatile unsigned char *)(apollo_timer + 0x3)); |
175 | pr_info("*(0x10803) %02x\n" , |
176 | *(volatile unsigned char *)(apollo_timer + 0x3)); |
177 | #endif |
178 | |
179 | if (request_irq(IRQ_APOLLO, dn_timer_int, 0, "time" , NULL)) |
180 | pr_err("Couldn't register timer interrupt\n" ); |
181 | } |
182 | |
183 | int dn_dummy_hwclk(int op, struct rtc_time *t) { |
184 | |
185 | |
186 | if(!op) { /* read */ |
187 | t->tm_sec=rtc->second; |
188 | t->tm_min=rtc->minute; |
189 | t->tm_hour=rtc->hours; |
190 | t->tm_mday=rtc->day_of_month; |
191 | t->tm_wday=rtc->day_of_week; |
192 | t->tm_mon = rtc->month - 1; |
193 | t->tm_year=rtc->year; |
194 | if (t->tm_year < 70) |
195 | t->tm_year += 100; |
196 | } else { |
197 | rtc->second=t->tm_sec; |
198 | rtc->minute=t->tm_min; |
199 | rtc->hours=t->tm_hour; |
200 | rtc->day_of_month=t->tm_mday; |
201 | if(t->tm_wday!=-1) |
202 | rtc->day_of_week=t->tm_wday; |
203 | rtc->month = t->tm_mon + 1; |
204 | rtc->year = t->tm_year % 100; |
205 | } |
206 | |
207 | return 0; |
208 | |
209 | } |
210 | |
211 | static void dn_dummy_reset(void) |
212 | { |
213 | dn_serial_print(str: "The end !\n" ); |
214 | |
215 | for(;;); |
216 | |
217 | } |
218 | |
219 | static void dn_get_model(char *model) |
220 | { |
221 | strcpy(p: model, q: "Apollo " ); |
222 | if (apollo_model >= APOLLO_DN3000 && apollo_model <= APOLLO_DN4500) |
223 | strcat(model, apollo_models[apollo_model - APOLLO_DN3000]); |
224 | } |
225 | |
226 | #ifdef CONFIG_HEARTBEAT |
227 | static int dn_cpuctrl=0xff00; |
228 | |
229 | static void dn_heartbeat(int on) { |
230 | |
231 | if(on) { |
232 | dn_cpuctrl&=~0x100; |
233 | cpuctrl=dn_cpuctrl; |
234 | } |
235 | else { |
236 | dn_cpuctrl&=~0x100; |
237 | dn_cpuctrl|=0x100; |
238 | cpuctrl=dn_cpuctrl; |
239 | } |
240 | } |
241 | #endif |
242 | |
243 | |