/* $NetBSD: s3c2800.c,v 1.14 2012/10/27 17:17:40 chs Exp $ */ /* * Copyright (c) 2002, 2003 Fujitsu Component Limited * Copyright (c) 2002, 2003 Genetec Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of The Fujitsu Component Limited nor the name of * Genetec corporation may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY FUJITSU COMPONENT LIMITED AND GENETEC * CORPORATION ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL FUJITSU COMPONENT LIMITED OR GENETEC * CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __KERNEL_RCSID(0, "$NetBSD: s3c2800.c,v 1.14 2012/10/27 17:17:40 chs Exp $"); #include #include #include #include #include #include #include #include #include #include #include #include "locators.h" #include "opt_cpuoptions.h" /* prototypes */ static int s3c2800_match(device_t, cfdata_t, void *); static void s3c2800_attach(device_t, device_t, void *); static int s3c2800_search(device_t, cfdata_t, const int *, void *); /* attach structures */ CFATTACH_DECL_NEW(ssio, sizeof(struct s3c2800_softc), s3c2800_match, s3c2800_attach, NULL, NULL); extern struct bus_space s3c2xx0_bs_tag; struct s3c2xx0_softc *s3c2xx0_softc; static int s3c2800_print(void *aux, const char *name) { struct s3c2xx0_attach_args *sa = (struct s3c2xx0_attach_args *) aux; if (sa->sa_size) aprint_normal(" addr 0x%lx", sa->sa_addr); if (sa->sa_size > 1) aprint_normal("-0x%lx", sa->sa_addr + sa->sa_size - 1); if (sa->sa_intr != SSIOCF_INTR_DEFAULT) aprint_normal(" intr %d", sa->sa_intr); if (sa->sa_index != SSIOCF_INDEX_DEFAULT) aprint_normal(" unit %d", sa->sa_index); return (UNCONF); } int s3c2800_match(device_t parent, cfdata_t match, void *aux) { return 1; } void s3c2800_attach(device_t parent, device_t self, void *aux) { struct s3c2800_softc *sc = device_private(self); bus_space_tag_t iot; const char *which_registers; /* for panic message */ #define FAIL(which) do { \ which_registers=(which); goto abort; }while(/*CONSTCOND*/0) s3c2xx0_softc = &(sc->sc_sx); sc->sc_sx.sc_iot = iot = &s3c2xx0_bs_tag; if (bus_space_map(iot, S3C2800_INTCTL_BASE, S3C2800_INTCTL_SIZE, BUS_SPACE_MAP_LINEAR, &sc->sc_sx.sc_intctl_ioh)) FAIL("intc"); /* tell register addresses to interrupt handler */ s3c2800_intr_init(sc); /* Map the GPIO registers */ if (bus_space_map(iot, S3C2800_GPIO_BASE, S3C2800_GPIO_SIZE, 0, &sc->sc_sx.sc_gpio_ioh)) FAIL("GPIO"); #if 0 /* Map the DMA controller registers */ if (bus_space_map(iot, S3C2800_DMAC_BASE, S3C2800_DMAC_SIZE, 0, &sc->sc_sx.sc_dmach)) FAIL("DMAC"); #endif /* Memory controller */ if (bus_space_map(iot, S3C2800_MEMCTL_BASE, S3C2800_MEMCTL_SIZE, 0, &sc->sc_sx.sc_memctl_ioh)) FAIL("MEMC"); /* Clock manager */ if (bus_space_map(iot, S3C2800_CLKMAN_BASE, S3C2800_CLKMAN_SIZE, 0, &sc->sc_sx.sc_clkman_ioh)) FAIL("CLK"); #if 0 /* Real time clock */ if (bus_space_map(iot, S3C2800_RTC_BASE, S3C2800_RTC_SIZE, 0, &sc->sc_sx.sc_rtc_ioh)) FAIL("RTC"); #endif if (bus_space_map(iot, S3C2800_TIMER0_BASE, S3C2800_TIMER_SIZE, 0, &sc->sc_tmr0_ioh)) FAIL("TIMER0"); if (bus_space_map(iot, S3C2800_TIMER1_BASE, S3C2800_TIMER_SIZE, 0, &sc->sc_tmr1_ioh)) FAIL("TIMER1"); /* calculate current clock frequency */ s3c2800_clock_freq(&sc->sc_sx); aprint_normal(": fclk %d MHz hclk %d MHz pclk %d MHz\n", sc->sc_sx.sc_fclk / 1000000, sc->sc_sx.sc_hclk / 1000000, sc->sc_sx.sc_pclk / 1000000); aprint_naive("\n"); /* * Attach devices. */ config_search_ia(s3c2800_search, self, "ssio", NULL); return; abort: panic("%s: unable to map %s registers", device_xname(self), which_registers); #undef FAIL } int s3c2800_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux) { struct s3c2800_softc *sc = device_private(parent); struct s3c2xx0_attach_args aa; aa.sa_sc = sc; aa.sa_iot = sc->sc_sx.sc_iot; aa.sa_addr = cf->cf_loc[SSIOCF_ADDR]; aa.sa_size = cf->cf_loc[SSIOCF_SIZE]; aa.sa_index = cf->cf_loc[SSIOCF_INDEX]; aa.sa_intr = cf->cf_loc[SSIOCF_INTR]; if (config_match(parent, cf, &aa)) config_attach(parent, cf, &aa, s3c2800_print); return 0; } /* * Issue software reset command. * called with MMU off. */ void s3c2800_softreset(void) { *(volatile unsigned int *)(S3C2800_CLKMAN_BASE + CLKMAN_SWRCON) = SWRCON_SWR; } /* * fill sc_pclk, sc_hclk, sc_fclk from values of clock controller register. * * s3c2800_clock_freq2() is meant to be called from kernel startup routines. * s3c2800_clock_freq() is for after kernel initialization is done. */ void s3c2800_clock_freq2(vaddr_t clkman_base, int *fclk, int *hclk, int *pclk) { uint32_t pllcon, clkcon; int mdiv, pdiv, sdiv; int f, h, p; pllcon = *(volatile uint32_t *)(clkman_base + CLKMAN_PLLCON); clkcon = *(volatile uint32_t *)(clkman_base + CLKMAN_CLKCON); mdiv = (pllcon & PLLCON_MDIV_MASK) >> PLLCON_MDIV_SHIFT; pdiv = (pllcon & PLLCON_PDIV_MASK) >> PLLCON_PDIV_SHIFT; sdiv = (pllcon & PLLCON_SDIV_MASK) >> PLLCON_SDIV_SHIFT; f = ((mdiv + 8) * S3C2XX0_XTAL_CLK) / ((pdiv + 2) * (1 << sdiv)); h = f; if (clkcon & CLKCON_HCLK) h /= 2; p = h; if (clkcon & CLKCON_PCLK) p /= 2; if (fclk) *fclk = f; if (hclk) *hclk = h; if (pclk) *pclk = p; } void s3c2800_clock_freq(struct s3c2xx0_softc *sc) { s3c2800_clock_freq2( (vaddr_t)bus_space_vaddr(sc->sc_iot, sc->sc_clkman_ioh), &sc->sc_fclk, &sc->sc_hclk, &sc->sc_pclk); }