]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/arm/s3c2xx0/s3c24x0.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / arm / s3c2xx0 / s3c24x0.c
1 /*      $NetBSD: s3c2410.c,v 1.4 2003/08/27 03:46:05 bsh Exp $ */
2
3 /*
4  * Copyright (c) 2003  Genetec corporation.  All rights reserved.
5  * Written by Hiroyuki Bessho for Genetec corporation.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The name of Genetec corporation may not be used to endorse
16  *    or promote products derived from this software without specific prior
17  *    written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY GENETEC CORP. ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL GENETEC CORP.
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/reboot.h>
39 #include <sys/malloc.h>
40 #include <sys/module.h>
41 #include <sys/bus.h>
42
43 #include <vm/vm.h>
44 #include <vm/pmap.h>
45
46 #include <machine/cpu.h>
47 #include <machine/bus.h>
48
49 #include <machine/cpufunc.h>
50 #include <machine/intr.h>
51 #include <arm/s3c2xx0/s3c2410reg.h>
52 #include <arm/s3c2xx0/s3c2440reg.h>
53 #include <arm/s3c2xx0/s3c24x0var.h>
54 #include <sys/rman.h>
55
56 #define S3C2XX0_XTAL_CLK 12000000
57
58 #define IPL_LEVELS 13
59 u_int irqmasks[IPL_LEVELS];
60
61 static struct {
62         uint32_t        idcode;
63         const char      *name;
64         s3c2xx0_cpu     cpu;
65 } s3c2x0_cpu_id[] = {
66         { CHIPID_S3C2410A, "S3C2410A", CPU_S3C2410 },
67         { CHIPID_S3C2440A, "S3C2440A", CPU_S3C2440 },
68         { CHIPID_S3C2442B, "S3C2442B", CPU_S3C2440 },
69
70         { 0, NULL }
71 };
72
73 static struct {
74         const char *name;
75         int prio;
76         int unit;
77         struct {
78                 int type;
79                 u_long start;
80                 u_long count;
81         } res[2];
82 } s3c24x0_children[] = {
83         { "rtc", 0, -1, {
84                 { SYS_RES_IOPORT, S3C24X0_RTC_PA_BASE, S3C24X0_RTC_SIZE },
85                 { 0 },
86         } },
87         { "timer", 0, -1, { { 0 }, } },
88         { "uart", 1, 0, {
89                 { SYS_RES_IRQ, S3C24X0_INT_UART0, 1 },
90                 { SYS_RES_IOPORT, S3C24X0_UART_PA_BASE(0),
91                   S3C24X0_UART_BASE(1) - S3C24X0_UART_BASE(0) },
92         } },
93         { "uart", 1, 1, {
94                 { SYS_RES_IRQ, S3C24X0_INT_UART1, 1 },
95                 { SYS_RES_IOPORT, S3C24X0_UART_PA_BASE(1),
96                   S3C24X0_UART_BASE(2) - S3C24X0_UART_BASE(1) },
97         } },
98         { "uart", 1, 2, {
99                 { SYS_RES_IRQ, S3C24X0_INT_UART2, 1 },
100                 { SYS_RES_IOPORT, S3C24X0_UART_PA_BASE(2),
101                   S3C24X0_UART_BASE(3) - S3C24X0_UART_BASE(2) },
102         } },
103         { "ohci", 0, -1, {
104                 { SYS_RES_IRQ, S3C24X0_INT_USBH, 0 },
105                 { SYS_RES_IOPORT, S3C24X0_USBHC_PA_BASE, S3C24X0_USBHC_SIZE },
106         } },
107         { NULL },
108 };
109
110
111 /* prototypes */
112 static device_t s3c24x0_add_child(device_t, int, const char *, int);
113
114 static int      s3c24x0_probe(device_t);
115 static int      s3c24x0_attach(device_t);
116 static void     s3c24x0_identify(driver_t *, device_t);
117 static int      s3c24x0_setup_intr(device_t, device_t, struct resource *, int,
118         driver_filter_t *, driver_intr_t *, void *, void **);
119 static int      s3c24x0_teardown_intr(device_t, device_t, struct resource *,
120         void *);
121 static int      s3c24x0_config_intr(device_t, int, enum intr_trigger,
122         enum intr_polarity);
123 static struct resource *s3c24x0_alloc_resource(device_t, device_t, int, int *,
124         u_long, u_long, u_long, u_int);
125 static int s3c24x0_activate_resource(device_t, device_t, int, int,
126         struct resource *);
127 static int s3c24x0_release_resource(device_t, device_t, int, int,
128         struct resource *);
129 static struct resource_list *s3c24x0_get_resource_list(device_t, device_t);
130
131 static void s3c24x0_identify_cpu(device_t);
132
133 static device_method_t s3c24x0_methods[] = {
134         DEVMETHOD(device_probe, s3c24x0_probe),
135         DEVMETHOD(device_attach, s3c24x0_attach),
136         DEVMETHOD(device_identify, s3c24x0_identify),
137         DEVMETHOD(bus_setup_intr, s3c24x0_setup_intr),
138         DEVMETHOD(bus_teardown_intr, s3c24x0_teardown_intr),
139         DEVMETHOD(bus_config_intr, s3c24x0_config_intr),
140         DEVMETHOD(bus_alloc_resource, s3c24x0_alloc_resource),
141         DEVMETHOD(bus_activate_resource, s3c24x0_activate_resource),
142         DEVMETHOD(bus_release_resource, s3c24x0_release_resource),
143         DEVMETHOD(bus_get_resource_list,s3c24x0_get_resource_list),
144         DEVMETHOD(bus_set_resource,     bus_generic_rl_set_resource),
145         DEVMETHOD(bus_get_resource,     bus_generic_rl_get_resource),
146         {0, 0},
147 };
148
149 static driver_t s3c24x0_driver = {
150         "s3c24x0",
151         s3c24x0_methods,
152         sizeof(struct s3c24x0_softc),
153 };
154 static devclass_t s3c24x0_devclass;
155
156 DRIVER_MODULE(s3c24x0, nexus, s3c24x0_driver, s3c24x0_devclass, 0, 0);
157
158 struct s3c2xx0_softc *s3c2xx0_softc = NULL;
159
160 static device_t
161 s3c24x0_add_child(device_t bus, int prio, const char *name, int unit)
162 {
163         device_t child;
164         struct s3c2xx0_ivar *ivar;
165
166         child = device_add_child_ordered(bus, prio, name, unit);
167         if (child == NULL)
168                 return (NULL);
169
170         ivar = malloc(sizeof(*ivar), M_DEVBUF, M_NOWAIT | M_ZERO);
171         if (ivar == NULL) {
172                 device_delete_child(bus, child);
173                 printf("Can't add alloc ivar\n");
174                 return (NULL);
175         }
176         device_set_ivars(child, ivar);
177         resource_list_init(&ivar->resources);
178
179         return (child);
180 }
181
182 static void
183 s3c24x0_enable_ext_intr(unsigned int irq)
184 {
185         uint32_t reg, value;
186         int offset;
187
188         if (irq <= 7) {
189                 reg = GPIO_PFCON;
190                 offset = irq * 2;
191         } else if (irq <= 23) {
192                 reg = GPIO_PGCON;
193                 offset = (irq - 8) * 2;
194         } else
195                 return;
196
197         /* Make the pin an interrupt source */
198         value = bus_space_read_4(s3c2xx0_softc->sc_iot,
199             s3c2xx0_softc->sc_gpio_ioh, reg);
200         value &= ~(3 << offset);
201         value |= 2 << offset;
202         bus_space_write_4(s3c2xx0_softc->sc_iot, s3c2xx0_softc->sc_gpio_ioh,
203             reg, value);
204 }
205
206 static int
207 s3c24x0_setup_intr(device_t dev, device_t child,
208         struct resource *ires,  int flags, driver_filter_t *filt,
209         driver_intr_t *intr, void *arg, void **cookiep)
210 {
211         int error, irq;
212
213         error = BUS_SETUP_INTR(device_get_parent(dev), child, ires, flags, filt,
214             intr, arg, cookiep);
215         if (error != 0)
216                 return (error);
217
218         for (irq = rman_get_start(ires); irq <= rman_get_end(ires); irq++) {
219                 if (irq >= S3C24X0_EXTIRQ_MIN && irq <= S3C24X0_EXTIRQ_MAX) {
220                         /* Enable the external interrupt pin */
221                         s3c24x0_enable_ext_intr(irq - S3C24X0_EXTIRQ_MIN);
222                 }
223                 arm_unmask_irq(irq);
224         }
225         return (0);
226 }
227
228 static int
229 s3c24x0_teardown_intr(device_t dev, device_t child, struct resource *res,
230         void *cookie)
231 {
232         return (BUS_TEARDOWN_INTR(device_get_parent(dev), child, res, cookie));
233 }
234
235 static int
236 s3c24x0_config_intr(device_t dev, int irq, enum intr_trigger trig,
237         enum intr_polarity pol)
238 {
239         uint32_t mask, reg, value;
240         int offset;
241
242         /* Only external interrupts can be configured */
243         if (irq < S3C24X0_EXTIRQ_MIN || irq > S3C24X0_EXTIRQ_MAX)
244                 return (EINVAL);
245
246         /* There is no standard trigger or polarity for the bus */
247         if (trig == INTR_TRIGGER_CONFORM || pol == INTR_POLARITY_CONFORM)
248                 return (EINVAL);
249
250         irq -= S3C24X0_EXTIRQ_MIN;
251
252         /* Get the bits to set */
253         mask = 0;
254         if (pol == INTR_POLARITY_LOW) {
255                 mask = 2;
256         } else if (pol == INTR_POLARITY_HIGH) {
257                 mask = 4;
258         }
259         if (trig == INTR_TRIGGER_LEVEL) {
260                 mask >>= 2;
261         }
262
263         /* Get the register to set */
264         if (irq <= 7) {
265                 reg = GPIO_EXTINT(0);
266                 offset = irq * 4;
267         } else if (irq <= 15) {
268                 reg = GPIO_EXTINT(1);
269                 offset = (irq - 8) * 4;
270         } else if (irq <= 23) {
271                 reg = GPIO_EXTINT(2);
272                 offset = (irq - 16) * 4;
273         } else {
274                 return (EINVAL);
275         }
276
277         /* Set the new signaling method */
278         value = bus_space_read_4(s3c2xx0_softc->sc_iot,
279             s3c2xx0_softc->sc_gpio_ioh, reg);
280         value &= ~(7 << offset);
281         value |= mask << offset;
282         bus_space_write_4(s3c2xx0_softc->sc_iot,
283             s3c2xx0_softc->sc_gpio_ioh, reg, value);
284
285         return (0);
286
287
288 static struct resource *
289 s3c24x0_alloc_resource(device_t bus, device_t child, int type, int *rid,
290         u_long start, u_long end, u_long count, u_int flags)
291 {
292         struct resource_list_entry *rle;
293         struct s3c2xx0_ivar *ivar = device_get_ivars(child);
294         struct resource_list *rl = &ivar->resources;
295         struct resource *res = NULL;
296
297         if (device_get_parent(child) != bus)
298                 return (BUS_ALLOC_RESOURCE(device_get_parent(bus), child,
299                     type, rid, start, end, count, flags));
300
301         rle = resource_list_find(rl, type, *rid);
302         if (rle != NULL) {
303                 /* There is a resource list. Use it */
304                 if (rle->res)
305                         panic("Resource rid %d type %d already in use", *rid,
306                             type);
307                 if (start == 0UL && end == ~0UL) {
308                         start = rle->start;
309                         count = ulmax(count, rle->count);
310                         end = ulmax(rle->end, start + count - 1);
311                 }
312                 /*
313                  * When allocating an irq with children irq's really
314                  * allocate the children as it is those we are interested
315                  * in receiving, not the parent.
316                  */
317                 if (type == SYS_RES_IRQ && start == end) {
318                         switch (start) {
319                         case S3C24X0_INT_ADCTC:
320                                 start = S3C24X0_INT_TC;
321                                 end = S3C24X0_INT_ADC;
322                                 break;
323 #ifdef S3C2440_INT_CAM
324                         case S3C2440_INT_CAM:
325                                 start = S3C2440_INT_CAM_C;
326                                 end = S3C2440_INT_CAM_P;
327                                 break;
328 #endif
329                         default:
330                                 break;
331                         }
332                         count = end - start + 1;
333                 }
334         }
335
336         switch (type) {
337         case SYS_RES_IRQ:
338                 res = rman_reserve_resource(
339                     &s3c2xx0_softc->s3c2xx0_irq_rman, start, end,
340                     count, flags, child);
341                 break;
342
343         case SYS_RES_IOPORT:
344         case SYS_RES_MEMORY:
345                 res = rman_reserve_resource(
346                     &s3c2xx0_softc->s3c2xx0_mem_rman,
347                     start, end, count, flags, child);
348                 if (res == NULL)
349                         panic("Unable to map address space %#lX-%#lX", start,
350                             end);
351
352                 rman_set_bustag(res, &s3c2xx0_bs_tag);
353                 rman_set_bushandle(res, start);
354                 if (flags & RF_ACTIVE) {
355                         if (bus_activate_resource(child, type, *rid, res)) {
356                                 rman_release_resource(res);
357                                 return (NULL);
358                         }
359                 } 
360                 break;
361         }
362
363         if (res != NULL) {
364                 rman_set_rid(res, *rid);
365                 if (rle != NULL) {
366                         rle->res = res;
367                         rle->start = rman_get_start(res);
368                         rle->end = rman_get_end(res);
369                         rle->count = count;
370                 }
371         }
372
373         return (res);
374 }
375
376 static int
377 s3c24x0_activate_resource(device_t bus, device_t child, int type, int rid,
378         struct resource *r)
379 {
380         bus_space_handle_t p;
381         int error;
382
383         if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) {
384                 error = bus_space_map(rman_get_bustag(r),
385                     rman_get_bushandle(r), rman_get_size(r), 0, &p);
386                 if (error)
387                         return (error);
388                 rman_set_bushandle(r, p);
389         }
390         return (rman_activate_resource(r));
391 }
392
393 static int
394 s3c24x0_release_resource(device_t bus, device_t child, int type, int rid,
395         struct resource *r)
396 {
397         struct s3c2xx0_ivar *ivar = device_get_ivars(child);
398         struct resource_list *rl = &ivar->resources;
399         struct resource_list_entry *rle;
400
401         if (rl == NULL)
402                 return (EINVAL);
403
404         rle = resource_list_find(rl, type, rid);
405         if (rle == NULL)
406                 return (EINVAL);
407
408         rman_release_resource(r);
409         rle->res = NULL;
410
411         return 0;
412 }
413
414 static struct resource_list *
415 s3c24x0_get_resource_list(device_t dev, device_t child)
416 {
417         struct s3c2xx0_ivar *ivar;
418
419         ivar = device_get_ivars(child);
420         return (&(ivar->resources));
421 }
422
423 void
424 s3c24x0_identify(driver_t *driver, device_t parent)
425 {
426         
427         BUS_ADD_CHILD(parent, 0, "s3c24x0", 0);
428 }
429
430 int
431 s3c24x0_probe(device_t dev)
432 {
433         return 0;
434 }
435
436 int
437 s3c24x0_attach(device_t dev)
438 {
439         struct s3c24x0_softc *sc = device_get_softc(dev);
440         bus_space_tag_t iot;
441         device_t child;
442         unsigned int i, j;
443         u_long irqmax;
444
445         s3c2xx0_softc = &(sc->sc_sx);
446         sc->sc_sx.sc_iot = iot = &s3c2xx0_bs_tag;
447         s3c2xx0_softc->s3c2xx0_irq_rman.rm_type = RMAN_ARRAY;
448         s3c2xx0_softc->s3c2xx0_irq_rman.rm_descr = "S3C24X0 IRQs";
449         s3c2xx0_softc->s3c2xx0_mem_rman.rm_type = RMAN_ARRAY;
450         s3c2xx0_softc->s3c2xx0_mem_rman.rm_descr = "S3C24X0 Device Registers";
451         /* Manage the registor memory space */
452         if ((rman_init(&s3c2xx0_softc->s3c2xx0_mem_rman) != 0) ||
453             (rman_manage_region(&s3c2xx0_softc->s3c2xx0_mem_rman,
454               S3C24X0_DEV_VA_OFFSET,
455               S3C24X0_DEV_VA_OFFSET + S3C24X0_DEV_VA_SIZE) != 0) ||
456             (rman_manage_region(&s3c2xx0_softc->s3c2xx0_mem_rman,
457               S3C24X0_DEV_START, S3C24X0_DEV_STOP) != 0))
458                 panic("s3c24x0_attach: failed to set up register rman");
459
460         /* These are needed for things without a proper device to attach to */
461         sc->sc_sx.sc_intctl_ioh = S3C24X0_INTCTL_BASE;
462         sc->sc_sx.sc_gpio_ioh = S3C24X0_GPIO_BASE;
463         sc->sc_sx.sc_clkman_ioh = S3C24X0_CLKMAN_BASE;
464         sc->sc_sx.sc_wdt_ioh = S3C24X0_WDT_BASE;
465         sc->sc_timer_ioh = S3C24X0_TIMER_BASE;
466
467         /*
468          * Identify the CPU
469          */
470         s3c24x0_identify_cpu(dev);
471
472         /*
473          * Manage the interrupt space.
474          * We need to put this after s3c24x0_identify_cpu as the avaliable
475          * interrupts change depending on which CPU we have.
476          */
477         if (sc->sc_sx.sc_cpu == CPU_S3C2410)
478                 irqmax = S3C2410_SUBIRQ_MAX;
479         else
480                 irqmax = S3C2440_SUBIRQ_MAX;
481         if (rman_init(&s3c2xx0_softc->s3c2xx0_irq_rman) != 0 ||
482             rman_manage_region(&s3c2xx0_softc->s3c2xx0_irq_rman, 0,
483             irqmax) != 0 ||
484             rman_manage_region(&s3c2xx0_softc->s3c2xx0_irq_rman,
485             S3C24X0_EXTIRQ_MIN, S3C24X0_EXTIRQ_MAX))
486                 panic("s3c24x0_attach: failed to set up IRQ rman");
487
488         /* calculate current clock frequency */
489         s3c24x0_clock_freq(&sc->sc_sx);
490         device_printf(dev, "fclk %d MHz hclk %d MHz pclk %d MHz\n",
491                sc->sc_sx.sc_fclk / 1000000, sc->sc_sx.sc_hclk / 1000000,
492                sc->sc_sx.sc_pclk / 1000000);
493
494         /*
495          * Attach children devices
496          */
497
498         for (i = 0; s3c24x0_children[i].name != NULL; i++) {
499                 child = s3c24x0_add_child(dev, s3c24x0_children[i].prio,
500                     s3c24x0_children[i].name, s3c24x0_children[i].unit);
501                 for (j = 0; j < sizeof(s3c24x0_children[i].res) /
502                      sizeof(s3c24x0_children[i].res[0]) &&
503                      s3c24x0_children[i].res[j].type != 0; j++) {
504                         bus_set_resource(child,
505                             s3c24x0_children[i].res[j].type, 0,
506                             s3c24x0_children[i].res[j].start,
507                             s3c24x0_children[i].res[j].count);
508                 }
509         }
510
511         bus_generic_probe(dev);
512         bus_generic_attach(dev);
513
514         return (0);
515 }
516
517 static void
518 s3c24x0_identify_cpu(device_t dev)
519 {
520         struct s3c24x0_softc *sc = device_get_softc(dev);
521         uint32_t idcode;
522         int i;
523
524         idcode = bus_space_read_4(sc->sc_sx.sc_iot, sc->sc_sx.sc_gpio_ioh,
525             GPIO_GSTATUS1);
526
527         for (i = 0; s3c2x0_cpu_id[i].name != NULL; i++) {
528                 if (s3c2x0_cpu_id[i].idcode == idcode)
529                         break;
530         }
531         if (s3c2x0_cpu_id[i].name == NULL)
532                 panic("Unknown CPU detected ((Chip ID: %#X)", idcode);
533         device_printf(dev, "Found %s CPU (Chip ID: %#X)\n",
534             s3c2x0_cpu_id[i].name, idcode);
535         sc->sc_sx.sc_cpu = s3c2x0_cpu_id[i].cpu;
536 }
537
538 /*
539  * fill sc_pclk, sc_hclk, sc_fclk from values of clock controller register.
540  *
541  * s3c24{1,4}0_clock_freq2() is meant to be called from kernel startup routines.
542  * s3c24x0_clock_freq() is for after kernel initialization is done.
543  *
544  * Because they can be called before bus_space is available we need to use
545  * volatile pointers rather than bus_space_read.
546  */
547 void
548 s3c2410_clock_freq2(vm_offset_t clkman_base, int *fclk, int *hclk, int *pclk)
549 {
550         uint32_t pllcon, divn;
551         unsigned int mdiv, pdiv, sdiv;
552         unsigned int f, h, p;
553
554         pllcon = *(volatile uint32_t *)(clkman_base + CLKMAN_MPLLCON);
555         divn = *(volatile uint32_t *)(clkman_base + CLKMAN_CLKDIVN);
556
557         mdiv = (pllcon & PLLCON_MDIV_MASK) >> PLLCON_MDIV_SHIFT;
558         pdiv = (pllcon & PLLCON_PDIV_MASK) >> PLLCON_PDIV_SHIFT;
559         sdiv = (pllcon & PLLCON_SDIV_MASK) >> PLLCON_SDIV_SHIFT;
560
561         f = ((mdiv + 8) * S3C2XX0_XTAL_CLK) / ((pdiv + 2) * (1 << sdiv));
562         h = f;
563         if (divn & S3C2410_CLKDIVN_HDIVN)
564                 h /= 2;
565         p = h;
566         if (divn & CLKDIVN_PDIVN)
567                 p /= 2;
568
569         if (fclk) *fclk = f;
570         if (hclk) *hclk = h;
571         if (pclk) *pclk = p;
572 }
573
574 void
575 s3c2440_clock_freq2(vm_offset_t clkman_base, int *fclk, int *hclk, int *pclk)
576 {
577         uint32_t pllcon, divn, camdivn;
578         unsigned int mdiv, pdiv, sdiv;
579         unsigned int f, h, p;
580
581         pllcon = *(volatile uint32_t *)(clkman_base + CLKMAN_MPLLCON);
582         divn = *(volatile uint32_t *)(clkman_base + CLKMAN_CLKDIVN);
583         camdivn = *(volatile uint32_t *)(clkman_base + S3C2440_CLKMAN_CAMDIVN);
584
585         mdiv = (pllcon & PLLCON_MDIV_MASK) >> PLLCON_MDIV_SHIFT;
586         pdiv = (pllcon & PLLCON_PDIV_MASK) >> PLLCON_PDIV_SHIFT;
587         sdiv = (pllcon & PLLCON_SDIV_MASK) >> PLLCON_SDIV_SHIFT;
588
589         f = (2 * (mdiv + 8) * S3C2XX0_XTAL_CLK) / ((pdiv + 2) * (1 << sdiv));
590         h = f;
591         switch((divn >> 1) & 3) {
592         case 0:
593                 break;
594         case 1:
595                 h /= 2;
596                 break;
597         case 2:
598                 if ((camdivn & S3C2440_CAMDIVN_HCLK4_HALF) ==
599                     S3C2440_CAMDIVN_HCLK4_HALF)
600                         h /= 8;
601                 else
602                         h /= 4;
603                 break;
604         case 3:
605                 if ((camdivn & S3C2440_CAMDIVN_HCLK3_HALF) ==
606                     S3C2440_CAMDIVN_HCLK3_HALF)
607                         h /= 6;
608                 else
609                         h /= 3;
610                 break;
611         }
612         p = h;
613         if (divn & CLKDIVN_PDIVN)
614                 p /= 2;
615
616         if (fclk) *fclk = f;
617         if (hclk) *hclk = h;
618         if (pclk) *pclk = p;
619 }
620
621 void
622 s3c24x0_clock_freq(struct s3c2xx0_softc *sc)
623 {
624         vm_offset_t va;
625         
626         va = sc->sc_clkman_ioh;
627         switch(sc->sc_cpu) {
628         case CPU_S3C2410:
629                 s3c2410_clock_freq2(va, &sc->sc_fclk, &sc->sc_hclk,
630                     &sc->sc_pclk);
631                 break;
632         case CPU_S3C2440:
633                 s3c2440_clock_freq2(va, &sc->sc_fclk, &sc->sc_hclk,
634                     &sc->sc_pclk);
635                 break;
636         }
637 }
638
639 void
640 cpu_reset(void)
641 {
642         (void) disable_interrupts(I32_bit|F32_bit);
643
644         bus_space_write_4(&s3c2xx0_bs_tag, s3c2xx0_softc->sc_wdt_ioh, WDT_WTCON,
645             WTCON_ENABLE | WTCON_CLKSEL_16 | WTCON_ENRST);
646         for(;;);
647 }
648
649 void
650 s3c24x0_sleep(int mode __unused)
651 {
652         int reg;
653
654         reg = bus_space_read_4(&s3c2xx0_bs_tag, s3c2xx0_softc->sc_clkman_ioh,
655             CLKMAN_CLKCON);
656         bus_space_write_4(&s3c2xx0_bs_tag, s3c2xx0_softc->sc_clkman_ioh,
657             CLKMAN_CLKCON, reg | CLKCON_IDLE);
658 }
659
660
661 int
662 arm_get_next_irq(int last __unused)
663 {
664         uint32_t intpnd;
665         int irq, subirq;
666
667         if ((irq = bus_space_read_4(&s3c2xx0_bs_tag,
668             s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTOFFSET)) != 0) {
669
670                 /* Clear the pending bit */
671                 intpnd = bus_space_read_4(&s3c2xx0_bs_tag,
672                     s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTPND);
673                 bus_space_write_4(&s3c2xx0_bs_tag, s3c2xx0_softc->sc_intctl_ioh,
674                     INTCTL_SRCPND, intpnd);
675                 bus_space_write_4(&s3c2xx0_bs_tag, s3c2xx0_softc->sc_intctl_ioh,
676                     INTCTL_INTPND, intpnd);
677
678                 switch (irq) {
679                 case S3C24X0_INT_ADCTC:
680                 case S3C24X0_INT_UART0:
681                 case S3C24X0_INT_UART1:
682                 case S3C24X0_INT_UART2:
683                         /* Find the sub IRQ */
684                         subirq = 0x7ff;
685                         subirq &= bus_space_read_4(&s3c2xx0_bs_tag,
686                             s3c2xx0_softc->sc_intctl_ioh, INTCTL_SUBSRCPND);
687                         subirq &= ~(bus_space_read_4(&s3c2xx0_bs_tag,
688                             s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTSUBMSK));
689                         if (subirq == 0)
690                                 return (irq);
691
692                         subirq = ffs(subirq) - 1;
693
694                         /* Clear the sub irq pending bit */
695                         bus_space_write_4(&s3c2xx0_bs_tag,
696                             s3c2xx0_softc->sc_intctl_ioh, INTCTL_SUBSRCPND,
697                             (1 << subirq));
698
699                         /*
700                          * Return the parent IRQ for UART
701                          * as it is all we ever need
702                          */
703                         if (subirq <= 8)
704                                 return (irq);
705
706                         return (S3C24X0_SUBIRQ_MIN + subirq);
707
708                 case S3C24X0_INT_0:
709                 case S3C24X0_INT_1:
710                 case S3C24X0_INT_2:
711                 case S3C24X0_INT_3:
712                         /* There is a 1:1 mapping to the IRQ we are handling */
713                         return S3C24X0_INT_EXT(irq);
714
715                 case S3C24X0_INT_4_7:
716                 case S3C24X0_INT_8_23:
717                         /* Find the external interrupt being called */
718                         subirq = 0x7fffff;
719                         subirq &= bus_space_read_4(&s3c2xx0_bs_tag,
720                             s3c2xx0_softc->sc_gpio_ioh, GPIO_EINTPEND);
721                         subirq &= ~bus_space_read_4(&s3c2xx0_bs_tag,
722                             s3c2xx0_softc->sc_gpio_ioh, GPIO_EINTMASK);
723                         if (subirq == 0)
724                                 return (irq);
725
726                         subirq = ffs(subirq) - 1;
727
728                         /* Clear the external irq pending bit */
729                         bus_space_write_4(&s3c2xx0_bs_tag,
730                             s3c2xx0_softc->sc_gpio_ioh, GPIO_EINTPEND,
731                             (1 << subirq));
732
733                         return S3C24X0_INT_EXT(subirq);
734                 }
735
736                 return (irq);
737         }
738         return (-1);
739 }
740
741 void
742 arm_mask_irq(uintptr_t irq)
743 {
744         u_int32_t mask;
745
746         if (irq >= S3C24X0_INT_EXT(0) && irq <= S3C24X0_INT_EXT(3)) {
747                 /* External interrupt 0..3 are directly mapped to irq 0..3 */
748                 irq -= S3C24X0_EXTIRQ_MIN;
749         }
750         if (irq < S3C24X0_SUBIRQ_MIN) {
751                 mask = bus_space_read_4(&s3c2xx0_bs_tag,
752                     s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTMSK);
753                 mask |= (1 << irq);
754                 bus_space_write_4(&s3c2xx0_bs_tag, 
755                     s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTMSK, mask);
756         } else if (irq < S3C24X0_EXTIRQ_MIN) {
757                 mask = bus_space_read_4(&s3c2xx0_bs_tag,
758                     s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTSUBMSK);
759                 mask |= (1 << (irq - S3C24X0_SUBIRQ_MIN));
760                 bus_space_write_4(&s3c2xx0_bs_tag, 
761                     s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTSUBMSK, mask);
762         } else {
763                 mask = bus_space_read_4(&s3c2xx0_bs_tag,
764                     s3c2xx0_softc->sc_gpio_ioh, GPIO_EINTMASK);
765                 mask |= (1 << (irq - S3C24X0_EXTIRQ_MIN));
766                 bus_space_write_4(&s3c2xx0_bs_tag, 
767                     s3c2xx0_softc->sc_intctl_ioh, GPIO_EINTMASK, mask);
768         }
769 }
770
771 void
772 arm_unmask_irq(uintptr_t irq)
773 {
774         u_int32_t mask;
775
776         if (irq >= S3C24X0_INT_EXT(0) && irq <= S3C24X0_INT_EXT(3)) {
777                 /* External interrupt 0..3 are directly mapped to irq 0..3 */
778                 irq -= S3C24X0_EXTIRQ_MIN;
779         }
780         if (irq < S3C24X0_SUBIRQ_MIN) {
781                 mask = bus_space_read_4(&s3c2xx0_bs_tag,
782                     s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTMSK);
783                 mask &= ~(1 << irq);
784                 bus_space_write_4(&s3c2xx0_bs_tag,
785                     s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTMSK, mask);
786         } else if (irq < S3C24X0_EXTIRQ_MIN) {
787                 mask = bus_space_read_4(&s3c2xx0_bs_tag,
788                     s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTSUBMSK);
789                 mask &= ~(1 << (irq - S3C24X0_SUBIRQ_MIN));
790                 bus_space_write_4(&s3c2xx0_bs_tag, 
791                     s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTSUBMSK, mask);
792         } else {
793                 mask = bus_space_read_4(&s3c2xx0_bs_tag,
794                     s3c2xx0_softc->sc_gpio_ioh, GPIO_EINTMASK);
795                 mask &= ~(1 << (irq - S3C24X0_EXTIRQ_MIN));
796                 bus_space_write_4(&s3c2xx0_bs_tag, 
797                     s3c2xx0_softc->sc_intctl_ioh, GPIO_EINTMASK, mask);
798         }
799 }