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