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
59u_int irqmasks[IPL_LEVELS];
60
61static 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
73static 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 */
112static device_t s3c24x0_add_child(device_t, int, const char *, int);
113
114static int	s3c24x0_probe(device_t);
115static int	s3c24x0_attach(device_t);
116static void	s3c24x0_identify(driver_t *, device_t);
117static int	s3c24x0_setup_intr(device_t, device_t, struct resource *, int,
118        driver_filter_t *, driver_intr_t *, void *, void **);
119static int	s3c24x0_teardown_intr(device_t, device_t, struct resource *,
120	void *);
121static int	s3c24x0_config_intr(device_t, int, enum intr_trigger,
122	enum intr_polarity);
123static struct resource *s3c24x0_alloc_resource(device_t, device_t, int, int *,
124        u_long, u_long, u_long, u_int);
125static int s3c24x0_activate_resource(device_t, device_t, int, int,
126        struct resource *);
127static int s3c24x0_release_resource(device_t, device_t, int, int,
128        struct resource *);
129static struct resource_list *s3c24x0_get_resource_list(device_t, device_t);
130
131static void s3c24x0_identify_cpu(device_t);
132
133static 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
149static driver_t s3c24x0_driver = {
150	"s3c24x0",
151	s3c24x0_methods,
152	sizeof(struct s3c24x0_softc),
153};
154static devclass_t s3c24x0_devclass;
155
156DRIVER_MODULE(s3c24x0, nexus, s3c24x0_driver, s3c24x0_devclass, 0, 0);
157
158struct s3c2xx0_softc *s3c2xx0_softc = NULL;
159
160static device_t
161s3c24x0_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
182static void
183s3c24x0_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
206static int
207s3c24x0_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
228static int
229s3c24x0_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
235static int
236s3c24x0_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
288static struct resource *
289s3c24x0_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
376static int
377s3c24x0_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
393static int
394s3c24x0_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
414static struct resource_list *
415s3c24x0_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
423void
424s3c24x0_identify(driver_t *driver, device_t parent)
425{
426
427	BUS_ADD_CHILD(parent, 0, "s3c24x0", 0);
428}
429
430int
431s3c24x0_probe(device_t dev)
432{
433	return 0;
434}
435
436int
437s3c24x0_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
517static void
518s3c24x0_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 */
547void
548s3c2410_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
574void
575s3c2440_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
621void
622s3c24x0_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
639void
640cpu_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
649void
650s3c24x0_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
661int
662arm_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
741void
742arm_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
771void
772arm_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}
800