/* $NetBSD: imx6_board.c,v 1.7 2016/11/24 08:41:20 hkenken Exp $ */ /* * Copyright (c) 2012 Genetec Corporation. All rights reserved. * Written by Hashimoto Kenichi for Genetec Corporation. * * 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. * * THIS SOFTWARE IS PROVIDED BY 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 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(1, "$NetBSD: imx6_board.c,v 1.7 2016/11/24 08:41:20 hkenken Exp $"); #include "opt_imx.h" #include "arml2cc.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include bus_space_tag_t imx6_ioreg_bst = &armv7_generic_bs_tag; bus_space_handle_t imx6_ioreg_bsh; bus_space_tag_t imx6_armcore_bst = &armv7_generic_bs_tag; bus_space_handle_t imx6_armcore_bsh; void imx6_bootstrap(vaddr_t iobase) { int error; /* imx6_ioreg is mapped AIPS1+AIPS2 region */ imx6_ioreg_bsh = (bus_space_handle_t)iobase; error = bus_space_map(imx6_ioreg_bst, IMX6_IOREG_PBASE, IMX6_IOREG_SIZE, 0, &imx6_ioreg_bsh); if (error) panic("%s: failed to map Imx %s registers: %d", __func__, "io", error); imx6_armcore_bsh = (bus_space_handle_t) iobase + IMX6_IOREG_SIZE; error = bus_space_map(imx6_armcore_bst, IMX6_ARMCORE_PBASE, IMX6_ARMCORE_SIZE, 0, &imx6_armcore_bsh); if (error) panic("%s: failed to map Imx %s registers: %d", __func__, "armcore", error); #if NARML2CC > 0 arml2cc_init(imx6_armcore_bst, imx6_armcore_bsh, ARMCORE_L2C_BASE); #endif } /* iMX6 SoC type */ uint32_t imx6_chip_id(void) { uint32_t v; /* read DIGPROG_SOLOLITE (IMX6SL only) */ v = bus_space_read_4(imx6_ioreg_bst, imx6_ioreg_bsh, AIPS1_CCM_BASE + USB_ANALOG_DIGPROG_SOLOLITE); if (__SHIFTOUT(v, USB_ANALOG_DIGPROG_MAJOR) == CHIPID_MAJOR_IMX6SL) return v; /* not SOLOLITE, read DIGPROG */ v = bus_space_read_4(imx6_ioreg_bst, imx6_ioreg_bsh, AIPS1_CCM_BASE + USB_ANALOG_DIGPROG); return v; } /* * probe DDR size from DDR Controller register */ psize_t imx6_memprobe(void) { uint32_t ctrl, misc; int bitwidth; ctrl = bus_space_read_4(imx6_ioreg_bst, imx6_ioreg_bsh, IMX6_AIPS1_SIZE + AIPS2_MMDC1_BASE + MMDC1_MDCTL); misc = bus_space_read_4(imx6_ioreg_bst, imx6_ioreg_bsh, IMX6_AIPS1_SIZE + AIPS2_MMDC1_BASE + MMDC1_MDMISC); /* row */ bitwidth = __SHIFTOUT(ctrl, MMDC1_MDCTL_ROW) + 11; /* column */ switch (__SHIFTOUT(ctrl, MMDC1_MDCTL_COL)) { case 0: bitwidth += 9; break; case 1: bitwidth += 10; break; case 2: bitwidth += 11; break; case 3: bitwidth += 8; break; case 4: bitwidth += 12; break; default: printf("AIPS2_MMDC1:MDCTL[COL]: " "unknown column address width: %llu\n", __SHIFTOUT(ctrl, MMDC1_MDCTL_COL)); return 0; } /* width */ bitwidth += __SHIFTOUT(ctrl, MMDC1_MDCTL_DSIZ) + 1; /* bank */ bitwidth += __SHIFTOUT(ctrl, MMDC1_MDCTL_SDE_1); bitwidth += (misc & MMDC1_MDMISC_DDR_4_BANK) ? 2 : 3; /* over 4GB ? limit 3840MB (SoC design limitation) */ if (bitwidth >= 32) { /* * XXX: bus_dma and uvm cannot treat 0xffffffff as high address * correctly because of 0xffffffff + 1 = 0x00000000. * therefore use 0xffffefff. */ return (psize_t)IMX6_MEM_SIZE - PAGE_SIZE; } return (psize_t)1 << bitwidth; } void imx6_reset(void) { delay(1000); /* wait for flushing FIFO of serial console */ cpsid(I32_bit|F32_bit); /* software reset signal on wdog */ bus_space_write_2(imx6_ioreg_bst, imx6_ioreg_bsh, AIPS1_WDOG1_BASE + IMX_WDOG_WCR, WCR_WDE); /* * write twice due to errata. * Reference: ERR004346: IMX6DQCE Chip Errata for the i.MX 6Dual/6Quad */ bus_space_write_2(imx6_ioreg_bst, imx6_ioreg_bsh, AIPS1_WDOG1_BASE + IMX_WDOG_WCR, WCR_WDE); for (;;) __asm("wfi"); } uint32_t imx6_armrootclk(void) { uint32_t clk; uint32_t v; v = bus_space_read_4(imx6_ioreg_bst, imx6_ioreg_bsh, AIPS1_CCM_BASE + CCM_ANALOG_PLL_ARM); clk = IMX6_OSC_FREQ * (v & CCM_ANALOG_PLL_ARM_DIV_SELECT) / 2; v = bus_space_read_4(imx6_ioreg_bst, imx6_ioreg_bsh, AIPS1_CCM_BASE + CCM_CACRR); v = __SHIFTOUT(v, CCM_CACRR_ARM_PODF); return clk / (v + 1); } void imx6_device_register(device_t self, void *aux) { prop_dictionary_t dict = device_properties(self); if (device_is_a(self, "armperiph") && device_is_a(device_parent(self), "mainbus")) { /* * XXX KLUDGE ALERT XXX * The iot mainbus supplies is completely wrong since it scales * addresses by 2. The simpliest remedy is to replace with our * bus space used for the armcore registers (which armperiph uses). */ struct mainbus_attach_args * const mb = aux; mb->mb_iot = imx6_armcore_bst; return; } /* * We need to tell the A9 Global/Watchdog Timer * what frequency it runs at. */ if (device_is_a(self, "a9tmr") || device_is_a(self, "a9wdt")) { prop_dictionary_set_uint32(dict, "frequency", imx6_armrootclk() / IMX6_PERIPHCLK_N); return; } } #ifdef MULTIPROCESSOR void imx6_cpu_hatch(struct cpu_info *ci) { a9tmr_init_cpu_clock(ci); } #endif void imx6_set_gpio(device_t self, const char *name, int32_t *gpio, int32_t *active, u_int dir) { prop_dictionary_t dict; const char *pin_data; int grp, pin; *gpio = -1; *active = -1; dict = device_properties(self); if (!prop_dictionary_get_cstring_nocopy(dict, name, &pin_data)) return; /* * "!1,6" -> gpio = GPIO_NO(1,6), active = GPIO_PIN_LOW * "3,31" -> gpio = GPIO_NO(3,31), active = GPIO_PIN_HIGH * "!" -> always not detected * none -> always detected */ if (*pin_data == '!') { *active = GPIO_PIN_LOW; pin_data++; } else *active = GPIO_PIN_HIGH; if (*pin_data == '\0') return; for (grp = 0; (*pin_data >= '0') && (*pin_data <= '9'); pin_data++) grp = grp * 10 + *pin_data - '0'; KASSERT(*pin_data == ','); pin_data++; for (pin = 0; (*pin_data >= '0') && (*pin_data <= '9'); pin_data++) pin = pin * 10 + *pin_data - '0'; KASSERT(*pin_data == '\0'); *gpio = GPIO_NO(grp, pin); #if NIMXGPIO > 0 gpio_set_direction(*gpio, dir); #endif }