]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sys/arm/s3c2xx0/s3c24x0.c
Copy head (r256279) to stable/10 as part of the 10.0-RELEASE cycle.
[FreeBSD/stable/10.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         }
224         return (0);
225 }
226
227 static int
228 s3c24x0_teardown_intr(device_t dev, device_t child, struct resource *res,
229         void *cookie)
230 {
231         return (BUS_TEARDOWN_INTR(device_get_parent(dev), child, res, cookie));
232 }
233
234 static int
235 s3c24x0_config_intr(device_t dev, int irq, enum intr_trigger trig,
236         enum intr_polarity pol)
237 {
238         uint32_t mask, reg, value;
239         int offset;
240
241         /* Only external interrupts can be configured */
242         if (irq < S3C24X0_EXTIRQ_MIN || irq > S3C24X0_EXTIRQ_MAX)
243                 return (EINVAL);
244
245         /* There is no standard trigger or polarity for the bus */
246         if (trig == INTR_TRIGGER_CONFORM || pol == INTR_POLARITY_CONFORM)
247                 return (EINVAL);
248
249         irq -= S3C24X0_EXTIRQ_MIN;
250
251         /* Get the bits to set */
252         mask = 0;
253         if (pol == INTR_POLARITY_LOW) {
254                 mask = 2;
255         } else if (pol == INTR_POLARITY_HIGH) {
256                 mask = 4;
257         }
258         if (trig == INTR_TRIGGER_LEVEL) {
259                 mask >>= 2;
260         }
261
262         /* Get the register to set */
263         if (irq <= 7) {
264                 reg = GPIO_EXTINT(0);
265                 offset = irq * 4;
266         } else if (irq <= 15) {
267                 reg = GPIO_EXTINT(1);
268                 offset = (irq - 8) * 4;
269         } else if (irq <= 23) {
270                 reg = GPIO_EXTINT(2);
271                 offset = (irq - 16) * 4;
272         } else {
273                 return (EINVAL);
274         }
275
276         /* Set the new signaling method */
277         value = bus_space_read_4(s3c2xx0_softc->sc_iot,
278             s3c2xx0_softc->sc_gpio_ioh, reg);
279         value &= ~(7 << offset);
280         value |= mask << offset;
281         bus_space_write_4(s3c2xx0_softc->sc_iot,
282             s3c2xx0_softc->sc_gpio_ioh, reg, value);
283
284         return (0);
285 }
286
287 static struct resource *
288 s3c24x0_alloc_resource(device_t bus, device_t child, int type, int *rid,
289         u_long start, u_long end, u_long count, u_int flags)
290 {
291         struct resource_list_entry *rle;
292         struct s3c2xx0_ivar *ivar = device_get_ivars(child);
293         struct resource_list *rl = &ivar->resources;
294         struct resource *res = NULL;
295
296         if (device_get_parent(child) != bus)
297                 return (BUS_ALLOC_RESOURCE(device_get_parent(bus), child,
298                     type, rid, start, end, count, flags));
299
300         rle = resource_list_find(rl, type, *rid);
301         if (rle != NULL) {
302                 /* There is a resource list. Use it */
303                 if (rle->res)
304                         panic("Resource rid %d type %d already in use", *rid,
305                             type);
306                 if (start == 0UL && end == ~0UL) {
307                         start = rle->start;
308                         count = ulmax(count, rle->count);
309                         end = ulmax(rle->end, start + count - 1);
310                 }
311                 /*
312                  * When allocating an irq with children irq's really
313                  * allocate the children as it is those we are interested
314                  * in receiving, not the parent.
315                  */
316                 if (type == SYS_RES_IRQ && start == end) {
317                         switch (start) {
318                         case S3C24X0_INT_ADCTC:
319                                 start = S3C24X0_INT_TC;
320                                 end = S3C24X0_INT_ADC;
321                                 break;
322 #ifdef S3C2440_INT_CAM
323                         case S3C2440_INT_CAM:
324                                 start = S3C2440_INT_CAM_C;
325                                 end = S3C2440_INT_CAM_P;
326                                 break;
327 #endif
328                         default:
329                                 break;
330                         }
331                         count = end - start + 1;
332                 }
333         }
334
335         switch (type) {
336         case SYS_RES_IRQ:
337                 res = rman_reserve_resource(
338                     &s3c2xx0_softc->s3c2xx0_irq_rman, start, end,
339                     count, flags, child);
340                 break;
341
342         case SYS_RES_IOPORT:
343         case SYS_RES_MEMORY:
344                 res = rman_reserve_resource(
345                     &s3c2xx0_softc->s3c2xx0_mem_rman,
346                     start, end, count, flags, child);
347                 if (res == NULL)
348                         panic("Unable to map address space %#lX-%#lX", start,
349                             end);
350
351                 rman_set_bustag(res, &s3c2xx0_bs_tag);
352                 rman_set_bushandle(res, start);
353                 if (flags & RF_ACTIVE) {
354                         if (bus_activate_resource(child, type, *rid, res)) {
355                                 rman_release_resource(res);
356                                 return (NULL);
357                         }
358                 }
359                 break;
360         }
361
362         if (res != NULL) {
363                 rman_set_rid(res, *rid);
364                 if (rle != NULL) {
365                         rle->res = res;
366                         rle->start = rman_get_start(res);
367                         rle->end = rman_get_end(res);
368                         rle->count = count;
369                 }
370         }
371
372         return (res);
373 }
374
375 static int
376 s3c24x0_activate_resource(device_t bus, device_t child, int type, int rid,
377         struct resource *r)
378 {
379         bus_space_handle_t p;
380         int error;
381
382         if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) {
383                 error = bus_space_map(rman_get_bustag(r),
384                     rman_get_bushandle(r), rman_get_size(r), 0, &p);
385                 if (error)
386                         return (error);
387                 rman_set_bushandle(r, p);
388         }
389         return (rman_activate_resource(r));
390 }
391
392 static int
393 s3c24x0_release_resource(device_t bus, device_t child, int type, int rid,
394         struct resource *r)
395 {
396         struct s3c2xx0_ivar *ivar = device_get_ivars(child);
397         struct resource_list *rl = &ivar->resources;
398         struct resource_list_entry *rle;
399
400         if (rl == NULL)
401                 return (EINVAL);
402
403         rle = resource_list_find(rl, type, rid);
404         if (rle == NULL)
405                 return (EINVAL);
406
407         rman_release_resource(r);
408         rle->res = NULL;
409
410         return 0;
411 }
412
413 static struct resource_list *
414 s3c24x0_get_resource_list(device_t dev, device_t child)
415 {
416         struct s3c2xx0_ivar *ivar;
417
418         ivar = device_get_ivars(child);
419         return (&(ivar->resources));
420 }
421
422 void
423 s3c24x0_identify(driver_t *driver, device_t parent)
424 {
425         
426         BUS_ADD_CHILD(parent, 0, "s3c24x0", 0);
427 }
428
429 int
430 s3c24x0_probe(device_t dev)
431 {
432         return 0;
433 }
434
435 int
436 s3c24x0_attach(device_t dev)
437 {
438         struct s3c24x0_softc *sc = device_get_softc(dev);
439         bus_space_tag_t iot;
440         device_t child;
441         unsigned int i, j;
442         u_long irqmax;
443
444         s3c2xx0_softc = &(sc->sc_sx);
445         sc->sc_sx.sc_iot = iot = &s3c2xx0_bs_tag;
446         s3c2xx0_softc->s3c2xx0_irq_rman.rm_type = RMAN_ARRAY;
447         s3c2xx0_softc->s3c2xx0_irq_rman.rm_descr = "S3C24X0 IRQs";
448         s3c2xx0_softc->s3c2xx0_mem_rman.rm_type = RMAN_ARRAY;
449         s3c2xx0_softc->s3c2xx0_mem_rman.rm_descr = "S3C24X0 Device Registers";
450         /* Manage the registor memory space */
451         if ((rman_init(&s3c2xx0_softc->s3c2xx0_mem_rman) != 0) ||
452             (rman_manage_region(&s3c2xx0_softc->s3c2xx0_mem_rman,
453               S3C24X0_DEV_VA_OFFSET,
454               S3C24X0_DEV_VA_OFFSET + S3C24X0_DEV_VA_SIZE) != 0) ||
455             (rman_manage_region(&s3c2xx0_softc->s3c2xx0_mem_rman,
456               S3C24X0_DEV_START, S3C24X0_DEV_STOP) != 0))
457                 panic("s3c24x0_attach: failed to set up register rman");
458
459         /* These are needed for things without a proper device to attach to */
460         sc->sc_sx.sc_intctl_ioh = S3C24X0_INTCTL_BASE;
461         sc->sc_sx.sc_gpio_ioh = S3C24X0_GPIO_BASE;
462         sc->sc_sx.sc_clkman_ioh = S3C24X0_CLKMAN_BASE;
463         sc->sc_sx.sc_wdt_ioh = S3C24X0_WDT_BASE;
464         sc->sc_timer_ioh = S3C24X0_TIMER_BASE;
465
466         /*
467          * Identify the CPU
468          */
469         s3c24x0_identify_cpu(dev);
470
471         /*
472          * Manage the interrupt space.
473          * We need to put this after s3c24x0_identify_cpu as the avaliable
474          * interrupts change depending on which CPU we have.
475          */
476         if (sc->sc_sx.sc_cpu == CPU_S3C2410)
477                 irqmax = S3C2410_SUBIRQ_MAX;
478         else
479                 irqmax = S3C2440_SUBIRQ_MAX;
480         if (rman_init(&s3c2xx0_softc->s3c2xx0_irq_rman) != 0 ||
481             rman_manage_region(&s3c2xx0_softc->s3c2xx0_irq_rman, 0,
482             irqmax) != 0 ||
483             rman_manage_region(&s3c2xx0_softc->s3c2xx0_irq_rman,
484             S3C24X0_EXTIRQ_MIN, S3C24X0_EXTIRQ_MAX))
485                 panic("s3c24x0_attach: failed to set up IRQ rman");
486
487         /* calculate current clock frequency */
488         s3c24x0_clock_freq(&sc->sc_sx);
489         device_printf(dev, "fclk %d MHz hclk %d MHz pclk %d MHz\n",
490                sc->sc_sx.sc_fclk / 1000000, sc->sc_sx.sc_hclk / 1000000,
491                sc->sc_sx.sc_pclk / 1000000);
492
493         /*
494          * Attach children devices
495          */
496
497         for (i = 0; s3c24x0_children[i].name != NULL; i++) {
498                 child = s3c24x0_add_child(dev, s3c24x0_children[i].prio,
499                     s3c24x0_children[i].name, s3c24x0_children[i].unit);
500                 for (j = 0; j < sizeof(s3c24x0_children[i].res) /
501                      sizeof(s3c24x0_children[i].res[0]) &&
502                      s3c24x0_children[i].res[j].type != 0; j++) {
503                         bus_set_resource(child,
504                             s3c24x0_children[i].res[j].type, 0,
505                             s3c24x0_children[i].res[j].start,
506                             s3c24x0_children[i].res[j].count);
507                 }
508         }
509
510         bus_generic_probe(dev);
511         bus_generic_attach(dev);
512
513         return (0);
514 }
515
516 static void
517 s3c24x0_identify_cpu(device_t dev)
518 {
519         struct s3c24x0_softc *sc = device_get_softc(dev);
520         uint32_t idcode;
521         int i;
522
523         idcode = bus_space_read_4(sc->sc_sx.sc_iot, sc->sc_sx.sc_gpio_ioh,
524             GPIO_GSTATUS1);
525
526         for (i = 0; s3c2x0_cpu_id[i].name != NULL; i++) {
527                 if (s3c2x0_cpu_id[i].idcode == idcode)
528                         break;
529         }
530         if (s3c2x0_cpu_id[i].name == NULL)
531                 panic("Unknown CPU detected ((Chip ID: %#X)", idcode);
532         device_printf(dev, "Found %s CPU (Chip ID: %#X)\n",
533             s3c2x0_cpu_id[i].name, idcode);
534         sc->sc_sx.sc_cpu = s3c2x0_cpu_id[i].cpu;
535 }
536
537 /*
538  * fill sc_pclk, sc_hclk, sc_fclk from values of clock controller register.
539  *
540  * s3c24{1,4}0_clock_freq2() is meant to be called from kernel startup routines.
541  * s3c24x0_clock_freq() is for after kernel initialization is done.
542  *
543  * Because they can be called before bus_space is available we need to use
544  * volatile pointers rather than bus_space_read.
545  */
546 void
547 s3c2410_clock_freq2(vm_offset_t clkman_base, int *fclk, int *hclk, int *pclk)
548 {
549         uint32_t pllcon, divn;
550         unsigned int mdiv, pdiv, sdiv;
551         unsigned int f, h, p;
552
553         pllcon = *(volatile uint32_t *)(clkman_base + CLKMAN_MPLLCON);
554         divn = *(volatile uint32_t *)(clkman_base + CLKMAN_CLKDIVN);
555
556         mdiv = (pllcon & PLLCON_MDIV_MASK) >> PLLCON_MDIV_SHIFT;
557         pdiv = (pllcon & PLLCON_PDIV_MASK) >> PLLCON_PDIV_SHIFT;
558         sdiv = (pllcon & PLLCON_SDIV_MASK) >> PLLCON_SDIV_SHIFT;
559
560         f = ((mdiv + 8) * S3C2XX0_XTAL_CLK) / ((pdiv + 2) * (1 << sdiv));
561         h = f;
562         if (divn & S3C2410_CLKDIVN_HDIVN)
563                 h /= 2;
564         p = h;
565         if (divn & CLKDIVN_PDIVN)
566                 p /= 2;
567
568         if (fclk) *fclk = f;
569         if (hclk) *hclk = h;
570         if (pclk) *pclk = p;
571 }
572
573 void
574 s3c2440_clock_freq2(vm_offset_t clkman_base, int *fclk, int *hclk, int *pclk)
575 {
576         uint32_t pllcon, divn, camdivn;
577         unsigned int mdiv, pdiv, sdiv;
578         unsigned int f, h, p;
579
580         pllcon = *(volatile uint32_t *)(clkman_base + CLKMAN_MPLLCON);
581         divn = *(volatile uint32_t *)(clkman_base + CLKMAN_CLKDIVN);
582         camdivn = *(volatile uint32_t *)(clkman_base + S3C2440_CLKMAN_CAMDIVN);
583
584         mdiv = (pllcon & PLLCON_MDIV_MASK) >> PLLCON_MDIV_SHIFT;
585         pdiv = (pllcon & PLLCON_PDIV_MASK) >> PLLCON_PDIV_SHIFT;
586         sdiv = (pllcon & PLLCON_SDIV_MASK) >> PLLCON_SDIV_SHIFT;
587
588         f = (2 * (mdiv + 8) * S3C2XX0_XTAL_CLK) / ((pdiv + 2) * (1 << sdiv));
589         h = f;
590         switch((divn >> 1) & 3) {
591         case 0:
592                 break;
593         case 1:
594                 h /= 2;
595                 break;
596         case 2:
597                 if ((camdivn & S3C2440_CAMDIVN_HCLK4_HALF) ==
598                     S3C2440_CAMDIVN_HCLK4_HALF)
599                         h /= 8;
600                 else
601                         h /= 4;
602                 break;
603         case 3:
604                 if ((camdivn & S3C2440_CAMDIVN_HCLK3_HALF) ==
605                     S3C2440_CAMDIVN_HCLK3_HALF)
606                         h /= 6;
607                 else
608                         h /= 3;
609                 break;
610         }
611         p = h;
612         if (divn & CLKDIVN_PDIVN)
613                 p /= 2;
614
615         if (fclk) *fclk = f;
616         if (hclk) *hclk = h;
617         if (pclk) *pclk = p;
618 }
619
620 void
621 s3c24x0_clock_freq(struct s3c2xx0_softc *sc)
622 {
623         vm_offset_t va;
624         
625         va = sc->sc_clkman_ioh;
626         switch(sc->sc_cpu) {
627         case CPU_S3C2410:
628                 s3c2410_clock_freq2(va, &sc->sc_fclk, &sc->sc_hclk,
629                     &sc->sc_pclk);
630                 break;
631         case CPU_S3C2440:
632                 s3c2440_clock_freq2(va, &sc->sc_fclk, &sc->sc_hclk,
633                     &sc->sc_pclk);
634                 break;
635         }
636 }
637
638 void
639 cpu_reset(void)
640 {
641         (void) disable_interrupts(I32_bit|F32_bit);
642
643         bus_space_write_4(&s3c2xx0_bs_tag, s3c2xx0_softc->sc_wdt_ioh, WDT_WTCON,
644             WTCON_ENABLE | WTCON_CLKSEL_16 | WTCON_ENRST);
645         for(;;);
646 }
647
648 void
649 s3c24x0_sleep(int mode __unused)
650 {
651         int reg;
652
653         reg = bus_space_read_4(&s3c2xx0_bs_tag, s3c2xx0_softc->sc_clkman_ioh,
654             CLKMAN_CLKCON);
655         bus_space_write_4(&s3c2xx0_bs_tag, s3c2xx0_softc->sc_clkman_ioh,
656             CLKMAN_CLKCON, reg | CLKCON_IDLE);
657 }
658
659
660 int
661 arm_get_next_irq(int last __unused)
662 {
663         uint32_t intpnd;
664         int irq, subirq;
665
666         if ((irq = bus_space_read_4(&s3c2xx0_bs_tag,
667             s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTOFFSET)) != 0) {
668
669                 /* Clear the pending bit */
670                 intpnd = bus_space_read_4(&s3c2xx0_bs_tag,
671                     s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTPND);
672                 bus_space_write_4(&s3c2xx0_bs_tag, s3c2xx0_softc->sc_intctl_ioh,
673                     INTCTL_SRCPND, intpnd);
674                 bus_space_write_4(&s3c2xx0_bs_tag, s3c2xx0_softc->sc_intctl_ioh,
675                     INTCTL_INTPND, intpnd);
676
677                 switch (irq) {
678                 case S3C24X0_INT_ADCTC:
679                 case S3C24X0_INT_UART0:
680                 case S3C24X0_INT_UART1:
681                 case S3C24X0_INT_UART2:
682                         /* Find the sub IRQ */
683                         subirq = 0x7ff;
684                         subirq &= bus_space_read_4(&s3c2xx0_bs_tag,
685                             s3c2xx0_softc->sc_intctl_ioh, INTCTL_SUBSRCPND);
686                         subirq &= ~(bus_space_read_4(&s3c2xx0_bs_tag,
687                             s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTSUBMSK));
688                         if (subirq == 0)
689                                 return (irq);
690
691                         subirq = ffs(subirq) - 1;
692
693                         /* Clear the sub irq pending bit */
694                         bus_space_write_4(&s3c2xx0_bs_tag,
695                             s3c2xx0_softc->sc_intctl_ioh, INTCTL_SUBSRCPND,
696                             (1 << subirq));
697
698                         /*
699                          * Return the parent IRQ for UART
700                          * as it is all we ever need
701                          */
702                         if (subirq <= 8)
703                                 return (irq);
704
705                         return (S3C24X0_SUBIRQ_MIN + subirq);
706
707                 case S3C24X0_INT_0:
708                 case S3C24X0_INT_1:
709                 case S3C24X0_INT_2:
710                 case S3C24X0_INT_3:
711                         /* There is a 1:1 mapping to the IRQ we are handling */
712                         return S3C24X0_INT_EXT(irq);
713
714                 case S3C24X0_INT_4_7:
715                 case S3C24X0_INT_8_23:
716                         /* Find the external interrupt being called */
717                         subirq = 0x7fffff;
718                         subirq &= bus_space_read_4(&s3c2xx0_bs_tag,
719                             s3c2xx0_softc->sc_gpio_ioh, GPIO_EINTPEND);
720                         subirq &= ~bus_space_read_4(&s3c2xx0_bs_tag,
721                             s3c2xx0_softc->sc_gpio_ioh, GPIO_EINTMASK);
722                         if (subirq == 0)
723                                 return (irq);
724
725                         subirq = ffs(subirq) - 1;
726
727                         /* Clear the external irq pending bit */
728                         bus_space_write_4(&s3c2xx0_bs_tag,
729                             s3c2xx0_softc->sc_gpio_ioh, GPIO_EINTPEND,
730                             (1 << subirq));
731
732                         return S3C24X0_INT_EXT(subirq);
733                 }
734
735                 return (irq);
736         }
737         return (-1);
738 }
739
740 void
741 arm_mask_irq(uintptr_t irq)
742 {
743         u_int32_t mask;
744
745         if (irq >= S3C24X0_INT_EXT(0) && irq <= S3C24X0_INT_EXT(3)) {
746                 /* External interrupt 0..3 are directly mapped to irq 0..3 */
747                 irq -= S3C24X0_EXTIRQ_MIN;
748         }
749         if (irq < S3C24X0_SUBIRQ_MIN) {
750                 mask = bus_space_read_4(&s3c2xx0_bs_tag,
751                     s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTMSK);
752                 mask |= (1 << irq);
753                 bus_space_write_4(&s3c2xx0_bs_tag,
754                     s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTMSK, mask);
755         } else if (irq < S3C24X0_EXTIRQ_MIN) {
756                 mask = bus_space_read_4(&s3c2xx0_bs_tag,
757                     s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTSUBMSK);
758                 mask |= (1 << (irq - S3C24X0_SUBIRQ_MIN));
759                 bus_space_write_4(&s3c2xx0_bs_tag,
760                     s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTSUBMSK, mask);
761         } else {
762                 mask = bus_space_read_4(&s3c2xx0_bs_tag,
763                     s3c2xx0_softc->sc_gpio_ioh, GPIO_EINTMASK);
764                 mask |= (1 << (irq - S3C24X0_EXTIRQ_MIN));
765                 bus_space_write_4(&s3c2xx0_bs_tag,
766                     s3c2xx0_softc->sc_intctl_ioh, GPIO_EINTMASK, mask);
767         }
768 }
769
770 void
771 arm_unmask_irq(uintptr_t irq)
772 {
773         u_int32_t mask;
774
775         if (irq >= S3C24X0_INT_EXT(0) && irq <= S3C24X0_INT_EXT(3)) {
776                 /* External interrupt 0..3 are directly mapped to irq 0..3 */
777                 irq -= S3C24X0_EXTIRQ_MIN;
778         }
779         if (irq < S3C24X0_SUBIRQ_MIN) {
780                 mask = bus_space_read_4(&s3c2xx0_bs_tag,
781                     s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTMSK);
782                 mask &= ~(1 << irq);
783                 bus_space_write_4(&s3c2xx0_bs_tag,
784                     s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTMSK, mask);
785         } else if (irq < S3C24X0_EXTIRQ_MIN) {
786                 mask = bus_space_read_4(&s3c2xx0_bs_tag,
787                     s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTSUBMSK);
788                 mask &= ~(1 << (irq - S3C24X0_SUBIRQ_MIN));
789                 bus_space_write_4(&s3c2xx0_bs_tag,
790                     s3c2xx0_softc->sc_intctl_ioh, INTCTL_INTSUBMSK, mask);
791         } else {
792                 mask = bus_space_read_4(&s3c2xx0_bs_tag,
793                     s3c2xx0_softc->sc_gpio_ioh, GPIO_EINTMASK);
794                 mask &= ~(1 << (irq - S3C24X0_EXTIRQ_MIN));
795                 bus_space_write_4(&s3c2xx0_bs_tag,
796                     s3c2xx0_softc->sc_intctl_ioh, GPIO_EINTMASK, mask);
797         }
798 }