at91rm9200.c revision 213496
1213496Scognet/*- 2213496Scognet * Copyright (c) 2005 Olivier Houchard. All rights reserved. 3213496Scognet * Copyright (c) 2010 Greg Ansley. All rights reserved. 4213496Scognet * 5213496Scognet * Redistribution and use in source and binary forms, with or without 6213496Scognet * modification, are permitted provided that the following conditions 7213496Scognet * are met: 8213496Scognet * 1. Redistributions of source code must retain the above copyright 9213496Scognet * notice, this list of conditions and the following disclaimer. 10213496Scognet * 2. Redistributions in binary form must reproduce the above copyright 11213496Scognet * notice, this list of conditions and the following disclaimer in the 12213496Scognet * documentation and/or other materials provided with the distribution. 13213496Scognet * 14213496Scognet * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15213496Scognet * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16213496Scognet * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17213496Scognet * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 18213496Scognet * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19213496Scognet * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20213496Scognet * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21213496Scognet * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22213496Scognet * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23213496Scognet * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24213496Scognet * SUCH DAMAGE. 25213496Scognet */ 26213496Scognet 27213496Scognet#include <sys/cdefs.h> 28213496Scognet__FBSDID("$FreeBSD: head/sys/arm/at91/at91rm9200.c 213496 2010-10-06 22:25:21Z cognet $"); 29213496Scognet 30213496Scognet#include <sys/param.h> 31213496Scognet#include <sys/systm.h> 32213496Scognet#include <sys/bus.h> 33213496Scognet#include <sys/kernel.h> 34213496Scognet#include <sys/malloc.h> 35213496Scognet#include <sys/module.h> 36213496Scognet 37213496Scognet#define _ARM32_BUS_DMA_PRIVATE 38213496Scognet#include <machine/bus.h> 39213496Scognet 40213496Scognet#include <arm/at91/at91var.h> 41213496Scognet#include <arm/at91/at91rm92reg.h> 42213496Scognet#include <arm/at91/at91_aicreg.h> 43213496Scognet#include <arm/at91/at91_pmcreg.h> 44213496Scognet#include <arm/at91/at91_pmcvar.h> 45213496Scognet 46213496Scognetstruct at91rm92_softc { 47213496Scognet device_t dev; 48213496Scognet bus_space_tag_t sc_st; 49213496Scognet bus_space_handle_t sc_sh; 50213496Scognet bus_space_handle_t sc_sys_sh; 51213496Scognet bus_space_handle_t sc_aic_sh; 52213496Scognet bus_space_handle_t sc_dbg_sh; 53213496Scognet bus_space_handle_t sc_matrix_sh; 54213496Scognet}; 55213496Scognet/* 56213496Scognet * Standard priority levels for the system. 0 is lowest and 7 is highest. 57213496Scognet * These values are the ones Atmel uses for its Linux port, which differ 58213496Scognet * a little form the ones that are in the standard distribution. Also, 59213496Scognet * the ones marked with 'TWEEK' are different based on experience. 60213496Scognet */ 61213496Scognetstatic const int at91_irq_prio[32] = 62213496Scognet{ 63213496Scognet 7, /* Advanced Interrupt Controller (FIQ) */ 64213496Scognet 7, /* System Peripherals */ 65213496Scognet 1, /* Parallel IO Controller A */ 66213496Scognet 1, /* Parallel IO Controller B */ 67213496Scognet 1, /* Parallel IO Controller C */ 68213496Scognet 1, /* Parallel IO Controller D */ 69213496Scognet 5, /* USART 0 */ 70213496Scognet 5, /* USART 1 */ 71213496Scognet 5, /* USART 2 */ 72213496Scognet 5, /* USART 3 */ 73213496Scognet 0, /* Multimedia Card Interface */ 74213496Scognet 2, /* USB Device Port */ 75213496Scognet 4, /* Two-Wire Interface */ /* TWEEK */ 76213496Scognet 5, /* Serial Peripheral Interface */ 77213496Scognet 4, /* Serial Synchronous Controller 0 */ 78213496Scognet 6, /* Serial Synchronous Controller 1 */ /* TWEEK */ 79213496Scognet 4, /* Serial Synchronous Controller 2 */ 80213496Scognet 0, /* Timer Counter 0 */ 81213496Scognet 6, /* Timer Counter 1 */ /* TWEEK */ 82213496Scognet 0, /* Timer Counter 2 */ 83213496Scognet 0, /* Timer Counter 3 */ 84213496Scognet 0, /* Timer Counter 4 */ 85213496Scognet 0, /* Timer Counter 5 */ 86213496Scognet 2, /* USB Host port */ 87213496Scognet 3, /* Ethernet MAC */ 88213496Scognet 0, /* Advanced Interrupt Controller (IRQ0) */ 89213496Scognet 0, /* Advanced Interrupt Controller (IRQ1) */ 90213496Scognet 0, /* Advanced Interrupt Controller (IRQ2) */ 91213496Scognet 0, /* Advanced Interrupt Controller (IRQ3) */ 92213496Scognet 0, /* Advanced Interrupt Controller (IRQ4) */ 93213496Scognet 0, /* Advanced Interrupt Controller (IRQ5) */ 94213496Scognet 0 /* Advanced Interrupt Controller (IRQ6) */ 95213496Scognet}; 96213496Scognet 97213496Scognet#define DEVICE(_name, _id, _unit) \ 98213496Scognet { \ 99213496Scognet _name, _unit, \ 100213496Scognet AT91RM92_ ## _id ##_BASE, \ 101213496Scognet AT91RM92_ ## _id ## _SIZE, \ 102213496Scognet AT91RM92_IRQ_ ## _id \ 103213496Scognet } 104213496Scognet 105213496Scognetstatic const struct cpu_devs at91_devs[] = 106213496Scognet{ 107213496Scognet DEVICE("at91_pmc", PMC, 0), 108213496Scognet DEVICE("at91_st", ST, 0), 109213496Scognet DEVICE("at91_pio", PIOA, 0), 110213496Scognet DEVICE("at91_pio", PIOB, 1), 111213496Scognet DEVICE("at91_pio", PIOC, 2), 112213496Scognet DEVICE("at91_pio", PIOD, 3), 113213496Scognet DEVICE("at91_rtc", RTC, 0), 114213496Scognet 115213496Scognet DEVICE("at91_mci", MCI, 0), 116213496Scognet DEVICE("at91_twi", TWI, 0), 117213496Scognet DEVICE("at91_udp", UDP, 0), 118213496Scognet DEVICE("ate", EMAC, 0), 119213496Scognet DEVICE("at91_ssc", SSC0, 0), 120213496Scognet DEVICE("at91_ssc", SSC1, 1), 121213496Scognet DEVICE("at91_ssc", SSC2, 2), 122213496Scognet DEVICE("spi", SPI, 0), 123213496Scognet 124213496Scognet#ifndef SKYEYE_WORKAROUNDS 125213496Scognet DEVICE("uart", DBGU, 0), 126213496Scognet DEVICE("uart", USART0, 1), 127213496Scognet DEVICE("uart", USART1, 2), 128213496Scognet DEVICE("uart", USART2, 3), 129213496Scognet DEVICE("uart", USART3, 4), 130213496Scognet#else 131213496Scognet DEVICE("uart", USART0, 0), 132213496Scognet#endif 133213496Scognet DEVICE("at91_aic", AIC, 0), 134213496Scognet DEVICE("at91_mc", MC, 0), 135213496Scognet DEVICE("at91_tc", TC0, 0), 136213496Scognet DEVICE("at91_tc", TC1, 1), 137213496Scognet DEVICE("ohci", OHCI, 0), 138213496Scognet DEVICE("af91_cfata", CF, 0), 139213496Scognet { 0, 0, 0, 0, 0 } 140213496Scognet}; 141213496Scognet 142213496Scognetstatic void 143213496Scognetat91_add_child(device_t dev, int prio, const char *name, int unit, 144213496Scognet bus_addr_t addr, bus_size_t size, int irq0, int irq1, int irq2) 145213496Scognet{ 146213496Scognet device_t kid; 147213496Scognet struct at91_ivar *ivar; 148213496Scognet 149213496Scognet kid = device_add_child_ordered(dev, prio, name, unit); 150213496Scognet if (kid == NULL) { 151213496Scognet printf("Can't add child %s%d ordered\n", name, unit); 152213496Scognet return; 153213496Scognet } 154213496Scognet ivar = malloc(sizeof(*ivar), M_DEVBUF, M_NOWAIT | M_ZERO); 155213496Scognet if (ivar == NULL) { 156213496Scognet device_delete_child(dev, kid); 157213496Scognet printf("Can't add alloc ivar\n"); 158213496Scognet return; 159213496Scognet } 160213496Scognet device_set_ivars(kid, ivar); 161213496Scognet resource_list_init(&ivar->resources); 162213496Scognet if (irq0 != -1) { 163213496Scognet bus_set_resource(kid, SYS_RES_IRQ, 0, irq0, 1); 164213496Scognet if (irq0 != AT91RM92_IRQ_SYSTEM) 165213496Scognet at91_pmc_clock_add(device_get_nameunit(kid), irq0, 0); 166213496Scognet } 167213496Scognet if (irq1 != 0) 168213496Scognet bus_set_resource(kid, SYS_RES_IRQ, 1, irq1, 1); 169213496Scognet if (irq2 != 0) 170213496Scognet bus_set_resource(kid, SYS_RES_IRQ, 2, irq2, 1); 171213496Scognet if (addr != 0 && addr < AT91RM92_BASE) 172213496Scognet addr += AT91RM92_BASE; 173213496Scognet if (addr != 0) 174213496Scognet bus_set_resource(kid, SYS_RES_MEMORY, 0, addr, size); 175213496Scognet} 176213496Scognet 177213496Scognetstatic void 178213496Scognetat91_cpu_add_builtin_children(device_t dev) 179213496Scognet{ 180213496Scognet int i; 181213496Scognet const struct cpu_devs *walker; 182213496Scognet 183213496Scognet for (i = 1, walker = at91_devs; walker->name; i++, walker++) { 184213496Scognet at91_add_child(dev, i, walker->name, walker->unit, 185213496Scognet walker->mem_base, walker->mem_len, walker->irq0, 186213496Scognet walker->irq1, walker->irq2); 187213496Scognet } 188213496Scognet} 189213496Scognet 190213496Scognetstatic uint32_t 191213496Scognetat91_pll_outb(int freq) 192213496Scognet{ 193213496Scognet 194213496Scognet if (freq > 155000000) 195213496Scognet return (0x0000); 196213496Scognet else 197213496Scognet return (0x8000); 198213496Scognet} 199213496Scognet 200213496Scognetstatic void 201213496Scognetat91_identify(driver_t *drv, device_t parent) 202213496Scognet{ 203213496Scognet 204213496Scognet if (at91_cpu_is(AT91_CPU_RM9200)) { 205213496Scognet at91_add_child(parent, 0, "at91rm920", 0, 0, 0, -1, 0, 0); 206213496Scognet at91_cpu_add_builtin_children(parent); 207213496Scognet } 208213496Scognet} 209213496Scognet 210213496Scognetstatic int 211213496Scognetat91_probe(device_t dev) 212213496Scognet{ 213213496Scognet 214213496Scognet if (at91_cpu_is(AT91_CPU_RM9200)) { 215213496Scognet device_set_desc(dev, "AT91RM9200"); 216213496Scognet return (0); 217213496Scognet } 218213496Scognet return (ENXIO); 219213496Scognet} 220213496Scognet 221213496Scognetstatic int 222213496Scognetat91_attach(device_t dev) 223213496Scognet{ 224213496Scognet struct at91_pmc_clock *clk; 225213496Scognet struct at91rm92_softc *sc = device_get_softc(dev); 226213496Scognet int i; 227213496Scognet 228213496Scognet struct at91_softc *at91sc = device_get_softc(device_get_parent(dev)); 229213496Scognet 230213496Scognet sc->sc_st = at91sc->sc_st; 231213496Scognet sc->sc_sh = at91sc->sc_sh; 232213496Scognet sc->dev = dev; 233213496Scognet 234213496Scognet if (bus_space_subregion(sc->sc_st, sc->sc_sh, AT91RM92_SYS_BASE, 235213496Scognet AT91RM92_SYS_SIZE, &sc->sc_sys_sh) != 0) 236213496Scognet panic("Enable to map system registers"); 237213496Scognet 238213496Scognet if (bus_space_subregion(sc->sc_st, sc->sc_sh, AT91RM92_DBGU_BASE, 239213496Scognet AT91RM92_DBGU_SIZE, &sc->sc_dbg_sh) != 0) 240213496Scognet panic("Enable to map DBGU registers"); 241213496Scognet 242213496Scognet if (bus_space_subregion(sc->sc_st, sc->sc_sh, AT91RM92_AIC_BASE, 243213496Scognet AT91RM92_AIC_SIZE, &sc->sc_aic_sh) != 0) 244213496Scognet panic("Enable to map system registers"); 245213496Scognet 246213496Scognet /* XXX Hack to tell atmelarm about the AIC */ 247213496Scognet at91sc->sc_aic_sh = sc->sc_aic_sh; 248213496Scognet at91sc->sc_irq_system = AT91RM92_IRQ_SYSTEM; 249213496Scognet 250213496Scognet for (i = 0; i < 32; i++) { 251213496Scognet bus_space_write_4(sc->sc_st, sc->sc_aic_sh, IC_SVR + 252213496Scognet i * 4, i); 253213496Scognet /* Priority. */ 254213496Scognet bus_space_write_4(sc->sc_st, sc->sc_aic_sh, IC_SMR + i * 4, 255213496Scognet at91_irq_prio[i]); 256213496Scognet if (i < 8) 257213496Scognet bus_space_write_4(sc->sc_st, sc->sc_aic_sh, IC_EOICR, 258213496Scognet 1); 259213496Scognet } 260213496Scognet 261213496Scognet bus_space_write_4(sc->sc_st, sc->sc_aic_sh, IC_SPU, 32); 262213496Scognet /* No debug. */ 263213496Scognet bus_space_write_4(sc->sc_st, sc->sc_aic_sh, IC_DCR, 0); 264213496Scognet /* Disable and clear all interrupts. */ 265213496Scognet bus_space_write_4(sc->sc_st, sc->sc_aic_sh, IC_IDCR, 0xffffffff); 266213496Scognet bus_space_write_4(sc->sc_st, sc->sc_aic_sh, IC_ICCR, 0xffffffff); 267213496Scognet 268213496Scognet /* Disable all interrupts for RTC (0xe24 == RTC_IDR) */ 269213496Scognet bus_space_write_4(sc->sc_st, sc->sc_sys_sh, 0xe24, 0xffffffff); 270213496Scognet 271213496Scognet /* Disable all interrupts for the SDRAM controller */ 272213496Scognet bus_space_write_4(sc->sc_st, sc->sc_sys_sh, 0xfa8, 0xffffffff); 273213496Scognet 274213496Scognet /* Disable all interrupts for DBGU */ 275213496Scognet bus_space_write_4(sc->sc_st, sc->sc_dbg_sh, 0x0c, 0xffffffff); 276213496Scognet 277213496Scognet /* Update USB device port clock info */ 278213496Scognet clk = at91_pmc_clock_ref("udpck"); 279213496Scognet clk->pmc_mask = PMC_SCER_UDP; 280213496Scognet at91_pmc_clock_deref(clk); 281213496Scognet 282213496Scognet /* Update USB host port clock info */ 283213496Scognet clk = at91_pmc_clock_ref("uhpck"); 284213496Scognet clk->pmc_mask = PMC_SCER_UHP; 285213496Scognet at91_pmc_clock_deref(clk); 286213496Scognet 287213496Scognet /* Each SOC has different PLL contraints */ 288213496Scognet clk = at91_pmc_clock_ref("plla"); 289213496Scognet clk->pll_min_in = RM9200_PLL_A_MIN_IN_FREQ; /* 1 MHz */ 290213496Scognet clk->pll_max_in = RM9200_PLL_A_MAX_IN_FREQ; /* 32 MHz */ 291213496Scognet clk->pll_min_out = RM9200_PLL_A_MIN_OUT_FREQ; /* 80 MHz */ 292213496Scognet clk->pll_max_out = RM9200_PLL_A_MAX_OUT_FREQ; /* 180 MHz */ 293213496Scognet clk->pll_mul_shift = RM9200_PLL_A_MUL_SHIFT; 294213496Scognet clk->pll_mul_mask = RM9200_PLL_A_MUL_MASK; 295213496Scognet clk->pll_div_shift = RM9200_PLL_A_DIV_SHIFT; 296213496Scognet clk->pll_div_mask = RM9200_PLL_A_DIV_MASK; 297213496Scognet clk->set_outb = at91_pll_outb; 298213496Scognet at91_pmc_clock_deref(clk); 299213496Scognet 300213496Scognet clk = at91_pmc_clock_ref("pllb"); 301213496Scognet clk->pll_min_in = RM9200_PLL_B_MIN_IN_FREQ; /* 100 KHz */ 302213496Scognet clk->pll_max_in = RM9200_PLL_B_MAX_IN_FREQ; /* 32 MHz */ 303213496Scognet clk->pll_min_out = RM9200_PLL_B_MIN_OUT_FREQ; /* 30 MHz */ 304213496Scognet clk->pll_max_out = RM9200_PLL_B_MAX_OUT_FREQ; /* 240 MHz */ 305213496Scognet clk->pll_mul_shift = RM9200_PLL_B_MUL_SHIFT; 306213496Scognet clk->pll_mul_mask = RM9200_PLL_B_MUL_MASK; 307213496Scognet clk->pll_div_shift = RM9200_PLL_B_DIV_SHIFT; 308213496Scognet clk->pll_div_mask = RM9200_PLL_B_DIV_MASK; 309213496Scognet clk->set_outb = at91_pll_outb; 310213496Scognet at91_pmc_clock_deref(clk); 311213496Scognet 312213496Scognet return (0); 313213496Scognet} 314213496Scognet 315213496Scognetstatic device_method_t at91_methods[] = { 316213496Scognet DEVMETHOD(device_probe, at91_probe), 317213496Scognet DEVMETHOD(device_attach, at91_attach), 318213496Scognet DEVMETHOD(device_identify, at91_identify), 319213496Scognet {0, 0}, 320213496Scognet}; 321213496Scognet 322213496Scognetstatic driver_t at91rm92_driver = { 323213496Scognet "at91rm920", 324213496Scognet at91_methods, 325213496Scognet sizeof(struct at91rm92_softc), 326213496Scognet}; 327213496Scognet 328213496Scognetstatic devclass_t at91rm92_devclass; 329213496Scognet 330213496ScognetDRIVER_MODULE(at91rm920, atmelarm, at91rm92_driver, at91rm92_devclass, 0, 0); 331