/* * (C) Copyright 2009-2010 Digi International, Inc. * Copyright (C) 2007 Sascha Hauer, Pengutronix * (c) 2011 Eukrea Electromatique, Eric BĂ©nard * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ccxmx51.h" static struct ccxmx51_ident ccxmx51_ids[] = { /* 0x00 */ { "Unknown", 0, 0, 0, 0, 0 }, /* 0x01 */ { "Not supported", 0, 0, 0, 0, 0 }, /* 0x02 */ { "i.MX515@800MHz, Wireless, PHY, Ext. Eth, Accel", SZ_512M, 0, 1, 1, 1 }, /* 0x03 */ { "i.MX515@800MHz, PHY, Ext. Eth, Accel", SZ_512M, 0, 1, 1, 0 }, /* 0x04 */ { "i.MX515@600MHz, Wireless, PHY, Ext. Eth, Accel", SZ_512M, 1, 1, 1, 1 }, /* 0x05 */ { "i.MX515@600MHz, PHY, Ext. Eth, Accel", SZ_512M, 1, 1, 1, 0 }, /* 0x06 */ { "i.MX515@800MHz, Wireless, PHY, Accel", SZ_512M, 0, 1, 0, 1 }, /* 0x07 */ { "i.MX515@800MHz, PHY, Accel", SZ_512M, 0, 1, 0, 0 }, /* 0x08 */ { "i.MX515@800MHz, Wireless, PHY, Accel", SZ_256M, 0, 1, 0, 1 }, /* 0x09 */ { "i.MX515@800MHz, PHY, Accel", SZ_256M, 0, 1, 0, 0 }, /* 0x0a */ { "i.MX515@600MHz, Wireless, PHY, Accel", SZ_256M, 1, 1, 0, 1 }, /* 0x0b */ { "i.MX515@600MHz, PHY, Accel", SZ_256M, 1, 1, 0, 0 }, /* 0x0c */ { "i.MX515@800MHz, Wireless, PHY, Accel", SZ_128M, 0, 1, 0, 1 }, /* 0x0d */ { "i.MX512@800MHz", SZ_128M, 0, 0, 0, 0 }, /* 0x0e */ { "i.MX515@800MHz, Wireless, PHY, Accel", SZ_512M, 0, 1, 0, 1 }, /* 0x0f */ { "i.MX515@600MHz, PHY, Accel", SZ_128M, 1, 1, 0, 0 }, /* 0x10 */ { "i.MX515@600MHz, Wireless, PHY, Accel", SZ_128M, 1, 1, 0, 1 }, /* 0x11 */ { "i.MX515@800MHz, PHY, Accel", SZ_128M, 0, 1, 0, 0 }, /* 0x12 */ { "i.MX515@600MHz, Wireless, PHY, Accel", SZ_512M, 1, 1, 0, 1 }, /* 0x13 */ { "i.MX515@800MHz, PHY, Accel", SZ_512M, 0, 1, 0, 0 }, }; struct ccxmx51_ident *ccxmx51_id; struct imx_nand_platform_data nand_info = { .width = 1, .hw_ecc = 1, .flash_bbt = 1, }; #ifdef CONFIG_DRIVER_NET_FEC_IMX static struct fec_platform_data fec_info = { .xcv_type = MII100, .phy_addr = 7, }; #endif static iomux_v3_cfg_t ccxmx51_pads[] = { /* UART1 */ MX51_PAD_UART1_RXD__UART1_RXD, MX51_PAD_UART1_TXD__UART1_TXD, /* UART2 */ MX51_PAD_UART2_RXD__UART2_RXD, MX51_PAD_UART2_TXD__UART2_TXD, /* UART3 */ MX51_PAD_UART3_RXD__UART3_RXD, MX51_PAD_UART3_TXD__UART3_TXD, /* I2C2 */ MX51_PAD_GPIO1_2__I2C2_SCL, MX51_PAD_GPIO1_3__I2C2_SDA, /* eCSPI1 */ MX51_PAD_CSPI1_MOSI__ECSPI1_MOSI, MX51_PAD_CSPI1_MISO__ECSPI1_MISO, MX51_PAD_CSPI1_SCLK__ECSPI1_SCLK, MX51_PAD_CSPI1_RDY__ECSPI1_RDY, MX51_PAD_CSPI1_SS0__ECSPI1_SS0, MX51_PAD_CSPI1_SS1__ECSPI1_SS1, /* FEC */ MX51_PAD_DISP2_DAT14__FEC_RDATA0, MX51_PAD_DI2_DISP_CLK__FEC_RDATA1, MX51_PAD_DI_GP4__FEC_RDATA2, MX51_PAD_DISP2_DAT0__FEC_RDATA3, MX51_PAD_DISP2_DAT15__FEC_TDATA0, MX51_PAD_DISP2_DAT6__FEC_TDATA1, MX51_PAD_DISP2_DAT7__FEC_TDATA2, MX51_PAD_DISP2_DAT8__FEC_TDATA3, MX51_PAD_DISP2_DAT9__FEC_TX_EN, MX51_PAD_DISP2_DAT10__FEC_COL, MX51_PAD_DISP2_DAT11__FEC_RX_CLK, MX51_PAD_DISP2_DAT12__FEC_RX_DV, MX51_PAD_DISP2_DAT13__FEC_TX_CLK, MX51_PAD_DI2_PIN2__FEC_MDC, MX51_PAD_DI2_PIN4__FEC_CRS, MX51_PAD_DI2_PIN3__FEC_MDIO, MX51_PAD_DI_GP3__FEC_TX_ER, MX51_PAD_DISP2_DAT1__FEC_RX_ER, /* WEIM */ MX51_PAD_EIM_DA0__EIM_DA0, MX51_PAD_EIM_DA1__EIM_DA1, MX51_PAD_EIM_DA2__EIM_DA2, MX51_PAD_EIM_DA3__EIM_DA3, MX51_PAD_EIM_DA4__EIM_DA4, MX51_PAD_EIM_DA5__EIM_DA5, MX51_PAD_EIM_DA6__EIM_DA6, MX51_PAD_EIM_DA7__EIM_DA7, MX51_PAD_EIM_D16__EIM_D16, MX51_PAD_EIM_D17__EIM_D17, MX51_PAD_EIM_D18__EIM_D18, MX51_PAD_EIM_D19__EIM_D19, MX51_PAD_EIM_D20__EIM_D20, MX51_PAD_EIM_D21__EIM_D21, MX51_PAD_EIM_D22__EIM_D22, MX51_PAD_EIM_D23__EIM_D23, MX51_PAD_EIM_D24__EIM_D24, MX51_PAD_EIM_D25__EIM_D25, MX51_PAD_EIM_D26__EIM_D26, MX51_PAD_EIM_D27__EIM_D27, MX51_PAD_EIM_D28__EIM_D28, MX51_PAD_EIM_D29__EIM_D29, MX51_PAD_EIM_D30__EIM_D30, MX51_PAD_EIM_D31__EIM_D31, MX51_PAD_EIM_OE__EIM_OE, MX51_PAD_EIM_CS5__EIM_CS5, /* NAND */ MX51_PAD_NANDF_D0__NANDF_D0, MX51_PAD_NANDF_D1__NANDF_D1, MX51_PAD_NANDF_D2__NANDF_D2, MX51_PAD_NANDF_D3__NANDF_D3, MX51_PAD_NANDF_D4__NANDF_D4, MX51_PAD_NANDF_D5__NANDF_D5, MX51_PAD_NANDF_D6__NANDF_D6, MX51_PAD_NANDF_D7__NANDF_D7, MX51_PAD_NANDF_ALE__NANDF_ALE, MX51_PAD_NANDF_CLE__NANDF_CLE, MX51_PAD_NANDF_RE_B__NANDF_RE_B, MX51_PAD_NANDF_WE_B__NANDF_WE_B, MX51_PAD_NANDF_WP_B__NANDF_WP_B, MX51_PAD_NANDF_CS0__NANDF_CS0, MX51_PAD_NANDF_RB0__NANDF_RB0, /* LAN9221 IRQ (GPIO1.9) */ MX51_PAD_GPIO1_9__GPIO1_9, /* MC13892 IRQ (GPIO1.5) */ MX51_PAD_GPIO1_5__GPIO1_5, /* MMA7455LR IRQ1 (GPIO1.7) */ MX51_PAD_GPIO1_7__GPIO1_7, /* MMA7455LR IRQ2 (GPIO1.6) */ MX51_PAD_GPIO1_6__GPIO1_6, }; #define CCXMX51_ECSPI1_CS0 IMX_GPIO_NR(4, 24) #define CCXMX51_ECSPI1_CS1 IMX_GPIO_NR(4, 25) static int ecspi_0_cs[] = { CCXMX51_ECSPI1_CS0, CCXMX51_ECSPI1_CS1, }; static struct spi_imx_master ecspi_0_data = { .chipselect = ecspi_0_cs, .num_chipselect = ARRAY_SIZE(ecspi_0_cs), }; static const struct spi_board_info ccxmx51_spi_board_info[] = { { .name = "mc13xxx-spi", .bus_num = 0, .chip_select = 0, }, }; static int ccxmx51_mem_init(void) { /* Add minimal SDRAM first */ arm_add_mem_device("ram0", MX51_CSD0_BASE_ADDR, SZ_128M); return 0; } mem_initcall(ccxmx51_mem_init); static void ccxmx51_otghost_init(void) { #define MX51_USBOTHER_REGS_OFFSET 0x800 #define MX51_USBCTRL_OFFSET 0x0 #define MX51_USB_PHY_CTR_FUNC_OFFSET 0x8 #define MX51_USB_PHY_CTR_FUNC2_OFFSET 0xc #define MX51_USB_UTMI_PHYCTRL1_PLLDIV_MASK 0x3 #define MX51_USB_PLL_DIV_19_2_MHZ 0x00 #define MX51_USB_PLL_DIV_24_MHZ 0x01 #define MX51_USB_PLL_DIV_26_MHZ 0x02 #define MX51_USB_PLL_DIV_27_MHZ 0x03 #define MX51_OTG_PHYCTRL_OC_DIS_BIT (1 << 8) #define MX51_OTG_UCTRL_OWIE_BIT (1 << 27) #define MX51_OTG_UCTRL_OPM_BIT (1 << 24) #define USBOTHER_BASE (MX51_OTG_BASE_ADDR + MX51_USBOTHER_REGS_OFFSET) u32 reg; /* Set sysclock to 24 MHz */ reg = readl(USBOTHER_BASE + MX51_USB_PHY_CTR_FUNC2_OFFSET); reg &= ~MX51_USB_UTMI_PHYCTRL1_PLLDIV_MASK; reg |= MX51_USB_PLL_DIV_24_MHZ; writel(reg, USBOTHER_BASE + MX51_USB_PHY_CTR_FUNC2_OFFSET); /* OC is not used */ reg = readl(USBOTHER_BASE + MX51_USB_PHY_CTR_FUNC_OFFSET); reg |= MX51_OTG_PHYCTRL_OC_DIS_BIT; writel(reg, USBOTHER_BASE + MX51_USB_PHY_CTR_FUNC_OFFSET); /* Power pins enable */ reg = readl(USBOTHER_BASE + MX51_USBCTRL_OFFSET); reg |= MX51_OTG_UCTRL_OWIE_BIT | MX51_OTG_UCTRL_OPM_BIT; writel(reg, USBOTHER_BASE + MX51_USBCTRL_OFFSET); /* Setup PORTSC */ reg = readl(MX51_OTG_BASE_ADDR + 0x184); reg &= ~(3 << 30); reg |= 1 << 28; writel(reg, MX51_OTG_BASE_ADDR + 0x184); mdelay(10); add_generic_usb_ehci_device(0, MX51_OTG_BASE_ADDR, NULL); } static int ccxmx51_power_init(void) { struct mc13xxx *mc13xxx_dev; u32 val; mc13xxx_dev = mc13xxx_get(); if (!mc13xxx_dev) return -ENODEV; mc13xxx_reg_read(mc13xxx_dev, MC13892_REG_POWER_MISC, &val); /* Reset devices by clearing GP01-GPO4 */ val &= ~((1 << 21) | (3 << 12) | (3 << 10) | (3 << 8) | (3 << 6)); /* Switching off the PWGT1SPIEN */ val |= (1 << 15); /* Switching on the PWGT2SPIEN */ val &= ~(1 << 16); /* Enable short circuit protection */ val |= (1 << 0); mc13xxx_reg_write(mc13xxx_dev, MC13892_REG_POWER_MISC, val); /* Allow charger to charge (4.2V and 560mA) */ val = 0x238033; mc13xxx_reg_write(mc13xxx_dev, MC13892_REG_CHARGE, val); /* Set core voltage (SW1) to 1.1V */ mc13xxx_reg_read(mc13xxx_dev, MC13892_REG_SW_0, &val); val &= ~0x00001f; val |= 0x000014; mc13xxx_reg_write(mc13xxx_dev, MC13892_REG_SW_0, val); if (imx_silicon_revision() < IMX_CHIP_REV_3_0) { /* Setup VCC (SW2) to 1.25 */ mc13xxx_reg_read(mc13xxx_dev, MC13892_REG_SW_1, &val); val &= ~0x00001f; val |= 0x00001a; mc13xxx_reg_write(mc13xxx_dev, MC13892_REG_SW_1, val); /* Setup 1V2_DIG1 (SW3) to 1.25 */ mc13xxx_reg_read(mc13xxx_dev, MC13892_REG_SW_2, &val); val &= ~0x00001f; val |= 0x00001a; mc13xxx_reg_write(mc13xxx_dev, MC13892_REG_SW_2, val); } else { /* Setup VCC (SW2) to 1.225 */ mc13xxx_reg_read(mc13xxx_dev, MC13892_REG_SW_1, &val); val &= ~0x00001f; val |= 0x000019; mc13xxx_reg_write(mc13xxx_dev, MC13892_REG_SW_1, val); /* Setup 1V2_DIG1 (SW3) to 1.2 */ mc13xxx_reg_read(mc13xxx_dev, MC13892_REG_SW_2, &val); val &= ~0x00001f; val |= 0x000018; mc13xxx_reg_write(mc13xxx_dev, MC13892_REG_SW_2, val); } if (mc13xxx_revision(mc13xxx_dev) <= MC13892_REVISION_2_0) { /* Set switchers in PWM mode for Atlas 2.0 and lower */ /* Setup the switcher mode for SW1 & SW2*/ mc13xxx_reg_read(mc13xxx_dev, MC13892_REG_SW_4, &val); val &= ~0x003c0f; val |= 0x001405; mc13xxx_reg_write(mc13xxx_dev, MC13892_REG_SW_4, val); /* Setup the switcher mode for SW3 & SW4 */ mc13xxx_reg_read(mc13xxx_dev, MC13892_REG_SW_5, &val); val &= ~0x000f0f; val |= 0x000505; mc13xxx_reg_write(mc13xxx_dev, MC13892_REG_SW_5, val); } else { /* Set switchers in Auto in NORMAL mode & STANDBY mode for Atlas 2.0a */ /* Setup the switcher mode for SW1 & SW2*/ mc13xxx_reg_read(mc13xxx_dev, MC13892_REG_SW_4, &val); val &= ~0x003c0f; val |= 0x002008; mc13xxx_reg_write(mc13xxx_dev, MC13892_REG_SW_4, val); /* Setup the switcher mode for SW3 & SW4 */ mc13xxx_reg_read(mc13xxx_dev, MC13892_REG_SW_5, &val); val &= ~0x000f0f; val |= 0x000808; mc13xxx_reg_write(mc13xxx_dev, MC13892_REG_SW_5, val); } /* Set VVIDEO to 2.775V, VAUDIO to 3V, VSD to 3.15V */ mc13xxx_reg_read(mc13xxx_dev, MC13892_REG_SETTING_1, &val); val &= ~0x0001fc; val |= 0x0001f4; mc13xxx_reg_write(mc13xxx_dev, MC13892_REG_SETTING_1, val); /* Configure VGEN3 and VCAM regulators to use external PNP */ val = 0x000208; mc13xxx_reg_write(mc13xxx_dev, MC13892_REG_MODE_1, val); udelay(200); /* Set VGEN3 to 1.8V */ mc13xxx_reg_read(mc13xxx_dev, MC13892_REG_SETTING_0, &val); val &= ~(1 << 14); mc13xxx_reg_write(mc13xxx_dev, MC13892_REG_SETTING_0, val); /* Enable VGEN3, VCAM, VAUDIO, VVIDEO, VSD regulators */ val = 0x049249; mc13xxx_reg_write(mc13xxx_dev, MC13892_REG_MODE_1, val); /* Enable USB1 charger */ val = 0x000409; mc13xxx_reg_write(mc13xxx_dev, MC13892_REG_USB1, val); /* Set VCOIN to 3.0V and Enable It */ mc13xxx_reg_read(mc13xxx_dev, MC13892_REG_POWER_CTL0, &val); val &= ~(7 << 20); val |= (4 << 20) | (1 << 23); mc13xxx_reg_write(mc13xxx_dev, MC13892_REG_POWER_CTL0, val); /* Keeps VSRTC and CLK32KMCU */ val |= (1 << 4); mc13xxx_reg_write(mc13xxx_dev, MC13892_REG_POWER_CTL0, val); /* De-assert reset of external devices on GP01, GPO2, GPO3 and GPO4 */ mc13xxx_reg_read(mc13xxx_dev, MC13892_REG_POWER_MISC, &val); /* GPO1 - External */ /* GP02 - LAN9221 */ /* GP03 - FEC */ /* GP04 - Wireless */ if (IS_ENABLED(CONFIG_DRIVER_NET_SMC911X) && ccxmx51_id->eth0) val |= (1 << 8); if (IS_ENABLED(CONFIG_DRIVER_NET_FEC_IMX) && ccxmx51_id->eth1) val |= (1 << 10); if (ccxmx51_id->wless) val |= (1 << 12); mc13xxx_reg_write(mc13xxx_dev, MC13892_REG_POWER_MISC, val); udelay(100); return 0; } static int ccxmx51_devices_init(void) { u8 hwid[6]; int pwr; char manloc; if ((imx_iim_read(1, 9, hwid, sizeof(hwid)) != sizeof(hwid)) || (hwid[0] < 0x02) || (hwid[0] >= ARRAY_SIZE(ccxmx51_ids))) memset(hwid, 0x00, sizeof(hwid)); ccxmx51_id = &ccxmx51_ids[hwid[0]]; printf("Module Variant: %s (0x%02x)\n", ccxmx51_id->id_string, hwid[0]); if (hwid[0]) { printf("Module HW Rev : %02x\n", hwid[1]); switch (hwid[2] & 0xc0) { case 0x00: manloc = 'B'; break; case 0x40: manloc = 'W'; break; case 0x80: manloc = 'S'; break; default: manloc = 'N'; break; } printf("Module Serial : %c%d\n", manloc, ((hwid[2] & 0x3f) << 24) | (hwid[3] << 16) | (hwid[4] << 8) | hwid[5]); if ((ccxmx51_id->mem_sz - SZ_128M) > 0) arm_add_mem_device("ram1", MX51_CSD0_BASE_ADDR + SZ_128M, ccxmx51_id->mem_sz - SZ_128M); } imx51_add_uart1(); imx51_add_uart2(); spi_register_board_info(ccxmx51_spi_board_info, ARRAY_SIZE(ccxmx51_spi_board_info)); imx51_add_spi0(&ecspi_0_data); pwr = ccxmx51_power_init(); console_flush(); imx51_init_lowlevel((ccxmx51_id->industrial || pwr) ? 600 : 800); clock_notifier_call_chain(); if (pwr) printf("Could not setup PMIC. Clocks not adjusted.\n"); imx51_add_i2c1(NULL); imx51_add_nand(&nand_info); devfs_add_partition("nand0", 0x00000, 0x80000, DEVFS_PARTITION_FIXED, "self_raw"); dev_add_bb_dev("self_raw", "self0"); devfs_add_partition("nand0", 0x80000, 0x40000, DEVFS_PARTITION_FIXED, "env_raw"); dev_add_bb_dev("env_raw", "env0"); #ifdef CONFIG_DRIVER_NET_FEC_IMX if (ccxmx51_id->eth0 && !pwr) { imx51_add_fec(&fec_info); eth_register_ethaddr(0, hwid); } #endif #ifdef CONFIG_DRIVER_NET_SMC911X if (ccxmx51_id->eth1 && !pwr) { /* Configure the WEIM CS5 timming, bus width, etc */ /* 16 bit on DATA[31..16], not multiplexed, async */ writel(0x00420081, MX51_WEIM_BASE_ADDR + WEIM_CSxGCR1(5)); /* ADH has not effect on non muxed bus */ writel(0, MX51_WEIM_BASE_ADDR + WEIM_CSxGCR2(5)); /* RWSC=50, RADVA=2, RADVN=6, OEA=0, OEN=0, RCSA=0, RCSN=0 */ writel(0x32260000, MX51_WEIM_BASE_ADDR + WEIM_CSxRCR1(5)); /* APR=0 */ writel(0, MX51_WEIM_BASE_ADDR + WEIM_CSxRCR2(5)); /* WAL=0, WBED=1, WWSC=50, WADVA=2, WADVN=6, WEA=0, WEN=0, WCSA=0 */ writel(0x72080f00, MX51_WEIM_BASE_ADDR + WEIM_CSxWCR1(5)); /* LAN9221 network controller */ add_generic_device("smc911x", 1, NULL, MX51_CS5_BASE_ADDR, SZ_4K, IORESOURCE_MEM, NULL); } #endif ccxmx51_otghost_init(); armlinux_set_bootparams((void *)(MX51_CSD0_BASE_ADDR + 0x100)); armlinux_set_architecture(ccxmx51_id->wless ? MACH_TYPE_CCWMX51 : MACH_TYPE_CCMX51); return 0; } device_initcall(ccxmx51_devices_init); static int ccxmx51_console_init(void) { mxc_iomux_v3_setup_multiple_pads(ccxmx51_pads, ARRAY_SIZE(ccxmx51_pads)); imx51_add_uart0(); return 0; } console_initcall(ccxmx51_console_init);