diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2011-05-06 11:35:08 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2011-05-06 11:35:08 +0200 |
commit | 30eb4cdf17e5cc0d42bcda5de6a5dccecb06bcf0 (patch) | |
tree | 38ebcb0dc08fc4cd35065b7cf4ce776315e03543 | |
parent | 3e19d858760a138cb8cba92a2395036bd70937e3 (diff) | |
parent | 64476d2177a53228319c4ab5b99cfb66ec8cc365 (diff) | |
download | barebox-30eb4cdf17e5cc0d42bcda5de6a5dccecb06bcf0.tar.gz barebox-30eb4cdf17e5cc0d42bcda5de6a5dccecb06bcf0.tar.xz |
Merge branch 'next'
159 files changed, 14313 insertions, 3710 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index aae0e992..6b2b4007 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -21,6 +21,11 @@ config ARM config ARM_AMBA bool +config ARM_LINUX + bool + default y + depends on CMD_BOOTZ || CMD_BOOTU || CMD_BOOTM + menu "System Type" choice @@ -115,9 +120,14 @@ config ARM_OPTIMZED_STRING_FUNCTIONS These functions work much faster than the normal versions but increase your binary size. +config ARM_EXCEPTIONS + bool "enable arm exception handling support" + default y + config ARM_UNWIND bool "enable stack unwinding support" depends on AEABI + depends on ARM_EXCEPTIONS help This option enables stack unwinding support in barebox using the information automatically generated by the diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 697c783f..21d6157f 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -83,7 +83,11 @@ board-$(CONFIG_MACH_MMCCPU) := mmccpu board-$(CONFIG_MACH_MX1ADS) := mx1ads board-$(CONFIG_MACH_NOMADIK_8815NHK) := nhk8815 board-$(CONFIG_MACH_NXDB500) := netx -board-$(CONFIG_ARCH_OMAP) := omap +board-$(CONFIG_MACH_OMAP343xSDP) := omap +board-$(CONFIG_MACH_BEAGLE) := omap +board-$(CONFIG_MACH_OMAP3EVM) := omap +board-$(CONFIG_MACH_PANDA) := panda +board-$(CONFIG_MACH_PCM049) := pcm049 board-$(CONFIG_MACH_PCA100) := phycard-i.MX27 board-$(CONFIG_MACH_PCM037) := pcm037 board-$(CONFIG_MACH_PCM038) := pcm038 @@ -132,6 +136,14 @@ ifeq ($(machine-y),netx) KBUILD_IMAGE := barebox.netx endif +barebox.bin.ift: barebox.bin + @echo " IFT " $@ + $(Q)scripts/omap_signGP barebox.bin $(TEXT_BASE) 1 + +ifeq ($(CONFIG_OMAP_BUILD_IFT),y) +KBUILD_IMAGE := barebox.bin.ift +endif + all: $(KBUILD_IMAGE) archprepare: maketools diff --git a/arch/arm/boards/eukrea_cpuimx35/eukrea_cpuimx35.c b/arch/arm/boards/eukrea_cpuimx35/eukrea_cpuimx35.c index 73bb2e10..f0eb0886 100644 --- a/arch/arm/boards/eukrea_cpuimx35/eukrea_cpuimx35.c +++ b/arch/arm/boards/eukrea_cpuimx35/eukrea_cpuimx35.c @@ -173,8 +173,6 @@ postcore_initcall(eukrea_cpuimx35_mmu_init); static int eukrea_cpuimx35_devices_init(void) { - unsigned int tmp; - imx35_add_nand(&nand_info); devfs_add_partition("nand0", 0x00000, 0x40000, PARTITION_FIXED, "self_raw"); diff --git a/arch/arm/boards/freescale-mx35-3-stack/3stack.c b/arch/arm/boards/freescale-mx35-3-stack/3stack.c index 127bfb40..03960a4c 100644 --- a/arch/arm/boards/freescale-mx35-3-stack/3stack.c +++ b/arch/arm/boards/freescale-mx35-3-stack/3stack.c @@ -207,6 +207,8 @@ static int f3s_devices_init(void) imx35_add_fec(&fec_info); register_device(&smc911x_dev); + imx35_add_mmc0(NULL); + register_device(&sdram_dev); imx35_add_fb(&ipu_fb_data); diff --git a/arch/arm/boards/karo-tx25/lowlevel.c b/arch/arm/boards/karo-tx25/lowlevel.c index 86319d72..868ba08d 100644 --- a/arch/arm/boards/karo-tx25/lowlevel.c +++ b/arch/arm/boards/karo-tx25/lowlevel.c @@ -72,7 +72,6 @@ static inline void __bare_init setup_sdram(uint32_t base, uint32_t esdctl, void __bare_init __naked board_init_lowlevel(void) { uint32_t r; - int i; #ifdef CONFIG_NAND_IMX_BOOT unsigned int *trg, *src; #endif diff --git a/arch/arm/boards/omap/Kconfig b/arch/arm/boards/omap/Kconfig deleted file mode 100644 index d6120647..00000000 --- a/arch/arm/boards/omap/Kconfig +++ /dev/null @@ -1,93 +0,0 @@ -# OMAP based Board Specific Configuration file -# -# (C) Copyright 2008 -# OMAP Architecture specific features -# Texas Instruments, <www.ti.com> -# Nishanth Menon <x0nishan@ti.com> -# -# 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 - -config ARCH_TEXT_BASE - hex - default 0x80e80000 if MACH_OMAP343xSDP - default 0x80e80000 if MACH_BEAGLE - -menu "OMAP Platform Features" - -config BOARDINFO - default "Texas Instrument's SDP343x" if MACH_OMAP343xSDP - default "Texas Instrument's Beagle" if MACH_BEAGLE - default "Texas Instrument's OMAP3EVM" if MACH_OMAP3EVM - -choice - prompt "Select OMAP platform" - -config MACH_OMAP343xSDP - bool "Texas Instrument's SDP343x" - select MACH_HAS_LOWLEVEL_INIT - select OMAP_CLOCK_ALL - select HAS_OMAP_NAND - help - Say Y here if you are using SDP343x platform - -config MACH_BEAGLE - bool "Texas Instrument's Beagle Board" - select MACH_HAS_LOWLEVEL_INIT - select OMAP_CLOCK_ALL - select HAS_OMAP_NAND - help - Say Y here if you are using Beagle Board - -config MACH_OMAP3EVM - bool "Texas Instrument's OMAP3 EVM" - select MACH_HAS_LOWLEVEL_INIT - select OMAP_CLOCK_ALL - select HAS_OMAP_NAND - help - Say Y here if you are using OMAP3EVM - -endchoice - -if MACH_OMAP3EVM - choice - prompt "Select UART" - - config OMAP3EVM_UART1 - bool "Use UART1" - depends on MACH_OMAP3EVM - help - Say Y here if you would like to use UART1 as console. - - config OMAP3EVM_UART3 - bool "Use UART3" - depends on MACH_OMAP3EVM - help - Say Y here if you would like to use UART3 as console. - endchoice -endif - -config MACH_OMAP_ADVANCED_MUX - bool "Enable advanced pin muxing" - depends on MACH_OMAP343xSDP - default n - help - Say Y here if you would like to have complete pin muxing to be - done at boot time - -config HAS_OMAP_NAND - bool - -endmenu diff --git a/arch/arm/boards/omap/Makefile b/arch/arm/boards/omap/Makefile index 1e74e241..8e0418cc 100644 --- a/arch/arm/boards/omap/Makefile +++ b/arch/arm/boards/omap/Makefile @@ -24,5 +24,3 @@ obj-$(CONFIG_MACH_DO_LOWLEVEL_INIT) += platform.o obj-$(CONFIG_MACH_OMAP343xSDP) += board-sdp343x.o obj-$(CONFIG_MACH_BEAGLE) += board-beagle.o obj-$(CONFIG_MACH_OMAP3EVM) += board-omap3evm.o -obj-y += devices-gpmc-nand.o - diff --git a/arch/arm/boards/omap/board-beagle.c b/arch/arm/boards/omap/board-beagle.c index 6de2cce7..ced3df74 100644 --- a/arch/arm/boards/omap/board-beagle.c +++ b/arch/arm/boards/omap/board-beagle.c @@ -56,6 +56,7 @@ #include <console.h> #include <init.h> #include <driver.h> +#include <sizes.h> #include <asm/io.h> #include <ns16550.h> #include <asm/armlinux.h> @@ -72,6 +73,7 @@ #include <i2c/i2c.h> #include <linux/err.h> #include <usb/ehci.h> +#include <mach/xload.h> #include "board.h" /******************** Board Boot Time *******************/ @@ -313,6 +315,13 @@ static struct i2c_board_info i2c_devices[] = { }, }; +static struct device_d hsmmc_dev = { + .id = -1, + .name = "omap-hsmmc", + .map_base = 0x4809C000, + .size = SZ_4K, +}; + static int beagle_devices_init(void) { int ret; @@ -332,7 +341,9 @@ static int beagle_devices_init(void) /* WP is made high and WAIT1 active Low */ gpmc_generic_init(0x10); #endif - gpmc_generic_nand_devices_init(0, 16, 1); + gpmc_generic_nand_devices_init(0, 16, OMAP_ECC_HAMMING_CODE_HW_ROMCODE); + + register_device(&hsmmc_dev); armlinux_add_dram(&sdram_dev); armlinux_set_bootparams((void *)0x80000100); @@ -341,3 +352,34 @@ failed: return ret; } device_initcall(beagle_devices_init); + +#ifdef CONFIG_SHELL_NONE + +int run_shell(void) +{ + int (*func)(void) = NULL; + + switch (omap3_bootsrc()) { + case OMAP_BOOTSRC_MMC1: + printf("booting from MMC1\n"); + func = omap_xload_boot_mmc(); + break; + case OMAP_BOOTSRC_UNKNOWN: + printf("unknown boot source. Fall back to nand\n"); + case OMAP_BOOTSRC_NAND: + printf("booting from NAND\n"); + func = omap_xload_boot_nand(SZ_128K, SZ_256K); + break; + } + + if (!func) { + printf("booting failed\n"); + while (1); + } + + shutdown_barebox(); + func(); + + while (1); +} +#endif diff --git a/arch/arm/boards/panda/Makefile b/arch/arm/boards/panda/Makefile new file mode 100644 index 00000000..c55e26e0 --- /dev/null +++ b/arch/arm/boards/panda/Makefile @@ -0,0 +1 @@ +obj-y += board.o lowlevel.o mux.o diff --git a/arch/arm/boards/panda/board.c b/arch/arm/boards/panda/board.c new file mode 100644 index 00000000..ff05f9e2 --- /dev/null +++ b/arch/arm/boards/panda/board.c @@ -0,0 +1,230 @@ +#include <common.h> +#include <console.h> +#include <init.h> +#include <fs.h> +#include <driver.h> +#include <asm/io.h> +#include <ns16550.h> +#include <asm/armlinux.h> +#include <linux/stat.h> +#include <generated/mach-types.h> +#include <mach/silicon.h> +#include <mach/sdrc.h> +#include <mach/sys_info.h> +#include <mach/syslib.h> +#include <mach/control.h> +#include <usb/ehci.h> +#include <linux/err.h> +#include <sizes.h> +#include <asm/mmu.h> +#include <mach/gpio.h> +#include <environment.h> +#include <mach/xload.h> + +static int board_revision; + +#define GPIO_HUB_POWER 1 +#define GPIO_HUB_NRESET_39 39 +#define GPIO_HUB_NRESET_62 62 +#define GPIO_BOARD_ID0 182 +#define GPIO_BOARD_ID1 101 +#define GPIO_BOARD_ID2 171 + +static struct NS16550_plat serial_plat = { + .clock = 48000000, /* 48MHz (APLL96/2) */ + .f_caps = CONSOLE_STDIN | CONSOLE_STDOUT | CONSOLE_STDERR, + .reg_read = omap_uart_read, + .reg_write = omap_uart_write, +}; + +static struct device_d panda_serial_device = { + .id = -1, + .name = "serial_ns16550", + .map_base = OMAP44XX_UART3_BASE, + .size = 1024, + .platform_data = (void *)&serial_plat, +}; + +static int panda_console_init(void) +{ + /* Register the serial port */ + return register_device(&panda_serial_device); +} +console_initcall(panda_console_init); + +static struct memory_platform_data sram_pdata = { + .name = "ram0", + .flags = DEVFS_RDWR, +}; + +static struct device_d sdram_dev = { + .id = -1, + .name = "mem", + .map_base = 0x80000000, + .size = SZ_1G, + .platform_data = &sram_pdata, +}; + +#ifdef CONFIG_MMU +static int panda_mmu_init(void) +{ + mmu_init(); + + arm_create_section(0x80000000, 0x80000000, 256, PMD_SECT_DEF_CACHED); + arm_create_section(0x90000000, 0x80000000, 256, PMD_SECT_DEF_UNCACHED); + + mmu_enable(); + + return 0; +} +device_initcall(panda_mmu_init); +#endif + +static struct ehci_platform_data ehci_pdata = { + .flags = 0, + .hccr_offset = 0x0, + .hcor_offset = 0x10, +}; + +static struct device_d usbh_dev = { + .id = -1, + .name = "ehci", + .map_base = 0x4a064c00, + .size = 4 * 1024, + .platform_data = &ehci_pdata, +}; + +static void panda_ehci_init(void) +{ + u32 val; + int hub_nreset; + + if (board_revision) + hub_nreset = GPIO_HUB_NRESET_62; + else + hub_nreset = GPIO_HUB_NRESET_39; + + /* disable the power to the usb hub prior to init */ + gpio_direction_output(GPIO_HUB_POWER, 0); + gpio_set_value(GPIO_HUB_POWER, 0); + + /* reset phy+hub */ + gpio_direction_output(hub_nreset, 0); + gpio_set_value(hub_nreset, 0); + gpio_set_value(hub_nreset, 1); + val = readl(0x4a009358); + val |= (1 << 24); + writel(val, 0x4a009358); + writel(0x7, 0x4a008180); + mdelay(10); + + writel(0x00000014, 0x4a064010); + writel(0x8000001c, 0x4a064040); + + /* enable power to hub */ + gpio_set_value(GPIO_HUB_POWER, 1); + + register_device(&usbh_dev); +} + +static void __init panda_boardrev_init(void) +{ + board_revision = gpio_get_value(GPIO_BOARD_ID0); + board_revision |= (gpio_get_value(GPIO_BOARD_ID1)<<1); + board_revision |= (gpio_get_value(GPIO_BOARD_ID2)<<2); + + pr_info("PandaBoard Revision: %03d\n", board_revision); +} + +static struct device_d hsmmc_dev = { + .id = -1, + .name = "omap-hsmmc", + .map_base = 0x4809C100, + .size = SZ_4K, +}; + +static int panda_devices_init(void) +{ + panda_boardrev_init(); + + if (gpio_get_value(182)) { + /* enable software ioreq */ + sr32(OMAP44XX_SCRM_AUXCLK3, 8, 1, 0x1); + /* set for sys_clk (38.4MHz) */ + sr32(OMAP44XX_SCRM_AUXCLK3, 1, 2, 0x0); + /* set divisor to 2 */ + sr32(OMAP44XX_SCRM_AUXCLK3, 16, 4, 0x1); + /* set the clock source to active */ + sr32(OMAP44XX_SCRM_ALTCLKSRC, 0, 1, 0x1); + /* enable clocks */ + sr32(OMAP44XX_SCRM_ALTCLKSRC, 2, 2, 0x3); + } else { + /* enable software ioreq */ + sr32(OMAP44XX_SCRM_AUXCLK1, 8, 1, 0x1); + /* set for PER_DPLL */ + sr32(OMAP44XX_SCRM_AUXCLK1, 1, 2, 0x2); + /* set divisor to 16 */ + sr32(OMAP44XX_SCRM_AUXCLK1, 16, 4, 0xf); + /* set the clock source to active */ + sr32(OMAP44XX_SCRM_ALTCLKSRC, 0, 1, 0x1); + /* enable clocks */ + sr32(OMAP44XX_SCRM_ALTCLKSRC, 2, 2, 0x3); + } + + register_device(&sdram_dev); + register_device(&hsmmc_dev); + panda_ehci_init(); + + armlinux_add_dram(&sdram_dev); + armlinux_set_bootparams((void *)0x80000100); + armlinux_set_architecture(MACH_TYPE_OMAP4_PANDA); + + return 0; +} +device_initcall(panda_devices_init); + +#ifdef CONFIG_DEFAULT_ENVIRONMENT +static int panda_env_init(void) +{ + struct stat s; + char *diskdev = "/dev/disk0.0"; + int ret; + + ret = stat(diskdev, &s); + if (ret) { + printf("no %s. using default env\n", diskdev); + return 0; + } + + mkdir ("/boot", 0666); + ret = mount(diskdev, "fat", "/boot"); + if (ret) { + printf("failed to mount %s\n", diskdev); + return 0; + } + + default_environment_path = "/boot/bareboxenv"; + + return 0; +} +late_initcall(panda_env_init); +#endif + + +#ifdef CONFIG_SHELL_NONE +int run_shell(void) +{ + int (*func)(void); + + func = omap_xload_boot_mmc(); + if (!func) { + printf("booting failed\n"); + while (1); + } + + shutdown_barebox(); + func(); + + while (1); +} +#endif diff --git a/arch/arm/boards/panda/config.h b/arch/arm/boards/panda/config.h new file mode 100644 index 00000000..da84fa5f --- /dev/null +++ b/arch/arm/boards/panda/config.h @@ -0,0 +1 @@ +/* nothing */ diff --git a/arch/arm/boards/panda/env/config b/arch/arm/boards/panda/env/config new file mode 100644 index 00000000..363208e9 --- /dev/null +++ b/arch/arm/boards/panda/env/config @@ -0,0 +1,45 @@ +#!/bin/sh + +machine=panda +user= + +# use 'dhcp' to do dhcp in barebox and in kernel +# use 'none' if you want to skip kernel ip autoconfiguration +ip=dhcp + +# or set your networking parameters here +#eth0.ipaddr=a.b.c.d +#eth0.netmask=a.b.c.d +#eth0.gateway=a.b.c.d +#eth0.serverip=a.b.c.d + +# can be either 'nfs', 'tftp', 'nor' or 'nand' +kernel_loc=tftp +# can be either 'net', 'nor', 'nand' or 'initrd' +rootfs_loc=net + +# The image type of the kernel. Can be uimage, zimage, raw, or raw_lzo +kernelimage_type=zimage +kernelimage=zImage-$machine +#kernelimage_type=uimage +#kernelimage=uImage-$machine +#kernelimage_type=raw +#kernelimage=Image-$machine +#kernelimage_type=raw_lzo +#kernelimage=Image-${machine}.lzo + +if [ -n $user ]; then + kernelimage="$user"-"$kernelimage" + nfsroot="$eth0.serverip:/home/$user/nfsroot/$machine" + rootfsimage="$user"-"$rootfsimage" +else + nfsroot="$eth0.serverip:/path/to/nfs/root" +fi + +autoboot_timeout=3 + +bootargs="console=ttyO2,115200" + +# set a fancy prompt (if support is compiled in) +PS1="\e[1;32mbarebox@\e[1;31m\h:\w\e[0m " + diff --git a/arch/arm/boards/panda/lowlevel.c b/arch/arm/boards/panda/lowlevel.c new file mode 100644 index 00000000..cc0c374a --- /dev/null +++ b/arch/arm/boards/panda/lowlevel.c @@ -0,0 +1,121 @@ +/* + * (C) Copyright 2004-2009 + * Texas Instruments, <www.ti.com> + * Richard Woodruff <r-woodruff2@ti.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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 <common.h> +#include <asm/io.h> +#include <mach/omap4-mux.h> +#include <mach/omap4-silicon.h> +#include <mach/omap4-clock.h> +#include <mach/syslib.h> +#include <asm/barebox-arm.h> + +void set_muxconf_regs(void); + +static const struct ddr_regs ddr_regs_400_mhz_2cs = { + /* tRRD changed from 10ns to 12.5ns because of the tFAW requirement*/ + .tim1 = 0x10eb0662, + .tim2 = 0x20370dd2, + .tim3 = 0x00b1c33f, + .phy_ctrl_1 = 0x849FF408, + .ref_ctrl = 0x00000618, + .config_init = 0x80000eb9, + .config_final = 0x80001ab9, + .zq_config = 0xD00b3215, + .mr1 = 0x83, + .mr2 = 0x4 +}; + +#define I2C_SLAVE 0x12 + +static int noinline scale_vcores(void) +{ + unsigned int rev = omap4_revision(); + + /* For VC bypass only VCOREx_CGF_FORCE is necessary and + * VCOREx_CFG_VOLTAGE changes can be discarded + */ + writel(0, OMAP44XX_PRM_VC_CFG_I2C_MODE); + writel(0x6026, OMAP44XX_PRM_VC_CFG_I2C_CLK); + + /* set VCORE1 force VSEL */ + omap4_power_i2c_send((0x3A55 << 8) | I2C_SLAVE); + + /* FIXME: set VCORE2 force VSEL, Check the reset value */ + omap4_power_i2c_send((0x295B << 8) | I2C_SLAVE); + + /* set VCORE3 force VSEL */ + switch (rev) { + case OMAP4430_ES2_0: + omap4_power_i2c_send((0x2961 << 8) | I2C_SLAVE); + break; + case OMAP4430_ES2_1: + omap4_power_i2c_send((0x2A61 << 8) | I2C_SLAVE); + break; + } + + return 0; +} + +static void noinline panda_init_lowlevel(void) +{ + struct dpll_param core = OMAP4_CORE_DPLL_PARAM_38M4_DDR400; + struct dpll_param mpu = OMAP4_MPU_DPLL_PARAM_38M4_MPU600; + struct dpll_param iva = OMAP4_IVA_DPLL_PARAM_38M4; + struct dpll_param per = OMAP4_PER_DPLL_PARAM_38M4; + struct dpll_param abe = OMAP4_ABE_DPLL_PARAM_38M4; + struct dpll_param usb = OMAP4_USB_DPLL_PARAM_38M4; + + writel(CM_SYS_CLKSEL_38M4, CM_SYS_CLKSEL); + + /* Configure all DPLL's at 100% OPP */ + omap4_configure_mpu_dpll(&mpu); + omap4_configure_iva_dpll(&iva); + omap4_configure_per_dpll(&per); + omap4_configure_abe_dpll(&abe); + omap4_configure_usb_dpll(&usb); + + /* Enable all clocks */ + omap4_enable_all_clocks(); + + set_muxconf_regs(); + + omap4_ddr_init(&ddr_regs_400_mhz_2cs, &core); + + /* Set VCORE1 = 1.3 V, VCORE2 = VCORE3 = 1.21V */ + scale_vcores(); + + board_init_lowlevel_return(); +} + +void board_init_lowlevel(void) +{ + u32 r; + + if (get_pc() > 0x80000000) + return; + + r = 0x4030d000; + __asm__ __volatile__("mov sp, %0" : : "r"(r)); + + panda_init_lowlevel(); +} + diff --git a/arch/arm/boards/panda/mux.c b/arch/arm/boards/panda/mux.c new file mode 100644 index 00000000..a0310401 --- /dev/null +++ b/arch/arm/boards/panda/mux.c @@ -0,0 +1,257 @@ +#include <common.h> +#include <init.h> +#include <asm/io.h> +#include <mach/omap4-silicon.h> +#include <mach/omap4-mux.h> + +static const struct pad_conf_entry core_padconf_array[] = { + { GPMC_AD0, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M1 /* sdmmc2_dat0 */ }, + { GPMC_AD1, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M1 /* sdmmc2_dat1 */ }, + { GPMC_AD2, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M1 /* sdmmc2_dat2 */ }, + { GPMC_AD3, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M1 /* sdmmc2_dat3 */ }, + { GPMC_AD4, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M1 /* sdmmc2_dat4 */ }, + { GPMC_AD5, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M1 /* sdmmc2_dat5 */ }, + { GPMC_AD6, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M1 /* sdmmc2_dat6 */ }, + { GPMC_AD7, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M1 /* sdmmc2_dat7 */ }, + { GPMC_AD8, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M3 /* gpio_32 */ }, + { GPMC_AD9, PTU | IEN | M3 /* gpio_33 */ }, + { GPMC_AD10, PTU | IEN | M3 /* gpio_34 */ }, + { GPMC_AD11, PTU | IEN | M3 /* gpio_35 */ }, + { GPMC_AD12, PTU | IEN | M3 /* gpio_36 */ }, + { GPMC_AD13, PTD | OFF_EN | OFF_PD | OFF_OUT_PTD | M3 /* gpio_37 */ }, + { GPMC_AD14, PTD | OFF_EN | OFF_PD | OFF_OUT_PTD | M3 /* gpio_38 */ }, + { GPMC_AD15, PTD | OFF_EN | OFF_PD | OFF_OUT_PTD | M3 /* gpio_39 */ }, + { GPMC_A16, M3 /* gpio_40 */ }, + { GPMC_A17, PTD | M3 /* gpio_41 */ }, + { GPMC_A18, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M1 /* kpd_row6 */ }, + { GPMC_A19, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M1 /* kpd_row7 */ }, + { GPMC_A20, IEN | M3 /* gpio_44 */ }, + { GPMC_A21, M3 /* gpio_45 */ }, + { GPMC_A22, M3 /* gpio_46 */ }, + { GPMC_A23, OFF_EN | OFF_PD | OFF_IN | M1 /* kpd_col7 */ }, + { GPMC_A24, PTD | M3 /* gpio_48 */ }, + { GPMC_A25, PTD | M3 /* gpio_49 */ }, + { GPMC_NCS0, M3 /* gpio_50 */ }, + { GPMC_NCS1, IEN | M3 /* gpio_51 */ }, + { GPMC_NCS2, IEN | M3 /* gpio_52 */ }, + { GPMC_NCS3, IEN | M3 /* gpio_53 */ }, + { GPMC_NWP, M3 /* gpio_54 */ }, + { GPMC_CLK, PTD | M3 /* gpio_55 */ }, + { GPMC_NADV_ALE, M3 /* gpio_56 */ }, + { GPMC_NOE, PTU | IEN | OFF_EN | OFF_OUT_PTD | M1 /* sdmmc2_clk */ }, + { GPMC_NWE, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M1 /* sdmmc2_cmd */ }, + { GPMC_NBE0_CLE, M3 /* gpio_59 */ }, + { GPMC_NBE1, PTD | M3 /* gpio_60 */ }, + { GPMC_WAIT0, PTU | IEN | M3 /* gpio_61 */ }, + { GPMC_WAIT1, PTD | OFF_EN | OFF_PD | OFF_OUT_PTD | M3 /* gpio_62 */ }, + { C2C_DATA11, PTD | M3 /* gpio_100 */ }, + { C2C_DATA12, PTD | IEN | M3 /* gpio_101 */ }, + { C2C_DATA13, PTD | M3 /* gpio_102 */ }, + { C2C_DATA14, M1 /* dsi2_te0 */ }, + { C2C_DATA15, PTD | M3 /* gpio_104 */ }, + { HDMI_HPD, M0 /* hdmi_hpd */ }, + { HDMI_CEC, M0 /* hdmi_cec */ }, + { HDMI_DDC_SCL, PTU | M0 /* hdmi_ddc_scl */ }, + { HDMI_DDC_SDA, PTU | IEN | M0 /* hdmi_ddc_sda */ }, + { CSI21_DX0, IEN | M0 /* csi21_dx0 */ }, + { CSI21_DY0, IEN | M0 /* csi21_dy0 */ }, + { CSI21_DX1, IEN | M0 /* csi21_dx1 */ }, + { CSI21_DY1, IEN | M0 /* csi21_dy1 */ }, + { CSI21_DX2, IEN | M0 /* csi21_dx2 */ }, + { CSI21_DY2, IEN | M0 /* csi21_dy2 */ }, + { CSI21_DX3, PTD | M7 /* csi21_dx3 */ }, + { CSI21_DY3, PTD | M7 /* csi21_dy3 */ }, + { CSI21_DX4, PTD | OFF_EN | OFF_PD | OFF_IN | M7 /* csi21_dx4 */ }, + { CSI21_DY4, PTD | OFF_EN | OFF_PD | OFF_IN | M7 /* csi21_dy4 */ }, + { CSI22_DX0, IEN | M0 /* csi22_dx0 */ }, + { CSI22_DY0, IEN | M0 /* csi22_dy0 */ }, + { CSI22_DX1, IEN | M0 /* csi22_dx1 */ }, + { CSI22_DY1, IEN | M0 /* csi22_dy1 */ }, + { CAM_SHUTTER, OFF_EN | OFF_PD | OFF_OUT_PTD | M0 /* cam_shutter */ }, + { CAM_STROBE, OFF_EN | OFF_PD | OFF_OUT_PTD | M0 /* cam_strobe */ }, + { CAM_GLOBALRESET, PTD | OFF_EN | OFF_PD | OFF_OUT_PTD | M3 /* gpio_83 */ }, + { USBB1_ULPITLL_CLK, PTD | IEN | OFF_EN | OFF_PD | OFF_IN | M4 /* usbb1_ulpiphy_clk */ }, + { USBB1_ULPITLL_STP, OFF_EN | OFF_OUT_PTD | M4 /* usbb1_ulpiphy_stp */ }, + { USBB1_ULPITLL_DIR, IEN | OFF_EN | OFF_PD | OFF_IN | M4 /* usbb1_ulpiphy_dir */ }, + { USBB1_ULPITLL_NXT, IEN | OFF_EN | OFF_PD | OFF_IN | M4 /* usbb1_ulpiphy_nxt */ }, + { USBB1_ULPITLL_DAT0, IEN | OFF_EN | OFF_PD | OFF_IN | M4 /* usbb1_ulpiphy_dat0 */ }, + { USBB1_ULPITLL_DAT1, IEN | OFF_EN | OFF_PD | OFF_IN | M4 /* usbb1_ulpiphy_dat1 */ }, + { USBB1_ULPITLL_DAT2, IEN | OFF_EN | OFF_PD | OFF_IN | M4 /* usbb1_ulpiphy_dat2 */ }, + { USBB1_ULPITLL_DAT3, IEN | OFF_EN | OFF_PD | OFF_IN | M4 /* usbb1_ulpiphy_dat3 */ }, + { USBB1_ULPITLL_DAT4, IEN | OFF_EN | OFF_PD | OFF_IN | M4 /* usbb1_ulpiphy_dat4 */ }, + { USBB1_ULPITLL_DAT5, IEN | OFF_EN | OFF_PD | OFF_IN | M4 /* usbb1_ulpiphy_dat5 */ }, + { USBB1_ULPITLL_DAT6, IEN | OFF_EN | OFF_PD | OFF_IN | M4 /* usbb1_ulpiphy_dat6 */ }, + { USBB1_ULPITLL_DAT7, IEN | OFF_EN | OFF_PD | OFF_IN | M4 /* usbb1_ulpiphy_dat7 */ }, + { USBB1_HSIC_DATA, IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* usbb1_hsic_data */ }, + { USBB1_HSIC_STROBE, IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* usbb1_hsic_strobe */ }, + { USBC1_ICUSB_DP, IEN | M0 /* usbc1_icusb_dp */ }, + { USBC1_ICUSB_DM, IEN | M0 /* usbc1_icusb_dm */ }, + { SDMMC1_CLK, PTU | OFF_EN | OFF_OUT_PTD | M0 /* sdmmc1_clk */ }, + { SDMMC1_CMD, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* sdmmc1_cmd */ }, + { SDMMC1_DAT0, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* sdmmc1_dat0 */ }, + { SDMMC1_DAT1, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* sdmmc1_dat1 */ }, + { SDMMC1_DAT2, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* sdmmc1_dat2 */ }, + { SDMMC1_DAT3, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* sdmmc1_dat3 */ }, + { SDMMC1_DAT4, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* sdmmc1_dat4 */ }, + { SDMMC1_DAT5, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* sdmmc1_dat5 */ }, + { SDMMC1_DAT6, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* sdmmc1_dat6 */ }, + { SDMMC1_DAT7, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* sdmmc1_dat7 */ }, + { ABE_MCBSP2_CLKX, IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* abe_mcbsp2_clkx */ }, + { ABE_MCBSP2_DR, IEN | OFF_EN | OFF_OUT_PTD | M0 /* abe_mcbsp2_dr */ }, + { ABE_MCBSP2_DX, OFF_EN | OFF_OUT_PTD | M0 /* abe_mcbsp2_dx */ }, + { ABE_MCBSP2_FSX, IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* abe_mcbsp2_fsx */ }, + { ABE_MCBSP1_CLKX, IEN | M1 /* abe_slimbus1_clock */ }, + { ABE_MCBSP1_DR, IEN | M1 /* abe_slimbus1_data */ }, + { ABE_MCBSP1_DX, OFF_EN | OFF_OUT_PTD | M0 /* abe_mcbsp1_dx */ }, + { ABE_MCBSP1_FSX, IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* abe_mcbsp1_fsx */ }, + { ABE_PDM_UL_DATA, PTD | IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* abe_pdm_ul_data */ }, + { ABE_PDM_DL_DATA, PTD | IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* abe_pdm_dl_data */ }, + { ABE_PDM_FRAME, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* abe_pdm_frame */ }, + { ABE_PDM_LB_CLK, PTD | IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* abe_pdm_lb_clk */ }, + { ABE_CLKS, PTD | IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* abe_clks */ }, + { ABE_DMIC_CLK1, M0 /* abe_dmic_clk1 */ }, + { ABE_DMIC_DIN1, IEN | M0 /* abe_dmic_din1 */ }, + { ABE_DMIC_DIN2, IEN | M0 /* abe_dmic_din2 */ }, + { ABE_DMIC_DIN3, IEN | M0 /* abe_dmic_din3 */ }, + { UART2_CTS, PTU | IEN | M0 /* uart2_cts */ }, + { UART2_RTS, M0 /* uart2_rts */ }, + { UART2_RX, PTU | IEN | M0 /* uart2_rx */ }, + { UART2_TX, M0 /* uart2_tx */ }, + { HDQ_SIO, M3 /* gpio_127 */ }, + { I2C1_SCL, PTU | IEN | M0 /* i2c1_scl */ }, + { I2C1_SDA, PTU | IEN | M0 /* i2c1_sda */ }, + { I2C2_SCL, PTU | IEN | M0 /* i2c2_scl */ }, + { I2C2_SDA, PTU | IEN | M0 /* i2c2_sda */ }, + { I2C3_SCL, PTU | IEN | M0 /* i2c3_scl */ }, + { I2C3_SDA, PTU | IEN | M0 /* i2c3_sda */ }, + { I2C4_SCL, PTU | IEN | M0 /* i2c4_scl */ }, + { I2C4_SDA, PTU | IEN | M0 /* i2c4_sda */ }, + { MCSPI1_CLK, IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* mcspi1_clk */ }, + { MCSPI1_SOMI, IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* mcspi1_somi */ }, + { MCSPI1_SIMO, IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* mcspi1_simo */ }, + { MCSPI1_CS0, PTD | IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* mcspi1_cs0 */ }, + { MCSPI1_CS1, PTD | IEN | OFF_EN | OFF_PD | OFF_IN | M3 /* mcspi1_cs1 */ }, + { MCSPI1_CS2, PTU | OFF_EN | OFF_OUT_PTU | M3 /* gpio_139 */ }, + { MCSPI1_CS3, PTU | IEN | M3 /* gpio_140 */ }, + { UART3_CTS_RCTX, PTU | IEN | M0 /* uart3_tx */ }, + { UART3_RTS_SD, M0 /* uart3_rts_sd */ }, + { UART3_RX_IRRX, IEN | M0 /* uart3_rx */ }, + { UART3_TX_IRTX, M0 /* uart3_tx */ }, + { SDMMC5_CLK, PTU | IEN | OFF_EN | OFF_OUT_PTD | M0 /* sdmmc5_clk */ }, + { SDMMC5_CMD, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* sdmmc5_cmd */ }, + { SDMMC5_DAT0, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* sdmmc5_dat0 */ }, + { SDMMC5_DAT1, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* sdmmc5_dat1 */ }, + { SDMMC5_DAT2, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* sdmmc5_dat2 */ }, + { SDMMC5_DAT3, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* sdmmc5_dat3 */ }, + { MCSPI4_CLK, IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* mcspi4_clk */ }, + { MCSPI4_SIMO, IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* mcspi4_simo */ }, + { MCSPI4_SOMI, IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* mcspi4_somi */ }, + { MCSPI4_CS0, PTD | IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* mcspi4_cs0 */ }, + { UART4_RX, IEN | M0 /* uart4_rx */ }, + { UART4_TX, M0 /* uart4_tx */ }, + { USBB2_ULPITLL_CLK, IEN | M3 /* gpio_157 */ }, + { USBB2_ULPITLL_STP, IEN | M5 /* dispc2_data23 */ }, + { USBB2_ULPITLL_DIR, IEN | M5 /* dispc2_data22 */ }, + { USBB2_ULPITLL_NXT, IEN | M5 /* dispc2_data21 */ }, + { USBB2_ULPITLL_DAT0, IEN | M5 /* dispc2_data20 */ }, + { USBB2_ULPITLL_DAT1, IEN | M5 /* dispc2_data19 */ }, + { USBB2_ULPITLL_DAT2, IEN | M5 /* dispc2_data18 */ }, + { USBB2_ULPITLL_DAT3, IEN | M5 /* dispc2_data15 */ }, + { USBB2_ULPITLL_DAT4, IEN | M5 /* dispc2_data14 */ }, + { USBB2_ULPITLL_DAT5, IEN | M5 /* dispc2_data13 */ }, + { USBB2_ULPITLL_DAT6, IEN | M5 /* dispc2_data12 */ }, + { USBB2_ULPITLL_DAT7, IEN | M5 /* dispc2_data11 */ }, + { USBB2_HSIC_DATA, PTD | OFF_EN | OFF_OUT_PTU | M3 /* gpio_169 */ }, + { USBB2_HSIC_STROBE, PTD | OFF_EN | OFF_OUT_PTU | M3 /* gpio_170 */ }, + { UNIPRO_TX0, PTD | IEN | M3 /* gpio_171 */ }, + { UNIPRO_TY0, OFF_EN | OFF_PD | OFF_IN | M1 /* kpd_col1 */ }, + { UNIPRO_TX1, OFF_EN | OFF_PD | OFF_IN | M1 /* kpd_col2 */ }, + { UNIPRO_TY1, OFF_EN | OFF_PD | OFF_IN | M1 /* kpd_col3 */ }, + { UNIPRO_TX2, PTU | IEN | M3 /* gpio_0 */ }, + { UNIPRO_TY2, PTU | IEN | M3 /* gpio_1 */ }, + { UNIPRO_RX0, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M1 /* kpd_row0 */ }, + { UNIPRO_RY0, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M1 /* kpd_row1 */ }, + { UNIPRO_RX1, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M1 /* kpd_row2 */ }, + { UNIPRO_RY1, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M1 /* kpd_row3 */ }, + { UNIPRO_RX2, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M1 /* kpd_row4 */ }, + { UNIPRO_RY2, PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M1 /* kpd_row5 */ }, + { USBA0_OTG_CE, PTD | OFF_EN | OFF_PD | OFF_OUT_PTD | M0 /* usba0_otg_ce */ }, + { USBA0_OTG_DP, IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* usba0_otg_dp */ }, + { USBA0_OTG_DM, IEN | OFF_EN | OFF_PD | OFF_IN | M0 /* usba0_otg_dm */ }, + { FREF_CLK1_OUT, M0 /* fref_clk1_out */ }, + { FREF_CLK2_OUT, PTD | IEN | M3 /* gpio_182 */ }, + { SYS_NIRQ1, PTU | IEN | M0 /* sys_nirq1 */ }, + { SYS_NIRQ2, PTU | IEN | M0 /* sys_nirq2 */ }, + { SYS_BOOT0, PTU | IEN | M3 /* gpio_184 */ }, + { SYS_BOOT1, M3 /* gpio_185 */ }, + { SYS_BOOT2, PTD | IEN | M3 /* gpio_186 */ }, + { SYS_BOOT3, M3 /* gpio_187 */ }, + { SYS_BOOT4, M3 /* gpio_188 */ }, + { SYS_BOOT5, PTD | IEN | M3 /* gpio_189 */ }, + { DPM_EMU0, IEN | M0 /* dpm_emu0 */ }, + { DPM_EMU1, IEN | M0 /* dpm_emu1 */ }, + { DPM_EMU2, IEN | M0 /* dpm_emu2 */ }, + { DPM_EMU3, IEN | M5 /* dispc2_data10 */ }, + { DPM_EMU4, IEN | M5 /* dispc2_data9 */ }, + { DPM_EMU5, IEN | M5 /* dispc2_data16 */ }, + { DPM_EMU6, IEN | M5 /* dispc2_data17 */ }, + { DPM_EMU7, IEN | M5 /* dispc2_hsync */ }, + { DPM_EMU8, IEN | M5 /* dispc2_pclk */ }, + { DPM_EMU9, IEN | M5 /* dispc2_vsync */ }, + { DPM_EMU10, IEN | M5 /* dispc2_de */ }, + { DPM_EMU11, IEN | M5 /* dispc2_data8 */ }, + { DPM_EMU12, IEN | M5 /* dispc2_data7 */ }, + { DPM_EMU13, IEN | M5 /* dispc2_data6 */ }, + { DPM_EMU14, IEN | M5 /* dispc2_data5 */ }, + { DPM_EMU15, IEN | M5 /* dispc2_data4 */ }, + { DPM_EMU16, M3 /* gpio_27 */ }, + { DPM_EMU17, IEN | M5 /* dispc2_data2 */ }, + { DPM_EMU18, IEN | M5 /* dispc2_data1 */ }, + { DPM_EMU19, IEN | M5 /* dispc2_data0 */ }, +}; + +static const struct pad_conf_entry wkup_padconf_array[] = { + { PAD0_SIM_IO, IEN | M0 /* sim_io */ }, + { PAD1_SIM_CLK, M0 /* sim_clk */ }, + { PAD0_SIM_RESET, M0 /* sim_reset */ }, + { PAD1_SIM_CD, PTU | IEN | M0 /* sim_cd */ }, + { PAD0_SIM_PWRCTRL, M0 /* sim_pwrctrl */ }, + { PAD1_SR_SCL, PTU | IEN | M0 /* sr_scl */ }, + { PAD0_SR_SDA, PTU | IEN | M0 /* sr_sda */ }, + { PAD1_FREF_XTAL_IN, M0 /* # */ }, + { PAD0_FREF_SLICER_IN, M0 /* fref_slicer_in */ }, + { PAD1_FREF_CLK_IOREQ, M0 /* fref_clk_ioreq */ }, + { PAD0_FREF_CLK0_OUT, M2 /* sys_drm_msecure */ }, + { PAD1_FREF_CLK3_REQ, PTU | IEN | M0 /* # */ }, + { PAD0_FREF_CLK3_OUT, M0 /* fref_clk3_out */ }, + { PAD1_FREF_CLK4_REQ, PTU | IEN | M0 /* # */ }, + { PAD0_FREF_CLK4_OUT, M0 /* # */ }, + { PAD1_SYS_32K, IEN | M0 /* sys_32k */ }, + { PAD0_SYS_NRESPWRON, M0 /* sys_nrespwron */ }, + { PAD1_SYS_NRESWARM, M0 /* sys_nreswarm */ }, + { PAD0_SYS_PWR_REQ, PTU | M0 /* sys_pwr_req */ }, + { PAD1_SYS_PWRON_RESET, M3 /* gpio_wk29 */ }, + { PAD0_SYS_BOOT6, IEN | M3 /* gpio_wk9 */ }, + { PAD1_SYS_BOOT7, IEN | M3 /* gpio_wk10 */ }, + { PAD1_FREF_CLK3_REQ, M3 /* gpio_wk30 */ }, + { PAD1_FREF_CLK4_REQ, M3 /* gpio_wk7 */ }, + { PAD0_FREF_CLK4_OUT, M3 /* gpio_wk8 */ }, +}; + +static void do_set_mux(u32 base, struct pad_conf_entry const *array, int size) +{ + int i; + struct pad_conf_entry *pad = (struct pad_conf_entry *) array; + + for (i = 0; i < size; i++, pad++) + writew(pad->val, base + pad->offset); +} + +void set_muxconf_regs(void) +{ + do_set_mux(OMAP44XX_CONTROL_PADCONF_CORE, core_padconf_array, + ARRAY_SIZE(core_padconf_array)); + + do_set_mux(OMAP44XX_CONTROL_PADCONF_WKUP, wkup_padconf_array, + ARRAY_SIZE(wkup_padconf_array)); +} diff --git a/arch/arm/boards/pcm049/Makefile b/arch/arm/boards/pcm049/Makefile new file mode 100644 index 00000000..1bb7212a --- /dev/null +++ b/arch/arm/boards/pcm049/Makefile @@ -0,0 +1 @@ +obj-y += board.o mux.o lowlevel.o diff --git a/arch/arm/boards/pcm049/board.c b/arch/arm/boards/pcm049/board.c new file mode 100644 index 00000000..a33a877f --- /dev/null +++ b/arch/arm/boards/pcm049/board.c @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2011 Sascha Hauer, Pengutronix + * + * 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 <common.h> +#include <console.h> +#include <init.h> +#include <driver.h> +#include <asm/io.h> +#include <ns16550.h> +#include <asm/armlinux.h> +#include <generated/mach-types.h> +#include <mach/silicon.h> +#include <mach/sdrc.h> +#include <mach/sys_info.h> +#include <mach/syslib.h> +#include <mach/control.h> +#include <linux/err.h> +#include <sizes.h> +#include <partition.h> +#include <nand.h> +#include <asm/mmu.h> +#include <mach/gpio.h> +#include <mach/gpmc.h> +#include <mach/gpmc_nand.h> +#include <mach/xload.h> + +static struct NS16550_plat serial_plat = { + .clock = 48000000, /* 48MHz (APLL96/2) */ + .f_caps = CONSOLE_STDIN | CONSOLE_STDOUT | CONSOLE_STDERR, + .reg_read = omap_uart_read, + .reg_write = omap_uart_write, +}; + +static struct device_d pcm049_serial_device = { + .id = -1, + .name = "serial_ns16550", + .map_base = OMAP44XX_UART3_BASE, + .size = 1024, + .platform_data = (void *)&serial_plat, +}; + +static int pcm049_console_init(void) +{ + /* Register the serial port */ + return register_device(&pcm049_serial_device); +} +console_initcall(pcm049_console_init); + +static struct memory_platform_data sram_pdata = { + .name = "sram0", + .flags = DEVFS_RDWR, +}; + +static struct device_d sram_dev = { + .id = -1, + .name = "mem", + .map_base = 0x40300000, + .size = 48 * 1024, + .platform_data = &sram_pdata, +}; + +static struct memory_platform_data sdram_pdata = { + .name = "ram0", + .flags = DEVFS_RDWR, +}; + +static struct device_d sdram_dev = { + .id = -1, + .name = "mem", + .map_base = 0x80000000, + .size = SZ_512M, + .platform_data = &sdram_pdata, +}; + +#ifdef CONFIG_MMU +static int pcm049_mmu_init(void) +{ + mmu_init(); + + arm_create_section(0x80000000, 0x80000000, 256, PMD_SECT_DEF_CACHED); + /* warning: This shadows the second half of our ram */ + arm_create_section(0x90000000, 0x80000000, 256, PMD_SECT_DEF_UNCACHED); + + mmu_enable(); + + return 0; +} +device_initcall(pcm049_mmu_init); +#endif + +static struct device_d hsmmc_dev = { + .id = -1, + .name = "omap-hsmmc", + .map_base = 0x4809C100, + .size = SZ_4K, +}; + +static struct device_d smc911x_dev = { + .id = -1, + .name = "smc911x", + .map_base = 0x2C000000, + .size = 0x4000, +}; + +static struct gpmc_config net_cfg = { + .cfg = { + 0x00001000, /* CONF1 */ + 0x001e1e01, /* CONF2 */ + 0x00080300, /* CONF3 */ + 0x1c091c09, /* CONF4 */ + 0x04181f1f, /* CONF5 */ + 0x00000FCF, /* CONF6 */ + }, + .base = 0x2C000000, + .size = GPMC_SIZE_16M, +}; + +static void pcm049_network_init(void) +{ + gpmc_cs_config(5, &net_cfg); + + register_device(&smc911x_dev); +} + +static int pcm049_devices_init(void) +{ + register_device(&sdram_dev); + register_device(&sram_dev); + register_device(&hsmmc_dev); + + pcm049_network_init(); + + gpmc_generic_nand_devices_init(0, 8, OMAP_ECC_BCH8_CODE_HW); + +#ifdef CONFIG_PARTITION + devfs_add_partition("nand0", 0x00000, SZ_128K, PARTITION_FIXED, "xload_raw"); + dev_add_bb_dev("xload_raw", "xload"); + devfs_add_partition("nand0", SZ_128K, SZ_256K, PARTITION_FIXED, "self_raw"); + dev_add_bb_dev("self_raw", "self0"); + devfs_add_partition("nand0", SZ_128K + SZ_256K, SZ_128K, PARTITION_FIXED, "env_raw"); + dev_add_bb_dev("env_raw", "env0"); +#endif + + armlinux_add_dram(&sdram_dev); + armlinux_set_bootparams((void *)0x80000100); + armlinux_set_architecture(MACH_TYPE_PCM049); + + return 0; +} +device_initcall(pcm049_devices_init); + +#ifdef CONFIG_SHELL_NONE +int run_shell(void) +{ + int (*func)(void) = NULL; + + switch (omap4_bootsrc()) { + case OMAP_BOOTSRC_MMC1: + printf("booting from MMC1\n"); + func = omap_xload_boot_mmc(); + break; + case OMAP_BOOTSRC_UNKNOWN: + printf("unknown boot source. Fall back to nand\n"); + case OMAP_BOOTSRC_NAND: + printf("booting from NAND\n"); + func = omap_xload_boot_nand(SZ_128K, SZ_256K); + break; + } + + if (!func) { + printf("booting failed\n"); + while (1); + } + + shutdown_barebox(); + func(); + + while (1); +} +#endif diff --git a/arch/arm/boards/pcm049/config.h b/arch/arm/boards/pcm049/config.h new file mode 100644 index 00000000..da84fa5f --- /dev/null +++ b/arch/arm/boards/pcm049/config.h @@ -0,0 +1 @@ +/* nothing */ diff --git a/arch/arm/boards/pcm049/env/bin/nand_bootstrap b/arch/arm/boards/pcm049/env/bin/nand_bootstrap new file mode 100644 index 00000000..acd00dc9 --- /dev/null +++ b/arch/arm/boards/pcm049/env/bin/nand_bootstrap @@ -0,0 +1,31 @@ + +echo "copying barebox to nand..." + +mci0.probe=1 +mkdir mnt + +mount /dev/disk0.0 fat /mnt +if [ $? != 0 ]; then + echo "failed to mount mmc card" + exit 1 +fi + +if [ ! -f /mnt/mlo-nand.bin ]; then + echo "mlo-nand.bin not found on mmc card" + exit 1 +fi + +if [ ! -f /mnt/barebox.bin ]; then + echo "barebox.bin not found on mmc card" +fi + +gpmc_nand0.eccmode=bch8_hw_romcode +erase /dev/nand0.xload.bb +cp /mnt/mlo-nand.bin /dev/nand0.xload.bb + +gpmc_nand0.eccmode=bch8_hw +erase /dev/nand0.barebox.bb +cp /mnt/barebox.bin /dev/nand0.barebox.bb + +echo "success" + diff --git a/arch/arm/boards/pcm049/env/config b/arch/arm/boards/pcm049/env/config new file mode 100644 index 00000000..54b2e3dd --- /dev/null +++ b/arch/arm/boards/pcm049/env/config @@ -0,0 +1,53 @@ +#!/bin/sh + +machine=pcm049 +eth0.serverip= +user= + +# use 'dhcp' to do dhcp in barebox and in kernel +# use 'none' if you want to skip kernel ip autoconfiguration +ip=dhcp + +# or set your networking parameters here +#eth0.ipaddr=a.b.c.d +#eth0.netmask=a.b.c.d +#eth0.gateway=a.b.c.d +#eth0.serverip=a.b.c.d + +# can be either 'nfs', 'tftp', 'nor' or 'nand' +kernel_loc=tftp +# can be either 'net', 'nor', 'nand' or 'initrd' +rootfs_loc=net + +# can be either 'jffs2' or 'ubifs' +rootfs_type=ubifs +rootfsimage=root-${machine}.$rootfs_type + +# The image type of the kernel. Can be uimage, zimage, raw, or raw_lzo +kernelimage_type=zimage +kernelimage=zImage-$machine +#kernelimage_type=uimage +#kernelimage=uImage-$machine +#kernelimage_type=raw +#kernelimage=Image-$machine +#kernelimage_type=raw_lzo +#kernelimage=Image-${machine}.lzo + +if [ -n $user ]; then + kernelimage="$user"-"$kernelimage" + nfsroot="$eth0.serverip:/home/$user/nfsroot/$machine" + rootfsimage="$user"-"$rootfsimage" +else + nfsroot="$eth0.serverip:/path/to/nfs/root" +fi + +autoboot_timeout=3 + +bootargs="console=ttyO2,115200" + +nand_parts="128k(xload)ro,256k(barebox),128k(bareboxenv),2M(kernel),-(root)" +rootfs_mtdblock_nand=4 + +# set a fancy prompt (if support is compiled in) +PS1="\e[1;32mbarebox@\e[1;31m\h:\w\e[0m " + diff --git a/arch/arm/boards/pcm049/lowlevel.c b/arch/arm/boards/pcm049/lowlevel.c new file mode 100644 index 00000000..e036ba74 --- /dev/null +++ b/arch/arm/boards/pcm049/lowlevel.c @@ -0,0 +1,127 @@ +/* + * (C) Copyright 2004-2009 + * Texas Instruments, <www.ti.com> + * Richard Woodruff <r-woodruff2@ti.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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 <common.h> +#include <asm/io.h> +#include <mach/omap4-mux.h> +#include <mach/omap4-silicon.h> +#include <mach/omap4-clock.h> +#include <mach/syslib.h> +#include <asm/barebox-arm.h> + +void set_muxconf_regs(void); + +/* Erstmal 200Mhz... */ +static const struct ddr_regs ddr_regs_mt42L64M64_3_200_mhz = { + .tim1 = 0x0aa8d4e3, + .tim2 = 0x202e0b92, + .tim3 = 0x009da2b3, + .phy_ctrl_1 = 0x849FF404, /* mostly from elpida */ + .ref_ctrl = 0x0000030c, /* from elpida 200MHz! */ + .config_init = 0x80000eb1, + .config_final = 0x80000eb1, + .zq_config = 0x500b3215, /* mostly from elpida */ + .mr1 = 0x23, /* from elpida 200MHz! */ + .mr2 = 0x1 /* from elpida 200MHz! */ +}; + +#define I2C_SLAVE 0x12 + +static int noinline scale_vcores(void) +{ + unsigned int rev = omap4_revision(); + + /* For VC bypass only VCOREx_CGF_FORCE is necessary and + * VCOREx_CFG_VOLTAGE changes can be discarded + */ + writel(0, OMAP44XX_PRM_VC_CFG_I2C_MODE); + writel(0x6026, OMAP44XX_PRM_VC_CFG_I2C_CLK); + + /* set VCORE1 force VSEL */ + omap4_power_i2c_send((0x3A55 << 8) | I2C_SLAVE); + + /* FIXME: set VCORE2 force VSEL, Check the reset value */ + omap4_power_i2c_send((0x295B << 8) | I2C_SLAVE); + + /* set VCORE3 force VSEL */ + switch (rev) { + case OMAP4430_ES2_0: + omap4_power_i2c_send((0x2961 << 8) | I2C_SLAVE); + break; + case OMAP4430_ES2_1: + omap4_power_i2c_send((0x2A61 << 8) | I2C_SLAVE); + break; + } + + return 0; +} + +static void noinline pcm049_init_lowlevel(void) +{ + struct dpll_param core = OMAP4_CORE_DPLL_PARAM_19M2_DDR200; + struct dpll_param mpu = OMAP4_MPU_DPLL_PARAM_19M2_MPU1000; + struct dpll_param iva = OMAP4_IVA_DPLL_PARAM_19M2; + struct dpll_param per = OMAP4_PER_DPLL_PARAM_19M2; + struct dpll_param abe = OMAP4_ABE_DPLL_PARAM_19M2; + struct dpll_param usb = OMAP4_USB_DPLL_PARAM_19M2; + + set_muxconf_regs(); + + omap4_ddr_init(&ddr_regs_mt42L64M64_3_200_mhz, &core); + + /* Set VCORE1 = 1.3 V, VCORE2 = VCORE3 = 1.21V */ + scale_vcores(); + + writel(CM_SYS_CLKSEL_19M2, CM_SYS_CLKSEL); + + /* Configure all DPLL's at 100% OPP */ + omap4_configure_mpu_dpll(&mpu); + omap4_configure_iva_dpll(&iva); + omap4_configure_per_dpll(&per); + omap4_configure_abe_dpll(&abe); + omap4_configure_usb_dpll(&usb); + + /* Enable all clocks */ + omap4_enable_all_clocks(); + + sr32(0x4A30a31C, 8, 1, 0x1); /* enable software ioreq */ + sr32(0x4A30a31C, 1, 2, 0x0); /* set for sys_clk (19.2MHz) */ + sr32(0x4A30a31C, 16, 4, 0x0); /* set divisor to 1 */ + sr32(0x4A30a110, 0, 1, 0x1); /* set the clock source to active */ + sr32(0x4A30a110, 2, 2, 0x3); /* enable clocks */ + + board_init_lowlevel_return(); +} + +void board_init_lowlevel(void) +{ + u32 r; + + if (get_pc() > 0x80000000) + return; + + r = 0x4030d000; + __asm__ __volatile__("mov sp, %0" : : "r"(r)); + + pcm049_init_lowlevel(); +} + diff --git a/arch/arm/boards/pcm049/mux.c b/arch/arm/boards/pcm049/mux.c new file mode 100644 index 00000000..d93d4589 --- /dev/null +++ b/arch/arm/boards/pcm049/mux.c @@ -0,0 +1,254 @@ +#include <common.h> +#include <init.h> +#include <asm/io.h> +#include <mach/omap4-silicon.h> +#include <mach/omap4-mux.h> + +static const struct pad_conf_entry core_padconf_array[] = { + {GPMC_AD0, (IEN | PTD | DIS | M0)}, /* gpmc_ad0 */ + {GPMC_AD1, (IEN | PTD | DIS | M0)}, /* gpmc_ad1 */ + {GPMC_AD2, (IEN | PTD | DIS | M0)}, /* gpmc_ad2 */ + {GPMC_AD3, (IEN | PTD | DIS | M0)}, /* gpmc_ad3 */ + {GPMC_AD4, (IEN | PTD | DIS | M0)}, /* gpmc_ad4 */ + {GPMC_AD5, (IEN | PTD | DIS | M0)}, /* gpmc_ad5 */ + {GPMC_AD6, (IEN | PTD | DIS | M0)}, /* gpmc_ad6 */ + {GPMC_AD7, (IEN | PTD | DIS | M0)}, /* gpmc_ad7 */ + {GPMC_AD8, (IEN | PTD | DIS | M0)}, /* gpmc_ad8 */ + {GPMC_AD9, (IEN | PTD | DIS | M0)}, /* gpmc_ad9 */ + {GPMC_AD10, (IEN | PTD | DIS | M0)}, /* gpmc_ad10 */ + {GPMC_AD11, (IEN | PTD | DIS | M0)}, /* gpmc_ad11 */ + {GPMC_AD12, (IEN | PTD | DIS | M0)}, /* gpmc_ad12 */ + {GPMC_AD13, (IEN | PTD | DIS | M0)}, /* gpmc_ad13 */ + {GPMC_AD14, (IEN | PTD | DIS | M0)}, /* gpmc_ad14 */ + {GPMC_AD15, (IEN | PTD | DIS | M0)}, /* gpmc_ad15 */ + {GPMC_A16, (IEN | PTD | DIS | M0)}, /* gpmc_a16 */ + {GPMC_A17, (IEN | PTD | DIS | M0)}, /* gpmc_a17 */ + {GPMC_A18, (IEN | PTD | DIS | M0)}, /* gpmc_a18 */ + {GPMC_A19, (IEN | PTD | DIS | M0)}, /* gpmc_a19 */ + {GPMC_A20, (IEN | PTD | DIS | M0)}, /* gpmc_a20 */ + {GPMC_A21, (IEN | PTD | DIS | M0)}, /* gpmc_a21 */ + {GPMC_A22, (IEN | PTD | DIS | M0)}, /* gpmc_a22 */ + {GPMC_A23, (IEN | PTD | DIS | M0)}, /* gpmc_a23 */ + {GPMC_A24, (IEN | PTD | DIS | M0)}, /* gpmc_a24 */ + {GPMC_A25, (IEN | PTD | DIS | M0)}, /* gpmc_a25 */ + {GPMC_NCS0, (IDIS | PTU | EN | M0)}, /* gpmc_nsc0 */ + {GPMC_NCS1, (IDIS | PTU | EN | M0)}, /* gpmc_nsc1 */ + {GPMC_NCS2, (SAFE_MODE)}, /* nc */ + {GPMC_NCS3, (SAFE_MODE)}, /* nc */ + {GPMC_NWP, (IEN | PTD | DIS | M0)}, /* gpmc_nwp */ + {GPMC_CLK, (SAFE_MODE)}, /* nc */ + {GPMC_NADV_ALE, (IDIS | PTD | DIS | M0)}, /* gpmc_ndav_ale */ + {GPMC_NOE, (IDIS | PTD | DIS | M0)}, /* gpmc_noe */ + {GPMC_NWE, (IDIS | PTD | DIS | M0)}, /* gpmc_nwe */ + {GPMC_NBE0_CLE, (IDIS | PTD | DIS | M0)}, /* gpmc_nbe0_cle */ + {GPMC_NBE1, (SAFE_MODE)}, /* nc */ + {GPMC_WAIT0, (IEN | PTU | EN | M0)}, /* gpmc_wait0 */ + {GPMC_WAIT1, (SAFE_MODE)}, /* nc */ + {C2C_DATA11, (SAFE_MODE)}, /* nc */ + {C2C_DATA12, (SAFE_MODE)}, /* nc */ + {C2C_DATA13, (IDIS | PTU | EN | M0)}, /* gpmc_nsc5 */ + {C2C_DATA14, (SAFE_MODE)}, /* nc */ + {C2C_DATA15, (SAFE_MODE)}, /* nc */ + {HDMI_HPD, (M0)}, /* hdmi_hpd */ + {HDMI_CEC, (DIS | IEN | M3)}, /* gpio_64 */ + {HDMI_DDC_SCL, (PTU | M0)}, /* hdmi_ddc_scl */ + {HDMI_DDC_SDA, (PTU | IEN | M0)}, /* hdmi_ddc_sda */ + {CSI21_DX0, (IEN | M0)}, /* csi21_dx0 */ + {CSI21_DY0, (IEN | M0)}, /* csi21_dy0 */ + {CSI21_DX1, (IEN | M0)}, /* csi21_dx1 */ + {CSI21_DY1, (IEN | M0)}, /* csi21_dy1 */ + {CSI21_DX2, (IEN | M0)}, /* csi21_dx2 */ + {CSI21_DY2, (IEN | M0)}, /* csi21_dy2 */ + {CSI21_DX3, (PTD | M7)}, /* csi21_dx3 */ + {CSI21_DY3, (PTD | M7)}, /* csi21_dy3 */ + {CSI21_DX4, (PTD | OFF_EN | OFF_PD | OFF_IN | M7)}, /* csi21_dx4 */ + {CSI21_DY4, (PTD | OFF_EN | OFF_PD | OFF_IN | M7)}, /* csi21_dy4 */ + {CSI22_DX0, (IEN | M0)}, /* csi22_dx0 */ + {CSI22_DY0, (IEN | M0)}, /* csi22_dy0 */ + {CSI22_DX1, (IEN | M0)}, /* csi22_dx1 */ + {CSI22_DY1, (IEN | M0)}, /* csi22_dy1 */ + {CAM_SHUTTER, (OFF_EN | OFF_PD | OFF_OUT_PTD | M0)}, /* cam_shutter */ + {CAM_STROBE, (OFF_EN | OFF_PD | OFF_OUT_PTD | M0)}, /* cam_strobe */ + {CAM_GLOBALRESET, (PTD | OFF_EN | OFF_PD | OFF_OUT_PTD | M3)}, /* gpio_83 */ + {USBB1_ULPITLL_CLK, (PTD | IEN | OFF_EN | OFF_PD | OFF_IN | M4)},/* usbb1_ulpiphy_clk */ + {USBB1_ULPITLL_STP, (OFF_EN | OFF_OUT_PTD | M4)}, /* usbb1_ulpiphy_stp */ + {USBB1_ULPITLL_DIR, (IEN | OFF_EN | OFF_PD | OFF_IN | M4)}, /* usbb1_ulpiphy_dir */ + {USBB1_ULPITLL_NXT, (IEN | OFF_EN | OFF_PD | OFF_IN | M4)}, /* usbb1_ulpiphy_nxt */ + {USBB1_ULPITLL_DAT0, (IEN | OFF_EN | OFF_PD | OFF_IN | M4)}, /* usbb1_ulpiphy_dat0 */ + {USBB1_ULPITLL_DAT1, (IEN | OFF_EN | OFF_PD | OFF_IN | M4)}, /* usbb1_ulpiphy_dat1 */ + {USBB1_ULPITLL_DAT2, (IEN | OFF_EN | OFF_PD | OFF_IN | M4)}, /* usbb1_ulpiphy_dat2 */ + {USBB1_ULPITLL_DAT3, (IEN | OFF_EN | OFF_PD | OFF_IN | M4)}, /* usbb1_ulpiphy_dat3 */ + {USBB1_ULPITLL_DAT4, (IEN | OFF_EN | OFF_PD | OFF_IN | M4)}, /* usbb1_ulpiphy_dat4 */ + {USBB1_ULPITLL_DAT5, (IEN | OFF_EN | OFF_PD | OFF_IN | M4)}, /* usbb1_ulpiphy_dat5 */ + {USBB1_ULPITLL_DAT6, (IEN | OFF_EN | OFF_PD | OFF_IN | M4)}, /* usbb1_ulpiphy_dat6 */ + {USBB1_ULPITLL_DAT7, (IEN | OFF_EN | OFF_PD | OFF_IN | M4)}, /* usbb1_ulpiphy_dat7 */ + {USBB1_HSIC_DATA, (DIS | IEN | M3)}, /* gpio_96 */ + {USBB1_HSIC_STROBE, (DIS | IEN | M3)}, /* gpio_97 */ + {USBC1_ICUSB_DP, (IEN | M0)}, /* usbc1_icusb_dp */ + {USBC1_ICUSB_DM, (IEN | M0)}, /* usbc1_icusb_dm */ + {SDMMC1_CLK, (PTU | OFF_EN | OFF_OUT_PTD | M0)}, /* sdmmc1_clk */ + {SDMMC1_CMD, (PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* sdmmc1_cmd */ + {SDMMC1_DAT0, (PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* sdmmc1_dat0 */ + {SDMMC1_DAT1, (PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* sdmmc1_dat1 */ + {SDMMC1_DAT2, (PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* sdmmc1_dat2 */ + {SDMMC1_DAT3, (PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* sdmmc1_dat3 */ + {SDMMC1_DAT4, (PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* sdmmc1_dat4 */ + {SDMMC1_DAT5, (PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* sdmmc1_dat5 */ + {SDMMC1_DAT6, (PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* sdmmc1_dat6 */ + {SDMMC1_DAT7, (PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* sdmmc1_dat7 */ + {ABE_MCBSP2_CLKX, (IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* abe_mcbsp2_clkx */ + {ABE_MCBSP2_DR, (IEN | OFF_EN | OFF_OUT_PTD | M0)}, /* abe_mcbsp2_dr */ + {ABE_MCBSP2_DX, (OFF_EN | OFF_OUT_PTD | M0)}, /* abe_mcbsp2_dx */ + {ABE_MCBSP2_FSX, (IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* abe_mcbsp2_fsx */ + {ABE_MCBSP1_CLKX, (DIS | IEN | M3)}, /* gpio_114 */ + {ABE_MCBSP1_DR, (DIS | IEN | M3)}, /* gpio_115 */ + {ABE_MCBSP1_DX, (DIS | IEN | M3)}, /* gpio_116 */ + {ABE_MCBSP1_FSX, (DIS | IEN | M2)}, /* abe_mcasp_amutein */ + {ABE_PDM_UL_DATA, (IEN | OFF_EN | OFF_OUT_PTD | M1)}, /* abe_mcbsp3_dr */ + {ABE_PDM_DL_DATA, (OFF_EN | OFF_OUT_PTD | M1)}, /* abe_mcbsp3_dx */ + {ABE_PDM_FRAME, (IEN | OFF_EN | OFF_PD | OFF_IN | M1)}, /* abe_mcbsp3_clkx */ + {ABE_PDM_LB_CLK, (IEN | OFF_EN | OFF_PD | OFF_IN | M1)}, /* abe_mcbsp3_fsx */ + {ABE_CLKS, (DIS | IEN | M3)}, /* gpio_118 */ + {ABE_DMIC_CLK1, (SAFE_MODE)}, /* nc */ + {ABE_DMIC_DIN1, (SAFE_MODE)}, /* nc */ + {ABE_DMIC_DIN2, (SAFE_MODE)}, /* nc */ + {ABE_DMIC_DIN3, (SAFE_MODE)}, /* nc */ + {UART2_CTS, (PTU | IEN | M0)}, /* uart2_cts */ + {UART2_RTS, (M0)}, /* uart2_rts */ + {UART2_RX, (PTU | IEN | M0)}, /* uart2_rx */ + {UART2_TX, (M0)}, /* uart2_tx */ + {HDQ_SIO, (M0)}, /* hdq_sio */ + {I2C1_SCL, (PTU | IEN | M0)}, /* i2c1_scl */ + {I2C1_SDA, (PTU | IEN | M0)}, /* i2c1_sda */ + {I2C2_SCL, (PTU | IEN | M1)}, /* uart1_rx */ + {I2C2_SDA, (M1)}, /* uart1_tx */ + {I2C3_SCL, (PTU | IEN | M0)}, /* i2c3_scl */ + {I2C3_SDA, (PTU | IEN | M0)}, /* i2c3_sda */ + {I2C4_SCL, (PTU | IEN | M0)}, /* i2c4_scl */ + {I2C4_SDA, (PTU | IEN | M0)}, /* i2c4_sda */ + {MCSPI1_CLK, (IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* mcspi1_clk */ + {MCSPI1_SOMI, (IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* mcspi1_somi */ + {MCSPI1_SIMO, (IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* mcspi1_simo */ + {MCSPI1_CS0, (PTD | IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* mcspi1_cs0 */ + {MCSPI1_CS1, (PTD | IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* mcspi1_cs1 */ + {MCSPI1_CS2, (PTD | IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* mcspi1_cs2 */ + {MCSPI1_CS3, (PTD | IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* mcspi1_cs3 */ + {UART3_CTS_RCTX, (PTU | IEN | M0)}, /* uart3_tx */ + {UART3_RTS_SD, (M0)}, /* uart3_rts_sd */ + {UART3_RX_IRRX, (IEN | M0)}, /* uart3_rx */ + {UART3_TX_IRTX, (M0)}, /* uart3_tx */ + {SDMMC5_CLK, (PTU | IEN | OFF_EN | OFF_OUT_PTD | M0)}, /* sdmmc5_clk */ + {SDMMC5_CMD, (PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* sdmmc5_cmd */ + {SDMMC5_DAT0, (PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* sdmmc5_dat0 */ + {SDMMC5_DAT1, (PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* sdmmc5_dat1 */ + {SDMMC5_DAT2, (PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* sdmmc5_dat2 */ + {SDMMC5_DAT3, (PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* sdmmc5_dat3 */ + {MCSPI4_CLK, (SAFE_MODE)}, /* nc */ + {MCSPI4_SIMO, (PTU | IEN | M3)}, /* gpio_152 */ + {MCSPI4_SOMI, (PTU | IEN | M3)}, /* gpio_153 */ + {MCSPI4_CS0, (SAFE_MODE)}, /* nc */ + {UART4_RX, (IEN | M0)}, /* uart4_rx */ + {UART4_TX, (M0)}, /* uart4_tx */ + {USBB2_ULPITLL_CLK, (IEN | M3)}, /* gpio_157 */ + {USBB2_ULPITLL_STP, (IEN | M5)}, /* dispc2_data23 */ + {USBB2_ULPITLL_DIR, (IEN | M5)}, /* dispc2_data22 */ + {USBB2_ULPITLL_NXT, (IEN | M5)}, /* dispc2_data21 */ + {USBB2_ULPITLL_DAT0, (IEN | M5)}, /* dispc2_data20 */ + {USBB2_ULPITLL_DAT1, (IEN | M5)}, /* dispc2_data19 */ + {USBB2_ULPITLL_DAT2, (IEN | M5)}, /* dispc2_data18 */ + {USBB2_ULPITLL_DAT3, (IEN | M5)}, /* dispc2_data15 */ + {USBB2_ULPITLL_DAT4, (IEN | M5)}, /* dispc2_data14 */ + {USBB2_ULPITLL_DAT5, (IEN | M5)}, /* dispc2_data13 */ + {USBB2_ULPITLL_DAT6, (IEN | M5)}, /* dispc2_data12 */ + {USBB2_ULPITLL_DAT7, (IEN | M5)}, /* dispc2_data11 */ + {USBB2_HSIC_DATA, (SAFE_MODE)}, /* nc */ + {USBB2_HSIC_STROBE, (SAFE_MODE)}, /* nc */ + {UNIPRO_TX0, (OFF_EN | OFF_PD | OFF_IN | M1)}, /* kpd_col0 */ + {UNIPRO_TY0, (OFF_EN | OFF_PD | OFF_IN | M1)}, /* kpd_col1 */ + {UNIPRO_TX1, (OFF_EN | OFF_PD | OFF_IN | M1)}, /* kpd_col2 */ + {UNIPRO_TY1, (OFF_EN | OFF_PD | OFF_IN | M1)}, /* kpd_col3 */ + {UNIPRO_TX2, (OFF_EN | OFF_PD | OFF_IN | M1)}, /* kpd_col4 */ + {UNIPRO_TY2, (SAFE_MODE)}, /* nc */ + {UNIPRO_RX0, (PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M1)}, /* kpd_row0 */ + {UNIPRO_RY0, (PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M1)}, /* kpd_row1 */ + {UNIPRO_RX1, (PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M1)}, /* kpd_row2 */ + {UNIPRO_RY1, (PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M1)}, /* kpd_row3 */ + {UNIPRO_RX2, (PTU | IEN | OFF_EN | OFF_PD | OFF_IN | M1)}, /* kpd_row4 */ + {UNIPRO_RY2, (DIS | IEN | M3)}, /* gpio_3 */ + {USBA0_OTG_CE, (PTD | OFF_EN | OFF_PD | OFF_OUT_PTD | M0)}, /* usba0_otg_ce */ + {USBA0_OTG_DP, (IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* usba0_otg_dp */ + {USBA0_OTG_DM, (IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* usba0_otg_dm */ + {FREF_CLK1_OUT, (SAFE_MODE)}, /* nc */ + {FREF_CLK2_OUT, (SAFE_MODE)}, /* nc */ + {SYS_NIRQ1, (PTU | IEN | M0)}, /* sys_nirq1 */ + {SYS_NIRQ2, (DIS | IEN | M3)}, /* gpio_183 */ + {SYS_BOOT0, (PTU | IEN | M3)}, /* gpio_184 */ + {SYS_BOOT1, (M3)}, /* gpio_185 */ + {SYS_BOOT2, (PTD | IEN | M3)}, /* gpio_186 */ + {SYS_BOOT3, (M3)}, /* gpio_187 */ + {SYS_BOOT4, (M3)}, /* gpio_188 */ + {SYS_BOOT5, (PTD | IEN | M3)}, /* gpio_189 */ + {DPM_EMU0, (IEN | M0)}, /* dpm_emu0 */ + {DPM_EMU1, (IEN | M0)}, /* dpm_emu1 */ + {DPM_EMU2, (SAFE_MODE)}, /* nc */ + {DPM_EMU3, (SAFE_MODE)}, /* nc */ + {DPM_EMU4, (SAFE_MODE)}, /* nc */ + {DPM_EMU5, (SAFE_MODE)}, /* nc */ + {DPM_EMU6, (SAFE_MODE)}, /* nc */ + {DPM_EMU7, (SAFE_MODE)}, /* nc */ + {DPM_EMU8, (SAFE_MODE)}, /* nc */ + {DPM_EMU9, (SAFE_MODE)}, /* nc */ + {DPM_EMU10, (SAFE_MODE)}, /* nc */ + {DPM_EMU11, (SAFE_MODE)}, /* nc */ + {DPM_EMU12, (SAFE_MODE)}, /* nc */ + {DPM_EMU13, (SAFE_MODE)}, /* nc */ + {DPM_EMU14, (SAFE_MODE)}, /* nc */ + {DPM_EMU15, (DIS | M3)}, /* gpio_26 */ + {DPM_EMU16, (M1)}, /* dmtimer8_pwm_evt */ + {DPM_EMU17, (M1)}, /* dmtimer9_pwm_evt */ + {DPM_EMU18, (IEN | M3)}, /* gpio_190 */ + {DPM_EMU19, (IEN | M3)}, /* gpio_191 */ +}; + +static const struct pad_conf_entry wkup_padconf_array[] = { + {PAD0_SIM_IO, (IEN | M3)}, /* gpio_wk0 */ + {PAD1_SIM_CLK, (IEN | M3)}, /* gpio_wk1 */ + {PAD0_SIM_RESET, (IEN | M3)}, /* gpio_wk2 */ + {PAD1_SIM_CD, (SAFE_MODE)}, /* should be gpio_wk3 but muxed with gpio_3*/ + {PAD0_SIM_PWRCTRL, (IEN | M3)}, /* gpio_wk4 */ + {PAD1_SR_SCL, (PTU | IEN | M0)}, /* sr_scl */ + {PAD0_SR_SDA, (PTU | IEN | M0)}, /* sr_sda */ + {PAD1_FREF_XTAL_IN, (M0)}, /* # */ + {PAD0_FREF_SLICER_IN, (SAFE_MODE)}, /* nc */ + {PAD1_FREF_CLK_IOREQ, (SAFE_MODE)}, /* nc */ + {PAD0_FREF_CLK0_OUT, (M2)}, /* sys_drm_msecure */ + {PAD1_FREF_CLK3_REQ, (SAFE_MODE)}, /* nc */ + {PAD0_FREF_CLK3_OUT, (M0)}, /* fref_clk3_out */ + {PAD1_FREF_CLK4_REQ, (M0)}, /* fref_clk4_req */ + {PAD0_FREF_CLK4_OUT, (M0)}, /* fref_clk4_out */ + {PAD1_SYS_32K, (IEN | M0)}, /* sys_32k */ + {PAD0_SYS_NRESPWRON, (M0)}, /* sys_nrespwron */ + {PAD1_SYS_NRESWARM, (M0)}, /* sys_nreswarm */ + {PAD0_SYS_PWR_REQ, (PTU | M0)}, /* sys_pwr_req */ + {PAD1_SYS_PWRON_RESET, (M0)}, /* sys_pwron_reset_out */ + {PAD0_SYS_BOOT6, (IEN | M3)}, /* gpio_wk9 */ + {PAD1_SYS_BOOT7, (IEN | M3)}, /* gpio_wk10 */ +}; + +static void do_set_mux(u32 base, struct pad_conf_entry const *array, int size) +{ + int i; + struct pad_conf_entry *pad = (struct pad_conf_entry *) array; + + for (i = 0; i < size; i++, pad++) + writew(pad->val, base + pad->offset); +} + +void set_muxconf_regs(void) +{ + do_set_mux(OMAP44XX_CONTROL_PADCONF_CORE, core_padconf_array, + ARRAY_SIZE(core_padconf_array)); + + do_set_mux(OMAP44XX_CONTROL_PADCONF_WKUP, wkup_padconf_array, + ARRAY_SIZE(wkup_padconf_array)); +} diff --git a/arch/arm/configs/omap3530_beagle_defconfig b/arch/arm/configs/omap3530_beagle_defconfig index d4b87374..8a12154c 100644 --- a/arch/arm/configs/omap3530_beagle_defconfig +++ b/arch/arm/configs/omap3530_beagle_defconfig @@ -2,16 +2,19 @@ CONFIG_ARCH_OMAP=y # CONFIG_OMAP3_COPY_CLOCK_SRAM is not set CONFIG_MACH_BEAGLE=y CONFIG_AEABI=y +CONFIG_ARM_OPTIMZED_STRING_FUNCTIONS=y +CONFIG_ARM_UNWIND=y CONFIG_TEXT_BASE=0x81000000 CONFIG_PROMPT="barebox> " CONFIG_LONGHELP=y CONFIG_GLOB=y CONFIG_HUSH_FANCY_PROMPT=y +CONFIG_HUSH_GETOPT=y CONFIG_CMDLINE_EDITING=y CONFIG_AUTO_COMPLETE=y # CONFIG_TIMESTAMP is not set CONFIG_PARTITION=y -# CONFIG_DEFAULT_ENVIRONMENT is not set +CONFIG_DEFAULT_ENVIRONMENT_GENERIC=y CONFIG_CMD_EDIT=y CONFIG_CMD_SLEEP=y CONFIG_CMD_SAVEENV=y @@ -27,6 +30,7 @@ CONFIG_CMD_RESET=y CONFIG_CMD_GO=y CONFIG_CMD_TIMEOUT=y CONFIG_CMD_PARTITION=y +CONFIG_CMD_GPIO=y CONFIG_CMD_UNLZO=y CONFIG_CMD_I2C=y CONFIG_NET=y @@ -34,14 +38,15 @@ CONFIG_NET_DHCP=y CONFIG_NET_NFS=y CONFIG_NET_PING=y CONFIG_NET_TFTP=y +CONFIG_NET_TFTP_PUSH=y CONFIG_DRIVER_SERIAL_NS16550=y CONFIG_DRIVER_SERIAL_NS16550_OMAP_EXTENSIONS=y CONFIG_NET_USB=y CONFIG_NET_USB_ASIX=y +CONFIG_NET_USB_SMSC95XX=y # CONFIG_SPI is not set CONFIG_I2C=y CONFIG_I2C_OMAP=y -CONFIG_I2C_TWL4030=y CONFIG_MTD=y CONFIG_NAND=y CONFIG_NAND_OMAP_GPMC=y @@ -49,3 +54,9 @@ CONFIG_USB=y CONFIG_USB_EHCI=y CONFIG_USB_EHCI_OMAP=y CONFIG_USB_TWL4030=y +CONFIG_MCI=y +CONFIG_MCI_OMAP_HSMMC=y +CONFIG_I2C_TWL4030=y +CONFIG_FS_FAT=y +CONFIG_FS_FAT_WRITE=y +CONFIG_FS_FAT_LFN=y diff --git a/arch/arm/configs/omap3530_beagle_xload_defconfig b/arch/arm/configs/omap3530_beagle_xload_defconfig new file mode 100644 index 00000000..58a23095 --- /dev/null +++ b/arch/arm/configs/omap3530_beagle_xload_defconfig @@ -0,0 +1,38 @@ +CONFIG_ARCH_OMAP=y +# CONFIG_OMAP3_COPY_CLOCK_SRAM is not set +CONFIG_OMAP_BUILD_IFT=y +CONFIG_MACH_BEAGLE=y +CONFIG_AEABI=y +# CONFIG_CMD_ARM_CPUINFO is not set +# CONFIG_ARM_EXCEPTIONS is not set +CONFIG_TEXT_BASE=0x40200000 +CONFIG_MEMORY_LAYOUT_FIXED=y +CONFIG_STACK_BASE=0x87BF7F10 +CONFIG_MALLOC_BASE=0x87BFFF10 +CONFIG_MALLOC_DUMMY=y +CONFIG_PROMPT="X-load Beagle>" +CONFIG_SHELL_NONE=y +# CONFIG_ERRNO_MESSAGES is not set +# CONFIG_TIMESTAMP is not set +# CONFIG_CONSOLE_FULL is not set +# CONFIG_DEFAULT_ENVIRONMENT is not set +CONFIG_DRIVER_SERIAL_NS16550=y +CONFIG_DRIVER_SERIAL_NS16550_OMAP_EXTENSIONS=y +# CONFIG_SPI is not set +CONFIG_MTD=y +CONFIG_NAND=y +# CONFIG_NAND_WRITE is not set +# CONFIG_NAND_ECC_SOFT is not set +# CONFIG_NAND_ECC_HW_SYNDROME is not set +# CONFIG_NAND_ECC_HW_NONE is not set +# CONFIG_NAND_INFO is not set +# CONFIG_NAND_BBT is not set +# CONFIG_NAND_READ_OOB is not set +CONFIG_NAND_OMAP_GPMC=y +CONFIG_MCI=y +CONFIG_MCI_STARTUP=y +# CONFIG_MCI_WRITE is not set +CONFIG_MCI_OMAP_HSMMC=y +# CONFIG_FS_RAMFS is not set +# CONFIG_FS_DEVFS is not set +CONFIG_FS_FAT=y diff --git a/arch/arm/configs/panda_defconfig b/arch/arm/configs/panda_defconfig new file mode 100644 index 00000000..38cc9582 --- /dev/null +++ b/arch/arm/configs/panda_defconfig @@ -0,0 +1,60 @@ +CONFIG_ARCH_OMAP=y +CONFIG_ARCH_OMAP4=y +CONFIG_AEABI=y +CONFIG_ARM_OPTIMZED_STRING_FUNCTIONS=y +CONFIG_ARM_UNWIND=y +CONFIG_MMU=y +CONFIG_TEXT_BASE=0x8f000000 +CONFIG_MALLOC_SIZE=0x2000000 +CONFIG_KALLSYMS=y +CONFIG_PROMPT="barebox> " +CONFIG_LONGHELP=y +CONFIG_GLOB=y +CONFIG_HUSH_FANCY_PROMPT=y +CONFIG_HUSH_GETOPT=y +CONFIG_CMDLINE_EDITING=y +CONFIG_AUTO_COMPLETE=y +# CONFIG_TIMESTAMP is not set +CONFIG_PARTITION=y +CONFIG_DEFAULT_ENVIRONMENT_GENERIC=y +CONFIG_DEFAULT_ENVIRONMENT_PATH="arch/arm/boards/pcm049/env" +CONFIG_CMD_EDIT=y +CONFIG_CMD_SLEEP=y +CONFIG_CMD_SAVEENV=y +CONFIG_CMD_LOADENV=y +CONFIG_CMD_EXPORT=y +CONFIG_CMD_PRINTENV=y +CONFIG_CMD_READLINE=y +CONFIG_CMD_ECHO_E=y +CONFIG_CMD_LOADB=y +CONFIG_CMD_FLASH=y +# CONFIG_CMD_BOOTM is not set +CONFIG_CMD_RESET=y +CONFIG_CMD_GO=y +CONFIG_CMD_TIMEOUT=y +CONFIG_CMD_PARTITION=y +CONFIG_CMD_GPIO=y +CONFIG_CMD_UNLZO=y +CONFIG_NET=y +CONFIG_NET_DHCP=y +CONFIG_NET_NFS=y +CONFIG_NET_PING=y +CONFIG_NET_TFTP=y +CONFIG_NET_TFTP_PUSH=y +CONFIG_DRIVER_SERIAL_NS16550=y +CONFIG_DRIVER_SERIAL_NS16550_OMAP_EXTENSIONS=y +CONFIG_NET_USB=y +CONFIG_NET_USB_SMSC95XX=y +# CONFIG_SPI is not set +CONFIG_MTD=y +CONFIG_NAND=y +CONFIG_NAND_OMAP_GPMC=y +CONFIG_UBI=y +CONFIG_USB=y +CONFIG_USB_EHCI=y +CONFIG_MCI=y +CONFIG_MCI_STARTUP=y +CONFIG_MCI_OMAP_HSMMC=y +CONFIG_FS_FAT=y +CONFIG_FS_FAT_WRITE=y +CONFIG_FS_FAT_LFN=y diff --git a/arch/arm/configs/panda_xload_defconfig b/arch/arm/configs/panda_xload_defconfig new file mode 100644 index 00000000..24452a67 --- /dev/null +++ b/arch/arm/configs/panda_xload_defconfig @@ -0,0 +1,26 @@ +CONFIG_ARCH_OMAP=y +CONFIG_ARCH_OMAP4=y +# CONFIG_OMAP_GPMC is not set +CONFIG_OMAP_BUILD_IFT=y +CONFIG_AEABI=y +# CONFIG_CMD_ARM_CPUINFO is not set +CONFIG_TEXT_BASE=0x40300000 +CONFIG_MEMORY_LAYOUT_FIXED=y +CONFIG_STACK_BASE=0x8f000000 +CONFIG_MALLOC_BASE=0x84000000 +CONFIG_MALLOC_SIZE=0x2000000 +CONFIG_PROMPT="barebox> " +CONFIG_SHELL_NONE=y +# CONFIG_ERRNO_MESSAGES is not set +# CONFIG_TIMESTAMP is not set +# CONFIG_CONSOLE_FULL is not set +CONFIG_PARTITION=y +# CONFIG_DEFAULT_ENVIRONMENT is not set +# CONFIG_COMMAND_SUPPORT is not set +CONFIG_DRIVER_SERIAL_NS16550=y +CONFIG_DRIVER_SERIAL_NS16550_OMAP_EXTENSIONS=y +# CONFIG_SPI is not set +CONFIG_MCI=y +CONFIG_MCI_STARTUP=y +CONFIG_MCI_OMAP_HSMMC=y +CONFIG_FS_FAT=y diff --git a/arch/arm/configs/pcm049_defconfig b/arch/arm/configs/pcm049_defconfig new file mode 100644 index 00000000..ed20f1bb --- /dev/null +++ b/arch/arm/configs/pcm049_defconfig @@ -0,0 +1,57 @@ +CONFIG_ARCH_OMAP=y +CONFIG_ARCH_OMAP4=y +CONFIG_MACH_PCM049=y +CONFIG_AEABI=y +CONFIG_ARM_OPTIMZED_STRING_FUNCTIONS=y +CONFIG_ARM_UNWIND=y +CONFIG_MMU=y +CONFIG_TEXT_BASE=0x8f000000 +CONFIG_MALLOC_SIZE=0x2000000 +CONFIG_KALLSYMS=y +CONFIG_PROMPT="barebox> " +CONFIG_LONGHELP=y +CONFIG_GLOB=y +CONFIG_HUSH_FANCY_PROMPT=y +CONFIG_HUSH_GETOPT=y +CONFIG_CMDLINE_EDITING=y +CONFIG_AUTO_COMPLETE=y +# CONFIG_TIMESTAMP is not set +CONFIG_PARTITION=y +CONFIG_DEFAULT_ENVIRONMENT_GENERIC=y +CONFIG_DEFAULT_ENVIRONMENT_PATH="arch/arm/boards/pcm049/env" +CONFIG_CMD_EDIT=y +CONFIG_CMD_SLEEP=y +CONFIG_CMD_SAVEENV=y +CONFIG_CMD_LOADENV=y +CONFIG_CMD_EXPORT=y +CONFIG_CMD_PRINTENV=y +CONFIG_CMD_READLINE=y +CONFIG_CMD_ECHO_E=y +CONFIG_CMD_LOADB=y +CONFIG_CMD_FLASH=y +# CONFIG_CMD_BOOTM is not set +CONFIG_CMD_RESET=y +CONFIG_CMD_GO=y +CONFIG_CMD_TIMEOUT=y +CONFIG_CMD_PARTITION=y +CONFIG_CMD_GPIO=y +CONFIG_CMD_UNLZO=y +CONFIG_NET=y +CONFIG_NET_DHCP=y +CONFIG_NET_NFS=y +CONFIG_NET_PING=y +CONFIG_NET_TFTP=y +CONFIG_NET_TFTP_PUSH=y +CONFIG_DRIVER_SERIAL_NS16550=y +CONFIG_DRIVER_SERIAL_NS16550_OMAP_EXTENSIONS=y +CONFIG_DRIVER_NET_SMC911X=y +# CONFIG_SPI is not set +CONFIG_MTD=y +CONFIG_NAND=y +CONFIG_NAND_OMAP_GPMC=y +CONFIG_UBI=y +CONFIG_MCI=y +CONFIG_MCI_OMAP_HSMMC=y +CONFIG_FS_FAT=y +CONFIG_FS_FAT_WRITE=y +CONFIG_FS_FAT_LFN=y diff --git a/arch/arm/configs/pcm049_xload_defconfig b/arch/arm/configs/pcm049_xload_defconfig new file mode 100644 index 00000000..7303c9a5 --- /dev/null +++ b/arch/arm/configs/pcm049_xload_defconfig @@ -0,0 +1,40 @@ +CONFIG_ARCH_OMAP=y +CONFIG_ARCH_OMAP4=y +CONFIG_OMAP_BUILD_IFT=y +CONFIG_MACH_PCM049=y +CONFIG_AEABI=y +# CONFIG_CMD_ARM_CPUINFO is not set +# CONFIG_ARM_EXCEPTIONS is not set +CONFIG_MMU=y +CONFIG_TEXT_BASE=0x40300000 +CONFIG_MEMORY_LAYOUT_FIXED=y +CONFIG_STACK_BASE=0x8f000000 +CONFIG_MALLOC_BASE=0x84000000 +CONFIG_MALLOC_SIZE=0x2000000 +CONFIG_MALLOC_DUMMY=y +CONFIG_PROMPT="barebox> " +CONFIG_SHELL_NONE=y +# CONFIG_ERRNO_MESSAGES is not set +# CONFIG_TIMESTAMP is not set +# CONFIG_CONSOLE_FULL is not set +# CONFIG_DEFAULT_ENVIRONMENT is not set +CONFIG_DRIVER_SERIAL_NS16550=y +CONFIG_DRIVER_SERIAL_NS16550_OMAP_EXTENSIONS=y +# CONFIG_SPI is not set +CONFIG_MTD=y +CONFIG_NAND=y +# CONFIG_NAND_WRITE is not set +# CONFIG_NAND_ECC_SOFT is not set +# CONFIG_NAND_ECC_HW_SYNDROME is not set +# CONFIG_NAND_ECC_HW_NONE is not set +# CONFIG_NAND_INFO is not set +# CONFIG_NAND_BBT is not set +# CONFIG_NAND_READ_OOB is not set +CONFIG_NAND_OMAP_GPMC=y +CONFIG_MCI=y +CONFIG_MCI_STARTUP=y +# CONFIG_MCI_WRITE is not set +CONFIG_MCI_OMAP_HSMMC=y +# CONFIG_FS_RAMFS is not set +# CONFIG_FS_DEVFS is not set +CONFIG_FS_FAT=y diff --git a/arch/arm/cpu/Makefile b/arch/arm/cpu/Makefile index 036768eb..e30ae1cc 100644 --- a/arch/arm/cpu/Makefile +++ b/arch/arm/cpu/Makefile @@ -1,6 +1,6 @@ obj-y += cpu.o -obj-y += exceptions.o -obj-y += interrupts.o +obj-$(CONFIG_ARM_EXCEPTIONS) += exceptions.o +obj-$(CONFIG_ARM_EXCEPTIONS) += interrupts.o obj-y += start.o # diff --git a/arch/arm/cpu/cpu.c b/arch/arm/cpu/cpu.c index 346f8dce..cf307890 100644 --- a/arch/arm/cpu/cpu.c +++ b/arch/arm/cpu/cpu.c @@ -97,7 +97,7 @@ void arch_shutdown(void) * default setting we are running in barebox, there's no special preparation * required. */ - +#ifdef CONFIG_COMMAND static int do_icache(struct command *cmdtp, int argc, char *argv[]) { if (argc == 1) { @@ -121,3 +121,4 @@ BAREBOX_CMD_START(icache) .usage = "show/change icache status", BAREBOX_CMD_HELP(cmd_icache_help) BAREBOX_CMD_END +#endif diff --git a/arch/arm/cpu/start.c b/arch/arm/cpu/start.c index e0fb7123..f20ce74a 100644 --- a/arch/arm/cpu/start.c +++ b/arch/arm/cpu/start.c @@ -31,6 +31,7 @@ void __naked __section(.text_entry) exception_vectors(void) { __asm__ __volatile__ ( "b reset\n" /* reset */ +#ifdef CONFIG_ARM_EXCEPTIONS "ldr pc, =undefined_instruction\n" /* undefined instruction */ "ldr pc, =software_interrupt\n" /* software interrupt (SWI) */ "ldr pc, =prefetch_abort\n" /* prefetch abort */ @@ -38,6 +39,21 @@ void __naked __section(.text_entry) exception_vectors(void) "ldr pc, =not_used\n" /* (reserved) */ "ldr pc, =irq\n" /* irq (interrupt) */ "ldr pc, =fiq\n" /* fiq (fast interrupt) */ +#else + "1: bne 1b\n" /* undefined instruction */ + "1: bne 1b\n" /* software interrupt (SWI) */ + "1: bne 1b\n" /* prefetch abort */ + "1: bne 1b\n" /* data abort */ + "1: bne 1b\n" /* (reserved) */ + "1: bne 1b\n" /* irq (interrupt) */ + "1: bne 1b\n" /* fiq (fast interrupt) */ +#endif + ".word 0x65726162\n" /* 'bare' */ + ".word 0x00786f62\n" /* 'box' */ + ".word _text\n" /* text base. If copied there, + * barebox can skip relocation + */ + ".word _barebox_image_size\n" /* image size to copy */ ); } diff --git a/arch/arm/include/asm/armlinux.h b/arch/arm/include/asm/armlinux.h index 2765f0df..3cab209d 100644 --- a/arch/arm/include/asm/armlinux.h +++ b/arch/arm/include/asm/armlinux.h @@ -25,9 +25,13 @@ static inline void armlinux_set_revision(unsigned int rev) { } -void armlinux_set_serial(u64 serial) +static inline void armlinux_set_serial(u64 serial) { } #endif +struct image_data; + +void start_linux(void *adr, int swap, struct image_data *data); + #endif /* __ARCH_ARMLINUX_H */ diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile index 3a010838..9383ae14 100644 --- a/arch/arm/lib/Makefile +++ b/arch/arm/lib/Makefile @@ -1,4 +1,7 @@ -obj-y += armlinux.o +obj-$(CONFIG_ARM_LINUX) += armlinux.o +obj-$(CONFIG_CMD_BOOTM) += bootm.o +obj-$(CONFIG_CMD_BOOTZ) += bootz.o +obj-$(CONFIG_CMD_BOOTU) += bootu.o obj-y += div0.o obj-y += findbit.o obj-y += arm.o diff --git a/arch/arm/lib/armlinux.c b/arch/arm/lib/armlinux.c index 9927b766..ce1cc6bc 100644 --- a/arch/arm/lib/armlinux.c +++ b/arch/arm/lib/armlinux.c @@ -215,162 +215,11 @@ void armlinux_set_serial(u64 serial) system_serial = serial; } -#ifdef CONFIG_CMD_BOOTM -static int do_bootm_linux(struct image_data *data) +void start_linux(void *adr, int swap, struct image_data *data) { - void (*theKernel)(int zero, int arch, void *params); - image_header_t *os_header = &data->os->header; + void (*kernel)(int zero, int arch, void *params) = adr; - if (image_get_type(os_header) == IH_TYPE_MULTI) { - printf("Multifile images not handled at the moment\n"); - return -1; - } - - if (armlinux_architecture == 0) { - printf("arm architecture not set. Please specify with -a option\n"); - return -1; - } - - if (!armlinux_bootparams) { - printf("Bootparams not set. Please fix your board code\n"); - return -1; - } - - theKernel = (void *)image_get_ep(os_header); - - debug("## Transferring control to Linux (at address 0x%p) ...\n", - theKernel); - - setup_tags(data, 0); - - if (relocate_image(data->os, (void *)image_get_load(os_header))) - return -1; - - if (data->initrd) - if (relocate_image(data->initrd, (void *)image_get_load(&data->initrd->header))) - return -1; - - /* we assume that the kernel is in place */ - printf("\nStarting kernel %s...\n\n", data->initrd ? "with initrd " : ""); - - shutdown_barebox(); - theKernel (0, armlinux_architecture, armlinux_bootparams); - - return -1; -} - -static int image_handle_cmdline_parse(struct image_data *data, int opt, - char *optarg) -{ - int ret = 1; - - switch (opt) { - case 'a': - armlinux_architecture = simple_strtoul(optarg, NULL, 0); - ret = 0; - break; - case 'R': - system_rev = simple_strtoul(optarg, NULL, 0); - ret = 0; - break; - default: - break; - } - - return ret; -} - -static struct image_handler handler = { - .cmdline_options = "a:R:", - .cmdline_parse = image_handle_cmdline_parse, - .help_string = " -a <arch> use architecture number <arch>\n" - " -R <system_rev> use system revison <system_rev>\n", - - .bootm = do_bootm_linux, - .image_type = IH_OS_LINUX, -}; - -static int armlinux_register_image_handler(void) -{ - return register_image_handler(&handler); -} - -late_initcall(armlinux_register_image_handler); -#endif /* CONFIG_CMD_BOOTM */ - -#ifdef CONFIG_CMD_BOOTZ -struct zimage_header { - u32 unused[9]; - u32 magic; - u32 start; - u32 end; -}; - -#define ZIMAGE_MAGIC 0x016F2818 - -static int do_bootz(struct command *cmdtp, int argc, char *argv[]) -{ - void (*theKernel)(int zero, int arch, void *params); - int fd, ret, swap = 0; - struct zimage_header header; - void *zimage; - u32 end; - - if (argc != 2) { - barebox_cmd_usage(cmdtp); - return 1; - } - - fd = open(argv[1], O_RDONLY); - if (fd < 0) { - perror("open"); - return 1; - } - - ret = read(fd, &header, sizeof(header)); - if (ret < sizeof(header)) { - printf("could not read %s\n", argv[1]); - goto err_out; - } - - switch (header.magic) { -#ifdef CONFIG_BOOT_ENDIANNESS_SWITCH - case swab32(ZIMAGE_MAGIC): - swap = 1; - /* fall through */ -#endif - case ZIMAGE_MAGIC: - break; - default: - printf("invalid magic 0x%08x\n", header.magic); - goto err_out; - } - - end = header.end; - - if (swap) - end = swab32(end); - - zimage = xmalloc(end); - memcpy(zimage, &header, sizeof(header)); - - ret = read(fd, zimage + sizeof(header), end - sizeof(header)); - if (ret < end - sizeof(header)) { - printf("could not read %s\n", argv[1]); - goto err_out1; - } - - if (swap) { - void *ptr; - for (ptr = zimage; ptr < zimage + end; ptr += 4) - *(u32 *)ptr = swab32(*(u32 *)ptr); - } - - theKernel = zimage; - - printf("loaded zImage from %s with size %d\n", argv[1], end); - - setup_tags(NULL, swap); + setup_tags(data, swap); shutdown_barebox(); if (swap) { @@ -380,61 +229,5 @@ static int do_bootz(struct command *cmdtp, int argc, char *argv[]) __asm__ __volatile__("mcr p15, 0, %0, c1, c0" :: "r" (reg)); } - theKernel(0, armlinux_architecture, armlinux_bootparams); - - return 0; - -err_out1: - free(zimage); -err_out: - close(fd); - - return 1; + kernel(0, armlinux_architecture, armlinux_bootparams); } - -static const __maybe_unused char cmd_bootz_help[] = -"Usage: bootz [FILE]\n" -"Boot a Linux zImage\n"; - -BAREBOX_CMD_START(bootz) - .cmd = do_bootz, - .usage = "bootz - start a zImage", - BAREBOX_CMD_HELP(cmd_bootz_help) -BAREBOX_CMD_END -#endif /* CONFIG_CMD_BOOTZ */ - -#ifdef CONFIG_CMD_BOOTU -static int do_bootu(struct command *cmdtp, int argc, char *argv[]) -{ - void (*theKernel)(int zero, int arch, void *params) = NULL; - int fd; - - if (argc != 2) { - barebox_cmd_usage(cmdtp); - return 1; - } - - fd = open(argv[1], O_RDONLY); - if (fd > 0) - theKernel = (void *)memmap(fd, PROT_READ); - - if (!theKernel) - theKernel = (void *)simple_strtoul(argv[1], NULL, 0); - - setup_tags(NULL, 0); - - shutdown_barebox(); - theKernel(0, armlinux_architecture, armlinux_bootparams); - - return 1; -} - -static const __maybe_unused char cmd_bootu_help[] = -"Usage: bootu <address>\n"; - -BAREBOX_CMD_START(bootu) - .cmd = do_bootu, - .usage = "bootu - start a raw linux image", - BAREBOX_CMD_HELP(cmd_bootu_help) -BAREBOX_CMD_END -#endif /* CONFIG_CMD_BOOTU */ diff --git a/arch/arm/lib/barebox.lds.S b/arch/arm/lib/barebox.lds.S index 7683f739..81a91236 100644 --- a/arch/arm/lib/barebox.lds.S +++ b/arch/arm/lib/barebox.lds.S @@ -93,4 +93,5 @@ SECTIONS .bss : { *(.bss*) } __bss_stop = .; _end = .; + _barebox_image_size = __bss_start - _text; } diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c new file mode 100644 index 00000000..b09fe70e --- /dev/null +++ b/arch/arm/lib/bootm.c @@ -0,0 +1,92 @@ +#include <boot.h> +#include <common.h> +#include <command.h> +#include <driver.h> +#include <environment.h> +#include <image.h> +#include <zlib.h> +#include <init.h> +#include <fs.h> +#include <linux/list.h> +#include <xfuncs.h> +#include <malloc.h> +#include <fcntl.h> +#include <errno.h> + +#include <asm/byteorder.h> +#include <asm/global_data.h> +#include <asm/setup.h> +#include <asm/barebox-arm.h> +#include <asm/armlinux.h> +#include <asm/system.h> + +static int do_bootm_linux(struct image_data *data) +{ + void (*theKernel)(int zero, int arch, void *params); + image_header_t *os_header = &data->os->header; + + if (image_get_type(os_header) == IH_TYPE_MULTI) { + printf("Multifile images not handled at the moment\n"); + return -1; + } + + theKernel = (void *)image_get_ep(os_header); + + debug("## Transferring control to Linux (at address 0x%p) ...\n", + theKernel); + + if (relocate_image(data->os, (void *)image_get_load(os_header))) + return -1; + + if (data->initrd) + if (relocate_image(data->initrd, (void *)image_get_load(&data->initrd->header))) + return -1; + + /* we assume that the kernel is in place */ + printf("\nStarting kernel %s...\n\n", data->initrd ? "with initrd " : ""); + + start_linux(theKernel, 0, data); + + return -1; +} + +static int image_handle_cmdline_parse(struct image_data *data, int opt, + char *optarg) +{ + int ret = 1; + int no; + + switch (opt) { + case 'a': + no = simple_strtoul(optarg, NULL, 0); + armlinux_set_architecture(no); + ret = 0; + break; + case 'R': + no = simple_strtoul(optarg, NULL, 0); + armlinux_set_revision(no); + ret = 0; + break; + default: + break; + } + + return ret; +} + +static struct image_handler handler = { + .cmdline_options = "a:R:", + .cmdline_parse = image_handle_cmdline_parse, + .help_string = " -a <arch> use architecture number <arch>\n" + " -R <system_rev> use system revison <system_rev>\n", + + .bootm = do_bootm_linux, + .image_type = IH_OS_LINUX, +}; + +static int armlinux_register_image_handler(void) +{ + return register_image_handler(&handler); +} + +late_initcall(armlinux_register_image_handler); diff --git a/arch/arm/lib/bootu.c b/arch/arm/lib/bootu.c new file mode 100644 index 00000000..e97ded0e --- /dev/null +++ b/arch/arm/lib/bootu.c @@ -0,0 +1,38 @@ +#include <common.h> +#include <command.h> +#include <fs.h> +#include <fcntl.h> +#include <errno.h> +#include <asm/armlinux.h> + +static int do_bootu(struct command *cmdtp, int argc, char *argv[]) +{ + int fd; + void *kernel = NULL; + + if (argc != 2) { + barebox_cmd_usage(cmdtp); + return 1; + } + + fd = open(argv[1], O_RDONLY); + if (fd > 0) + kernel = (void *)memmap(fd, PROT_READ); + + if (!kernel) + kernel = (void *)simple_strtoul(argv[1], NULL, 0); + + start_linux(kernel, 0, NULL); + + return 1; +} + +static const __maybe_unused char cmd_bootu_help[] = +"Usage: bootu <address>\n"; + +BAREBOX_CMD_START(bootu) + .cmd = do_bootu, + .usage = "bootu - start a raw linux image", + BAREBOX_CMD_HELP(cmd_bootu_help) +BAREBOX_CMD_END + diff --git a/arch/arm/lib/bootz.c b/arch/arm/lib/bootz.c new file mode 100644 index 00000000..cd8f4958 --- /dev/null +++ b/arch/arm/lib/bootz.c @@ -0,0 +1,100 @@ +#include <common.h> +#include <command.h> +#include <fs.h> +#include <fcntl.h> +#include <errno.h> +#include <malloc.h> +#include <asm/byteorder.h> +#include <asm/armlinux.h> +#include <asm/system.h> + +struct zimage_header { + u32 unused[9]; + u32 magic; + u32 start; + u32 end; +}; + +#define ZIMAGE_MAGIC 0x016F2818 + +static int do_bootz(struct command *cmdtp, int argc, char *argv[]) +{ + int fd, ret, swap = 0; + struct zimage_header header; + void *zimage; + u32 end; + + if (argc != 2) { + barebox_cmd_usage(cmdtp); + return 1; + } + + fd = open(argv[1], O_RDONLY); + if (fd < 0) { + perror("open"); + return 1; + } + + ret = read(fd, &header, sizeof(header)); + if (ret < sizeof(header)) { + printf("could not read %s\n", argv[1]); + goto err_out; + } + + switch (header.magic) { +#ifdef CONFIG_BOOT_ENDIANNESS_SWITCH + case swab32(ZIMAGE_MAGIC): + swap = 1; + /* fall through */ +#endif + case ZIMAGE_MAGIC: + break; + default: + printf("invalid magic 0x%08x\n", header.magic); + goto err_out; + } + + end = header.end; + + if (swap) + end = swab32(end); + + zimage = xmalloc(end); + memcpy(zimage, &header, sizeof(header)); + + ret = read(fd, zimage + sizeof(header), end - sizeof(header)); + if (ret < end - sizeof(header)) { + printf("could not read %s\n", argv[1]); + goto err_out1; + } + + if (swap) { + void *ptr; + for (ptr = zimage; ptr < zimage + end; ptr += 4) + *(u32 *)ptr = swab32(*(u32 *)ptr); + } + + printf("loaded zImage from %s with size %d\n", argv[1], end); + + start_linux(zimage, swap, NULL); + + return 0; + +err_out1: + free(zimage); +err_out: + close(fd); + + return 1; +} + +static const __maybe_unused char cmd_bootz_help[] = +"Usage: bootz [FILE]\n" +"Boot a Linux zImage\n"; + +BAREBOX_CMD_START(bootz) + .cmd = do_bootz, + .usage = "bootz - start a zImage", + BAREBOX_CMD_HELP(cmd_bootz_help) +BAREBOX_CMD_END + diff --git a/arch/arm/mach-omap/Kconfig b/arch/arm/mach-omap/Kconfig index 630405b1..f256310c 100644 --- a/arch/arm/mach-omap/Kconfig +++ b/arch/arm/mach-omap/Kconfig @@ -32,66 +32,159 @@ choice config ARCH_OMAP3 bool "OMAP3" select CPU_V7 + select GENERIC_GPIO select ARCH_HAS_LOWLEVEL_INIT select OMAP_CLOCK_SOURCE_S32K help Say Y here if you are using Texas Instrument's OMAP343x based platform +config ARCH_OMAP4 + bool "OMAP4" + select CPU_V7 + select GENERIC_GPIO + select OMAP_CLOCK_SOURCE_S32K + help + Say Y here if you are using Texas Instrument's OMAP4 based platform + endchoice - ### Generic Clock configurations to be enabled by Mach - invisible to enable. - config OMAP_CLOCK_UART - bool - config OMAP_CLOCK_UART2 - bool - config OMAP_CLOCK_UART3 - bool - config OMAP_CLOCK_I2C - bool - - # Blind enable all possible clocks.. think twice before you do this. - config OMAP_CLOCK_ALL - bool - - config OMAP_CLOCK_SOURCE_S32K - bool - - config OMAP3_CLOCK_CONFIG - prompt "Clock Configuration" - bool - depends on ARCH_OMAP3 - default y - help - Say Y here if you like to have OMAP3 Clock configuration done. - - config OMAP3_COPY_CLOCK_SRAM - prompt "SRAM copy of Clock code" - bool - depends on OMAP3_CLOCK_CONFIG - default y - help - Say Y here if you like to have initial OMAP3 Clock configuration done from SRAM. - -config GPMC +### Generic Clock configurations to be enabled by Mach - invisible to enable. +config OMAP_CLOCK_UART + bool +config OMAP_CLOCK_UART2 + bool +config OMAP_CLOCK_UART3 + bool +config OMAP_CLOCK_I2C + bool + +# Blind enable all possible clocks.. think twice before you do this. +config OMAP_CLOCK_ALL + bool + +config OMAP_CLOCK_SOURCE_S32K + bool + +config OMAP3_CLOCK_CONFIG + prompt "Clock Configuration" + bool + depends on ARCH_OMAP3 + default y + help + Say Y here if you like to have OMAP3 Clock configuration done. + +config OMAP3_COPY_CLOCK_SRAM + prompt "SRAM copy of Clock code" + bool + depends on OMAP3_CLOCK_CONFIG + default y + help + Say Y here if you like to have initial OMAP3 Clock configuration done from SRAM. + +config OMAP_GPMC prompt "Support for GPMC configuration" bool - depends on (ARCH_OMAP2 || ARCH_OMAP3) + depends on (ARCH_OMAP2 || ARCH_OMAP3 || ARCH_OMAP4) default y help Enable this if you use Texas Instrument's General purpose Memory Controller(GPMC). GPMC allows you to configure devices such as NOR, NAND, OneNAND etc. -config GPIO - prompt "Support for GPIO configuration" +config OMAP_BUILD_IFT + prompt "build ift binary" bool - select GENERIC_GPIO - depends on (ARCH_OMAP2 || ARCH_OMAP3) - default y + +config ARCH_TEXT_BASE + hex + default 0x80e80000 if MACH_OMAP343xSDP + default 0x80e80000 if MACH_BEAGLE + +config BOARDINFO + default "Texas Instrument's SDP343x" if MACH_OMAP343xSDP + default "Texas Instrument's Beagle" if MACH_BEAGLE + default "Texas Instrument's OMAP3EVM" if MACH_OMAP3EVM + default "Texas Instrument's Panda" if MACH_PANDA + default "Phytec phyCORE pcm049" if MACH_PCM049 + +choice + prompt "Select OMAP board" + +config MACH_OMAP343xSDP + bool "Texas Instrument's SDP343x" + select MACH_HAS_LOWLEVEL_INIT + select OMAP_CLOCK_ALL + select HAS_OMAP_NAND + depends on ARCH_OMAP3 + help + Say Y here if you are using SDP343x platform + +config MACH_BEAGLE + bool "Texas Instrument's Beagle Board" + select MACH_HAS_LOWLEVEL_INIT + select OMAP_CLOCK_ALL + select HAVE_NOSHELL + select HAS_OMAP_NAND + depends on ARCH_OMAP3 + help + Say Y here if you are using Beagle Board + +config MACH_OMAP3EVM + bool "Texas Instrument's OMAP3 EVM" + select MACH_HAS_LOWLEVEL_INIT + select OMAP_CLOCK_ALL + select HAS_OMAP_NAND + depends on ARCH_OMAP3 + help + Say Y here if you are using OMAP3EVM + +config MACH_PANDA + bool "Texas Instrument's Panda Board" + select HAVE_MMU + select HAVE_NOSHELL + select MACH_HAS_LOWLEVEL_INIT + help + Say Y here if you are using OMAP4 Panda board + +config MACH_PCM049 + bool "Phytec phyCORE pcm049" + select HAVE_MMU + select HAVE_NOSHELL + depends on ARCH_OMAP4 + select MACH_HAS_LOWLEVEL_INIT help - Enable this if you use Texas Instrument's General Purpose IO + Say Y here if you are using Phytecs phyCORE pcm049 board + based on OMAP4 + +endchoice + +if MACH_OMAP3EVM + choice + prompt "Select UART" -# Get the board specific configurations -source arch/arm/boards/omap/Kconfig + config OMAP3EVM_UART1 + bool "Use UART1" + depends on MACH_OMAP3EVM + help + Say Y here if you would like to use UART1 as console. + + config OMAP3EVM_UART3 + bool "Use UART3" + depends on MACH_OMAP3EVM + help + Say Y here if you would like to use UART3 as console. + endchoice +endif + +config MACH_OMAP_ADVANCED_MUX + bool "Enable advanced pin muxing" + depends on MACH_OMAP343xSDP + default n + help + Say Y here if you would like to have complete pin muxing to be + done at boot time + +config HAS_OMAP_NAND + bool endmenu diff --git a/arch/arm/mach-omap/Makefile b/arch/arm/mach-omap/Makefile index 57bab998..a4b9a557 100644 --- a/arch/arm/mach-omap/Makefile +++ b/arch/arm/mach-omap/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_ARCH_OMAP) += syslib.o obj-$(CONFIG_OMAP_CLOCK_SOURCE_S32K) += s32k_clksource.o obj-$(CONFIG_ARCH_OMAP3) += omap3_core.o omap3_generic.o +obj-$(CONFIG_ARCH_OMAP4) += omap4_generic.o omap4_clock.o obj-$(CONFIG_OMAP3_CLOCK_CONFIG) += omap3_clock_core.o omap3_clock.o -obj-$(CONFIG_GPMC) += gpmc.o -obj-$(CONFIG_GPIO) += gpio.o +obj-$(CONFIG_OMAP_GPMC) += gpmc.o devices-gpmc-nand.o +obj-y += omap-uart.o gpio.o xload.o diff --git a/arch/arm/boards/omap/devices-gpmc-nand.c b/arch/arm/mach-omap/devices-gpmc-nand.c index 4369aa00..c2a2b0d1 100644 --- a/arch/arm/boards/omap/devices-gpmc-nand.c +++ b/arch/arm/mach-omap/devices-gpmc-nand.c @@ -84,7 +84,8 @@ static struct device_d gpmc_generic_nand_nand_device = { * * @return success/fail based on device funtion */ -int gpmc_generic_nand_devices_init(int cs, int width, int hwecc) +int gpmc_generic_nand_devices_init(int cs, int width, + enum gpmc_ecc_mode eccmode) { nand_plat.cs = cs; @@ -94,7 +95,7 @@ int gpmc_generic_nand_devices_init(int cs, int width, int hwecc) nand_cfg.cfg[0] = GPMC_CONF1_VALx8; nand_plat.device_width = width; - nand_plat.plat_options = hwecc ? NAND_HWECC_ENABLE : 0; + nand_plat.ecc_mode = eccmode; /* Configure GPMC CS before register */ gpmc_cs_config(nand_plat.cs, &nand_cfg); diff --git a/arch/arm/mach-omap/gpio.c b/arch/arm/mach-omap/gpio.c index 240ac8e2..b0bd8056 100644 --- a/arch/arm/mach-omap/gpio.c +++ b/arch/arm/mach-omap/gpio.c @@ -40,20 +40,45 @@ #include <asm/io.h> #include <errno.h> -static struct gpio_bank gpio_bank_34xx[6] = { - { (void *)OMAP34XX_GPIO1_BASE, METHOD_GPIO_24XX }, - { (void *)OMAP34XX_GPIO2_BASE, METHOD_GPIO_24XX }, - { (void *)OMAP34XX_GPIO3_BASE, METHOD_GPIO_24XX }, - { (void *)OMAP34XX_GPIO4_BASE, METHOD_GPIO_24XX }, - { (void *)OMAP34XX_GPIO5_BASE, METHOD_GPIO_24XX }, - { (void *)OMAP34XX_GPIO6_BASE, METHOD_GPIO_24XX }, +#ifdef CONFIG_ARCH_OMAP3 + +#define OMAP_GPIO_OE 0x0034 +#define OMAP_GPIO_DATAIN 0x0038 +#define OMAP_GPIO_DATAOUT 0x003c +#define OMAP_GPIO_CLEARDATAOUT 0x0090 +#define OMAP_GPIO_SETDATAOUT 0x0094 + +static void __iomem *gpio_bank[] = { + (void *)0x48310000, + (void *)0x49050000, + (void *)0x49052000, + (void *)0x49054000, + (void *)0x49056000, + (void *)0x49058000, }; +#endif + +#ifdef CONFIG_ARCH_OMAP4 + +#define OMAP_GPIO_OE 0x0134 +#define OMAP_GPIO_DATAIN 0x0138 +#define OMAP_GPIO_DATAOUT 0x013c +#define OMAP_GPIO_CLEARDATAOUT 0x0190 +#define OMAP_GPIO_SETDATAOUT 0x0194 + +static void __iomem *gpio_bank[] = { + (void *)0x4a310000, + (void *)0x48055000, + (void *)0x48057000, + (void *)0x48059000, + (void *)0x4805b000, + (void *)0x4805d000, +}; +#endif -static struct gpio_bank *gpio_bank = &gpio_bank_34xx[0]; - -static inline struct gpio_bank *get_gpio_bank(int gpio) +static inline void __iomem *get_gpio_base(int gpio) { - return &gpio_bank[gpio >> 5]; + return gpio_bank[gpio >> 5]; } static inline int get_gpio_index(int gpio) @@ -65,7 +90,7 @@ static inline int gpio_valid(int gpio) { if (gpio < 0) return -1; - if (gpio < 192) + if (gpio / 32 < ARRAY_SIZE(gpio_bank)) return 0; return -1; } @@ -81,50 +106,35 @@ static int check_gpio(int gpio) void gpio_set_value(unsigned gpio, int value) { - struct gpio_bank *bank; - void *reg; + void __iomem *reg; u32 l = 0; if (check_gpio(gpio) < 0) return; - bank = get_gpio_bank(gpio); - reg = bank->base; - - switch (bank->method) { - case METHOD_GPIO_24XX: - if (value) - reg += OMAP24XX_GPIO_SETDATAOUT; - else - reg += OMAP24XX_GPIO_CLEARDATAOUT; - l = 1 << get_gpio_index(gpio); - break; - default: - printf("omap3-gpio unknown bank method %s %d\n", - __FILE__, __LINE__); - return; - } + + reg = get_gpio_base(gpio); + + if (value) + reg += OMAP_GPIO_SETDATAOUT; + else + reg += OMAP_GPIO_CLEARDATAOUT; + l = 1 << get_gpio_index(gpio); + __raw_writel(l, reg); } int gpio_direction_input(unsigned gpio) { - struct gpio_bank *bank; - void *reg; + void __iomem *reg; u32 val; if (check_gpio(gpio) < 0) return -EINVAL; - bank = get_gpio_bank(gpio); - reg = bank->base; + reg = get_gpio_base(gpio); + + reg += OMAP_GPIO_OE; - switch (bank->method) { - case METHOD_GPIO_24XX: - reg += OMAP24XX_GPIO_OE; - break; - default: - return -EINVAL; - } val = __raw_readl(reg); val |= 1 << get_gpio_index(gpio); __raw_writel(val, reg); @@ -134,25 +144,16 @@ int gpio_direction_input(unsigned gpio) int gpio_direction_output(unsigned gpio, int value) { - struct gpio_bank *bank; - void *reg; + void __iomem *reg; u32 val; if (check_gpio(gpio) < 0) return -EINVAL; - bank = get_gpio_bank(gpio); - - reg = bank->base; + reg = get_gpio_base(gpio); gpio_set_value(gpio, value); - switch (bank->method) { - case METHOD_GPIO_24XX: - reg += OMAP24XX_GPIO_OE; - break; - default: - return -EINVAL; - } + reg += OMAP_GPIO_OE; val = __raw_readl(reg); val &= ~(1 << get_gpio_index(gpio)); @@ -163,44 +164,13 @@ int gpio_direction_output(unsigned gpio, int value) int gpio_get_value(unsigned gpio) { - struct gpio_bank *bank; - void *reg; - - if (check_gpio(gpio) < 0) - return -EINVAL; - bank = get_gpio_bank(gpio); - reg = bank->base; - switch (bank->method) { - case METHOD_GPIO_24XX: - reg += OMAP24XX_GPIO_DATAIN; - break; - default: - return -EINVAL; - } - return (__raw_readl(reg) - & (1 << get_gpio_index(gpio))) != 0; -} - -static void _reset_gpio(int gpio) -{ - gpio_direction_input(gpio); -} + void __iomem *reg; -int omap_request_gpio(int gpio) -{ if (check_gpio(gpio) < 0) return -EINVAL; + reg = get_gpio_base(gpio); - return 0; -} - -void omap_free_gpio(int gpio) -{ - struct gpio_bank *bank; - - if (check_gpio(gpio) < 0) - return; - bank = get_gpio_bank(gpio); + reg += OMAP_GPIO_DATAIN; - _reset_gpio(gpio); + return (__raw_readl(reg) & (1 << get_gpio_index(gpio))) != 0; } diff --git a/arch/arm/mach-omap/include/mach/gpio.h b/arch/arm/mach-omap/include/mach/gpio.h index 9840b6e9..245f781e 100644 --- a/arch/arm/mach-omap/include/mach/gpio.h +++ b/arch/arm/mach-omap/include/mach/gpio.h @@ -38,52 +38,6 @@ #ifndef _GPIO_H #define _GPIO_H -#define OMAP24XX_GPIO_REVISION 0x0000 -#define OMAP24XX_GPIO_SYSCONFIG 0x0010 -#define OMAP24XX_GPIO_SYSSTATUS 0x0014 -#define OMAP24XX_GPIO_IRQSTATUS1 0x0018 -#define OMAP24XX_GPIO_IRQSTATUS2 0x0028 -#define OMAP24XX_GPIO_IRQENABLE2 0x002c -#define OMAP24XX_GPIO_IRQENABLE1 0x001c -#define OMAP24XX_GPIO_WAKE_EN 0x0020 -#define OMAP24XX_GPIO_CTRL 0x0030 -#define OMAP24XX_GPIO_OE 0x0034 -#define OMAP24XX_GPIO_DATAIN 0x0038 -#define OMAP24XX_GPIO_DATAOUT 0x003c -#define OMAP24XX_GPIO_LEVELDETECT0 0x0040 -#define OMAP24XX_GPIO_LEVELDETECT1 0x0044 -#define OMAP24XX_GPIO_RISINGDETECT 0x0048 -#define OMAP24XX_GPIO_FALLINGDETECT 0x004c -#define OMAP24XX_GPIO_DEBOUNCE_EN 0x0050 -#define OMAP24XX_GPIO_DEBOUNCE_VAL 0x0054 -#define OMAP24XX_GPIO_CLEARIRQENABLE1 0x0060 -#define OMAP24XX_GPIO_SETIRQENABLE1 0x0064 -#define OMAP24XX_GPIO_CLEARWKUENA 0x0080 -#define OMAP24XX_GPIO_SETWKUENA 0x0084 -#define OMAP24XX_GPIO_CLEARDATAOUT 0x0090 -#define OMAP24XX_GPIO_SETDATAOUT 0x0094 - -struct gpio_bank { - void *base; - int method; -}; - -/* OMAP3 GPIO registers */ -#define OMAP34XX_GPIO1_BASE 0x48310000 -#define OMAP34XX_GPIO2_BASE 0x49050000 -#define OMAP34XX_GPIO3_BASE 0x49052000 -#define OMAP34XX_GPIO4_BASE 0x49054000 -#define OMAP34XX_GPIO5_BASE 0x49056000 -#define OMAP34XX_GPIO6_BASE 0x49058000 - -#define METHOD_GPIO_24XX 4 - -/* This is the interface */ - -/* Request a gpio before using it */ -int omap_request_gpio(int gpio); -/* Reset and free a gpio after using it */ -void omap_free_gpio(int gpio); void gpio_set_value(unsigned gpio, int value); int gpio_get_value(unsigned gpio); int gpio_direction_output(unsigned gpio, int value); diff --git a/arch/arm/mach-omap/include/mach/gpmc.h b/arch/arm/mach-omap/include/mach/gpmc.h index a658cf00..3ddc5f5b 100644 --- a/arch/arm/mach-omap/include/mach/gpmc.h +++ b/arch/arm/mach-omap/include/mach/gpmc.h @@ -66,6 +66,7 @@ #define GPMC_ECC7_RESULT (0x218) #define GPMC_ECC8_RESULT (0x21C) #define GPMC_ECC9_RESULT (0x220) +#define GPMC_ECC_BCH_RESULT_0 0x240 #define GPMC_CONFIG1_0 (0x60) #define GPMC_CONFIG1_1 (0x90) diff --git a/arch/arm/mach-omap/include/mach/gpmc_nand.h b/arch/arm/mach-omap/include/mach/gpmc_nand.h index e23faabc..1bc52ffe 100644 --- a/arch/arm/mach-omap/include/mach/gpmc_nand.h +++ b/arch/arm/mach-omap/include/mach/gpmc_nand.h @@ -33,6 +33,14 @@ #include <linux/mtd/nand.h> #include <linux/mtd/nand_ecc.h> +enum gpmc_ecc_mode { + OMAP_ECC_SOFT, + OMAP_ECC_HAMMING_CODE_HW_ROMCODE, + OMAP_ECC_BCH4_CODE_HW, + OMAP_ECC_BCH8_CODE_HW, + OMAP_ECC_BCH8_CODE_HW_ROMCODE, +}; + /** omap nand platform data structure */ struct gpmc_nand_platform_data { /** Chip select you want to use */ @@ -46,6 +54,8 @@ struct gpmc_nand_platform_data { * platform specific configs here */ unsigned short plat_options; + /** ecc mode to use */ + enum gpmc_ecc_mode ecc_mode; /** setup any special options */ unsigned int options; /** set up device access as 8,16 as per GPMC config */ @@ -68,11 +78,6 @@ struct gpmc_nand_platform_data { #define NAND_WAITPOL_HIGH (1 << 0) #define NAND_WAITPOL_MASK (1 << 0) -/** plat_options: hw ecc enabled */ -#define NAND_HWECC_ENABLE (1 << 1) -/** plat_options: hw ecc disabled */ -#define NAND_HWECC_MASK (1 << 1) - -int gpmc_generic_nand_devices_init(int cs, int width, int hwecc); +int gpmc_generic_nand_devices_init(int cs, int width, enum gpmc_ecc_mode); #endif /* __ASM_OMAP_NAND_GPMC_H */ diff --git a/arch/arm/mach-omap/include/mach/omap4-clock.h b/arch/arm/mach-omap/include/mach/omap4-clock.h new file mode 100644 index 00000000..0a31d09e --- /dev/null +++ b/arch/arm/mach-omap/include/mach/omap4-clock.h @@ -0,0 +1,320 @@ + /* PRCM */ +#define CM_SYS_CLKSEL 0x4a306110 + +#define CM_SYS_CLKSEL_19M2 0x4 +#define CM_SYS_CLKSEL_38M4 0x7 + +/* PRM.CKGEN module registers */ +#define CM_ABE_PLL_REF_CLKSEL 0x4a30610c + + +/* PRM.WKUP_CM module registers */ +#define CM_WKUP_CLKSTCTRL 0x4a307800 +#define CM_WKUP_L4WKUP_CLKCTRL 0x4a307820 +#define CM_WKUP_WDT1_CLKCTRL 0x4a307828 +#define CM_WKUP_WDT2_CLKCTRL 0x4a307830 +#define CM_WKUP_GPIO1_CLKCTRL 0x4a307838 +#define CM_WKUP_TIMER1_CLKCTRL 0x4a307840 +#define CM_WKUP_TIMER12_CLKCTRL 0x4a307848 +#define CM_WKUP_SYNCTIMER_CLKCTRL 0x4a307850 +#define CM_WKUP_USIM_CLKCTRL 0x4a307858 +#define CM_WKUP_SARRAM_CLKCTRL 0x4a307860 +#define CM_WKUP_KEYBOARD_CLKCTRL 0x4a307878 +#define CM_WKUP_RTC_CLKCTRL 0x4a307880 +#define CM_WKUP_BANDGAP_CLKCTRL 0x4a307888 + +/* CM1.CKGEN module registers */ +#define CM_CLKSEL_CORE 0x4a004100 +#define CM_CLKSEL_ABE 0x4a004108 +#define CM_DLL_CTRL 0x4a004110 +#define CM_CLKMODE_DPLL_CORE 0x4a004120 +#define CM_IDLEST_DPLL_CORE 0x4a004124 +#define CM_AUTOIDLE_DPLL_CORE 0x4a004128 +#define CM_CLKSEL_DPLL_CORE 0x4a00412c +#define CM_DIV_M2_DPLL_CORE 0x4a004130 +#define CM_DIV_M3_DPLL_CORE 0x4a004134 +#define CM_DIV_M4_DPLL_CORE 0x4a004138 +#define CM_DIV_M5_DPLL_CORE 0x4a00413c +#define CM_DIV_M6_DPLL_CORE 0x4a004140 +#define CM_DIV_M7_DPLL_CORE 0x4a004144 +#define CM_SSC_DELTAMSTEP_DPLL_CORE 0x4a004148 +#define CM_SSC_MODFREQDIV_DPLL_CORE 0x4a00414c +#define CM_EMU_OVERRIDE_DPLL_CORE 0x4a004150 +#define CM_CLKMODE_DPLL_MPU 0x4a004160 +#define CM_IDLEST_DPLL_MPU 0x4a004164 +#define CM_AUTOIDLE_DPLL_MPU 0x4a004168 +#define CM_CLKSEL_DPLL_MPU 0x4a00416c +#define CM_DIV_M2_DPLL_MPU 0x4a004170 +#define CM_SSC_DELTAMSTEP_DPLL_MPU 0x4a004188 +#define CM_SSC_MODFREQDIV_DPLL_MPU 0x4a00418c +#define CM_BYPCLK_DPLL_MPU 0x4a00419c +#define CM_CLKMODE_DPLL_IVA 0x4a0041a0 +#define CM_IDLEST_DPLL_IVA 0x4a0041a4 +#define CM_AUTOIDLE_DPLL_IVA 0x4a0041a8 +#define CM_CLKSEL_DPLL_IVA 0x4a0041ac +#define CM_DIV_M4_DPLL_IVA 0x4a0041b8 +#define CM_DIV_M5_DPLL_IVA 0x4a0041bc +#define CM_SSC_DELTAMSTEP_DPLL_IVA 0x4a0041c8 +#define CM_SSC_MODFREQDIV_DPLL_IVA 0x4a0041cc +#define CM_BYPCLK_DPLL_IVA 0x4a0041dc +#define CM_CLKMODE_DPLL_ABE 0x4a0041e0 +#define CM_IDLEST_DPLL_ABE 0x4a0041e4 +#define CM_AUTOIDLE_DPLL_ABE 0x4a0041e8 +#define CM_CLKSEL_DPLL_ABE 0x4a0041ec +#define CM_DIV_M2_DPLL_ABE 0x4a0041f0 +#define CM_DIV_M3_DPLL_ABE 0x4a0041f4 +#define CM_SSC_DELTAMSTEP_DPLL_ABE 0x4a004208 +#define CM_SSC_MODFREQDIV_DPLL_ABE 0x4a00420c +#define CM_CLKMODE_DPLL_DDRPHY 0x4a004220 +#define CM_IDLEST_DPLL_DDRPHY 0x4a004224 +#define CM_AUTOIDLE_DPLL_DDRPHY 0x4a004228 +#define CM_CLKSEL_DPLL_DDRPHY 0x4a00422c +#define CM_DIV_M2_DPLL_DDRPHY 0x4a004230 +#define CM_DIV_M4_DPLL_DDRPHY 0x4a004238 +#define CM_DIV_M5_DPLL_DDRPHY 0x4a00423c +#define CM_DIV_M6_DPLL_DDRPHY 0x4a004240 +#define CM_SSC_DELTAMSTEP_DPLL_DDRPHY 0x4a004248 + +/* CM1.ABE register offsets */ +#define CM1_ABE_CLKSTCTRL 0x4a004500 +#define CM1_ABE_L4ABE_CLKCTRL 0x4a004520 +#define CM1_ABE_AESS_CLKCTRL 0x4a004528 +#define CM1_ABE_PDM_CLKCTRL 0x4a004530 +#define CM1_ABE_DMIC_CLKCTRL 0x4a004538 +#define CM1_ABE_MCASP_CLKCTRL 0x4a004540 +#define CM1_ABE_MCBSP1_CLKCTRL 0x4a004548 +#define CM1_ABE_MCBSP2_CLKCTRL 0x4a004550 +#define CM1_ABE_MCBSP3_CLKCTRL 0x4a004558 +#define CM1_ABE_SLIMBUS_CLKCTRL 0x4a004560 +#define CM1_ABE_TIMER5_CLKCTRL 0x4a004568 +#define CM1_ABE_TIMER6_CLKCTRL 0x4a004570 +#define CM1_ABE_TIMER7_CLKCTRL 0x4a004578 +#define CM1_ABE_TIMER8_CLKCTRL 0x4a004580 +#define CM1_ABE_WDT3_CLKCTRL 0x4a004588 + +/* CM1.DSP register offsets */ +#define DSP_CLKSTCTRL 0x4a004400 +#define DSP_DSP_CLKCTRL 0x4a004420 + +/* CM2.CKGEN module registers */ +#define CM_CLKSEL_DUCATI_ISS_ROOT 0x4a008100 +#define CM_CLKSEL_USB_60MHz 0x4a008104 +#define CM_SCALE_FCLK 0x4a008108 +#define CM_CORE_DVFS_PERF1 0x4a008110 +#define CM_CORE_DVFS_PERF2 0x4a008114 +#define CM_CORE_DVFS_PERF3 0x4a008118 +#define CM_CORE_DVFS_PERF4 0x4a00811c +#define CM_CORE_DVFS_CURRENT 0x4a008124 +#define CM_IVA_DVFS_PERF_TESLA 0x4a008128 +#define CM_IVA_DVFS_PERF_IVAHD 0x4a00812c +#define CM_IVA_DVFS_PERF_ABE 0x4a008130 +#define CM_IVA_DVFS_CURRENT 0x4a008138 +#define CM_CLKMODE_DPLL_PER 0x4a008140 +#define CM_IDLEST_DPLL_PER 0x4a008144 +#define CM_AUTOIDLE_DPLL_PER 0x4a008148 +#define CM_CLKSEL_DPLL_PER 0x4a00814c +#define CM_DIV_M2_DPLL_PER 0x4a008150 +#define CM_DIV_M3_DPLL_PER 0x4a008154 +#define CM_DIV_M4_DPLL_PER 0x4a008158 +#define CM_DIV_M5_DPLL_PER 0x4a00815c +#define CM_DIV_M6_DPLL_PER 0x4a008160 +#define CM_DIV_M7_DPLL_PER 0x4a008164 +#define CM_SSC_DELTAMSTEP_DPLL_PER 0x4a008168 +#define CM_SSC_MODFREQDIV_DPLL_PER 0x4a00816c +#define CM_EMU_OVERRIDE_DPLL_PER 0x4a008170 +#define CM_CLKMODE_DPLL_USB 0x4a008180 +#define CM_IDLEST_DPLL_USB 0x4a008184 +#define CM_AUTOIDLE_DPLL_USB 0x4a008188 +#define CM_CLKSEL_DPLL_USB 0x4a00818c +#define CM_DIV_M2_DPLL_USB 0x4a008190 +#define CM_SSC_DELTAMSTEP_DPLL_USB 0x4a0081a8 +#define CM_SSC_MODFREQDIV_DPLL_USB 0x4a0081ac +#define CM_CLKDCOLDO_DPLL_USB 0x4a0081b4 +#define CM_CLKMODE_DPLL_UNIPRO 0x4a0081c0 +#define CM_IDLEST_DPLL_UNIPRO 0x4a0081c4 +#define CM_AUTOIDLE_DPLL_UNIPRO 0x4a0081c8 +#define CM_CLKSEL_DPLL_UNIPRO 0x4a0081cc +#define CM_DIV_M2_DPLL_UNIPRO 0x4a0081d0 +#define CM_SSC_DELTAMSTEP_DPLL_UNIPRO 0x4a0081e8 +#define CM_SSC_MODFREQDIV_DPLL_UNIPRO 0x4a0081ec + +/* CM2.CORE module registers */ +#define CM_L3_1_CLKSTCTRL 0x4a008700 +#define CM_L3_1_DYNAMICDEP 0x4a008708 +#define CM_L3_1_L3_1_CLKCTRL 0x4a008720 +#define CM_L3_2_CLKSTCTRL 0x4a008800 +#define CM_L3_2_DYNAMICDEP 0x4a008808 +#define CM_L3_2_L3_2_CLKCTRL 0x4a008820 +#define CM_L3_2_GPMC_CLKCTRL 0x4a008828 +#define CM_L3_2_OCMC_RAM_CLKCTRL 0x4a008830 +#define CM_DUCATI_CLKSTCTRL 0x4a008900 +#define CM_DUCATI_STATICDEP 0x4a008904 +#define CM_DUCATI_DYNAMICDEP 0x4a008908 +#define CM_DUCATI_DUCATI_CLKCTRL 0x4a008920 +#define CM_SDMA_CLKSTCTRL 0x4a008a00 +#define CM_SDMA_STATICDEP 0x4a008a04 +#define CM_SDMA_DYNAMICDEP 0x4a008a08 +#define CM_SDMA_SDMA_CLKCTRL 0x4a008a20 +#define CM_MEMIF_CLKSTCTRL 0x4a008b00 +#define CM_MEMIF_DMM_CLKCTRL 0x4a008b20 +#define CM_MEMIF_EMIF_FW_CLKCTRL 0x4a008b28 +#define CM_MEMIF_EMIF_1_CLKCTRL 0x4a008b30 +#define CM_MEMIF_EMIF_2_CLKCTRL 0x4a008b38 +#define CM_MEMIF_DLL_CLKCTRL 0x4a008b40 +#define CM_MEMIF_EMIF_H1_CLKCTRL 0x4a008b50 +#define CM_MEMIF_EMIF_H2_CLKCTRL 0x4a008b58 +#define CM_MEMIF_DLL_H_CLKCTRL 0x4a008b60 +#define CM_D2D_CLKSTCTRL 0x4a008c00 +#define CM_D2D_STATICDEP 0x4a008c04 +#define CM_D2D_DYNAMICDEP 0x4a008c08 +#define CM_D2D_SAD2D_CLKCTRL 0x4a008c20 +#define CM_D2D_MODEM_ICR_CLKCTRL 0x4a008c28 +#define CM_D2D_SAD2D_FW_CLKCTRL 0x4a008c30 +#define CM_L4CFG_CLKSTCTRL 0x4a008d00 +#define CM_L4CFG_DYNAMICDEP 0x4a008d08 +#define CM_L4CFG_L4_CFG_CLKCTRL 0x4a008d20 +#define CM_L4CFG_HW_SEM_CLKCTRL 0x4a008d28 +#define CM_L4CFG_MAILBOX_CLKCTRL 0x4a008d30 +#define CM_L4CFG_SAR_ROM_CLKCTRL 0x4a008d38 +#define CM_L3INSTR_CLKSTCTRL 0x4a008e00 +#define CM_L3INSTR_L3_3_CLKCTRL 0x4a008e20 +#define CM_L3INSTR_L3_INSTR_CLKCTRL 0x4a008e28 +#define CM_L3INSTR_OCP_WP1_CLKCTRL 0x4a008e40 + +/* CM2.L4PER register offsets */ +#define CM_L4PER_CLKSTCTRL 0x4a009400 +#define CM_L4PER_DYNAMICDEP 0x4a009408 +#define CM_L4PER_ADC_CLKCTRL 0x4a009420 +#define CM_L4PER_DMTIMER10_CLKCTRL 0x4a009428 +#define CM_L4PER_DMTIMER11_CLKCTRL 0x4a009430 +#define CM_L4PER_DMTIMER2_CLKCTRL 0x4a009438 +#define CM_L4PER_DMTIMER3_CLKCTRL 0x4a009440 +#define CM_L4PER_DMTIMER4_CLKCTRL 0x4a009448 +#define CM_L4PER_DMTIMER9_CLKCTRL 0x4a009450 +#define CM_L4PER_ELM_CLKCTRL 0x4a009458 +#define CM_L4PER_GPIO2_CLKCTRL 0x4a009460 +#define CM_L4PER_GPIO3_CLKCTRL 0x4a009468 +#define CM_L4PER_GPIO4_CLKCTRL 0x4a009470 +#define CM_L4PER_GPIO5_CLKCTRL 0x4a009478 +#define CM_L4PER_GPIO6_CLKCTRL 0x4a009480 +#define CM_L4PER_HDQ1W_CLKCTRL 0x4a009488 +#define CM_L4PER_HECC1_CLKCTRL 0x4a009490 +#define CM_L4PER_HECC2_CLKCTRL 0x4a009498 +#define CM_L4PER_I2C1_CLKCTRL 0x4a0094a0 +#define CM_L4PER_I2C2_CLKCTRL 0x4a0094a8 +#define CM_L4PER_I2C3_CLKCTRL 0x4a0094b0 +#define CM_L4PER_I2C4_CLKCTRL 0x4a0094b8 +#define CM_L4PER_L4PER_CLKCTRL 0x4a0094c0 +#define CM_L4PER_MCASP2_CLKCTRL 0x4a0094d0 +#define CM_L4PER_MCASP3_CLKCTRL 0x4a0094d8 +#define CM_L4PER_MCBSP4_CLKCTRL 0x4a0094e0 +#define CM_L4PER_MGATE_CLKCTRL 0x4a0094e8 +#define CM_L4PER_MCSPI1_CLKCTRL 0x4a0094f0 +#define CM_L4PER_MCSPI2_CLKCTRL 0x4a0094f8 +#define CM_L4PER_MCSPI3_CLKCTRL 0x4a009500 +#define CM_L4PER_MCSPI4_CLKCTRL 0x4a009508 +#define CM_L4PER_MMCSD3_CLKCTRL 0x4a009520 +#define CM_L4PER_MMCSD4_CLKCTRL 0x4a009528 +#define CM_L4PER_MSPROHG_CLKCTRL 0x4a009530 +#define CM_L4PER_SLIMBUS2_CLKCTRL 0x4a009538 +#define CM_L4PER_UART1_CLKCTRL 0x4a009540 +#define CM_L4PER_UART2_CLKCTRL 0x4a009548 +#define CM_L4PER_UART3_CLKCTRL 0x4a009550 +#define CM_L4PER_UART4_CLKCTRL 0x4a009558 +#define CM_L4PER_MMCSD5_CLKCTRL 0x4a009560 +#define CM_L4PER_I2C5_CLKCTRL 0x4a009568 +#define CM_L4SEC_CLKSTCTRL 0x4a009580 +#define CM_L4SEC_STATICDEP 0x4a009584 +#define CM_L4SEC_DYNAMICDEP 0x4a009588 +#define CM_L4SEC_AES1_CLKCTRL 0x4a0095a0 +#define CM_L4SEC_AES2_CLKCTRL 0x4a0095a8 +#define CM_L4SEC_DES3DES_CLKCTRL 0x4a0095b0 +#define CM_L4SEC_PKAEIP29_CLKCTRL 0x4a0095b8 +#define CM_L4SEC_RNG_CLKCTRL 0x4a0095c0 +#define CM_L4SEC_SHA2MD51_CLKCTRL 0x4a0095c8 +#define CM_L4SEC_CRYPTODMA_CLKCTRL 0x4a0095d8 + +/* CM2.IVAHD */ +#define IVAHD_CLKSTCTRL 0x4a008f00 +#define IVAHD_IVAHD_CLKCTRL 0x4a008f20 +#define IVAHD_SL2_CLKCTRL 0x4a008f28 + +/* CM2.L3INIT */ +#define CM_L3INIT_HSMMC1_CLKCTRL 0x4a009328 +#define CM_L3INIT_HSMMC2_CLKCTRL 0x4a009330 +#define CM_L3INIT_HSI_CLKCTRL 0x4a009338 +#define CM_L3INIT_UNIPRO1_CLKCTRL 0x4a009340 +#define CM_L3INIT_HSUSBHOST_CLKCTRL 0x4a009358 +#define CM_L3INIT_HSUSBOTG_CLKCTRL 0x4a009360 +#define CM_L3INIT_HSUSBTLL_CLKCTRL 0x4a009368 +#define CM_L3INIT_P1500_CLKCTRL 0x4a009378 +#define CM_L3INIT_FSUSB_CLKCTRL 0x4a0093d0 +#define CM_L3INIT_USBPHY_CLKCTRL 0x4a0093e0 + +/* CM2.CAM */ +#define CM_CAM_CLKSTCTRL 0x4a009000 +#define CM_CAM_ISS_CLKCTRL 0x4a009020 +#define CM_CAM_FDIF_CLKCTRL 0x4a009028 + +/* CM2.DSS */ +#define CM_DSS_CLKSTCTRL 0x4a009100 +#define CM_DSS_DSS_CLKCTRL 0x4a009120 +#define CM_DSS_DEISS_CLKCTRL 0x4a009128 + +/* CM2.SGX */ +#define CM_SGX_CLKSTCTRL 0x4a009200 +#define CM_SGX_SGX_CLKCTRL 0x4a009220 + +#define PLL_STOP 1 /* PER & IVA */ +#define PLL_MN_POWER_BYPASS 4 +#define PLL_LOW_POWER_BYPASS 5 /* MPU, IVA & CORE */ +#define PLL_FAST_RELOCK_BYPASS 6 /* CORE */ +#define PLL_LOCK 7 /* MPU, IVA, CORE & PER */ + +/* Used to index into DPLL parameter tables */ +struct dpll_param { + unsigned int m; + unsigned int n; + unsigned int m2; + unsigned int m3; + unsigned int m4; + unsigned int m5; + unsigned int m6; + unsigned int m7; +}; + +#define OMAP4_MPU_DPLL_PARAM_19M2 {0x34, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00} +#define OMAP4_MPU_DPLL_PARAM_19M2_MPU600 {0x7d, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00} +#define OMAP4_MPU_DPLL_PARAM_19M2_MPU1000 {0x69, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00} +#define OMAP4_MPU_DPLL_PARAM_38M4 {0x1a, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00} +#define OMAP4_MPU_DPLL_PARAM_38M4_MPU600 {0x7d, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00} +#define OMAP4_MPU_DPLL_PARAM_38M4_MPU1000 {0x69, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00} + +#define OMAP4_IVA_DPLL_PARAM_19M2 {0x61, 0x01, 0x00, 0x00, 0x04, 0x07, 0x00, 0x00} +#define OMAP4_IVA_DPLL_PARAM_38M4 {0x61, 0x03, 0x00, 0x00, 0x04, 0x07, 0x00, 0x00} + +#define OMAP4_PER_DPLL_PARAM_19M2 {0x28, 0x00, 0x08, 0x06, 0x0c, 0x09, 0x04, 0x05} +#define OMAP4_PER_DPLL_PARAM_38M4 {0x14, 0x00, 0x08, 0x06, 0x0c, 0x09, 0x04, 0x05} + +#define OMAP4_ABE_DPLL_PARAM_19M2 {0x80, 0x18, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00} +#define OMAP4_ABE_DPLL_PARAM_38M4 {0x40, 0x18, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00} + +#define OMAP4_USB_DPLL_PARAM_19M2 {0x32, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0} +#define OMAP4_USB_DPLL_PARAM_38M4 {0x32, 0x1, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0} + +#define OMAP4_CORE_DPLL_PARAM_19M2_DDR200 {0x7d, 0x02, 0x02, 0x05, 0x08, 0x04, 0x06, 0x05} +#define OMAP4_CORE_DPLL_PARAM_19M2_DDR333 {0x410, 0x09, 0x03, 0x0c, 0x14, 0x0a, 0x0f, 0x0c} +#define OMAP4_CORE_DPLL_PARAM_19M2_DDR400 {0x7d, 0x02, 0x01, 0x05, 0x08, 0x04, 0x06, 0x05} +#define OMAP4_CORE_DPLL_PARAM_38M4_DDR200 {0x7d, 0x05, 0x02, 0x05, 0x08, 0x04, 0x06, 0x05} +#define OMAP4_CORE_DPLL_PARAM_38M4_DDR400 {0x7d, 0x05, 0x01, 0x05, 0x08, 0x04, 0x06, 0x05} + +void omap4_configure_mpu_dpll(const struct dpll_param *dpll_param); +void omap4_configure_iva_dpll(const struct dpll_param *dpll_param); +void omap4_configure_per_dpll(const struct dpll_param *dpll_param); +void omap4_configure_abe_dpll(const struct dpll_param *dpll_param); +void omap4_configure_usb_dpll(const struct dpll_param *dpll_param); +void omap4_configure_core_dpll_no_lock(const struct dpll_param *param); +void omap4_lock_core_dpll(void); +void omap4_lock_core_dpll_shadow(const struct dpll_param *param); +void omap4_enable_all_clocks(void); + diff --git a/arch/arm/mach-omap/include/mach/omap4-mux.h b/arch/arm/mach-omap/include/mach/omap4-mux.h new file mode 100644 index 00000000..019574b0 --- /dev/null +++ b/arch/arm/mach-omap/include/mach/omap4-mux.h @@ -0,0 +1,344 @@ +/* + * (C) Copyright 2004-2009 + * Texas Instruments Incorporated + * Richard Woodruff <r-woodruff2@ti.com> + * Aneesh V <aneesh@ti.com> + * Balaji Krishnamoorthy <balajitk@ti.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ +#ifndef _MUX_OMAP4_H_ +#define _MUX_OMAP4_H_ + +#include <asm/types.h> + +struct pad_conf_entry { + + u16 offset; + + u16 val; + +} __attribute__ ((packed)); + +#ifdef CONFIG_OFF_PADCONF +#define OFF_PD (1 << 12) +#define OFF_PU (3 << 12) +#define OFF_OUT_PTD (0 << 10) +#define OFF_OUT_PTU (2 << 10) +#define OFF_IN (1 << 10) +#define OFF_OUT (0 << 10) +#define OFF_EN (1 << 9) +#else +#define OFF_PD (0 << 12) +#define OFF_PU (0 << 12) +#define OFF_OUT_PTD (0 << 10) +#define OFF_OUT_PTU (0 << 10) +#define OFF_IN (0 << 10) +#define OFF_OUT (0 << 10) +#define OFF_EN (0 << 9) +#endif + +#define IEN (1 << 8) +#define IDIS (0 << 8) +#define PTU (3 << 3) +#define PTD (1 << 3) +#define EN (1 << 3) +#define DIS (0 << 3) + +#define M0 0 +#define M1 1 +#define M2 2 +#define M3 3 +#define M4 4 +#define M5 5 +#define M6 6 +#define M7 7 + +#define SAFE_MODE M7 + +#ifdef CONFIG_OFF_PADCONF +#define OFF_IN_PD (OFF_PD | OFF_IN | OFF_EN) +#define OFF_IN_PU (OFF_PU | OFF_IN | OFF_EN) +#define OFF_OUT_PD (OFF_OUT_PTD | OFF_OUT | OFF_EN) +#define OFF_OUT_PU (OFF_OUT_PTU | OFF_OUT | OFF_EN) +#else +#define OFF_IN_PD 0 +#define OFF_IN_PU 0 +#define OFF_OUT_PD 0 +#define OFF_OUT_PU 0 +#endif + +#define CORE_REVISION 0x0000 +#define CORE_HWINFO 0x0004 +#define CORE_SYSCONFIG 0x0010 +#define GPMC_AD0 0x0040 +#define GPMC_AD1 0x0042 +#define GPMC_AD2 0x0044 +#define GPMC_AD3 0x0046 +#define GPMC_AD4 0x0048 +#define GPMC_AD5 0x004A +#define GPMC_AD6 0x004C +#define GPMC_AD7 0x004E +#define GPMC_AD8 0x0050 +#define GPMC_AD9 0x0052 +#define GPMC_AD10 0x0054 +#define GPMC_AD11 0x0056 +#define GPMC_AD12 0x0058 +#define GPMC_AD13 0x005A +#define GPMC_AD14 0x005C +#define GPMC_AD15 0x005E +#define GPMC_A16 0x0060 +#define GPMC_A17 0x0062 +#define GPMC_A18 0x0064 +#define GPMC_A19 0x0066 +#define GPMC_A20 0x0068 +#define GPMC_A21 0x006A +#define GPMC_A22 0x006C +#define GPMC_A23 0x006E +#define GPMC_A24 0x0070 +#define GPMC_A25 0x0072 +#define GPMC_NCS0 0x0074 +#define GPMC_NCS1 0x0076 +#define GPMC_NCS2 0x0078 +#define GPMC_NCS3 0x007A +#define GPMC_NWP 0x007C +#define GPMC_CLK 0x007E +#define GPMC_NADV_ALE 0x0080 +#define GPMC_NOE 0x0082 +#define GPMC_NWE 0x0084 +#define GPMC_NBE0_CLE 0x0086 +#define GPMC_NBE1 0x0088 +#define GPMC_WAIT0 0x008A +#define GPMC_WAIT1 0x008C +#define C2C_DATA11 0x008E +#define C2C_DATA12 0x0090 +#define C2C_DATA13 0x0092 +#define C2C_DATA14 0x0094 +#define C2C_DATA15 0x0096 +#define HDMI_HPD 0x0098 +#define HDMI_CEC 0x009A +#define HDMI_DDC_SCL 0x009C +#define HDMI_DDC_SDA 0x009E +#define CSI21_DX0 0x00A0 +#define CSI21_DY0 0x00A2 +#define CSI21_DX1 0x00A4 +#define CSI21_DY1 0x00A6 +#define CSI21_DX2 0x00A8 +#define CSI21_DY2 0x00AA +#define CSI21_DX3 0x00AC +#define CSI21_DY3 0x00AE +#define CSI21_DX4 0x00B0 +#define CSI21_DY4 0x00B2 +#define CSI22_DX0 0x00B4 +#define CSI22_DY0 0x00B6 +#define CSI22_DX1 0x00B8 +#define CSI22_DY1 0x00BA +#define CAM_SHUTTER 0x00BC +#define CAM_STROBE 0x00BE +#define CAM_GLOBALRESET 0x00C0 +#define USBB1_ULPITLL_CLK 0x00C2 +#define USBB1_ULPITLL_STP 0x00C4 +#define USBB1_ULPITLL_DIR 0x00C6 +#define USBB1_ULPITLL_NXT 0x00C8 +#define USBB1_ULPITLL_DAT0 0x00CA +#define USBB1_ULPITLL_DAT1 0x00CC +#define USBB1_ULPITLL_DAT2 0x00CE +#define USBB1_ULPITLL_DAT3 0x00D0 +#define USBB1_ULPITLL_DAT4 0x00D2 +#define USBB1_ULPITLL_DAT5 0x00D4 +#define USBB1_ULPITLL_DAT6 0x00D6 +#define USBB1_ULPITLL_DAT7 0x00D8 +#define USBB1_HSIC_DATA 0x00DA +#define USBB1_HSIC_STROBE 0x00DC +#define USBC1_ICUSB_DP 0x00DE +#define USBC1_ICUSB_DM 0x00E0 +#define SDMMC1_CLK 0x00E2 +#define SDMMC1_CMD 0x00E4 +#define SDMMC1_DAT0 0x00E6 +#define SDMMC1_DAT1 0x00E8 +#define SDMMC1_DAT2 0x00EA +#define SDMMC1_DAT3 0x00EC +#define SDMMC1_DAT4 0x00EE +#define SDMMC1_DAT5 0x00F0 +#define SDMMC1_DAT6 0x00F2 +#define SDMMC1_DAT7 0x00F4 +#define ABE_MCBSP2_CLKX 0x00F6 +#define ABE_MCBSP2_DR 0x00F8 +#define ABE_MCBSP2_DX 0x00FA +#define ABE_MCBSP2_FSX 0x00FC +#define ABE_MCBSP1_CLKX 0x00FE +#define ABE_MCBSP1_DR 0x0100 +#define ABE_MCBSP1_DX 0x0102 +#define ABE_MCBSP1_FSX 0x0104 +#define ABE_PDM_UL_DATA 0x0106 +#define ABE_PDM_DL_DATA 0x0108 +#define ABE_PDM_FRAME 0x010A +#define ABE_PDM_LB_CLK 0x010C +#define ABE_CLKS 0x010E +#define ABE_DMIC_CLK1 0x0110 +#define ABE_DMIC_DIN1 0x0112 +#define ABE_DMIC_DIN2 0x0114 +#define ABE_DMIC_DIN3 0x0116 +#define UART2_CTS 0x0118 +#define UART2_RTS 0x011A +#define UART2_RX 0x011C +#define UART2_TX 0x011E +#define HDQ_SIO 0x0120 +#define I2C1_SCL 0x0122 +#define I2C1_SDA 0x0124 +#define I2C2_SCL 0x0126 +#define I2C2_SDA 0x0128 +#define I2C3_SCL 0x012A +#define I2C3_SDA 0x012C +#define I2C4_SCL 0x012E +#define I2C4_SDA 0x0130 +#define MCSPI1_CLK 0x0132 +#define MCSPI1_SOMI 0x0134 +#define MCSPI1_SIMO 0x0136 +#define MCSPI1_CS0 0x0138 +#define MCSPI1_CS1 0x013A +#define MCSPI1_CS2 0x013C +#define MCSPI1_CS3 0x013E +#define UART3_CTS_RCTX 0x0140 +#define UART3_RTS_SD 0x0142 +#define UART3_RX_IRRX 0x0144 +#define UART3_TX_IRTX 0x0146 +#define SDMMC5_CLK 0x0148 +#define SDMMC5_CMD 0x014A +#define SDMMC5_DAT0 0x014C +#define SDMMC5_DAT1 0x014E +#define SDMMC5_DAT2 0x0150 +#define SDMMC5_DAT3 0x0152 +#define MCSPI4_CLK 0x0154 +#define MCSPI4_SIMO 0x0156 +#define MCSPI4_SOMI 0x0158 +#define MCSPI4_CS0 0x015A +#define UART4_RX 0x015C +#define UART4_TX 0x015E +#define USBB2_ULPITLL_CLK 0x0160 +#define USBB2_ULPITLL_STP 0x0162 +#define USBB2_ULPITLL_DIR 0x0164 +#define USBB2_ULPITLL_NXT 0x0166 +#define USBB2_ULPITLL_DAT0 0x0168 +#define USBB2_ULPITLL_DAT1 0x016A +#define USBB2_ULPITLL_DAT2 0x016C +#define USBB2_ULPITLL_DAT3 0x016E +#define USBB2_ULPITLL_DAT4 0x0170 +#define USBB2_ULPITLL_DAT5 0x0172 +#define USBB2_ULPITLL_DAT6 0x0174 +#define USBB2_ULPITLL_DAT7 0x0176 +#define USBB2_HSIC_DATA 0x0178 +#define USBB2_HSIC_STROBE 0x017A +#define UNIPRO_TX0 0x017C +#define UNIPRO_TY0 0x017E +#define UNIPRO_TX1 0x0180 +#define UNIPRO_TY1 0x0182 +#define UNIPRO_TX2 0x0184 +#define UNIPRO_TY2 0x0186 +#define UNIPRO_RX0 0x0188 +#define UNIPRO_RY0 0x018A +#define UNIPRO_RX1 0x018C +#define UNIPRO_RY1 0x018E +#define UNIPRO_RX2 0x0190 +#define UNIPRO_RY2 0x0192 +#define USBA0_OTG_CE 0x0194 +#define USBA0_OTG_DP 0x0196 +#define USBA0_OTG_DM 0x0198 +#define FREF_CLK1_OUT 0x019A +#define FREF_CLK2_OUT 0x019C +#define SYS_NIRQ1 0x019E +#define SYS_NIRQ2 0x01A0 +#define SYS_BOOT0 0x01A2 +#define SYS_BOOT1 0x01A4 +#define SYS_BOOT2 0x01A6 +#define SYS_BOOT3 0x01A8 +#define SYS_BOOT4 0x01AA +#define SYS_BOOT5 0x01AC +#define DPM_EMU0 0x01AE +#define DPM_EMU1 0x01B0 +#define DPM_EMU2 0x01B2 +#define DPM_EMU3 0x01B4 +#define DPM_EMU4 0x01B6 +#define DPM_EMU5 0x01B8 +#define DPM_EMU6 0x01BA +#define DPM_EMU7 0x01BC +#define DPM_EMU8 0x01BE +#define DPM_EMU9 0x01C0 +#define DPM_EMU10 0x01C2 +#define DPM_EMU11 0x01C4 +#define DPM_EMU12 0x01C6 +#define DPM_EMU13 0x01C8 +#define DPM_EMU14 0x01CA +#define DPM_EMU15 0x01CC +#define DPM_EMU16 0x01CE +#define DPM_EMU17 0x01D0 +#define DPM_EMU18 0x01D2 +#define DPM_EMU19 0x01D4 +#define WAKEUPEVENT_0 0x01D8 +#define WAKEUPEVENT_1 0x01DC +#define WAKEUPEVENT_2 0x01E0 +#define WAKEUPEVENT_3 0x01E4 +#define WAKEUPEVENT_4 0x01E8 +#define WAKEUPEVENT_5 0x01EC +#define WAKEUPEVENT_6 0x01F0 + +#define WKUP_REVISION 0x0000 +#define WKUP_HWINFO 0x0004 +#define WKUP_SYSCONFIG 0x0010 +#define PAD0_SIM_IO 0x0040 +#define PAD1_SIM_CLK 0x0042 +#define PAD0_SIM_RESET 0x0044 +#define PAD1_SIM_CD 0x0046 +#define PAD0_SIM_PWRCTRL 0x0048 +#define PAD1_SR_SCL 0x004A +#define PAD0_SR_SDA 0x004C +#define PAD1_FREF_XTAL_IN 0x004E +#define PAD0_FREF_SLICER_IN 0x0050 +#define PAD1_FREF_CLK_IOREQ 0x0052 +#define PAD0_FREF_CLK0_OUT 0x0054 +#define PAD1_FREF_CLK3_REQ 0x0056 +#define PAD0_FREF_CLK3_OUT 0x0058 +#define PAD1_FREF_CLK4_REQ 0x005A +#define PAD0_FREF_CLK4_OUT 0x005C +#define PAD1_SYS_32K 0x005E +#define PAD0_SYS_NRESPWRON 0x0060 +#define PAD1_SYS_NRESWARM 0x0062 +#define PAD0_SYS_PWR_REQ 0x0064 +#define PAD1_SYS_PWRON_RESET 0x0066 +#define PAD0_SYS_BOOT6 0x0068 +#define PAD1_SYS_BOOT7 0x006A +#define PAD0_JTAG_NTRST 0x006C +#define PAD1_JTAG_TCK 0x006D +#define PAD0_JTAG_RTCK 0x0070 +#define PAD1_JTAG_TMS_TMSC 0x0072 +#define PAD0_JTAG_TDI 0x0074 +#define PAD1_JTAG_TDO 0x0076 +#define PADCONF_WAKEUPEVENT_0 0x007C +#define CONTROL_SMART1NOPMIO_PADCONF_0 0x05A0 +#define CONTROL_SMART1NOPMIO_PADCONF_1 0x05A4 +#define PADCONF_MODE 0x05A8 +#define CONTROL_XTAL_OSCILLATOR 0x05AC +#define CONTROL_CONTROL_I2C_2 0x0604 +#define CONTROL_CONTROL_JTAG 0x0608 +#define CONTROL_CONTROL_SYS 0x060C +#define CONTROL_SPARE_RW 0x0614 +#define CONTROL_SPARE_R 0x0618 +#define CONTROL_SPARE_R_C0 0x061C + +#endif /* _MUX_OMAP4_H_ */ diff --git a/arch/arm/mach-omap/include/mach/omap4-silicon.h b/arch/arm/mach-omap/include/mach/omap4-silicon.h new file mode 100644 index 00000000..db0dfdff --- /dev/null +++ b/arch/arm/mach-omap/include/mach/omap4-silicon.h @@ -0,0 +1,179 @@ +/* + * (C) Copyright 2010 + * Texas Instruments, <www.ti.com> + * + * Authors: + * Aneesh V <aneesh@ti.com> + * + * Derived from OMAP3 work by + * Richard Woodruff <r-woodruff2@ti.com> + * Syed Mohammed Khasim <x0khasim@ti.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +#ifndef _OMAP4_H_ +#define _OMAP4_H_ + +#if !(defined(__KERNEL_STRICT_NAMES) || defined(__ASSEMBLY__)) +#include <asm/types.h> +#endif /* !(__KERNEL_STRICT_NAMES || __ASSEMBLY__) */ + +/* + * L4 Peripherals - L4 Wakeup and L4 Core now + */ +#define OMAP44XX_L4_CORE_BASE 0x4A000000 +#define OMAP44XX_WAKEUP_L4_IO_BASE 0x4A300000 +#define OMAP44XX_L4_WKUP_BASE 0x4A300000 +#define OMAP44XX_L4_PER_BASE 0x48000000 + +/* EMIF and DMM registers */ +#define OMAP44XX_EMIF1_BASE 0x4c000000 +#define OMAP44XX_EMIF2_BASE 0x4d000000 + +#define OMAP44XX_DRAM_ADDR_SPACE_START 0x80000000 +#define OMAP44XX_DRAM_ADDR_SPACE_END 0xD0000000 + + +/* CONTROL */ +#define OMAP44XX_CTRL_BASE (OMAP44XX_L4_CORE_BASE + 0x2000) +#define OMAP44XX_CONTROL_PADCONF_CORE (OMAP44XX_L4_CORE_BASE + 0x100000) +#define OMAP44XX_CONTROL_PADCONF_WKUP (OMAP44XX_L4_CORE_BASE + 0x31E000) + +/* PRM */ +#define OMAP44XX_PRM_VC_VAL_BYPASS (OMAP44XX_WAKEUP_L4_IO_BASE + 0x7ba0) +#define OMAP44XX_PRM_VC_CFG_I2C_MODE (OMAP44XX_WAKEUP_L4_IO_BASE + 0x7ba8) +#define OMAP44XX_PRM_VC_CFG_I2C_CLK (OMAP44XX_WAKEUP_L4_IO_BASE + 0x7bac) + +/* IRQ */ +#define OMAP44XX_PRM_IRQSTATUS_MPU_A9 (OMAP44XX_WAKEUP_L4_IO_BASE + 0x6010) + +/* UART */ +#define OMAP44XX_UART1_BASE (OMAP44XX_L4_PER_BASE + 0x6a000) +#define OMAP44XX_UART2_BASE (OMAP44XX_L4_PER_BASE + 0x6c000) +#define OMAP44XX_UART3_BASE (OMAP44XX_L4_PER_BASE + 0x20000) + +/* General Purpose Timers */ +#define OMAP44XX_GPT1_BASE (OMAP44XX_L4_WKUP_BASE + 0x18000) +#define OMAP44XX_GPT2_BASE (OMAP44XX_L4_PER_BASE + 0x32000) +#define OMAP44XX_GPT3_BASE (OMAP44XX_L4_PER_BASE + 0x34000) + +/* Watchdog Timer2 - MPU watchdog */ +#define OMAP44XX_WDT2_BASE (OMAP44XX_L4_WKUP_BASE + 0x14000) + +#define OMAP44XX_SCRM_BASE 0x4a30a000 +#define OMAP44XX_SCRM_ALTCLKSRC (OMAP44XX_SCRM_BASE + 0x110) +#define OMAP44XX_SCRM_AUXCLK1 (OMAP44XX_SCRM_BASE + 0x314) +#define OMAP44XX_SCRM_AUXCLK3 (OMAP44XX_SCRM_BASE + 0x31c) + +/* 32KTIMER */ +#define OMAP_32KTIMER_BASE (OMAP44XX_L4_WKUP_BASE + 0x4000) + +/* GPMC */ +#define OMAP_GPMC_BASE 0x50000000 + +/* DMM */ +#define OMAP44XX_DMM_BASE 0x4E000000 +#define DMM_LISA_MAP_BASE (OMAP44XX_DMM_BASE + 0x40) +#define DMM_LISA_MAP_SYS_SIZE_MASK (7 << 20) +#define DMM_LISA_MAP_SYS_SIZE_SHIFT 20 +#define DMM_LISA_MAP_SYS_ADDR_MASK (0xFF << 24) +/* + * Hardware Register Details + */ + +/* Watchdog Timer */ +#define WD_UNLOCK1 0xAAAA +#define WD_UNLOCK2 0x5555 + +/* GP Timer */ +#define TCLR_ST (0x1 << 0) +#define TCLR_AR (0x1 << 1) +#define TCLR_PRE (0x1 << 5) + +/* + * PRCM + */ + +/* PRM */ +#define PRM_BASE 0x4A306000 +#define PRM_DEVICE_BASE (PRM_BASE + 0x1B00) + +#define PRM_RSTCTRL PRM_DEVICE_BASE +#define PRM_RSTCTRL_RESET 0x01 + +#ifndef __ASSEMBLY__ + +struct s32ktimer { + unsigned char res[0x10]; + unsigned int s32k_cr; /* 0x10 */ +}; + +#endif /* __ASSEMBLY__ */ + +/* + * Non-secure SRAM Addresses + * Non-secure RAM starts at 0x40300000 for GP devices. But we keep SRAM_BASE + * at 0x40304000(EMU base) so that our code works for both EMU and GP + */ +#define NON_SECURE_SRAM_START 0x40304000 +#define NON_SECURE_SRAM_END 0x4030E000 /* Not inclusive */ +/* base address for indirect vectors (internal boot mode) */ +#define SRAM_ROM_VECT_BASE 0x4030D000 +/* Temporary SRAM stack used while low level init is done */ +#define LOW_LEVEL_SRAM_STACK NON_SECURE_SRAM_END + +/* + * OMAP4 real hardware: + * TODO: Change this to the IDCODE in the hw regsiter + */ +#define CPU_OMAP4430_ES10 1 +#define CPU_OMAP4430_ES20 2 + +#define CM_DLL_CTRL 0x4a004110 +#define CM_MEMIF_EMIF_1_CLKCTRL 0x4a008b30 +#define CM_MEMIF_EMIF_2_CLKCTRL 0x4a008b38 + +/* Silicon revisions */ +#define OMAP4430_SILICON_ID_INVALID 0 +#define OMAP4430_ES1_0 1 +#define OMAP4430_ES2_0 2 +#define OMAP4430_ES2_1 3 +#define OMAP4430_ES2_2 4 + +struct ddr_regs { + u32 tim1; + u32 tim2; + u32 tim3; + u32 phy_ctrl_1; + u32 ref_ctrl; + u32 config_init; + u32 config_final; + u32 zq_config; + u8 mr1; + u8 mr2; +}; + +struct dpll_param; + +void omap4_ddr_init(const struct ddr_regs *, const struct dpll_param *); +void omap4_power_i2c_send(u32); +unsigned int omap4_revision(void); + +#endif diff --git a/arch/arm/mach-omap/include/mach/silicon.h b/arch/arm/mach-omap/include/mach/silicon.h index 22daa5c9..c2f0c412 100644 --- a/arch/arm/mach-omap/include/mach/silicon.h +++ b/arch/arm/mach-omap/include/mach/silicon.h @@ -25,6 +25,9 @@ #ifdef CONFIG_ARCH_OMAP3 #include <mach/omap3-silicon.h> #endif +#ifdef CONFIG_ARCH_OMAP4 +#include <mach/omap4-silicon.h> +#endif /* If Architecture specific init functions are present */ #ifdef CONFIG_ARCH_HAS_LOWLEVEL_INIT diff --git a/arch/arm/mach-omap/include/mach/syslib.h b/arch/arm/mach-omap/include/mach/syslib.h index c89f50b8..6a7044ad 100644 --- a/arch/arm/mach-omap/include/mach/syslib.h +++ b/arch/arm/mach-omap/include/mach/syslib.h @@ -30,9 +30,30 @@ */ #ifndef __ASM_ARCH_OMAP_SYSLIB_H_ #define __ASM_ARCH_OMAP_SYSLIB_H_ +#include <asm/io.h> /** System Independent functions */ -void sr32(u32 addr, u32 start_bit, u32 num_bits, u32 value); + +/** + * @brief clear & set a value in a bit range for a 32 bit address + * + * @param[in] addr Address to set/read from + * @param[in] start_bit Where to put the value + * @param[in] num_bits number of bits the value should be set + * @param[in] value the value to set + * + * @return void + */ +static inline void sr32(u32 addr, u32 start_bit, u32 num_bits, u32 value) +{ + u32 tmp, msk = 0; + msk = 1 << num_bits; + --msk; + tmp = readl(addr) & ~(msk << start_bit); + tmp |= value << start_bit; + writel(tmp, addr); +} + u32 wait_on_value(u32 read_bit_mask, u32 match_value, u32 read_addr, u32 bound); void sdelay(unsigned long loops); diff --git a/arch/arm/mach-omap/include/mach/xload.h b/arch/arm/mach-omap/include/mach/xload.h new file mode 100644 index 00000000..844b57f0 --- /dev/null +++ b/arch/arm/mach-omap/include/mach/xload.h @@ -0,0 +1,16 @@ +#ifndef _MACH_XLOAD_H +#define _MACH_XLOAD_H + +void *omap_xload_boot_nand(int offset, int size); +void *omap_xload_boot_mmc(void); + +enum omap_boot_src { + OMAP_BOOTSRC_UNKNOWN, + OMAP_BOOTSRC_MMC1, + OMAP_BOOTSRC_NAND, +}; + +enum omap_boot_src omap3_bootsrc(void); +enum omap_boot_src omap4_bootsrc(void); + +#endif /* _MACH_XLOAD_H */ diff --git a/arch/arm/mach-omap/omap-uart.c b/arch/arm/mach-omap/omap-uart.c new file mode 100644 index 00000000..477452d9 --- /dev/null +++ b/arch/arm/mach-omap/omap-uart.c @@ -0,0 +1,36 @@ +#include <common.h> +#include <asm/io.h> + +/** + * @brief Uart port register read function for OMAP3 + * + * @param base base address of UART + * @param reg_idx register index + * + * @return character read from register + */ +unsigned int omap_uart_read(unsigned long base, unsigned char reg_idx) +{ + unsigned int *reg_addr = (unsigned int *)base; + reg_addr += reg_idx; + return readb(reg_addr); +} +EXPORT_SYMBOL(omap_uart_read); + +/** + * @brief Uart port register write function for OMAP3 + * + * @param val value to write + * @param base base address of UART + * @param reg_idx register index + * + * @return void + */ +void omap_uart_write(unsigned int val, unsigned long base, + unsigned char reg_idx) +{ + unsigned int *reg_addr = (unsigned int *)base; + reg_addr += reg_idx; + writeb(val, reg_addr); +} +EXPORT_SYMBOL(omap_uart_write); diff --git a/arch/arm/mach-omap/omap3_generic.c b/arch/arm/mach-omap/omap3_generic.c index 843143ba..661a971f 100644 --- a/arch/arm/mach-omap/omap3_generic.c +++ b/arch/arm/mach-omap/omap3_generic.c @@ -46,6 +46,7 @@ #include <mach/wdt.h> #include <mach/sys_info.h> #include <mach/syslib.h> +#include <mach/xload.h> /** * @brief Reset the CPU @@ -484,36 +485,15 @@ void a_init(void) } -/** - * @brief Uart port register read function for OMAP3 - * - * @param base base address of UART - * @param reg_idx register index - * - * @return character read from register - */ -unsigned int omap_uart_read(unsigned long base, unsigned char reg_idx) -{ - unsigned int *reg_addr = (unsigned int *)base; - reg_addr += reg_idx; - return readb(reg_addr); -} -EXPORT_SYMBOL(omap_uart_read); +#define OMAP3_TRACING_VECTOR1 0x4020ffb4 -/** - * @brief Uart port register write function for OMAP3 - * - * @param val value to write - * @param base base address of UART - * @param reg_idx register index - * - * @return void - */ -void omap_uart_write(unsigned int val, unsigned long base, - unsigned char reg_idx) +enum omap_boot_src omap3_bootsrc(void) { - unsigned int *reg_addr = (unsigned int *)base; - reg_addr += reg_idx; - writeb(val, reg_addr); + u32 bootsrc = readl(OMAP3_TRACING_VECTOR1); + + if (bootsrc & (1 << 2)) + return OMAP_BOOTSRC_NAND; + if (bootsrc & (1 << 6)) + return OMAP_BOOTSRC_MMC1; + return OMAP_BOOTSRC_UNKNOWN; } -EXPORT_SYMBOL(omap_uart_write); diff --git a/arch/arm/mach-omap/omap4_clock.c b/arch/arm/mach-omap/omap4_clock.c new file mode 100644 index 00000000..23a77d06 --- /dev/null +++ b/arch/arm/mach-omap/omap4_clock.c @@ -0,0 +1,380 @@ +#include <common.h> +#include <mach/syslib.h> +#include <asm/io.h> +#include <mach/omap4-clock.h> + +#define LDELAY 12000000 + +void omap4_configure_mpu_dpll(const struct dpll_param *dpll_param) +{ + /* Unlock the MPU dpll */ + sr32(CM_CLKMODE_DPLL_MPU, 0, 3, PLL_MN_POWER_BYPASS); + wait_on_value((1 << 0), 0, CM_IDLEST_DPLL_MPU, LDELAY); + + sr32(CM_AUTOIDLE_DPLL_MPU, 0, 3, 0x0); /* Disable DPLL autoidle */ + + /* Set M,N,M2 values */ + sr32(CM_CLKSEL_DPLL_MPU, 8, 11, dpll_param->m); + sr32(CM_CLKSEL_DPLL_MPU, 0, 6, dpll_param->n); + sr32(CM_DIV_M2_DPLL_MPU, 0, 5, dpll_param->m2); + sr32(CM_DIV_M2_DPLL_MPU, 8, 1, 0x1); + + /* Lock the mpu dpll */ + sr32(CM_CLKMODE_DPLL_MPU, 0, 3, PLL_LOCK | 0x10); + wait_on_value((1 << 0), 1, CM_IDLEST_DPLL_MPU, LDELAY); +} + +void omap4_configure_iva_dpll(const struct dpll_param *dpll_param) +{ + /* Unlock the IVA dpll */ + sr32(CM_CLKMODE_DPLL_IVA, 0, 3, PLL_MN_POWER_BYPASS); + wait_on_value((1 << 0), 0, CM_IDLEST_DPLL_IVA, LDELAY); + + /* CM_BYPCLK_DPLL_IVA = CORE_X2_CLK/2 */ + sr32(CM_BYPCLK_DPLL_IVA, 0, 2, 0x1); + + sr32(CM_AUTOIDLE_DPLL_IVA, 0, 3, 0x0); /* Disable DPLL autoidle */ + + /* Set M,N,M4,M5 */ + sr32(CM_CLKSEL_DPLL_IVA, 8, 11, dpll_param->m); + sr32(CM_CLKSEL_DPLL_IVA, 0, 7, dpll_param->n); + sr32(CM_DIV_M4_DPLL_IVA, 0, 5, dpll_param->m4); + sr32(CM_DIV_M4_DPLL_IVA, 8, 1, 0x1); + sr32(CM_DIV_M5_DPLL_IVA, 0, 5, dpll_param->m5); + sr32(CM_DIV_M5_DPLL_IVA, 8, 1, 0x1); + + /* Lock the iva dpll */ + sr32(CM_CLKMODE_DPLL_IVA, 0, 3, PLL_LOCK); + wait_on_value((1 << 0), 1, CM_IDLEST_DPLL_IVA, LDELAY); +} + +void omap4_configure_per_dpll(const struct dpll_param *dpll_param) +{ + /* Unlock the PER dpll */ + sr32(CM_CLKMODE_DPLL_PER, 0, 3, PLL_MN_POWER_BYPASS); + wait_on_value((1 << 0), 0, CM_IDLEST_DPLL_PER, LDELAY); + + /* Disable autoidle */ + sr32(CM_AUTOIDLE_DPLL_PER, 0, 3, 0x0); + + sr32(CM_CLKSEL_DPLL_PER, 8, 11, dpll_param->m); + sr32(CM_CLKSEL_DPLL_PER, 0, 6, dpll_param->n); + sr32(CM_DIV_M2_DPLL_PER, 0, 5, dpll_param->m2); + sr32(CM_DIV_M3_DPLL_PER, 0, 5, dpll_param->m3); + sr32(CM_DIV_M4_DPLL_PER, 0, 5, dpll_param->m4); + sr32(CM_DIV_M5_DPLL_PER, 0, 5, dpll_param->m5); + sr32(CM_DIV_M6_DPLL_PER, 0, 5, dpll_param->m6); + sr32(CM_DIV_M7_DPLL_PER, 0, 5, dpll_param->m7); + + sr32(CM_DIV_M2_DPLL_PER, 8, 1, 0x1); + sr32(CM_DIV_M3_DPLL_PER, 8, 1, 0x1); + sr32(CM_DIV_M4_DPLL_PER, 8, 1, 0x1); + sr32(CM_DIV_M5_DPLL_PER, 8, 1, 0x1); + sr32(CM_DIV_M6_DPLL_PER, 8, 1, 0x1); + sr32(CM_DIV_M7_DPLL_PER, 8, 1, 0x1); + + /* Lock the per dpll */ + sr32(CM_CLKMODE_DPLL_PER, 0, 3, PLL_LOCK); + wait_on_value((1 << 0), 1, CM_IDLEST_DPLL_PER, LDELAY); + + return; +} + +void omap4_configure_abe_dpll(const struct dpll_param *dpll_param) +{ + /* Select sys_clk as ref clk for ABE dpll */ + sr32(CM_ABE_PLL_REF_CLKSEL, 0, 32, 0x0); + + /* Unlock the ABE dpll */ + sr32(CM_CLKMODE_DPLL_ABE, 0, 3, PLL_MN_POWER_BYPASS); + wait_on_value((1 << 0), 0, CM_IDLEST_DPLL_ABE, LDELAY); + + /* Disable autoidle */ + sr32(CM_AUTOIDLE_DPLL_ABE, 0, 3, 0x0); + + sr32(CM_CLKSEL_DPLL_ABE, 8, 11, dpll_param->m); + sr32(CM_CLKSEL_DPLL_ABE, 0, 6, dpll_param->n); + + /* Force DPLL CLKOUTHIF to stay enabled */ + sr32(CM_DIV_M2_DPLL_ABE, 0, 32, 0x500); + sr32(CM_DIV_M2_DPLL_ABE, 0, 5, dpll_param->m2); + sr32(CM_DIV_M2_DPLL_ABE, 8, 1, 0x1); + /* Force DPLL CLKOUTHIF to stay enabled */ + sr32(CM_DIV_M3_DPLL_ABE, 0, 32, 0x100); + sr32(CM_DIV_M3_DPLL_ABE, 0, 5, dpll_param->m3); + sr32(CM_DIV_M3_DPLL_ABE, 8, 1, 0x1); + + /* Lock the abe dpll */ + sr32(CM_CLKMODE_DPLL_ABE, 0, 3, PLL_LOCK); + wait_on_value((1 << 0), 1, CM_IDLEST_DPLL_ABE, LDELAY); + + return; +} + +void omap4_configure_usb_dpll(const struct dpll_param *dpll_param) +{ + /* Select the 60Mhz clock 480/8 = 60*/ + sr32(CM_CLKSEL_USB_60MHz, 0, 32, 0x1); + + /* Unlock the USB dpll */ + sr32(CM_CLKMODE_DPLL_USB, 0, 3, PLL_MN_POWER_BYPASS); + wait_on_value((1 << 0), 0, CM_IDLEST_DPLL_USB, LDELAY); + + /* Disable autoidle */ + sr32(CM_AUTOIDLE_DPLL_USB, 0, 3, 0x0); + + sr32(CM_CLKSEL_DPLL_USB, 8, 11, dpll_param->m); + sr32(CM_CLKSEL_DPLL_USB, 0, 6, dpll_param->n); + + /* Force DPLL CLKOUT to stay active */ + sr32(CM_DIV_M2_DPLL_USB, 0, 32, 0x100); + sr32(CM_DIV_M2_DPLL_USB, 0, 5, dpll_param->m2); + sr32(CM_DIV_M2_DPLL_USB, 8, 1, 0x1); + sr32(CM_CLKDCOLDO_DPLL_USB, 8, 1, 0x1); + + /* Lock the usb dpll */ + sr32(CM_CLKMODE_DPLL_USB, 0, 3, PLL_LOCK); + wait_on_value((1 << 0), 1, CM_IDLEST_DPLL_USB, LDELAY); + + /* force enable the CLKDCOLDO clock */ + sr32(CM_CLKDCOLDO_DPLL_USB, 0, 32, 0x100); + + return; +} + +void omap4_configure_core_dpll_no_lock(const struct dpll_param *param) +{ + /* CORE_CLK=CORE_X2_CLK/2, L3_CLK=CORE_CLK/2, L4_CLK=L3_CLK/2 */ + sr32(CM_CLKSEL_CORE, 0, 32, 0x110); + + /* Unlock the CORE dpll */ + sr32(CM_CLKMODE_DPLL_CORE, 0, 3, PLL_MN_POWER_BYPASS); + wait_on_value((1 << 0), 0, CM_IDLEST_DPLL_CORE, LDELAY); + + /* Disable autoidle */ + sr32(CM_AUTOIDLE_DPLL_CORE, 0, 3, 0x0); + + sr32(CM_CLKSEL_DPLL_CORE, 8, 11, param->m); + sr32(CM_CLKSEL_DPLL_CORE, 0, 6, param->n); + sr32(CM_DIV_M2_DPLL_CORE, 0, 5, param->m2); + sr32(CM_DIV_M3_DPLL_CORE, 0, 5, param->m3); + sr32(CM_DIV_M4_DPLL_CORE, 0, 5, param->m4); + sr32(CM_DIV_M5_DPLL_CORE, 0, 5, param->m5); + sr32(CM_DIV_M6_DPLL_CORE, 0, 5, param->m6); + sr32(CM_DIV_M7_DPLL_CORE, 0, 5, param->m7); + + sr32(CM_DIV_M2_DPLL_CORE, 8, 1, 0x1); + sr32(CM_DIV_M3_DPLL_CORE, 8, 1, 0x1); + sr32(CM_DIV_M4_DPLL_CORE, 8, 1, 0x1); + sr32(CM_DIV_M5_DPLL_CORE, 8, 1, 0x1); + sr32(CM_DIV_M6_DPLL_CORE, 8, 1, 0x0); + sr32(CM_DIV_M7_DPLL_CORE, 8, 1, 0x1); +} + +void omap4_lock_core_dpll(void) +{ + /* Lock the core dpll */ + sr32(CM_CLKMODE_DPLL_CORE, 0, 3, PLL_LOCK); + wait_on_value((1 << 0), 1, CM_IDLEST_DPLL_CORE, LDELAY); + + return; +} + +void omap4_lock_core_dpll_shadow(const struct dpll_param *param) +{ + /* Lock the core dpll using freq update method */ + *(volatile int*)0x4A004120 = 10; //(CM_CLKMODE_DPLL_CORE) + + /* CM_SHADOW_FREQ_CONFIG1: DLL_OVERRIDE = 1(hack), DLL_RESET = 1, + * DPLL_CORE_M2_DIV =1, DPLL_CORE_DPLL_EN = 0x7, FREQ_UPDATE = 1 + */ + *(volatile int*)0x4A004260 = 0x70D | (param->m2 << 11); + + /* Wait for Freq_Update to get cleared: CM_SHADOW_FREQ_CONFIG1 */ + while( ( (*(volatile int*)0x4A004260) & 0x1) == 0x1 ); + + /* Wait for DPLL to Lock : CM_IDLEST_DPLL_CORE */ + wait_on_value((1 << 0), 1, CM_IDLEST_DPLL_CORE, LDELAY); +} + +void omap4_enable_all_clocks(void) +{ + /* Enable Ducati clocks */ + sr32(CM_DUCATI_DUCATI_CLKCTRL, 0, 32, 0x1); + sr32(CM_DUCATI_CLKSTCTRL, 0, 32, 0x2); + + wait_on_value((1 << 8), (1 << 8), CM_DUCATI_CLKSTCTRL, LDELAY); + + /* Enable ivahd and sl2 clocks */ + sr32(IVAHD_IVAHD_CLKCTRL, 0, 32, 0x1); + sr32(IVAHD_SL2_CLKCTRL, 0, 32, 0x1); + sr32(IVAHD_CLKSTCTRL, 0, 32, 0x2); + + wait_on_value((1 << 8), (1 << 8), IVAHD_CLKSTCTRL, LDELAY); + + /* Enable Tesla clocks */ + sr32(DSP_DSP_CLKCTRL, 0, 32, 0x1); + sr32(DSP_CLKSTCTRL, 0, 32, 0x2); + + wait_on_value((1 << 8), (1 << 8), DSP_CLKSTCTRL, LDELAY); + + /* wait for tesla to become accessible */ + + /* ABE clocks */ + sr32(CM1_ABE_CLKSTCTRL, 0, 32, 0x3); + sr32(CM1_ABE_AESS_CLKCTRL, 0, 32, 0x2); + sr32(CM1_ABE_PDM_CLKCTRL, 0, 32, 0x2); + sr32(CM1_ABE_DMIC_CLKCTRL, 0, 32, 0x2); + sr32(CM1_ABE_MCASP_CLKCTRL, 0, 32, 0x2); + sr32(CM1_ABE_MCBSP1_CLKCTRL, 0, 32, 0x08000002); + sr32(CM1_ABE_MCBSP2_CLKCTRL, 0, 32, 0x08000002); + sr32(CM1_ABE_MCBSP3_CLKCTRL, 0, 32, 0x08000002); + sr32(CM1_ABE_SLIMBUS_CLKCTRL, 0, 32, 0xf02); + sr32(CM1_ABE_TIMER5_CLKCTRL, 0, 32, 0x2); + sr32(CM1_ABE_TIMER6_CLKCTRL, 0, 32, 0x2); + sr32(CM1_ABE_TIMER7_CLKCTRL, 0, 32, 0x2); + sr32(CM1_ABE_TIMER8_CLKCTRL, 0, 32, 0x2); + sr32(CM1_ABE_WDT3_CLKCTRL, 0, 32, 0x2); + /* Disable sleep transitions */ + sr32(CM1_ABE_CLKSTCTRL, 0, 32, 0x0); + + /* L4PER clocks */ + sr32(CM_L4PER_CLKSTCTRL, 0, 32, 0x2); + sr32(CM_L4PER_DMTIMER10_CLKCTRL, 0, 32, 0x2); + wait_on_value((1 << 17)|(1 << 16), 0, CM_L4PER_DMTIMER10_CLKCTRL, LDELAY); + sr32(CM_L4PER_DMTIMER11_CLKCTRL, 0, 32, 0x2); + wait_on_value((1 << 17)|(1 << 16), 0, CM_L4PER_DMTIMER11_CLKCTRL, LDELAY); + sr32(CM_L4PER_DMTIMER2_CLKCTRL, 0, 32, 0x2); + wait_on_value((1 << 17)|(1 << 16), 0, CM_L4PER_DMTIMER2_CLKCTRL, LDELAY); + sr32(CM_L4PER_DMTIMER3_CLKCTRL, 0, 32, 0x2); + wait_on_value((1 << 17)|(1 << 16), 0, CM_L4PER_DMTIMER3_CLKCTRL, LDELAY); + sr32(CM_L4PER_DMTIMER4_CLKCTRL, 0, 32, 0x2); + wait_on_value((1 << 17)|(1 << 16), 0, CM_L4PER_DMTIMER4_CLKCTRL, LDELAY); + sr32(CM_L4PER_DMTIMER9_CLKCTRL, 0, 32, 0x2); + wait_on_value((1 << 17)|(1 << 16), 0, CM_L4PER_DMTIMER9_CLKCTRL, LDELAY); + + /* GPIO clocks */ + sr32(CM_L4PER_GPIO2_CLKCTRL, 0, 32, 0x1); + wait_on_value((1 << 17)|(1 << 16), 0, CM_L4PER_GPIO2_CLKCTRL, LDELAY); + sr32(CM_L4PER_GPIO3_CLKCTRL, 0, 32, 0x1); + wait_on_value((1 << 17)|(1 << 16), 0, CM_L4PER_GPIO3_CLKCTRL, LDELAY); + sr32(CM_L4PER_GPIO4_CLKCTRL, 0, 32, 0x1); + wait_on_value((1 << 17)|(1 << 16), 0, CM_L4PER_GPIO4_CLKCTRL, LDELAY); + sr32(CM_L4PER_GPIO5_CLKCTRL, 0, 32, 0x1); + wait_on_value((1 << 17)|(1 << 16), 0, CM_L4PER_GPIO5_CLKCTRL, LDELAY); + sr32(CM_L4PER_GPIO6_CLKCTRL, 0, 32, 0x1); + wait_on_value((1 << 17)|(1 << 16), 0, CM_L4PER_GPIO6_CLKCTRL, LDELAY); + + sr32(CM_L4PER_HDQ1W_CLKCTRL, 0, 32, 0x2); + + /* I2C clocks */ + sr32(CM_L4PER_I2C1_CLKCTRL, 0, 32, 0x2); + wait_on_value((1 << 17)|(1 << 16), 0, CM_L4PER_I2C1_CLKCTRL, LDELAY); + sr32(CM_L4PER_I2C2_CLKCTRL, 0, 32, 0x2); + wait_on_value((1 << 17)|(1 << 16), 0, CM_L4PER_I2C2_CLKCTRL, LDELAY); + sr32(CM_L4PER_I2C3_CLKCTRL, 0, 32, 0x2); + wait_on_value((1 << 17)|(1 << 16), 0, CM_L4PER_I2C3_CLKCTRL, LDELAY); + sr32(CM_L4PER_I2C4_CLKCTRL, 0, 32, 0x2); + wait_on_value((1 << 17)|(1 << 16), 0, CM_L4PER_I2C4_CLKCTRL, LDELAY); + + sr32(CM_L4PER_MCBSP4_CLKCTRL, 0, 32, 0x2); + wait_on_value((1 << 17)|(1 << 16), 0, CM_L4PER_MCBSP4_CLKCTRL, LDELAY); + + /* MCSPI clocks */ + sr32(CM_L4PER_MCSPI1_CLKCTRL, 0, 32, 0x2); + wait_on_value((1 << 17)|(1 << 16), 0, CM_L4PER_MCSPI1_CLKCTRL, LDELAY); + sr32(CM_L4PER_MCSPI2_CLKCTRL, 0, 32, 0x2); + wait_on_value((1 << 17)|(1 << 16), 0, CM_L4PER_MCSPI2_CLKCTRL, LDELAY); + sr32(CM_L4PER_MCSPI3_CLKCTRL, 0, 32, 0x2); + wait_on_value((1 << 17)|(1 << 16), 0, CM_L4PER_MCSPI3_CLKCTRL, LDELAY); + sr32(CM_L4PER_MCSPI4_CLKCTRL, 0, 32, 0x2); + wait_on_value((1 << 17)|(1 << 16), 0, CM_L4PER_MCSPI4_CLKCTRL, LDELAY); + + /* MMC clocks */ + sr32(CM_L3INIT_HSMMC1_CLKCTRL, 0, 2, 0x2); + sr32(CM_L3INIT_HSMMC1_CLKCTRL, 24, 1, 0x1); + sr32(CM_L3INIT_HSMMC2_CLKCTRL, 0, 2, 0x2); + sr32(CM_L3INIT_HSMMC2_CLKCTRL, 24, 1, 0x1); + sr32(CM_L4PER_MMCSD3_CLKCTRL, 0, 32, 0x2); + wait_on_value((1 << 18)|(1 << 17)|(1 << 16), 0, CM_L4PER_MMCSD3_CLKCTRL, LDELAY); + sr32(CM_L4PER_MMCSD4_CLKCTRL, 0, 32, 0x2); + wait_on_value((1 << 18)|(1 << 17)|(1 << 16), 0, CM_L4PER_MMCSD4_CLKCTRL, LDELAY); + sr32(CM_L4PER_MMCSD5_CLKCTRL, 0, 32, 0x2); + wait_on_value((1 << 17)|(1 << 16), 0, CM_L4PER_MMCSD5_CLKCTRL, LDELAY); + + /* UART clocks */ + sr32(CM_L4PER_UART1_CLKCTRL, 0, 32, 0x2); + wait_on_value((1 << 17)|(1 << 16), 0, CM_L4PER_UART1_CLKCTRL, LDELAY); + sr32(CM_L4PER_UART2_CLKCTRL, 0, 32, 0x2); + wait_on_value((1 << 17)|(1 << 16), 0, CM_L4PER_UART2_CLKCTRL, LDELAY); + sr32(CM_L4PER_UART3_CLKCTRL, 0, 32, 0x2); + wait_on_value((1 << 17)|(1 << 16), 0, CM_L4PER_UART3_CLKCTRL, LDELAY); + sr32(CM_L4PER_UART4_CLKCTRL, 0, 32, 0x2); + wait_on_value((1 << 17)|(1 << 16), 0, CM_L4PER_UART4_CLKCTRL, LDELAY); + + /* WKUP clocks */ + sr32(CM_WKUP_GPIO1_CLKCTRL, 0, 32, 0x1); + wait_on_value((1 << 17)|(1 << 16), 0, CM_WKUP_GPIO1_CLKCTRL, LDELAY); + sr32(CM_WKUP_TIMER1_CLKCTRL, 0, 32, 0x01000002); + wait_on_value((1 << 17)|(1 << 16), 0, CM_WKUP_TIMER1_CLKCTRL, LDELAY); + + sr32(CM_WKUP_KEYBOARD_CLKCTRL, 0, 32, 0x2); + wait_on_value((1 << 17)|(1 << 16), 0, CM_WKUP_KEYBOARD_CLKCTRL, LDELAY); + + sr32(CM_SDMA_CLKSTCTRL, 0, 32, 0x0); + sr32(CM_MEMIF_CLKSTCTRL, 0, 32, 0x3); + sr32(CM_MEMIF_EMIF_1_CLKCTRL, 0, 32, 0x1); + wait_on_value((1 << 17)|(1 << 16), 0, CM_MEMIF_EMIF_1_CLKCTRL, LDELAY); + sr32(CM_MEMIF_EMIF_2_CLKCTRL, 0, 32, 0x1); + wait_on_value((1 << 17)|(1 << 16), 0, CM_MEMIF_EMIF_2_CLKCTRL, LDELAY); + sr32(CM_D2D_CLKSTCTRL, 0, 32, 0x3); + sr32(CM_L3_2_GPMC_CLKCTRL, 0, 32, 0x1); + wait_on_value((1 << 17)|(1 << 16), 0, CM_L3_2_GPMC_CLKCTRL, LDELAY); + sr32(CM_L3INSTR_L3_3_CLKCTRL, 0, 32, 0x1); + wait_on_value((1 << 17)|(1 << 16), 0, CM_L3INSTR_L3_3_CLKCTRL, LDELAY); + sr32(CM_L3INSTR_L3_INSTR_CLKCTRL, 0, 32, 0x1); + wait_on_value((1 << 17)|(1 << 16), 0, CM_L3INSTR_L3_INSTR_CLKCTRL, LDELAY); + sr32(CM_L3INSTR_OCP_WP1_CLKCTRL, 0, 32, 0x1); + wait_on_value((1 << 17)|(1 << 16), 0, CM_L3INSTR_OCP_WP1_CLKCTRL, LDELAY); + + /* WDT clocks */ + sr32(CM_WKUP_WDT2_CLKCTRL, 0, 32, 0x2); + wait_on_value((1 << 17)|(1 << 16), 0, CM_WKUP_WDT2_CLKCTRL, LDELAY); + + /* Enable Camera clocks */ + sr32(CM_CAM_CLKSTCTRL, 0, 32, 0x3); + sr32(CM_CAM_ISS_CLKCTRL, 0, 32, 0x102); + sr32(CM_CAM_FDIF_CLKCTRL, 0, 32, 0x2); + sr32(CM_CAM_CLKSTCTRL, 0, 32, 0x0); + + /* Enable DSS clocks */ + /* PM_DSS_PWRSTCTRL ON State and LogicState = 1 (Retention) */ + __raw_writel(7, 0x4A307100); /* DSS_PRM */ + + sr32(CM_DSS_CLKSTCTRL, 0, 32, 0x2); + sr32(CM_DSS_DSS_CLKCTRL, 0, 32, 0xf02); + sr32(CM_DSS_DEISS_CLKCTRL, 0, 32, 0x2); + + /* Check for DSS Clocks */ + while ((__raw_readl(0x4A009100) & 0xF00) != 0xE00) + ; + /* Set HW_AUTO transition mode */ + sr32(CM_DSS_CLKSTCTRL, 0, 32, 0x3); + + /* Enable SGX clocks */ + sr32(CM_SGX_CLKSTCTRL, 0, 32, 0x2); + sr32(CM_SGX_SGX_CLKCTRL, 0, 32, 0x2); + /* Check for SGX FCLK and ICLK */ + while (__raw_readl(0x4A009200) != 0x302) + ; + /* Enable hsi/unipro/usb clocks */ + sr32(CM_L3INIT_HSI_CLKCTRL, 0, 32, 0x1); + sr32(CM_L3INIT_UNIPRO1_CLKCTRL, 0, 32, 0x2); + sr32(CM_L3INIT_HSUSBHOST_CLKCTRL, 0, 32, 0x2); + sr32(CM_L3INIT_HSUSBOTG_CLKCTRL, 0, 32, 0x1); + sr32(CM_L3INIT_HSUSBTLL_CLKCTRL, 0, 32, 0x1); + sr32(CM_L3INIT_FSUSB_CLKCTRL, 0, 32, 0x2); + /* enable the 32K, 48M optional clocks and enable the module */ + sr32(CM_L3INIT_USBPHY_CLKCTRL, 0, 32, 0x301); +} + diff --git a/arch/arm/mach-omap/omap4_generic.c b/arch/arm/mach-omap/omap4_generic.c new file mode 100644 index 00000000..313e5e99 --- /dev/null +++ b/arch/arm/mach-omap/omap4_generic.c @@ -0,0 +1,420 @@ +#include <common.h> +#include <init.h> +#include <mach/silicon.h> +#include <asm/io.h> +#include <mach/omap4-silicon.h> +#include <mach/omap4-clock.h> +#include <mach/syslib.h> +#include <mach/xload.h> + +void __noreturn reset_cpu(unsigned long addr) +{ + writel(PRM_RSTCTRL_RESET, PRM_RSTCTRL); + + while (1); +} + +#define WATCHDOG_WSPR 0x48 +#define WATCHDOG_WWPS 0x34 + +static void wait_for_command_complete(void) +{ + int pending = 1; + + do { + pending = readl(OMAP44XX_WDT2_BASE + WATCHDOG_WWPS); + } while (pending); +} + +/* EMIF */ +#define EMIF_MOD_ID_REV 0x0000 +#define EMIF_STATUS 0x0004 +#define EMIF_SDRAM_CONFIG 0x0008 +#define EMIF_LPDDR2_NVM_CONFIG 0x000C +#define EMIF_SDRAM_REF_CTRL 0x0010 +#define EMIF_SDRAM_REF_CTRL_SHDW 0x0014 +#define EMIF_SDRAM_TIM_1 0x0018 +#define EMIF_SDRAM_TIM_1_SHDW 0x001C +#define EMIF_SDRAM_TIM_2 0x0020 +#define EMIF_SDRAM_TIM_2_SHDW 0x0024 +#define EMIF_SDRAM_TIM_3 0x0028 +#define EMIF_SDRAM_TIM_3_SHDW 0x002C +#define EMIF_LPDDR2_NVM_TIM 0x0030 +#define EMIF_LPDDR2_NVM_TIM_SHDW 0x0034 +#define EMIF_PWR_MGMT_CTRL 0x0038 +#define EMIF_PWR_MGMT_CTRL_SHDW 0x003C +#define EMIF_LPDDR2_MODE_REG_DATA 0x0040 +#define EMIF_LPDDR2_MODE_REG_CFG 0x0050 +#define EMIF_L3_CONFIG 0x0054 +#define EMIF_L3_CFG_VAL_1 0x0058 +#define EMIF_L3_CFG_VAL_2 0x005C +#define IODFT_TLGC 0x0060 +#define EMIF_PERF_CNT_1 0x0080 +#define EMIF_PERF_CNT_2 0x0084 +#define EMIF_PERF_CNT_CFG 0x0088 +#define EMIF_PERF_CNT_SEL 0x008C +#define EMIF_PERF_CNT_TIM 0x0090 +#define EMIF_READ_IDLE_CTRL 0x0098 +#define EMIF_READ_IDLE_CTRL_SHDW 0x009c +#define EMIF_ZQ_CONFIG 0x00C8 +#define EMIF_DDR_PHY_CTRL_1 0x00E4 +#define EMIF_DDR_PHY_CTRL_1_SHDW 0x00E8 +#define EMIF_DDR_PHY_CTRL_2 0x00EC + +#define DMM_LISA_MAP_0 0x0040 +#define DMM_LISA_MAP_1 0x0044 +#define DMM_LISA_MAP_2 0x0048 +#define DMM_LISA_MAP_3 0x004C + +#define MR0_ADDR 0 +#define MR1_ADDR 1 +#define MR2_ADDR 2 +#define MR4_ADDR 4 +#define MR10_ADDR 10 +#define MR16_ADDR 16 +#define REF_EN 0x40000000 +/* defines for MR1 */ +#define MR1_BL4 2 +#define MR1_BL8 3 +#define MR1_BL16 4 + +#define MR1_BT_SEQ 0 +#define BT_INT 1 + +#define MR1_WC 0 +#define MR1_NWC 1 + +#define MR1_NWR3 1 +#define MR1_NWR4 2 +#define MR1_NWR5 3 +#define MR1_NWR6 4 +#define MR1_NWR7 5 +#define MR1_NWR8 6 + +#define MR1_VALUE (MR1_NWR3 << 5) | (MR1_WC << 4) | (MR1_BT_SEQ << 3) \ + | (MR1_BL8 << 0) + +/* defines for MR2 */ +#define MR2_RL3_WL1 1 +#define MR2_RL4_WL2 2 +#define MR2_RL5_WL2 3 +#define MR2_RL6_WL3 4 + +/* defines for MR10 */ +#define MR10_ZQINIT 0xFF +#define MR10_ZQRESET 0xC3 +#define MR10_ZQCL 0xAB +#define MR10_ZQCS 0x56 + + +/* TODO: FREQ update method is not working so shadow registers programming + * is just for same of completeness. This would be safer if auto + * trasnitions are working + */ +#define FREQ_UPDATE_EMIF +/* EMIF Needs to be configured@19.2 MHz and shadow registers + * should be programmed for new OPP. + */ +/* Elpida 2x2Gbit */ +#define SDRAM_CONFIG_INIT 0x80800EB1 +#define DDR_PHY_CTRL_1_INIT 0x849FFFF5 +#define READ_IDLE_CTRL 0x000501FF +#define PWR_MGMT_CTRL 0x4000000f +#define PWR_MGMT_CTRL_OPP100 0x4000000f +#define ZQ_CONFIG 0x500b3215 + +#define CS1_MR(mr) ((mr) | 0x80000000) + +static inline void delay(unsigned long loops) +{ + __asm__ volatile ("1:\n" "subs %0, %1, #1\n" + "bne 1b" : "=r" (loops) : "0"(loops)); +} + +int omap4_emif_config(unsigned int base, const struct ddr_regs *ddr_regs) +{ + /* + * set SDRAM CONFIG register + * EMIF_SDRAM_CONFIG[31:29] REG_SDRAM_TYPE = 4 for LPDDR2-S4 + * EMIF_SDRAM_CONFIG[28:27] REG_IBANK_POS = 0 + * EMIF_SDRAM_CONFIG[13:10] REG_CL = 3 + * EMIF_SDRAM_CONFIG[6:4] REG_IBANK = 3 - 8 banks + * EMIF_SDRAM_CONFIG[3] REG_EBANK = 0 - CS0 + * EMIF_SDRAM_CONFIG[2:0] REG_PAGESIZE = 2 - 512- 9 column + * JDEC specs - S4-2Gb --8 banks -- R0-R13, C0-c8 + */ + writel(readl(base + EMIF_LPDDR2_NVM_CONFIG) & 0xbfffffff, + base + EMIF_LPDDR2_NVM_CONFIG); + writel(ddr_regs->config_init, base + EMIF_SDRAM_CONFIG); + + /* PHY control values */ + writel(DDR_PHY_CTRL_1_INIT, base + EMIF_DDR_PHY_CTRL_1); + writel(ddr_regs->phy_ctrl_1, base + EMIF_DDR_PHY_CTRL_1_SHDW); + + /* + * EMIF_READ_IDLE_CTRL + */ + writel(READ_IDLE_CTRL, base + EMIF_READ_IDLE_CTRL); + writel(READ_IDLE_CTRL, base + EMIF_READ_IDLE_CTRL); + + /* + * EMIF_SDRAM_TIM_1 + */ + writel(ddr_regs->tim1, base + EMIF_SDRAM_TIM_1); + writel(ddr_regs->tim1, base + EMIF_SDRAM_TIM_1_SHDW); + + /* + * EMIF_SDRAM_TIM_2 + */ + writel(ddr_regs->tim2, base + EMIF_SDRAM_TIM_2); + writel(ddr_regs->tim2, base + EMIF_SDRAM_TIM_2_SHDW); + + /* + * EMIF_SDRAM_TIM_3 + */ + writel(ddr_regs->tim3, base + EMIF_SDRAM_TIM_3); + writel(ddr_regs->tim3, base + EMIF_SDRAM_TIM_3_SHDW); + + writel(ddr_regs->zq_config, base + EMIF_ZQ_CONFIG); + + /* + * poll MR0 register (DAI bit) + * REG_CS[31] = 0 -- Mode register command to CS0 + * REG_REFRESH_EN[30] = 1 -- Refresh enable after MRW + * REG_ADDRESS[7:0] = 00 -- Refresh enable after MRW + */ + + writel(MR0_ADDR, base + EMIF_LPDDR2_MODE_REG_CFG); + + while (readl(base + EMIF_LPDDR2_MODE_REG_DATA) & 1) + ; + + writel(CS1_MR(MR0_ADDR), base + EMIF_LPDDR2_MODE_REG_CFG); + + while (readl(base + EMIF_LPDDR2_MODE_REG_DATA) & 1) + ; + + + /* set MR10 register */ + writel(MR10_ADDR, base + EMIF_LPDDR2_MODE_REG_CFG); + writel(MR10_ZQINIT, base + EMIF_LPDDR2_MODE_REG_DATA); + writel(CS1_MR(MR10_ADDR), base + EMIF_LPDDR2_MODE_REG_CFG); + writel(MR10_ZQINIT, base + EMIF_LPDDR2_MODE_REG_DATA); + + /* wait for tZQINIT=1us */ + delay(10); + + /* set MR1 register */ + writel(MR1_ADDR, base + EMIF_LPDDR2_MODE_REG_CFG); + writel(ddr_regs->mr1, base + EMIF_LPDDR2_MODE_REG_DATA); + writel(CS1_MR(MR1_ADDR), base + EMIF_LPDDR2_MODE_REG_CFG); + writel(ddr_regs->mr1, base + EMIF_LPDDR2_MODE_REG_DATA); + + /* set MR2 register RL=6 for OPP100 */ + writel(MR2_ADDR, base + EMIF_LPDDR2_MODE_REG_CFG); + writel(ddr_regs->mr2, base + EMIF_LPDDR2_MODE_REG_DATA); + writel(CS1_MR(MR2_ADDR), base + EMIF_LPDDR2_MODE_REG_CFG); + writel(ddr_regs->mr2, base + EMIF_LPDDR2_MODE_REG_DATA); + + /* Set SDRAM CONFIG register again here with final RL-WL value */ + writel(ddr_regs->config_final, base + EMIF_SDRAM_CONFIG); + writel(ddr_regs->phy_ctrl_1, base + EMIF_DDR_PHY_CTRL_1); + + /* + * EMIF_SDRAM_REF_CTRL + * refresh rate = DDR_CLK / reg_refresh_rate + * 3.9 uS = (400MHz) / reg_refresh_rate + */ + writel(ddr_regs->ref_ctrl, base + EMIF_SDRAM_REF_CTRL); + writel(ddr_regs->ref_ctrl, base + EMIF_SDRAM_REF_CTRL_SHDW); + + /* set MR16 register */ + writel(MR16_ADDR | REF_EN, base + EMIF_LPDDR2_MODE_REG_CFG); + writel(0, base + EMIF_LPDDR2_MODE_REG_DATA); + writel(CS1_MR(MR16_ADDR | REF_EN), + base + EMIF_LPDDR2_MODE_REG_CFG); + writel(0, base + EMIF_LPDDR2_MODE_REG_DATA); + + /* LPDDR2 init complete */ + + return 0; +} + +static void reset_phy(unsigned int base) +{ + *(volatile int*)(base + IODFT_TLGC) |= (1 << 10); +} + +void omap4_ddr_init(const struct ddr_regs *ddr_regs, + const struct dpll_param *core) +{ + unsigned int rev; + rev = omap4_revision(); + + if (rev == OMAP4430_ES2_0) { + writel(0x9e9e9e9e, 0x4A100638); + writel(0x9e9e9e9e, 0x4A10063c); + writel(0x9e9e9e9e, 0x4A100640); + writel(0x9e9e9e9e, 0x4A100648); + writel(0x9e9e9e9e, 0x4A10064c); + writel(0x9e9e9e9e, 0x4A100650); + /* LPDDR2IO set to NMOS PTV */ + writel(0x00ffc000, 0x4A100704); + } + + /* + * DMM Configuration + */ + + /* Both EMIFs 128 byte interleaved */ + writel(0x80640300, OMAP44XX_DMM_BASE + DMM_LISA_MAP_0); + + *(volatile int*)(OMAP44XX_DMM_BASE + DMM_LISA_MAP_2) = 0x00000000; + *(volatile int*)(OMAP44XX_DMM_BASE + DMM_LISA_MAP_3) = 0xFF020100; + + /* DDR needs to be initialised @ 19.2 MHz + * So put core DPLL in bypass mode + * Configure the Core DPLL but don't lock it + */ + omap4_configure_core_dpll_no_lock(core); + + /* No IDLE: BUG in SDC */ + sr32(CM_MEMIF_CLKSTCTRL, 0, 32, 0x2); + while(((*(volatile int*)CM_MEMIF_CLKSTCTRL) & 0x700) != 0x700); + + *(volatile int*)(OMAP44XX_EMIF1_BASE + EMIF_PWR_MGMT_CTRL) = 0x0; + *(volatile int*)(OMAP44XX_EMIF2_BASE + EMIF_PWR_MGMT_CTRL) = 0x0; + + omap4_emif_config(OMAP44XX_EMIF1_BASE, ddr_regs); + omap4_emif_config(OMAP44XX_EMIF2_BASE, ddr_regs); + + /* Lock Core using shadow CM_SHADOW_FREQ_CONFIG1 */ + omap4_lock_core_dpll_shadow(core); + + /* Set DLL_OVERRIDE = 0 */ + *(volatile int*)CM_DLL_CTRL = 0x0; + + delay(200); + + /* Check for DDR PHY ready for EMIF1 & EMIF2 */ + while((((*(volatile int*)(OMAP44XX_EMIF1_BASE + EMIF_STATUS))&(0x04)) != 0x04) \ + || (((*(volatile int*)(OMAP44XX_EMIF2_BASE + EMIF_STATUS))&(0x04)) != 0x04)); + + /* Reprogram the DDR PYHY Control register */ + /* PHY control values */ + + sr32(CM_MEMIF_EMIF_1_CLKCTRL, 0, 32, 0x1); + sr32(CM_MEMIF_EMIF_2_CLKCTRL, 0, 32, 0x1); + + /* Put the Core Subsystem PD to ON State */ + + /* No IDLE: BUG in SDC */ + //sr32(CM_MEMIF_CLKSTCTRL, 0, 32, 0x2); + //while(((*(volatile int*)CM_MEMIF_CLKSTCTRL) & 0x700) != 0x700); + *(volatile int*)(OMAP44XX_EMIF1_BASE + EMIF_PWR_MGMT_CTRL) = 0x80000000; + *(volatile int*)(OMAP44XX_EMIF2_BASE + EMIF_PWR_MGMT_CTRL) = 0x80000000; + + /* + * DMM : DMM_LISA_MAP_0(Section_0) + * [31:24] SYS_ADDR 0x80 + * [22:20] SYS_SIZE 0x7 - 2Gb + * [19:18] SDRC_INTLDMM 0x1 - 128 byte + * [17:16] SDRC_ADDRSPC 0x0 + * [9:8] SDRC_MAP 0x3 + * [7:0] SDRC_ADDR 0X0 + */ + reset_phy(OMAP44XX_EMIF1_BASE); + reset_phy(OMAP44XX_EMIF2_BASE); + + *((volatile int *)0x80000000) = 0; + *((volatile int *)0x80000080) = 0; +} + +void omap4_power_i2c_send(u32 r) +{ + u32 val; + + writel(r, OMAP44XX_PRM_VC_VAL_BYPASS); + + val = readl(OMAP44XX_PRM_VC_VAL_BYPASS); + val |= 0x1000000; + writel(val, OMAP44XX_PRM_VC_VAL_BYPASS); + + while (readl(OMAP44XX_PRM_VC_VAL_BYPASS) & 0x1000000) + ; + + val = readl(OMAP44XX_PRM_IRQSTATUS_MPU_A9); + writel(val, OMAP44XX_PRM_IRQSTATUS_MPU_A9); +} + +static unsigned int cortex_a9_rev(void) +{ + + unsigned int i; + + asm ("mrc p15, 0, %0, c0, c0, 0" : "=r" (i)); + + return i; +} + +unsigned int omap4_revision(void) +{ + unsigned int chip_rev = 0; + unsigned int rev = cortex_a9_rev(); + + switch(rev) { + case 0x410FC091: + return OMAP4430_ES1_0; + case 0x411FC092: + chip_rev = (readl(OMAP44XX_CTRL_BASE + 0x204) >> 28) & 0xF; + if (chip_rev == 3) + return OMAP4430_ES2_1; + else if (chip_rev >= 4) + return OMAP4430_ES2_2; + else + return OMAP4430_ES2_0; + } + return OMAP4430_SILICON_ID_INVALID; +} + +/* + * shutdown watchdog + */ +static int watchdog_init(void) +{ + void __iomem *wd2_base = (void *)OMAP44XX_WDT2_BASE; + + writel(WD_UNLOCK1, wd2_base + WATCHDOG_WSPR); + wait_for_command_complete(); + writel(WD_UNLOCK2, wd2_base + WATCHDOG_WSPR); + + return 0; +} +late_initcall(watchdog_init); + +static int omap_vector_init(void) +{ + __asm__ __volatile__ ( + "mov r0, #0;" + "mcr p15, #0, r0, c12, c0, #0;" + : + : + : "r0" + ); + + return 0; +} +core_initcall(omap_vector_init); + +#define OMAP4_TRACING_VECTOR3 0x4030d048 + +enum omap_boot_src omap4_bootsrc(void) +{ + u32 bootsrc = readl(OMAP4_TRACING_VECTOR3); + + if (bootsrc & (1 << 5)) + return OMAP_BOOTSRC_MMC1; + if (bootsrc & (1 << 3)) + return OMAP_BOOTSRC_NAND; + return OMAP_BOOTSRC_UNKNOWN; +} diff --git a/arch/arm/mach-omap/syslib.c b/arch/arm/mach-omap/syslib.c index 2b25dc15..677de6a2 100644 --- a/arch/arm/mach-omap/syslib.c +++ b/arch/arm/mach-omap/syslib.c @@ -53,26 +53,6 @@ void sdelay(unsigned long loops) } /** - * @brief clear & set a value in a bit range for a 32 bit address - * - * @param[in] addr Address to set/read from - * @param[in] start_bit Where to put the value - * @param[in] num_bits number of bits the value should be set - * @param[in] value the value to set - * - * @return void - */ -void sr32(u32 addr, u32 start_bit, u32 num_bits, u32 value) -{ - u32 tmp, msk = 0; - msk = 1 << num_bits; - --msk; - tmp = readl(addr) & ~(msk << start_bit); - tmp |= value << start_bit; - writel(tmp, addr); -} - -/** * @brief common routine to allow waiting for changes in volatile regs. * * @param[in] read_bit_mask the bit mask to read diff --git a/arch/arm/mach-omap/xload.c b/arch/arm/mach-omap/xload.c new file mode 100644 index 00000000..216b9b5a --- /dev/null +++ b/arch/arm/mach-omap/xload.c @@ -0,0 +1,54 @@ +#include <common.h> +#include <partition.h> +#include <nand.h> +#include <driver.h> +#include <linux/mtd/mtd.h> +#include <fs.h> +#include <fcntl.h> +#include <mach/xload.h> +#include <sizes.h> + +void *omap_xload_boot_nand(int offset, int size) +{ + int ret; + void *to = xmalloc(size); + struct cdev *cdev; + + devfs_add_partition("nand0", offset, size, PARTITION_FIXED, "x"); + dev_add_bb_dev("x", "bbx"); + + cdev = cdev_open("bbx", O_RDONLY); + if (!cdev) { + printf("failed to open nand\n"); + return NULL; + } + + ret = cdev_read(cdev, to, size, 0, 0); + if (ret != size) { + printf("failed to read from nand\n"); + return NULL; + } + + return to; +} + +void *omap_xload_boot_mmc(void) +{ + int ret; + void *buf; + int len; + + ret = mount("disk0.0", "fat", "/"); + if (ret) { + printf("mounting sd card failed with %d\n", ret); + return NULL; + } + + buf = read_file("/barebox.bin", &len); + if (!buf) { + printf("could not read barebox.bin from sd card\n"); + return NULL; + } + + return buf; +} diff --git a/arch/nios2/boards/generic/generic.c b/arch/nios2/boards/generic/generic.c index 99c855d2..7e939ce2 100644 --- a/arch/nios2/boards/generic/generic.c +++ b/arch/nios2/boards/generic/generic.c @@ -11,11 +11,14 @@ static struct device_d cfi_dev = { .size = NIOS_SOPC_FLASH_SIZE, }; +static int phy_address = 1; + static struct device_d mac_dev = { - .id = -1, - .name = "altera_tse", - .map_base = NIOS_SOPC_TSE_BASE, - .size = 0x00000400, + .id = -1, + .name = "altera_tse", + .map_base = NIOS_SOPC_TSE_BASE, + .size = 0x00000400, + .platform_data = &phy_address, }; static struct memory_platform_data ram_pdata = { diff --git a/arch/nios2/lib/Makefile b/arch/nios2/lib/Makefile index 8776c61e..fc9d4d7d 100644 --- a/arch/nios2/lib/Makefile +++ b/arch/nios2/lib/Makefile @@ -2,5 +2,6 @@ obj-y += board.o obj-y += libgcc.o obj-y += clock.o obj-y += cache.o +obj-y += bootm.o obj-$(CONFIG_EARLY_PRINTF) += early_printf.o diff --git a/arch/nios2/lib/bootm.c b/arch/nios2/lib/bootm.c new file mode 100644 index 00000000..34e2bd26 --- /dev/null +++ b/arch/nios2/lib/bootm.c @@ -0,0 +1,83 @@ +/* + * barebox - bootm.c + * + * (C) Copyright 2011 - Franck JULLIEN <elec4fun@gmail.com> + * + * (C) Copyright 2003, Psyent Corporation <www.psyent.com> + * Scott McNutt <smcnutt@psyent.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 <common.h> +#include <command.h> +#include <image.h> +#include <environment.h> +#include <init.h> +#include <boot.h> +#include <asm/cache.h> + +#define NIOS_MAGIC 0x534f494e /* enable command line and initrd passing */ + +static int do_bootm_linux(struct image_data *idata) +{ + image_header_t *os_header = &idata->os->header; + void (*kernel)(int, int, int, const char *); + const char *commandline = getenv ("bootargs"); + + kernel = (void (*)(int, int, int, const char *))ntohl(os_header->ih_ep); + +#ifdef CONFIG_USE_IRQ + disable_interrupts(); +#endif + + if (relocate_image(idata->os, (void *)ntohl(os_header->ih_load))) + return -1; + + /* kernel parameters passing + * r4 : NIOS magic + * r5 : initrd start + * r6 : initrd end or fdt + * r7 : kernel command line + * fdt is passed to kernel via r6, the same as initrd_end. fdt will be + * verified with fdt magic. when both initrd and fdt are used at the + * same time, fdt must follow immediately after initrd. + */ + + /* flushes data and instruction caches before calling the kernel */ + flush_cache_all(); + + kernel(NIOS_MAGIC, 0, 0, commandline); + /* does not return */ + + return 1; +} + +static struct image_handler handler = { + .bootm = do_bootm_linux, + .image_type = IH_OS_LINUX, +}; + +int nios2_register_image_handler(void) +{ + return register_image_handler(&handler); +} + +late_initcall(nios2_register_image_handler); + diff --git a/commands/Kconfig b/commands/Kconfig index f137f473..f192d309 100644 --- a/commands/Kconfig +++ b/commands/Kconfig @@ -1,7 +1,14 @@ config REGINFO bool -menu "Commands " +config COMMAND_SUPPORT + bool + depends on !SHELL_NONE + default y + +if COMMAND_SUPPORT + +menu "commands " menu "scripting " @@ -29,11 +36,13 @@ config CMD_LOADENV prompt "loadenv" config CMD_EXPORT + depends on ENVIRONMENT_VARIABLES tristate prompt "export" config CMD_PRINTENV tristate + depends on ENVIRONMENT_VARIABLES prompt "printenv" config CMD_READLINE @@ -142,6 +151,12 @@ config CMD_UMOUNT default y prompt "umount" +config CMD_NAND + tristate + default y + depends on NAND + prompt "nand" + endmenu menu "console " @@ -387,4 +402,14 @@ config CMD_LED_TRIGGER The trigger command allows to control LED triggers from the command line. +config CMD_USB + bool + depends on USB + prompt "usb command" + default y + help + The usb command allows to rescan for USB devices. + endmenu + +endif diff --git a/commands/Makefile b/commands/Makefile index c89adcff..f7ef9a8a 100644 --- a/commands/Makefile +++ b/commands/Makefile @@ -37,7 +37,7 @@ obj-$(CONFIG_CMD_EXPORT) += export.o obj-$(CONFIG_CMD_PRINTENV) += printenv.o obj-$(CONFIG_CMD_SAVEENV) += saveenv.o obj-$(CONFIG_CMD_LOADENV) += loadenv.o -obj-$(CONFIG_NAND) += nand.o +obj-$(CONFIG_CMD_NAND) += nand.o obj-$(CONFIG_CMD_TRUE) += true.o obj-$(CONFIG_CMD_FALSE) += false.o obj-$(CONFIG_CMD_VERSION) += version.o @@ -55,3 +55,4 @@ obj-$(CONFIG_CMD_PASSWD) += passwd.o obj-$(CONFIG_CMD_LOGIN) += login.o obj-$(CONFIG_CMD_LED) += led.o obj-$(CONFIG_CMD_LED_TRIGGER) += trigger.o +obj-$(CONFIG_CMD_USB) += usb.o diff --git a/commands/cp.c b/commands/cp.c index ae8719b2..34281052 100644 --- a/commands/cp.c +++ b/commands/cp.c @@ -31,6 +31,7 @@ #include <libbb.h> #include <fs.h> #include <malloc.h> +#include <libgen.h> /** * @param[in] cmdtp FIXME @@ -60,7 +61,7 @@ static int do_cp(struct command *cmdtp, int argc, char *argv[]) for (i = 1; i < argc - 1; i++) { if (last_is_dir) { char *dst; - dst = concat_path_file(argv[argc - 1], argv[i]); + dst = concat_path_file(argv[argc - 1], basename(argv[i])); ret = copy_file(argv[i], dst); if (ret) goto out; diff --git a/commands/loadenv.c b/commands/loadenv.c index c33c34fc..5568aced 100644 --- a/commands/loadenv.c +++ b/commands/loadenv.c @@ -36,7 +36,7 @@ static int do_loadenv(struct command *cmdtp, int argc, char *argv[]) else dirname = argv[2]; if (argc < 2) - filename = "/dev/env0"; + filename = default_environment_path; else filename = argv[1]; printf("loading environment from %s\n", filename); diff --git a/commands/nand.c b/commands/nand.c index d3921b9f..88f242df 100644 --- a/commands/nand.c +++ b/commands/nand.c @@ -32,249 +32,6 @@ #include <fcntl.h> #include <libgen.h> -struct nand_bb { - char *devname; - char *name; - int open; - int needs_write; - - struct mtd_info_user info; - - size_t raw_size; - size_t size; - int fd; - off_t offset; - void *writebuf; - - struct cdev cdev; -}; - -static ssize_t nand_bb_read(struct cdev *cdev, void *buf, size_t count, - unsigned long offset, ulong flags) -{ - struct nand_bb *bb = cdev->priv; - int ret, bytes = 0, now; - - debug("%s %d %d\n", __func__, offset, count); - - while(count) { - ret = ioctl(bb->fd, MEMGETBADBLOCK, (void *)bb->offset); - if (ret < 0) - return ret; - - if (ret) { - printf("skipping bad block at 0x%08lx\n", bb->offset); - bb->offset += bb->info.erasesize; - continue; - } - - now = min(count, (size_t)(bb->info.erasesize - - (bb->offset % bb->info.erasesize))); - lseek(bb->fd, bb->offset, SEEK_SET); - ret = read(bb->fd, buf, now); - if (ret < 0) - return ret; - buf += now; - count -= now; - bb->offset += now; - bytes += now; - }; - - return bytes; -} - -/* Must be a multiple of the largest NAND page size */ -#define BB_WRITEBUF_SIZE 4096 - -static int nand_bb_write_buf(struct nand_bb *bb, size_t count) -{ - int ret, now; - void *buf = bb->writebuf; - int cur_ofs = bb->offset & ~(BB_WRITEBUF_SIZE - 1); - - while (count) { - ret = ioctl(bb->fd, MEMGETBADBLOCK, (void *)cur_ofs); - if (ret < 0) - return ret; - - if (ret) { - debug("skipping bad block at 0x%08x\n", cur_ofs); - bb->offset += bb->info.erasesize; - cur_ofs += bb->info.erasesize; - continue; - } - - now = min(count, (size_t)(bb->info.erasesize)); - lseek(bb->fd, cur_ofs, SEEK_SET); - ret = write(bb->fd, buf, now); - if (ret < 0) - return ret; - buf += now; - count -= now; - cur_ofs += now; - }; - - return 0; -} - -static ssize_t nand_bb_write(struct cdev *cdev, const void *buf, size_t count, - unsigned long offset, ulong flags) -{ - struct nand_bb *bb = cdev->priv; - int bytes = count, now, wroffs, ret; - - debug("%s offset: 0x%08x count: 0x%08x\n", __func__, offset, count); - - while (count) { - wroffs = bb->offset % BB_WRITEBUF_SIZE; - now = min((int)count, BB_WRITEBUF_SIZE - wroffs); - memcpy(bb->writebuf + wroffs, buf, now); - - if (wroffs + now == BB_WRITEBUF_SIZE) { - bb->needs_write = 0; - ret = nand_bb_write_buf(bb, BB_WRITEBUF_SIZE); - if (ret) - return ret; - } else { - bb->needs_write = 1; - } - - bb->offset += now; - count -= now; - buf += now; - } - - return bytes; -} - -static int nand_bb_erase(struct cdev *cdev, size_t count, unsigned long offset) -{ - struct nand_bb *bb = cdev->priv; - - if (offset != 0) { - printf("can only erase from beginning of device\n"); - return -EINVAL; - } - - lseek(bb->fd, 0, SEEK_SET); - - return erase(bb->fd, bb->raw_size, 0); -} - -static int nand_bb_open(struct cdev *cdev, struct filep *f) -{ - struct nand_bb *bb = cdev->priv; - - if (bb->open) - return -EBUSY; - - bb->open = 1; - bb->offset = 0; - bb->needs_write = 0; - bb->writebuf = xmalloc(BB_WRITEBUF_SIZE); - - return 0; -} - -static int nand_bb_close(struct cdev *cdev, struct filep *f) -{ - struct nand_bb *bb = cdev->priv; - - if (bb->needs_write) - nand_bb_write_buf(bb, bb->offset % BB_WRITEBUF_SIZE); - - bb->open = 0; - free(bb->writebuf); - - return 0; -} - -static int nand_bb_calc_size(struct nand_bb *bb) -{ - ulong pos = 0; - int ret; - - while (pos < bb->raw_size) { - ret = ioctl(bb->fd, MEMGETBADBLOCK, (void *)pos); - if (ret < 0) - return ret; - if (!ret) - bb->cdev.size += bb->info.erasesize; - - pos += bb->info.erasesize; - } - - return 0; -} - -static struct file_operations nand_bb_ops = { - .open = nand_bb_open, - .close = nand_bb_close, - .read = nand_bb_read, - .write = nand_bb_write, - .erase = nand_bb_erase, -}; - -/** - * Add a bad block aware device ontop of another (NAND) device - * @param[in] dev The device to add a partition on - * @param[in] name Partition name (can be obtained with devinfo command) - * @return The device representing the new partition. - */ -int dev_add_bb_dev(char *path, const char *name) -{ - struct nand_bb *bb; - int ret = -ENOMEM; - struct stat s; - - bb = xzalloc(sizeof(*bb)); - bb->devname = asprintf("/dev/%s", basename(path)); - if (!bb->devname) - goto out1; - - if (name) - bb->cdev.name = strdup(name); - else - bb->cdev.name = asprintf("%s.bb", basename(path)); - - if (!bb->cdev.name) - goto out2; - - ret = stat(bb->devname, &s); - if (ret) - goto out3; - - bb->raw_size = s.st_size; - - bb->fd = open(bb->devname, O_RDWR); - if (bb->fd < 0) { - ret = -ENODEV; - goto out3; - } - - ret = ioctl(bb->fd, MEMGETINFO, &bb->info); - if (ret) - goto out4; - - nand_bb_calc_size(bb); - bb->cdev.ops = &nand_bb_ops; - bb->cdev.priv = bb; - - devfs_create(&bb->cdev); - - return 0; - -out4: - close(bb->fd); -out3: - free(bb->cdev.name); -out2: - free(bb->devname); -out1: - free(bb); - return ret; -} - #define NAND_ADD (1 << 0) #define NAND_DEL (1 << 1) #define NAND_MARKBAD (1 << 2) @@ -282,7 +39,6 @@ out1: static int do_nand(struct command *cmdtp, int argc, char *argv[]) { int opt; - struct nand_bb *bb; int command = 0, badblock = 0; while((opt = getopt(argc, argv, "adb:")) > 0) { @@ -306,7 +62,7 @@ static int do_nand(struct command *cmdtp, int argc, char *argv[]) if (command & NAND_ADD) { while (optind < argc) { - if (dev_add_bb_dev(argv[optind], NULL)) + if (dev_add_bb_dev(basename(argv[optind]), NULL)) return 1; optind++; @@ -315,17 +71,7 @@ static int do_nand(struct command *cmdtp, int argc, char *argv[]) if (command & NAND_DEL) { while (optind < argc) { - struct cdev *cdev; - - cdev = cdev_by_name(basename(argv[optind])); - if (!cdev) { - printf("no such device: %s\n", argv[optind]); - return 1; - } - bb = cdev->priv; - close(bb->fd); - devfs_remove(cdev); - free(bb); + dev_remove_bb_dev(basename(argv[optind])); optind++; } } diff --git a/commands/saveenv.c b/commands/saveenv.c index 2f969fe7..11a9fee5 100644 --- a/commands/saveenv.c +++ b/commands/saveenv.c @@ -41,7 +41,7 @@ static int do_saveenv(struct command *cmdtp, int argc, char *argv[]) else dirname = argv[2]; if (argc < 2) - filename = "/dev/env0"; + filename = default_environment_path; else filename = argv[1]; diff --git a/commands/usb.c b/commands/usb.c new file mode 100644 index 00000000..0aac78ef --- /dev/null +++ b/commands/usb.c @@ -0,0 +1,41 @@ +/* + * usb.c - rescan for USB devices + * + * Copyright (c) 2011 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 <common.h> +#include <command.h> +#include <usb/usb.h> + +static int do_usb(struct command *cmdtp, int argc, char *argv[]) +{ + usb_rescan(); + + return 0; +} + +static const __maybe_unused char cmd_usb_help[] = +"Usage: usb\n" +"(re-)detect USB devices\n"; + +BAREBOX_CMD_START(usb) + .cmd = do_usb, + .usage = "(re-)detect USB devices", + BAREBOX_CMD_HELP(cmd_usb_help) +BAREBOX_CMD_END diff --git a/common/Kconfig b/common/Kconfig index 9e30579a..7d2367b0 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -31,6 +31,15 @@ config ENV_HANDLING config GENERIC_GPIO bool +config BLOCK + bool + +config BLOCK_WRITE + bool + +config HAVE_NOSHELL + bool + menu "General Settings " config LOCALVERSION_AUTO @@ -56,6 +65,9 @@ config LOCALVERSION_AUTO config BOARDINFO string +config ENVIRONMENT_VARIABLES + bool "environment variables support" + menu "memory layout " config HAVE_MMU @@ -136,6 +148,22 @@ config EXPERIMENTAL bool prompt "Prompt for experimental code" +choice + prompt "malloc implementation" + +config MALLOC_DLMALLOC + bool "dlmalloc" + +config MALLOC_DUMMY + bool "dummy malloc" + depends on SHELL_NONE + help + select this option to use a dummy malloc implementation. With this + memory is never freed. This is suitable for well tested noninteractive + environments only. + +endchoice + config MODULES depends on HAS_MODULES depends on EXPERIMENTAL @@ -213,14 +241,26 @@ choice config SHELL_HUSH bool "hush parser" + select ENVIRONMENT_VARIABLES + select COMMAND_SUPPORT help Enable hush support. This is the most advanced shell available for barebox. config SHELL_SIMPLE bool "Simple parser" + select ENVIRONMENT_VARIABLES + select COMMAND_SUPPORT help simple shell. No if/then, no return values from commands, no loops + + config SHELL_NONE + depends on HAVE_NOSHELL + bool "no shell (noninteractive build)" + help + No shell at all. This means no shell is started and your board has + to provide a run_shell() function which is started at the end of + the barebox startup process. endchoice config GLOB @@ -389,7 +429,7 @@ config DEFAULT_ENVIRONMENT config DEFAULT_ENVIRONMENT_GENERIC bool depends on DEFAULT_ENVIRONMENT - select SHELL_HUSH + depends on SHELL_HUSH select HUSH_GETOPT select CMD_CRC select CMD_CRC_CMP diff --git a/common/Makefile b/common/Makefile index 6cc61435..9fed2ae5 100644 --- a/common/Makefile +++ b/common/Makefile @@ -6,14 +6,18 @@ obj-$(CONFIG_KALLSYMS) += kallsyms.o obj-$(CONFIG_ENV_HANDLING) += environment.o obj-$(CONFIG_AUTO_COMPLETE) += complete.o obj-$(CONFIG_POLLER) += poller.o +obj-$(CONFIG_BLOCK) += block.o -obj-y += dlmalloc.o +obj-y += memory.o +obj-$(CONFIG_MALLOC_DLMALLOC) += dlmalloc.o +obj-$(CONFIG_MALLOC_DUMMY) += dummy_malloc.o obj-y += clock.o -obj-y += command.o +obj-y += version.o +obj-$(CONFIG_COMMAND_SUPPORT) += command.o obj-$(CONFIG_CONSOLE_FULL) += console.o obj-$(CONFIG_CONSOLE_SIMPLE) += console_simple.o obj-$(CONFIG_DIGEST) += digest.o -obj-y += env.o +obj-$(CONFIG_ENVIRONMENT_VARIABLES) += env.o obj-$(CONFIG_CMD_BOOTM) += image.o obj-y += startup.o obj-y += misc.o diff --git a/common/block.c b/common/block.c new file mode 100644 index 00000000..24377c62 --- /dev/null +++ b/common/block.c @@ -0,0 +1,263 @@ +/* + * block.c - simple block layer + * + * Copyright (c) 2011 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 <common.h> +#include <block.h> +#include <linux/err.h> + +#define BLOCKSIZE(blk) (1 << blk->blockbits) + +#define WRBUFFER_LAST(blk) (blk->wrblock + blk->wrbufblocks - 1) + +#ifdef CONFIG_BLOCK_WRITE +static int writebuffer_flush(struct block_device *blk) +{ + if (!blk->wrbufblocks) + return 0; + + blk->ops->write(blk, blk->wrbuf, blk->wrblock, + blk->wrbufblocks); + + blk->wrbufblocks = 0; + + return 0; +} + +static int block_put(struct block_device *blk, const void *buf, int block) +{ + if (block >= blk->num_blocks) + return -EIO; + + if (block < blk->wrblock || block > blk->wrblock + blk->wrbufblocks) { + writebuffer_flush(blk); + } + + if (blk->wrbufblocks == 0) { + blk->wrblock = block; + blk->wrbufblocks = 1; + } + + memcpy(blk->wrbuf + (block - blk->wrblock) * BLOCKSIZE(blk), + buf, BLOCKSIZE(blk)); + + if (block > WRBUFFER_LAST(blk)) + blk->wrbufblocks++; + + if (blk->wrbufblocks == blk->wrbufsize) + writebuffer_flush(blk); + + return 0; +} + +#else +static int writebuffer_flush(struct block_device *blk) +{ + return 0; +} +#endif + +static void *block_get(struct block_device *blk, int block) +{ + int ret; + int num_blocks; + + if (block >= blk->num_blocks) + return ERR_PTR(-EIO); + + /* first look into write buffer */ + if (block >= blk->wrblock && block <= WRBUFFER_LAST(blk)) + return blk->wrbuf + (block - blk->wrblock) * BLOCKSIZE(blk); + + /* then look into read buffer */ + if (block >= blk->rdblock && block <= blk->rdblockend) + return blk->rdbuf + (block - blk->rdblock) * BLOCKSIZE(blk); + + /* + * If none of the buffers above match read the block from + * the device + */ + num_blocks = min(blk->rdbufsize, blk->num_blocks - block); + + ret = blk->ops->read(blk, blk->rdbuf, block, num_blocks); + if (ret) + return ERR_PTR(ret); + + blk->rdblock = block; + blk->rdblockend = block + num_blocks - 1; + + return blk->rdbuf; +} + +static ssize_t block_read(struct cdev *cdev, void *buf, size_t count, + unsigned long offset, unsigned long flags) +{ + struct block_device *blk = cdev->priv; + unsigned long mask = BLOCKSIZE(blk) - 1; + unsigned long block = offset >> blk->blockbits; + size_t icount = count; + int blocks; + + if (offset & mask) { + size_t now = BLOCKSIZE(blk) - (offset & mask); + void *iobuf = block_get(blk, block); + + now = min(count, now); + + if (IS_ERR(iobuf)) + return PTR_ERR(iobuf); + + memcpy(buf, iobuf + (offset & mask), now); + buf += now; + count -= now; + block++; + } + + blocks = count >> blk->blockbits; + + while (blocks) { + void *iobuf = block_get(blk, block); + + if (IS_ERR(iobuf)) + return PTR_ERR(iobuf); + + memcpy(buf, iobuf, BLOCKSIZE(blk)); + buf += BLOCKSIZE(blk); + blocks--; + block++; + count -= BLOCKSIZE(blk); + } + + if (count) { + void *iobuf = block_get(blk, block); + + if (IS_ERR(iobuf)) + return PTR_ERR(iobuf); + + memcpy(buf, iobuf, count); + } + + return icount; +} + +#ifdef CONFIG_BLOCK_WRITE +static ssize_t block_write(struct cdev *cdev, const void *buf, size_t count, + unsigned long offset, ulong flags) +{ + struct block_device *blk = cdev->priv; + unsigned long mask = BLOCKSIZE(blk) - 1; + unsigned long block = offset >> blk->blockbits; + size_t icount = count; + int blocks; + + if (offset & mask) { + size_t now = BLOCKSIZE(blk) - (offset & mask); + void *iobuf = block_get(blk, block); + + now = min(count, now); + + if (IS_ERR(iobuf)) + return PTR_ERR(iobuf); + + memcpy(iobuf + (offset & mask), buf, now); + block_put(blk, iobuf, block); + buf += now; + count -= now; + block++; + } + + blocks = count >> blk->blockbits; + + while (blocks) { + block_put(blk, buf, block); + buf += BLOCKSIZE(blk); + blocks--; + block++; + count -= BLOCKSIZE(blk); + } + + if (count) { + void *iobuf = block_get(blk, block); + + if (IS_ERR(iobuf)) + return PTR_ERR(iobuf); + + memcpy(iobuf, buf, count); + block_put(blk, iobuf, block); + } + + return icount; +} +#endif + +static int block_close(struct cdev *cdev) +{ + struct block_device *blk = cdev->priv; + + return writebuffer_flush(blk); +} + +static int block_flush(struct cdev *cdev) +{ + struct block_device *blk = cdev->priv; + + return writebuffer_flush(blk); +} + +struct file_operations block_ops = { + .read = block_read, +#ifdef CONFIG_BLOCK_WRITE + .write = block_write, +#endif + .close = block_close, + .flush = block_flush, + .lseek = dev_lseek_default, +}; + +int blockdevice_register(struct block_device *blk) +{ + size_t size = blk->num_blocks * BLOCKSIZE(blk); + int ret; + + blk->cdev.size = size; + blk->cdev.dev = blk->dev; + blk->cdev.ops = &block_ops; + blk->cdev.priv = blk; + blk->rdbufsize = PAGE_SIZE >> blk->blockbits; + blk->rdbuf = xmalloc(PAGE_SIZE); + blk->rdblock = 1; + blk->rdblockend = 0; + blk->wrbufsize = PAGE_SIZE >> blk->blockbits; + blk->wrbuf = xmalloc(PAGE_SIZE); + blk->wrblock = 0; + blk->wrbufblocks = 0; + + ret = devfs_create(&blk->cdev); + if (ret) + return ret; + + return 0; +} + +int blockdevice_unregister(struct block_device *blk) +{ + return 0; +} + diff --git a/common/command.c b/common/command.c index f1aeeb9d..ab02ed53 100644 --- a/common/command.c +++ b/common/command.c @@ -33,12 +33,8 @@ #include <linux/list.h> #include <init.h> #include <complete.h> -#include <generated/utsrelease.h> #include <getopt.h> -const char version_string[] = - "barebox " UTS_RELEASE " (" __DATE__ " - " __TIME__ ")"; - LIST_HEAD(command_list); EXPORT_SYMBOL(command_list); diff --git a/common/console.c b/common/console.c index 5548a409..d33a249c 100644 --- a/common/console.c +++ b/common/console.c @@ -45,12 +45,6 @@ EXPORT_SYMBOL(console_list); #define CONSOLE_INIT_EARLY 1 #define CONSOLE_INIT_FULL 2 -static void display_banner (void) -{ - printf (RELOC("\n\n%s\n\n"), RELOC_VAR(version_string)); - printf(RELOC("Board: " CONFIG_BOARDINFO "\n")); -} - static int __early_initdata initialized = 0; static int console_std_set(struct device_d *dev, struct param_d *param, @@ -169,7 +163,7 @@ int console_register(struct console_device *newcdev) #ifndef CONFIG_HAS_EARLY_INIT if (first) - display_banner(); + barebox_banner(); #endif return 0; @@ -420,7 +414,7 @@ void early_console_start(const char *name, int baudrate) early_console_init(base, baudrate); INITDATA(initialized) = CONSOLE_INIT_EARLY; INITDATA(early_console_base) = base; - display_banner(); + barebox_banner(); } } diff --git a/common/console_simple.c b/common/console_simple.c index 1b58deaf..7304d8ee 100644 --- a/common/console_simple.c +++ b/common/console_simple.c @@ -152,6 +152,8 @@ int console_register(struct console_device *newcdev) console = newcdev; console_list.prev = console_list.next = &newcdev->list; newcdev->list.prev = newcdev->list.next = &console_list; + + barebox_banner(); } return 0; } diff --git a/common/dlmalloc.c b/common/dlmalloc.c index ff63fbec..f9e1828c 100644 --- a/common/dlmalloc.c +++ b/common/dlmalloc.c @@ -882,56 +882,6 @@ static mbinptr av_[NAV * 2 + 2] = { /* ----------------------------------------------------------------------- */ -/* - * Begin and End of memory area for malloc(), and current "brk" - */ -static ulong malloc_start; -static ulong malloc_end; -static ulong malloc_brk; - -ulong mem_malloc_start(void) -{ - return malloc_start; -} - -ulong mem_malloc_end(void) -{ - return malloc_end; -} - -void mem_malloc_init(void *start, void *end) -{ - malloc_start = (ulong)start; - malloc_end = (ulong)end; - malloc_brk = malloc_start; -} - -static void *sbrk_no_zero(ptrdiff_t increment) -{ - ulong old = malloc_brk; - ulong new = old + increment; - - if ((new < malloc_start) || (new > malloc_end)) - return NULL; - - malloc_brk = new; - - return (void *)old; -} - -static void *sbrk(ptrdiff_t increment) -{ - void *old = sbrk_no_zero(increment); - - /* Only clear increment, if valid address was returned */ - if (old != NULL) - memset(old, 0, increment); - - return old; -} - -/* ----------------------------------------------------------------------- */ - /* Other static bookkeeping data */ /* variables holding tunable values */ diff --git a/common/dummy_malloc.c b/common/dummy_malloc.c new file mode 100644 index 00000000..213e51d8 --- /dev/null +++ b/common/dummy_malloc.c @@ -0,0 +1,26 @@ +#include <common.h> +#include <malloc.h> + +void *memalign(size_t alignment, size_t bytes) +{ + unsigned long mem = (unsigned long)sbrk(bytes + alignment); + + mem = (mem + alignment) & ~(alignment - 1); + + return (void *)mem; +} + +void *malloc(size_t size) +{ + return memalign(8, size); +} + +void free(void *ptr) +{ +} + +void *realloc(void *ptr, size_t size) +{ + BUG(); +} + diff --git a/common/environment.c b/common/environment.c index e5f24ec2..0fdbd03e 100644 --- a/common/environment.c +++ b/common/environment.c @@ -44,6 +44,8 @@ #define EXPORT_SYMBOL(x) #endif +char *default_environment_path = "/dev/env0"; + int file_size_action(const char *filename, struct stat *statbuf, void *userdata, int depth) { diff --git a/common/hush.c b/common/hush.c index 77610bba..573bd3ef 100644 --- a/common/hush.c +++ b/common/hush.c @@ -121,6 +121,7 @@ #include <libbb.h> #include <glob.h> #include <getopt.h> +#include <libbb.h> #include <linux/list.h> /*cmd_boot.c*/ @@ -361,20 +362,6 @@ static int b_addqchr(o_string *o, int ch, int quote) return b_addchr(o, ch); } -/* belongs in utility.c */ -static char *simple_itoa(unsigned int i) -{ - /* 21 digits plus null terminator, good for 64-bit or smaller ints */ - static char local[22]; - char *p = &local[21]; - *p-- = '\0'; - do { - *p-- = '0' + i % 10; - i /= 10; - } while (i > 0); - return p + 1; -} - static int b_adduint(o_string *o, unsigned int i) { int r; diff --git a/common/memory.c b/common/memory.c new file mode 100644 index 00000000..8f4a7681 --- /dev/null +++ b/common/memory.c @@ -0,0 +1,71 @@ +/* + * memory.c + * + * Copyright (c) 2011 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 <common.h> + +/* + * Begin and End of memory area for malloc(), and current "brk" + */ +static unsigned long malloc_start; +static unsigned long malloc_end; +static unsigned long malloc_brk; + +unsigned long mem_malloc_start(void) +{ + return malloc_start; +} + +unsigned long mem_malloc_end(void) +{ + return malloc_end; +} + +void mem_malloc_init(void *start, void *end) +{ + malloc_start = (unsigned long)start; + malloc_end = (unsigned long)end; + malloc_brk = malloc_start; +} + +static void *sbrk_no_zero(ptrdiff_t increment) +{ + unsigned long old = malloc_brk; + unsigned long new = old + increment; + + if ((new < malloc_start) || (new > malloc_end)) + return NULL; + + malloc_brk = new; + + return (void *)old; +} + +void *sbrk(ptrdiff_t increment) +{ + void *old = sbrk_no_zero(increment); + + /* Only clear increment, if valid address was returned */ + if (old != NULL) + memset(old, 0, increment); + + return old; +} diff --git a/common/startup.c b/common/startup.c index aa76cb75..00bc9a00 100644 --- a/common/startup.c +++ b/common/startup.c @@ -103,6 +103,7 @@ static int register_default_env(void) device_initcall(register_default_env); #endif +#if defined CONFIG_FS_RAMFS && defined CONFIG_FS_DEVFS static int mount_root(void) { mount("none", "ramfs", "/"); @@ -111,12 +112,15 @@ static int mount_root(void) return 0; } fs_initcall(mount_root); +#endif void start_barebox (void) { initcall_t *initcall; int result; +#ifdef CONFIG_COMMAND_SUPPORT struct stat s; +#endif #ifdef CONFIG_HAS_EARLY_INIT /* We are running from RAM now, copy early initdata from @@ -140,14 +144,16 @@ void start_barebox (void) display_meminfo(); #ifdef CONFIG_ENV_HANDLING - if (envfs_load("/dev/env0", "/env")) { + if (envfs_load(default_environment_path, "/env")) { #ifdef CONFIG_DEFAULT_ENVIRONMENT - printf("no valid environment found on /dev/env0. " - "Using default environment\n"); + printf("no valid environment found on %s. " + "Using default environment\n", + default_environment_path); envfs_load("/dev/defaultenv", "/env"); #endif } #endif +#ifdef CONFIG_COMMAND_SUPPORT printf("running /env/bin/init...\n"); if (!stat("/env/bin/init", &s)) { @@ -155,7 +161,7 @@ void start_barebox (void) } else { printf("not found\n"); } - +#endif /* main_loop() can return to retry autoboot, if so just run it again. */ for (;;) run_shell(); diff --git a/common/version.c b/common/version.c new file mode 100644 index 00000000..945475fc --- /dev/null +++ b/common/version.c @@ -0,0 +1,13 @@ +#include <common.h> +#include <reloc.h> +#include <generated/utsrelease.h> + +const char version_string[] = + "barebox " UTS_RELEASE " (" __DATE__ " - " __TIME__ ")"; + +void barebox_banner (void) +{ + printf (RELOC("\n\n%s\n\n"), RELOC_VAR(version_string)); + printf(RELOC("Board: " CONFIG_BOARDINFO "\n")); +} + diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index b43c975d..d7f4dcbf 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -1,4 +1,5 @@ menuconfig ATA + select BLOCK bool "ATA " help Add support for ATA types of drives like harddisks and CDROMs. @@ -7,6 +8,10 @@ if ATA comment "drive types" +config ATA_WRITE + select BLOCK_WRITE + bool "support writing to ATA drives" + config ATA_DISK bool "disk drives" help diff --git a/drivers/ata/disk_drive.c b/drivers/ata/disk_drive.c index a54429a5..23837691 100644 --- a/drivers/ata/disk_drive.c +++ b/drivers/ata/disk_drive.c @@ -36,6 +36,8 @@ #include <string.h> #include <linux/kernel.h> #include <malloc.h> +#include <common.h> +#include <block.h> /** * Description of one partition table entry (D*S type) @@ -58,6 +60,7 @@ struct partition_entry { * @param table partition table * @return size in sectors */ +#ifdef CONFIG_ATA_BIOS static unsigned long disk_guess_size(struct device_d *dev, struct partition_entry *table) { int part_order[4] = {0, 1, 2, 3}; @@ -81,6 +84,7 @@ static unsigned long disk_guess_size(struct device_d *dev, struct partition_entr #endif return size; } +#endif /** * Register partitions found on the drive @@ -121,149 +125,35 @@ static int disk_register_partitions(struct device_d *dev, struct partition_entry return 0; } -/** - * Write some data to a disk - * @param cdev the device to write to - * @param _buf source of data - * @param count byte count to write - * @param offset where to write to disk - * @param flags Ignored - * @return Written bytes or negative value in case of failure - */ -static ssize_t disk_write(struct cdev *cdev, const void *_buf, size_t count, ulong offset, ulong flags) -{ - struct device_d *dev = cdev->dev; - struct ata_interface *intf = dev->platform_data; - int rc; - unsigned sep_count = offset & (SECTOR_SIZE - 1); - ssize_t written = 0; - - /* starting at no sector boundary? */ - if (sep_count != 0) { - uint8_t tmp_buf[SECTOR_SIZE]; - unsigned to_write = min(SECTOR_SIZE - sep_count, count); - - rc = intf->read(dev, offset / SECTOR_SIZE, 1, tmp_buf); - if (rc != 0) { - dev_err(dev, "Cannot read data\n"); - return -1; - } - memcpy(&tmp_buf[sep_count], _buf, to_write); - rc = intf->write(dev, offset / SECTOR_SIZE, 1, tmp_buf); - if (rc != 0) { - dev_err(dev, "Cannot write data\n"); - return -1; - } - - _buf += to_write; - offset += to_write; - count -= to_write; - written += to_write; - } - - /* full sector part */ - sep_count = count / SECTOR_SIZE; - if (sep_count) { - rc = intf->write(dev, offset / SECTOR_SIZE, sep_count, _buf); - if (rc != 0) { - dev_err(dev, "Cannot write data\n"); - return -1; - } - _buf += sep_count * SECTOR_SIZE; - offset += sep_count * SECTOR_SIZE; - count -= sep_count * SECTOR_SIZE; - written += sep_count * SECTOR_SIZE; - } - - /* ending at no sector boundary? */ - if (count) { - uint8_t tmp_buf[SECTOR_SIZE]; +struct ata_block_device { + struct block_device blk; + struct device_d *dev; + struct ata_interface *intf; +}; - rc = intf->read(dev, offset / SECTOR_SIZE, 1, tmp_buf); - if (rc != 0) { - dev_err(dev, "Cannot read data\n"); - return -1; - } - memcpy(tmp_buf, _buf, count); - rc = intf->write(dev, offset / SECTOR_SIZE, 1, tmp_buf); - if (rc != 0) { - dev_err(dev, "Cannot write data\n"); - return -1; - } - written += count; - } +static int atablk_read(struct block_device *blk, void *buf, int block, + int num_blocks) +{ + struct ata_block_device *atablk = container_of(blk, struct ata_block_device, blk); - return written; + return atablk->intf->read(atablk->dev, block, num_blocks, buf); } -/** - * Read some data from a disk - * @param cdev the device to read from - * @param _buf destination of the data - * @param count byte count to read - * @param offset where to read from - * @param flags Ignored - * @return Read bytes or negative value in case of failure - */ -static ssize_t disk_read(struct cdev *cdev, void *_buf, size_t count, ulong offset, ulong flags) +#ifdef CONFIG_ATA_WRITE +static int atablk_write(struct block_device *blk, const void *buf, int block, + int num_blocks) { - struct device_d *dev = cdev->dev; - struct ata_interface *intf = dev->platform_data; - int rc; - unsigned sep_count = offset & (SECTOR_SIZE - 1); - ssize_t read = 0; - - /* starting at no sector boundary? */ - if (sep_count != 0) { - uint8_t tmp_buf[SECTOR_SIZE]; - unsigned to_read = min(SECTOR_SIZE - sep_count, count); + struct ata_block_device *atablk = container_of(blk, struct ata_block_device, blk); - rc = intf->read(dev, offset / SECTOR_SIZE, 1, tmp_buf); - if (rc != 0) { - dev_err(dev, "Cannot read data\n"); - return -1; - } - memcpy(_buf, &tmp_buf[sep_count], to_read); - _buf += to_read; - offset += to_read; - count -= to_read; - read += to_read; - } - - /* full sector part */ - sep_count = count / SECTOR_SIZE; - if (sep_count) { - rc = intf->read(dev, offset / SECTOR_SIZE, sep_count, _buf); - if (rc != 0) { - dev_err(dev, "Cannot read data\n"); - return -1; - } - _buf += sep_count * SECTOR_SIZE; - offset += sep_count * SECTOR_SIZE; - count -= sep_count * SECTOR_SIZE; - read += sep_count * SECTOR_SIZE; - } - - /* ending at no sector boundary? */ - if (count) { - uint8_t tmp_buf[SECTOR_SIZE]; - - rc = intf->read(dev, offset / SECTOR_SIZE, 1, tmp_buf); - if (rc != 0) { - dev_err(dev, "Cannot read data\n"); - return -1; - } - memcpy(_buf, tmp_buf, count); - read += count; - } - - return read; + return atablk->intf->write(atablk->dev, block, num_blocks, buf); } +#endif -static struct file_operations disk_ops = { - .read = disk_read, - .write = disk_write, - .lseek = dev_lseek_default, +static struct block_device_ops ataops = { + .read = atablk_read, +#ifdef CONFIG_ATA_WRITE + .write = atablk_write, +#endif }; /** @@ -274,7 +164,7 @@ static int disk_probe(struct device_d *dev) uint8_t *sector; int rc; struct ata_interface *intf = dev->platform_data; - struct cdev *disk_cdev; + struct ata_block_device *atablk = xzalloc(sizeof(*atablk)); sector = xmalloc(SECTOR_SIZE); @@ -285,9 +175,6 @@ static int disk_probe(struct device_d *dev) goto on_error; } - /* It seems a valuable disk. Register it */ - disk_cdev = xzalloc(sizeof(struct cdev)); - /* * BIOS based disks needs special handling. Not the driver can * enumerate the hardware, the BIOS did it already. To show the user @@ -296,23 +183,26 @@ static int disk_probe(struct device_d *dev) */ #ifdef CONFIG_ATA_BIOS if (strcmp(dev->driver->name, "biosdisk") == 0) - disk_cdev->name = asprintf("biosdisk%d", dev->id); + atablk->blk.cdev.name = asprintf("biosdisk%d", dev->id); else #endif - disk_cdev->name = asprintf("disk%d", dev->id); + atablk->blk.cdev.name = asprintf("disk%d", dev->id); +#ifdef CONFIG_ATA_BIOS /* On x86, BIOS based disks are coming without a valid .size field */ if (dev->size == 0) { - /* - * We need always the size of the drive, else its nearly impossible - * to do anything with it (at least with the generic routines) - */ - disk_cdev->size = 32; - } else - disk_cdev->size = dev->size; - disk_cdev->ops = &disk_ops; - disk_cdev->dev = dev; - devfs_create(disk_cdev); + /* guess the size of this drive if not otherwise given */ + dev->size = disk_guess_size(dev, + (struct partition_entry*)§or[446]) * SECTOR_SIZE; + dev_info(dev, "Drive size guessed to %u kiB\n", dev->size / 1024); + } +#endif + atablk->blk.num_blocks = dev->size / SECTOR_SIZE; + atablk->blk.ops = &ataops; + atablk->blk.blockbits = 9; + atablk->dev = dev; + atablk->intf = intf; + blockdevice_register(&atablk->blk); if ((sector[510] != 0x55) || (sector[511] != 0xAA)) { dev_info(dev, "No partition table found\n"); @@ -320,13 +210,6 @@ static int disk_probe(struct device_d *dev) goto on_error; } - if (dev->size == 0) { - /* guess the size of this drive if not otherwise given */ - dev->size = disk_guess_size(dev, - (struct partition_entry*)§or[446]) * SECTOR_SIZE; - dev_info(dev, "Drive size guessed to %u kiB\n", dev->size / 1024); - disk_cdev->size = dev->size; - } rc = disk_register_partitions(dev, (struct partition_entry*)§or[446]); diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig index b11f2670..5d8adbdf 100644 --- a/drivers/mci/Kconfig +++ b/drivers/mci/Kconfig @@ -25,6 +25,11 @@ config MCI_INFO This entry adds more info about the attached MCI card, when the 'devinfo' command is used on the mci device. +config MCI_WRITE + bool "Support writing to MCI cards" + default y + select ATA_WRITE + comment "--- MCI host drivers ---" config MCI_MXS @@ -61,4 +66,11 @@ config MCI_IMX_ESDHC_PIO help mostly useful for debugging. Normally you should use DMA. +config MCI_OMAP_HSMMC + bool "OMAP HSMMC" + depends on ARCH_OMAP4 || ARCH_OMAP3 + help + Enable this entry to add support to read and write SD cards on a + OMAP4 based system. + endif diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile index f175bbac..2bb9a93b 100644 --- a/drivers/mci/Makefile +++ b/drivers/mci/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_MCI_MXS) += mxs.o obj-$(CONFIG_MCI_S3C) += s3c.o obj-$(CONFIG_MCI_IMX) += imx.o obj-$(CONFIG_MCI_IMX_ESDHC) += imx-esdhc.o +obj-$(CONFIG_MCI_OMAP_HSMMC) += omap_hsmmc.o diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c index f3d975d6..a06c997e 100644 --- a/drivers/mci/mci-core.c +++ b/drivers/mci/mci-core.c @@ -106,6 +106,7 @@ static void *sector_buf; * @param blocknum Block number to write * @return Transaction status (0 on success) */ +#ifdef CONFIG_MCI_WRITE static int mci_block_write(struct device_d *mci_dev, const void *src, unsigned blocknum) { struct mci *mci = GET_MCI_DATA(mci_dev); @@ -132,6 +133,7 @@ static int mci_block_write(struct device_d *mci_dev, const void *src, unsigned b return mci_send_cmd(mci_dev, &cmd, &data); } +#endif /** * Read one block of data from the card @@ -597,6 +599,7 @@ static void mci_detect_version_from_csd(struct device_d *mci_dev) { struct mci *mci = GET_MCI_DATA(mci_dev); int version; + char *vstr; if (mci->version == MMC_VERSION_UNKNOWN) { /* the version is coded in the bits 127:126 (left aligned) */ @@ -604,30 +607,31 @@ static void mci_detect_version_from_csd(struct device_d *mci_dev) switch (version) { case 0: - printf("Detecting a 1.2 revision card\n"); + vstr = "1.2"; mci->version = MMC_VERSION_1_2; break; case 1: - printf("Detecting a 1.4 revision card\n"); + vstr = "1.4"; mci->version = MMC_VERSION_1_4; break; case 2: - printf("Detecting a 2.2 revision card\n"); + vstr = "2.2"; mci->version = MMC_VERSION_2_2; break; case 3: - printf("Detecting a 3.0 revision card\n"); + vstr = "3.0"; mci->version = MMC_VERSION_3; break; case 4: - printf("Detecting a 4.0 revision card\n"); + vstr = "4.0"; mci->version = MMC_VERSION_4; break; default: - printf("Unknow revision. Falling back to a 1.2 revision card\n"); + vstr = "unknown, fallback to 1.2"; mci->version = MMC_VERSION_1_2; break; } + printf("detected card version %s\n", vstr); } } @@ -680,7 +684,7 @@ static void mci_extract_max_tran_speed_from_csd(struct device_d *mci_dev) unit = tran_speed_unit[(mci->csd[0] & 0x7)]; time = tran_speed_time[((mci->csd[0] >> 3) & 0xf)]; if ((unit == 0) || (time == 0)) { - pr_warning("Unsupported 'TRAN_SPEED' unit/time value." + pr_debug("Unsupported 'TRAN_SPEED' unit/time value." " Can't calculate card's max. transfer speed\n"); return; } @@ -798,13 +802,13 @@ static int mci_startup(struct device_d *mci_dev) /* sanitiy? */ if (mci->read_bl_len > 512) { mci->read_bl_len = 512; - pr_warning("Limiting max. read block size down to %u\n", + pr_debug("Limiting max. read block size down to %u\n", mci->read_bl_len); } if (mci->write_bl_len > 512) { mci->write_bl_len = 512; - pr_warning("Limiting max. write block size down to %u\n", + pr_debug("Limiting max. write block size down to %u\n", mci->read_bl_len); } pr_debug("Read block length: %u, Write block length: %u\n", @@ -944,6 +948,7 @@ static int sd_send_if_cond(struct device_d *mci_dev) * * This routine expects the buffer has the correct size to read all data! */ +#ifdef CONFIG_MCI_WRITE static int mci_sd_write(struct device_d *disk_dev, uint64_t sector_start, unsigned sector_count, const void *buffer) { @@ -952,11 +957,11 @@ static int mci_sd_write(struct device_d *disk_dev, uint64_t sector_start, struct mci *mci = GET_MCI_DATA(mci_dev); int rc; - pr_debug("%s called: Write %u block(s), starting at %lu", - __func__, sector_count, (unsigned)sector_count); + pr_debug("%s: Write %u block(s), starting at %u", + __func__, sector_count, (unsigned)sector_start); if (mci->write_bl_len != 512) { - pr_warning("MMC/SD block size is not 512 bytes (its %u bytes instead)\n", + pr_debug("MMC/SD block size is not 512 bytes (its %u bytes instead)\n", mci->read_bl_len); return -EINVAL; } @@ -964,13 +969,13 @@ static int mci_sd_write(struct device_d *disk_dev, uint64_t sector_start, while (sector_count) { /* size of the block number field in the MMC/SD command is 32 bit only */ if (sector_start > MAX_BUFFER_NUMBER) { - pr_err("Cannot handle block number %llu. Too large!\n", + pr_debug("Cannot handle block number %llu. Too large!\n", sector_start); return -EINVAL; } rc = mci_block_write(mci_dev, buffer, sector_start); if (rc != 0) { - pr_err("Writing block %u failed with %d\n", (unsigned)sector_start, rc); + pr_debug("Writing block %u failed with %d\n", (unsigned)sector_start, rc); return rc; } sector_count--; @@ -980,6 +985,7 @@ static int mci_sd_write(struct device_d *disk_dev, uint64_t sector_start, return 0; } +#endif /** * Read a chunk of sectors from media @@ -999,11 +1005,11 @@ static int mci_sd_read(struct device_d *disk_dev, uint64_t sector_start, struct mci *mci = GET_MCI_DATA(mci_dev); int rc; - pr_debug("%s called: Read %u block(s), starting at %lu to %08X\n", - __func__, sector_count, (unsigned)sector_start, buffer); + pr_debug("%s: Read %u block(s), starting at %u\n", + __func__, sector_count, (unsigned)sector_start); if (mci->read_bl_len != 512) { - pr_warning("MMC/SD block size is not 512 bytes (its %u bytes instead)\n", + pr_debug("MMC/SD block size is not 512 bytes (its %u bytes instead)\n", mci->read_bl_len); return -EINVAL; } @@ -1017,7 +1023,7 @@ static int mci_sd_read(struct device_d *disk_dev, uint64_t sector_start, } rc = mci_read_block(mci_dev, buffer, (unsigned)sector_start, now); if (rc != 0) { - pr_err("Reading block %u failed with %d\n", (unsigned)sector_start, rc); + pr_debug("Reading block %u failed with %d\n", (unsigned)sector_start, rc); return rc; } sector_count -= now; @@ -1186,7 +1192,7 @@ static int mci_card_probe(struct device_d *mci_dev) /* reset the card */ rc = mci_go_idle(mci_dev); if (rc) { - pr_warning("Cannot reset the SD/MMC card\n"); + pr_warn("Cannot reset the SD/MMC card\n"); goto on_error; } @@ -1204,7 +1210,7 @@ static int mci_card_probe(struct device_d *mci_dev) rc = mci_startup(mci_dev); if (rc) { - printf("Card's startup fails with %d\n", rc); + pr_debug("Card's startup fails with %d\n", rc); goto on_error; } @@ -1218,7 +1224,9 @@ static int mci_card_probe(struct device_d *mci_dev) disk_dev = xzalloc(sizeof(struct device_d) + sizeof(struct ata_interface)); p = (struct ata_interface*)&disk_dev[1]; +#ifdef CONFIG_MCI_WRITE p->write = mci_sd_write; +#endif p->read = mci_sd_read; p->priv = mci_dev; @@ -1313,7 +1321,7 @@ static int mci_probe(struct device_d *mci_dev) */ rc = add_mci_parameter(mci_dev); if (rc != 0) { - pr_err("Failed to add 'probe' parameter to the MCI device\n"); + pr_debug("Failed to add 'probe' parameter to the MCI device\n"); goto on_error; } } @@ -1323,7 +1331,7 @@ static int mci_probe(struct device_d *mci_dev) /* add params on demand */ rc = add_mci_parameter(mci_dev); if (rc != 0) { - pr_err("Failed to add 'probe' parameter to the MCI device\n"); + pr_debug("Failed to add 'probe' parameter to the MCI device\n"); goto on_error; } #endif @@ -1338,7 +1346,9 @@ on_error: static struct driver_d mci_driver = { .name = "mci", .probe = mci_probe, +#ifdef CONFIG_MCI_INFO .info = mci_info, +#endif }; static int mci_init(void) diff --git a/drivers/mci/omap_hsmmc.c b/drivers/mci/omap_hsmmc.c new file mode 100644 index 00000000..07992978 --- /dev/null +++ b/drivers/mci/omap_hsmmc.c @@ -0,0 +1,585 @@ +/* + * (C) Copyright 2008 + * Texas Instruments, <www.ti.com> + * Sukumar Ghorai <s-ghorai@ti.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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's version 2 of + * the License. + * + * 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 + */ +/* #define DEBUG */ +#include <config.h> +#include <common.h> +#include <init.h> +#include <driver.h> +#include <mci.h> +#include <clock.h> +#include <errno.h> +#include <asm/io.h> + +struct hsmmc { + unsigned char res1[0x10]; + unsigned int sysconfig; /* 0x10 */ + unsigned int sysstatus; /* 0x14 */ + unsigned char res2[0x14]; + unsigned int con; /* 0x2C */ + unsigned char res3[0xD4]; + unsigned int blk; /* 0x104 */ + unsigned int arg; /* 0x108 */ + unsigned int cmd; /* 0x10C */ + unsigned int rsp10; /* 0x110 */ + unsigned int rsp32; /* 0x114 */ + unsigned int rsp54; /* 0x118 */ + unsigned int rsp76; /* 0x11C */ + unsigned int data; /* 0x120 */ + unsigned int pstate; /* 0x124 */ + unsigned int hctl; /* 0x128 */ + unsigned int sysctl; /* 0x12C */ + unsigned int stat; /* 0x130 */ + unsigned int ie; /* 0x134 */ + unsigned char res4[0x8]; + unsigned int capa; /* 0x140 */ +}; + +/* + * OMAP HS MMC Bit definitions + */ +#define MMC_SOFTRESET (0x1 << 1) +#define RESETDONE (0x1 << 0) +#define NOOPENDRAIN (0x0 << 0) +#define OPENDRAIN (0x1 << 0) +#define OD (0x1 << 0) +#define INIT_NOINIT (0x0 << 1) +#define INIT_INITSTREAM (0x1 << 1) +#define HR_NOHOSTRESP (0x0 << 2) +#define STR_BLOCK (0x0 << 3) +#define MODE_FUNC (0x0 << 4) +#define DW8_1_4BITMODE (0x0 << 5) +#define MIT_CTO (0x0 << 6) +#define CDP_ACTIVEHIGH (0x0 << 7) +#define WPP_ACTIVEHIGH (0x0 << 8) +#define RESERVED_MASK (0x3 << 9) +#define CTPL_MMC_SD (0x0 << 11) +#define BLEN_512BYTESLEN (0x200 << 0) +#define NBLK_STPCNT (0x0 << 16) +#define DE_DISABLE (0x0 << 0) +#define BCE_DISABLE (0x0 << 1) +#define BCE_ENABLE (0x1 << 1) +#define ACEN_DISABLE (0x0 << 2) +#define DDIR_OFFSET (4) +#define DDIR_MASK (0x1 << 4) +#define DDIR_WRITE (0x0 << 4) +#define DDIR_READ (0x1 << 4) +#define MSBS_SGLEBLK (0x0 << 5) +#define MSBS_MULTIBLK (0x1 << 5) +#define RSP_TYPE_OFFSET (16) +#define RSP_TYPE_MASK (0x3 << 16) +#define RSP_TYPE_NORSP (0x0 << 16) +#define RSP_TYPE_LGHT136 (0x1 << 16) +#define RSP_TYPE_LGHT48 (0x2 << 16) +#define RSP_TYPE_LGHT48B (0x3 << 16) +#define CCCE_NOCHECK (0x0 << 19) +#define CCCE_CHECK (0x1 << 19) +#define CICE_NOCHECK (0x0 << 20) +#define CICE_CHECK (0x1 << 20) +#define DP_OFFSET (21) +#define DP_MASK (0x1 << 21) +#define DP_NO_DATA (0x0 << 21) +#define DP_DATA (0x1 << 21) +#define CMD_TYPE_NORMAL (0x0 << 22) +#define INDEX_OFFSET (24) +#define INDEX_MASK (0x3f << 24) +#define INDEX(i) (i << 24) +#define DATI_MASK (0x1 << 1) +#define DATI_CMDDIS (0x1 << 1) +#define DTW_1_BITMODE (0x0 << 1) +#define DTW_4_BITMODE (0x1 << 1) +#define DTW_8_BITMODE (0x1 << 5) /* CON[DW8]*/ +#define SDBP_PWROFF (0x0 << 8) +#define SDBP_PWRON (0x1 << 8) +#define SDVS_1V8 (0x5 << 9) +#define SDVS_3V0 (0x6 << 9) +#define ICE_MASK (0x1 << 0) +#define ICE_STOP (0x0 << 0) +#define ICS_MASK (0x1 << 1) +#define ICS_NOTREADY (0x0 << 1) +#define ICE_OSCILLATE (0x1 << 0) +#define CEN_MASK (0x1 << 2) +#define CEN_DISABLE (0x0 << 2) +#define CEN_ENABLE (0x1 << 2) +#define CLKD_OFFSET (6) +#define CLKD_MASK (0x3FF << 6) +#define DTO_MASK (0xF << 16) +#define DTO_15THDTO (0xE << 16) +#define SOFTRESETALL (0x1 << 24) +#define CC_MASK (0x1 << 0) +#define TC_MASK (0x1 << 1) +#define BWR_MASK (0x1 << 4) +#define BRR_MASK (0x1 << 5) +#define ERRI_MASK (0x1 << 15) +#define IE_CC (0x01 << 0) +#define IE_TC (0x01 << 1) +#define IE_BWR (0x01 << 4) +#define IE_BRR (0x01 << 5) +#define IE_CTO (0x01 << 16) +#define IE_CCRC (0x01 << 17) +#define IE_CEB (0x01 << 18) +#define IE_CIE (0x01 << 19) +#define IE_DTO (0x01 << 20) +#define IE_DCRC (0x01 << 21) +#define IE_DEB (0x01 << 22) +#define IE_CERR (0x01 << 28) +#define IE_BADA (0x01 << 29) + +#define VS30_3V0SUP (1 << 25) +#define VS18_1V8SUP (1 << 26) + +/* Driver definitions */ +#define MMCSD_SECTOR_SIZE 512 +#define MMC_CARD 0 +#define SD_CARD 1 +#define BYTE_MODE 0 +#define SECTOR_MODE 1 +#define CLK_INITSEQ 0 +#define CLK_400KHZ 1 +#define CLK_MISC 2 + +#define RSP_TYPE_NONE (RSP_TYPE_NORSP | CCCE_NOCHECK | CICE_NOCHECK) +#define MMC_CMD0 (INDEX(0) | RSP_TYPE_NONE | DP_NO_DATA | DDIR_WRITE) + +/* Clock Configurations and Macros */ +#define MMC_CLOCK_REFERENCE 96 /* MHz */ + +#define mmc_reg_out(addr, mask, val)\ + writel((readl(addr) & (~(mask))) | ((val) & (mask)), (addr)) + +struct omap_hsmmc { + struct mci_host mci; + struct device_d *dev; + struct hsmmc *base; +}; + +#define to_hsmmc(mci) container_of(mci, struct omap_hsmmc, mci) + +static int mmc_init_stream(struct omap_hsmmc *hsmmc) +{ + uint64_t start; + struct hsmmc *mmc_base = hsmmc->base; + + writel(readl(&mmc_base->con) | INIT_INITSTREAM, &mmc_base->con); + + writel(MMC_CMD0, &mmc_base->cmd); + start = get_time_ns(); + while (!(readl(&mmc_base->stat) & CC_MASK)) { + if (is_timeout(start, SECOND)) { + dev_dbg(hsmmc->dev, "timedout waiting for cc!\n"); + return -ETIMEDOUT; + } + } + writel(CC_MASK, &mmc_base->stat); + writel(MMC_CMD0, &mmc_base->cmd); + + start = get_time_ns(); + while (!(readl(&mmc_base->stat) & CC_MASK)) { + if (is_timeout(start, SECOND)) { + dev_dbg(hsmmc->dev, "timedout waiting for cc2!\n"); + return -ETIMEDOUT; + } + } + writel(readl(&mmc_base->con) & ~INIT_INITSTREAM, &mmc_base->con); + + return 0; +} + +static int mmc_init_setup(struct mci_host *mci, struct device_d *dev) +{ + struct omap_hsmmc *hsmmc = to_hsmmc(mci); + struct hsmmc *mmc_base = hsmmc->base; + unsigned int reg_val; + unsigned int dsor; + uint64_t start; + + writel(readl(&mmc_base->sysconfig) | MMC_SOFTRESET, + &mmc_base->sysconfig); + + start = get_time_ns(); + while ((readl(&mmc_base->sysstatus) & RESETDONE) == 0) { + if (is_timeout(start, SECOND)) { + dev_dbg(hsmmc->dev, "timedout waiting for cc2!\n"); + return -ETIMEDOUT; + } + } + + writel(readl(&mmc_base->sysctl) | SOFTRESETALL, &mmc_base->sysctl); + + start = get_time_ns(); + while ((readl(&mmc_base->sysctl) & SOFTRESETALL) != 0x0) { + if (is_timeout(start, SECOND)) { + dev_dbg(hsmmc->dev, "timedout waiting for softresetall!\n"); + return -ETIMEDOUT; + } + } + + writel(DTW_1_BITMODE | SDBP_PWROFF | SDVS_3V0, &mmc_base->hctl); + writel(readl(&mmc_base->capa) | VS30_3V0SUP | VS18_1V8SUP, + &mmc_base->capa); + + reg_val = readl(&mmc_base->con) & RESERVED_MASK; + + writel(CTPL_MMC_SD | reg_val | WPP_ACTIVEHIGH | CDP_ACTIVEHIGH | + MIT_CTO | DW8_1_4BITMODE | MODE_FUNC | STR_BLOCK | + HR_NOHOSTRESP | INIT_NOINIT | NOOPENDRAIN, &mmc_base->con); + + dsor = 240; + mmc_reg_out(&mmc_base->sysctl, (ICE_MASK | DTO_MASK | CEN_MASK), + (ICE_STOP | DTO_15THDTO | CEN_DISABLE)); + mmc_reg_out(&mmc_base->sysctl, ICE_MASK | CLKD_MASK, + (dsor << CLKD_OFFSET) | ICE_OSCILLATE); + + start = get_time_ns(); + while ((readl(&mmc_base->sysctl) & ICS_MASK) == ICS_NOTREADY) { + if (is_timeout(start, SECOND)) { + dev_dbg(hsmmc->dev, "timedout waiting for ics!\n"); + return -ETIMEDOUT; + } + } + + writel(readl(&mmc_base->sysctl) | CEN_ENABLE, &mmc_base->sysctl); + + writel(readl(&mmc_base->hctl) | SDBP_PWRON, &mmc_base->hctl); + + writel(IE_BADA | IE_CERR | IE_DEB | IE_DCRC | IE_DTO | IE_CIE | + IE_CEB | IE_CCRC | IE_CTO | IE_BRR | IE_BWR | IE_TC | IE_CC, + &mmc_base->ie); + + return mmc_init_stream(hsmmc); +} + +static int mmc_read_data(struct omap_hsmmc *hsmmc, char *buf, unsigned int size) +{ + struct hsmmc *mmc_base = hsmmc->base; + unsigned int *output_buf = (unsigned int *)buf; + unsigned int mmc_stat; + unsigned int count; + + /* + * Start Polled Read + */ + count = (size > MMCSD_SECTOR_SIZE) ? MMCSD_SECTOR_SIZE : size; + count /= 4; + + while (size) { + uint64_t start = get_time_ns(); + do { + mmc_stat = readl(&mmc_base->stat); + if (is_timeout(start, SECOND)) { + dev_dbg(hsmmc->dev, "timedout waiting for status!\n"); + return -ETIMEDOUT; + } + } while (mmc_stat == 0); + + if ((mmc_stat & ERRI_MASK) != 0) + return 1; + + if (mmc_stat & BRR_MASK) { + unsigned int k; + + writel(readl(&mmc_base->stat) | BRR_MASK, + &mmc_base->stat); + for (k = 0; k < count; k++) { + *output_buf = readl(&mmc_base->data); + output_buf++; + } + size -= (count*4); + } + + if (mmc_stat & BWR_MASK) + writel(readl(&mmc_base->stat) | BWR_MASK, + &mmc_base->stat); + + if (mmc_stat & TC_MASK) { + writel(readl(&mmc_base->stat) | TC_MASK, + &mmc_base->stat); + break; + } + } + return 0; +} + +#ifdef CONFIG_MCI_WRITE +static int mmc_write_data(struct omap_hsmmc *hsmmc, const char *buf, unsigned int size) +{ + struct hsmmc *mmc_base = hsmmc->base; + unsigned int *input_buf = (unsigned int *)buf; + unsigned int mmc_stat; + unsigned int count; + + /* + * Start Polled Read + */ + count = (size > MMCSD_SECTOR_SIZE) ? MMCSD_SECTOR_SIZE : size; + count /= 4; + + while (size) { + uint64_t start = get_time_ns(); + do { + mmc_stat = readl(&mmc_base->stat); + if (is_timeout(start, SECOND)) { + dev_dbg(hsmmc->dev, "timedout waiting for status!\n"); + return -ETIMEDOUT; + } + } while (mmc_stat == 0); + + if ((mmc_stat & ERRI_MASK) != 0) + return 1; + + if (mmc_stat & BWR_MASK) { + unsigned int k; + + writel(readl(&mmc_base->stat) | BWR_MASK, + &mmc_base->stat); + for (k = 0; k < count; k++) { + writel(*input_buf, &mmc_base->data); + input_buf++; + } + size -= (count * 4); + } + + if (mmc_stat & BRR_MASK) + writel(readl(&mmc_base->stat) | BRR_MASK, + &mmc_base->stat); + + if (mmc_stat & TC_MASK) { + writel(readl(&mmc_base->stat) | TC_MASK, + &mmc_base->stat); + break; + } + } + return 0; +} +#endif + +static int mmc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd, + struct mci_data *data) +{ + struct omap_hsmmc *hsmmc = to_hsmmc(mci); + struct hsmmc *mmc_base = hsmmc->base; + unsigned int flags, mmc_stat; + uint64_t start; + + start = get_time_ns(); + while ((readl(&mmc_base->pstate) & DATI_MASK) == DATI_CMDDIS) { + if (is_timeout(start, SECOND)) { + dev_dbg(hsmmc->dev, "timedout waiting for cmddis!\n"); + return -ETIMEDOUT; + } + } + + writel(0xFFFFFFFF, &mmc_base->stat); + start = get_time_ns(); + while (readl(&mmc_base->stat)) { + if (is_timeout(start, SECOND)) { + dev_dbg(hsmmc->dev, "timedout waiting for stat!\n"); + return -ETIMEDOUT; + } + } + + /* + * CMDREG + * CMDIDX[13:8] : Command index + * DATAPRNT[5] : Data Present Select + * ENCMDIDX[4] : Command Index Check Enable + * ENCMDCRC[3] : Command CRC Check Enable + * RSPTYP[1:0] + * 00 = No Response + * 01 = Length 136 + * 10 = Length 48 + * 11 = Length 48 Check busy after response + */ + /* Delay added before checking the status of frq change + * retry not supported by mmc.c(core file) + */ + if (cmd->cmdidx == SD_CMD_APP_SEND_SCR) + udelay(50000); /* wait 50 ms */ + + if (!(cmd->resp_type & MMC_RSP_PRESENT)) + flags = 0; + else if (cmd->resp_type & MMC_RSP_136) + flags = RSP_TYPE_LGHT136 | CICE_NOCHECK; + else if (cmd->resp_type & MMC_RSP_BUSY) + flags = RSP_TYPE_LGHT48B; + else + flags = RSP_TYPE_LGHT48; + + /* enable default flags */ + flags = flags | (CMD_TYPE_NORMAL | CICE_NOCHECK | CCCE_NOCHECK | + MSBS_SGLEBLK | ACEN_DISABLE | BCE_DISABLE | DE_DISABLE); + + if (cmd->resp_type & MMC_RSP_CRC) + flags |= CCCE_CHECK; + if (cmd->resp_type & MMC_RSP_OPCODE) + flags |= CICE_CHECK; + + if (data) { + if ((cmd->cmdidx == MMC_CMD_READ_MULTIPLE_BLOCK) || + (cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK)) { + flags |= (MSBS_MULTIBLK | BCE_ENABLE); + data->blocksize = 512; + writel(data->blocksize | (data->blocks << 16), + &mmc_base->blk); + } else + writel(data->blocksize | NBLK_STPCNT, &mmc_base->blk); + + if (data->flags & MMC_DATA_READ) + flags |= (DP_DATA | DDIR_READ); + else + flags |= (DP_DATA | DDIR_WRITE); + } + + writel(cmd->cmdarg, &mmc_base->arg); + writel((cmd->cmdidx << 24) | flags, &mmc_base->cmd); + + start = get_time_ns(); + do { + mmc_stat = readl(&mmc_base->stat); + if (is_timeout(start, SECOND)) { + dev_dbg(hsmmc->dev, "timeout: No status update\n"); + return -ETIMEDOUT; + } + } while (!mmc_stat); + + if ((mmc_stat & IE_CTO) != 0) + return -ETIMEDOUT; + else if ((mmc_stat & ERRI_MASK) != 0) + return -1; + + if (mmc_stat & CC_MASK) { + writel(CC_MASK, &mmc_base->stat); + if (cmd->resp_type & MMC_RSP_PRESENT) { + if (cmd->resp_type & MMC_RSP_136) { + /* response type 2 */ + cmd->response[3] = readl(&mmc_base->rsp10); + cmd->response[2] = readl(&mmc_base->rsp32); + cmd->response[1] = readl(&mmc_base->rsp54); + cmd->response[0] = readl(&mmc_base->rsp76); + } else + /* response types 1, 1b, 3, 4, 5, 6 */ + cmd->response[0] = readl(&mmc_base->rsp10); + } + } + + if (data && (data->flags & MMC_DATA_READ)) + mmc_read_data(hsmmc, data->dest, data->blocksize * data->blocks); +#ifdef CONFIG_MCI_WRITE + else if (data && (data->flags & MMC_DATA_WRITE)) + mmc_write_data(hsmmc, data->src, data->blocksize * data->blocks); +#endif + return 0; +} + +static void mmc_set_ios(struct mci_host *mci, struct device_d *dev, + unsigned bus_width, unsigned clock) +{ + struct omap_hsmmc *hsmmc = to_hsmmc(mci); + struct hsmmc *mmc_base = hsmmc->base; + unsigned int dsor = 0; + uint64_t start; + + /* configue bus width */ + switch (bus_width) { + case 8: + writel(readl(&mmc_base->con) | DTW_8_BITMODE, + &mmc_base->con); + break; + + case 4: + writel(readl(&mmc_base->con) & ~DTW_8_BITMODE, + &mmc_base->con); + writel(readl(&mmc_base->hctl) | DTW_4_BITMODE, + &mmc_base->hctl); + break; + + case 1: + default: + writel(readl(&mmc_base->con) & ~DTW_8_BITMODE, + &mmc_base->con); + writel(readl(&mmc_base->hctl) & ~DTW_4_BITMODE, + &mmc_base->hctl); + break; + } + + /* configure clock with 96Mhz system clock. + */ + if (clock != 0) { + dsor = (MMC_CLOCK_REFERENCE * 1000000 / clock); + if ((MMC_CLOCK_REFERENCE * 1000000) / dsor > clock) + dsor++; + } + + mmc_reg_out(&mmc_base->sysctl, (ICE_MASK | DTO_MASK | CEN_MASK), + (ICE_STOP | DTO_15THDTO | CEN_DISABLE)); + + mmc_reg_out(&mmc_base->sysctl, ICE_MASK | CLKD_MASK, + (dsor << CLKD_OFFSET) | ICE_OSCILLATE); + + start = get_time_ns(); + while ((readl(&mmc_base->sysctl) & ICS_MASK) == ICS_NOTREADY) { + if (is_timeout(start, SECOND)) { + dev_dbg(hsmmc->dev, "timedout waiting for ics!\n"); + return; + } + } + writel(readl(&mmc_base->sysctl) | CEN_ENABLE, &mmc_base->sysctl); +} + +static int omap_mmc_probe(struct device_d *dev) +{ + struct omap_hsmmc *hsmmc; + + hsmmc = xzalloc(sizeof(*hsmmc)); + + hsmmc->dev = dev; + hsmmc->mci.send_cmd = mmc_send_cmd; + hsmmc->mci.set_ios = mmc_set_ios; + hsmmc->mci.init = mmc_init_setup; + hsmmc->mci.host_caps = MMC_MODE_4BIT | MMC_MODE_HS_52MHz | MMC_MODE_HS; + + hsmmc->base = (struct hsmmc *)dev->map_base; + + hsmmc->mci.voltages = MMC_VDD_32_33 | MMC_VDD_33_34; + + hsmmc->mci.f_min = 400000; + hsmmc->mci.f_max = 52000000; + + mci_register(&hsmmc->mci); + + return 0; +} + +static struct driver_d omap_mmc_driver = { + .name = "omap-hsmmc", + .probe = omap_mmc_probe, +}; + +static int omap_mmc_init_driver(void) +{ + register_driver(&omap_mmc_driver); + return 0; +} + +device_initcall(omap_mmc_init_driver); + diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 12e1237e..126f7cc7 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -8,6 +8,52 @@ menuconfig NAND if NAND +config NAND_WRITE + bool + default y + prompt "Support writing to Nand" + +config NAND_ECC_SOFT + bool + default y + prompt "Support software ecc" + +config NAND_ECC_HW + bool + default y + prompt "Support hardware ecc" + +config NAND_ECC_HW_SYNDROME + bool + default y + prompt "Support syndrome hardware ecc controllers" + +config NAND_ECC_HW_NONE + bool + default y + prompt "Support skipping ecc support" + +config NAND_INFO + bool + default y + prompt "Nand vendor/size information" + help + Show informational strings about the vendor and nand flash type + during startup + +config NAND_BBT + bool + default y + prompt "support bad block tables" + help + Say y here to include support for bad block tables. This speeds + up the process of checking for bad blocks + +config NAND_READ_OOB + bool + default y + prompt "create a device for reading the OOB data" + config NAND_IMX bool prompt "i.MX NAND driver" @@ -15,7 +61,7 @@ config NAND_IMX config NAND_OMAP_GPMC tristate "NAND Flash Support for GPMC based OMAP platforms" - depends on ((ARCH_OMAP2 || ARCH_OMAP3) && GPMC) + depends on OMAP_GPMC help Support for NAND flash using GPMC. GPMC is a common memory interface found on Texas Instrument's OMAP platforms diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index bac39e75..149cbbfa 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -1,13 +1,17 @@ # Generic NAND options obj-$(CONFIG_NAND) += nand.o nand_ecc.o +obj-$(CONFIG_NAND_WRITE) += nand_write.o +obj-$(CONFIG_NAND_ECC_SOFT) += nand_ecc.o nand_swecc.o +obj-$(CONFIG_NAND_ECC_HW) += nand_hwecc.o +obj-$(CONFIG_NAND_ECC_HW_SYNDROME) += nand_hwecc_syndrome.o obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o -obj-$(CONFIG_NAND) += nand_base.o nand_bbt.o +obj-$(CONFIG_NAND) += nand_base.o nand-bb.o +obj-$(CONFIG_NAND_BBT) += nand_bbt.o obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o obj-$(CONFIG_NAND_IMX) += nand_imx.o -obj-$(CONFIG_NAND_OMAP_GPMC) += nand_omap_gpmc.o +obj-$(CONFIG_NAND_OMAP_GPMC) += nand_omap_gpmc.o nand_omap_bch_decoder.o obj-$(CONFIG_NAND_ATMEL) += atmel_nand.o obj-$(CONFIG_NAND_S3C24X0) += nand_s3c2410.o -#obj-$(CONFIG_NAND) += nand_util.o diff --git a/drivers/mtd/nand/nand-bb.c b/drivers/mtd/nand/nand-bb.c new file mode 100644 index 00000000..dbfb8e38 --- /dev/null +++ b/drivers/mtd/nand/nand-bb.c @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 <common.h> +#include <command.h> +#include <fs.h> +#include <linux/stat.h> +#include <errno.h> +#include <malloc.h> +#include <getopt.h> +#include <xfuncs.h> +#include <init.h> +#include <ioctl.h> +#include <nand.h> +#include <linux/mtd/mtd-abi.h> +#include <fcntl.h> +#include <libgen.h> +#include <linux/list.h> + +struct nand_bb { + char cdevname[MAX_DRIVER_NAME]; + struct cdev *cdev_parent; + char *name; + int open; + int needs_write; + + struct mtd_info_user info; + + size_t raw_size; + size_t size; + off_t offset; + void *writebuf; + + struct cdev cdev; + + struct list_head list; +}; + +static ssize_t nand_bb_read(struct cdev *cdev, void *buf, size_t count, + unsigned long offset, ulong flags) +{ + struct nand_bb *bb = cdev->priv; + struct cdev *parent = bb->cdev_parent; + int ret, bytes = 0, now; + + debug("%s %d %d\n", __func__, offset, count); + + while(count) { + ret = cdev_ioctl(parent, MEMGETBADBLOCK, (void *)bb->offset); + if (ret < 0) + return ret; + + if (ret) { + printf("skipping bad block at 0x%08lx\n", bb->offset); + bb->offset += bb->info.erasesize; + continue; + } + + now = min(count, (size_t)(bb->info.erasesize - + (bb->offset % bb->info.erasesize))); + ret = cdev_read(parent, buf, now, bb->offset, 0); + if (ret < 0) + return ret; + buf += now; + count -= now; + bb->offset += now; + bytes += now; + }; + + return bytes; +} + +/* Must be a multiple of the largest NAND page size */ +#define BB_WRITEBUF_SIZE 4096 + +#ifdef CONFIG_NAND_WRITE +static int nand_bb_write_buf(struct nand_bb *bb, size_t count) +{ + int ret, now; + struct cdev *parent = bb->cdev_parent; + void *buf = bb->writebuf; + int cur_ofs = bb->offset & ~(BB_WRITEBUF_SIZE - 1); + + while (count) { + ret = cdev_ioctl(parent, MEMGETBADBLOCK, (void *)cur_ofs); + if (ret < 0) + return ret; + + if (ret) { + debug("skipping bad block at 0x%08x\n", cur_ofs); + bb->offset += bb->info.erasesize; + cur_ofs += bb->info.erasesize; + continue; + } + + now = min(count, (size_t)(bb->info.erasesize)); + ret = cdev_write(parent, buf, now, cur_ofs, 0); + if (ret < 0) + return ret; + buf += now; + count -= now; + cur_ofs += now; + }; + + return 0; +} + +static ssize_t nand_bb_write(struct cdev *cdev, const void *buf, size_t count, + unsigned long offset, ulong flags) +{ + struct nand_bb *bb = cdev->priv; + int bytes = count, now, wroffs, ret; + + debug("%s offset: 0x%08x count: 0x%08x\n", __func__, offset, count); + + while (count) { + wroffs = bb->offset % BB_WRITEBUF_SIZE; + now = min((int)count, BB_WRITEBUF_SIZE - wroffs); + memcpy(bb->writebuf + wroffs, buf, now); + + if (wroffs + now == BB_WRITEBUF_SIZE) { + bb->needs_write = 0; + ret = nand_bb_write_buf(bb, BB_WRITEBUF_SIZE); + if (ret) + return ret; + } else { + bb->needs_write = 1; + } + + bb->offset += now; + count -= now; + buf += now; + } + + return bytes; +} + +static int nand_bb_erase(struct cdev *cdev, size_t count, unsigned long offset) +{ + struct nand_bb *bb = cdev->priv; + + if (offset != 0) { + printf("can only erase from beginning of device\n"); + return -EINVAL; + } + + return cdev_erase(bb->cdev_parent, bb->raw_size, 0); +} +#endif + +static int nand_bb_open(struct cdev *cdev) +{ + struct nand_bb *bb = cdev->priv; + + if (bb->open) + return -EBUSY; + + bb->open = 1; + bb->offset = 0; + bb->needs_write = 0; + bb->writebuf = xmalloc(BB_WRITEBUF_SIZE); + + return 0; +} + +static int nand_bb_close(struct cdev *cdev) +{ + struct nand_bb *bb = cdev->priv; + +#ifdef CONFIG_NAND_WRITE + if (bb->needs_write) + nand_bb_write_buf(bb, bb->offset % BB_WRITEBUF_SIZE); +#endif + bb->open = 0; + free(bb->writebuf); + + return 0; +} + +static int nand_bb_calc_size(struct nand_bb *bb) +{ + ulong pos = 0; + int ret; + + while (pos < bb->raw_size) { + ret = cdev_ioctl(bb->cdev_parent, MEMGETBADBLOCK, (void *)pos); + if (ret < 0) + return ret; + if (!ret) + bb->cdev.size += bb->info.erasesize; + + pos += bb->info.erasesize; + } + + return 0; +} + +static struct file_operations nand_bb_ops = { + .open = nand_bb_open, + .close = nand_bb_close, + .read = nand_bb_read, +#ifdef CONFIG_NAND_WRITE + .write = nand_bb_write, + .erase = nand_bb_erase, +#endif +}; + +static LIST_HEAD(bb_list); + +/** + * Add a bad block aware device ontop of another (NAND) device + * @param[in] dev The device to add a partition on + * @param[in] name Partition name (can be obtained with devinfo command) + * @return The device representing the new partition. + */ +int dev_add_bb_dev(char *path, const char *name) +{ + struct nand_bb *bb; + int ret = -ENOMEM; + + bb = xzalloc(sizeof(*bb)); + + bb->cdev_parent = cdev_open(path, O_RDWR); + if (!bb->cdev_parent) + goto out1; + + if (name) { + strcpy(bb->cdevname, name); + } else { + strcpy(bb->cdevname, path); + strcat(bb->cdevname, ".bb"); + } + + bb->cdev.name = bb->cdevname; + + bb->raw_size = bb->cdev_parent->size; + + ret = cdev_ioctl(bb->cdev_parent, MEMGETINFO, &bb->info); + if (ret) + goto out4; + + nand_bb_calc_size(bb); + bb->cdev.ops = &nand_bb_ops; + bb->cdev.priv = bb; + + ret = devfs_create(&bb->cdev); + if (ret) + goto out4; + + list_add_tail(&bb->list, &bb_list); + + return 0; + +out4: + cdev_close(bb->cdev_parent); +out1: + free(bb); + return ret; +} + +int dev_remove_bb_dev(const char *name) +{ + struct nand_bb *bb; + + list_for_each_entry(bb, &bb_list, list) { + if (!strcmp(bb->cdev.name, name)) { + devfs_remove(&bb->cdev); + cdev_close(bb->cdev_parent); + free(bb); + return 0; + } + } + + return -ENODEV; +} diff --git a/drivers/mtd/nand/nand.c b/drivers/mtd/nand/nand.c index 08b5cc15..9423ac80 100644 --- a/drivers/mtd/nand/nand.c +++ b/drivers/mtd/nand/nand.c @@ -50,6 +50,7 @@ static ssize_t nand_read(struct cdev *cdev, void* buf, size_t count, ulong offs #define NOTALIGNED(x) (x & (info->writesize - 1)) != 0 +#ifdef CONFIG_NAND_WRITE static int all_ff(const void *buf, int len) { int i; @@ -104,6 +105,7 @@ static ssize_t nand_write(struct cdev* cdev, const void *buf, size_t _count, ulo out: return ret ? ret : _count; } +#endif static int nand_ioctl(struct cdev *cdev, int request, void *buf) { @@ -114,9 +116,11 @@ static int nand_ioctl(struct cdev *cdev, int request, void *buf) case MEMGETBADBLOCK: debug("MEMGETBADBLOCK: 0x%08lx\n", (off_t)buf); return info->block_isbad(info, (off_t)buf); +#ifdef CONFIG_NAND_WRITE case MEMSETBADBLOCK: debug("MEMSETBADBLOCK: 0x%08lx\n", (off_t)buf); return info->block_markbad(info, (off_t)buf); +#endif case MEMGETINFO: user->type = info->type; user->flags = info->flags; @@ -133,6 +137,7 @@ static int nand_ioctl(struct cdev *cdev, int request, void *buf) return 0; } +#ifdef CONFIG_NAND_WRITE static ssize_t nand_erase(struct cdev *cdev, size_t count, unsigned long offset) { struct mtd_info *info = cdev->priv; @@ -162,6 +167,8 @@ static ssize_t nand_erase(struct cdev *cdev, size_t count, unsigned long offset) return 0; } +#endif + #if 0 static char* mtd_get_size(struct device_d *, struct param_d *param) { @@ -171,12 +178,15 @@ static char* mtd_get_size(struct device_d *, struct param_d *param) static struct file_operations nand_ops = { .read = nand_read, +#ifdef CONFIG_NAND_WRITE .write = nand_write, + .erase = nand_erase, +#endif .ioctl = nand_ioctl, .lseek = dev_lseek_default, - .erase = nand_erase, }; +#ifdef CONFIG_NAND_READ_OOB static ssize_t nand_read_oob(struct cdev *cdev, void *buf, size_t count, ulong offset, ulong flags) { struct mtd_info *info = cdev->priv; @@ -208,9 +218,39 @@ static struct file_operations nand_ops_oob = { .lseek = dev_lseek_default, }; -int add_mtd_device(struct mtd_info *mtd) +static int nand_init_oob_cdev(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; + + mtd->cdev_oob.ops = &nand_ops_oob; + mtd->cdev_oob.size = (mtd->size >> chip->page_shift) * mtd->oobsize; + mtd->cdev_oob.name = asprintf("nand_oob%d", mtd->class_dev.id); + mtd->cdev_oob.priv = mtd; + mtd->cdev_oob.dev = &mtd->class_dev; + devfs_create(&mtd->cdev_oob); + + return 0; +} + +static void nand_exit_oob_cdev(struct mtd_info *mtd) +{ + free(mtd->cdev_oob.name); +} +#else + +static int nand_init_oob_cdev(struct mtd_info *mtd) +{ + return 0; +} + +static void nand_exit_oob_cdev(struct mtd_info *mtd) +{ + return; +} +#endif + +int add_mtd_device(struct mtd_info *mtd) +{ char str[16]; strcpy(mtd->class_dev.name, "nand"); @@ -228,12 +268,7 @@ int add_mtd_device(struct mtd_info *mtd) devfs_create(&mtd->cdev); - mtd->cdev_oob.ops = &nand_ops_oob; - mtd->cdev_oob.size = (mtd->size >> chip->page_shift) * mtd->oobsize; - mtd->cdev_oob.name = asprintf("nand_oob%d", mtd->class_dev.id); - mtd->cdev_oob.priv = mtd; - mtd->cdev_oob.dev = &mtd->class_dev; - devfs_create(&mtd->cdev_oob); + nand_init_oob_cdev(mtd); return 0; } @@ -241,7 +276,7 @@ int add_mtd_device(struct mtd_info *mtd) int del_mtd_device (struct mtd_info *mtd) { unregister_device(&mtd->class_dev); - free(mtd->cdev_oob.name); + nand_exit_oob_cdev(mtd); free(mtd->param_size.value); free(mtd->cdev.name); return 0; diff --git a/drivers/mtd/nand/nand.h b/drivers/mtd/nand/nand.h new file mode 100644 index 00000000..123258d7 --- /dev/null +++ b/drivers/mtd/nand/nand.h @@ -0,0 +1,31 @@ +#ifndef __NAND_H +#define __NAND_H + +int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, + int page, int sndcmd); +int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, + int page); +int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs); +int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, + int allowbbt); +int nand_block_isbad(struct mtd_info *mtd, loff_t offs); +int nand_block_markbad(struct mtd_info *mtd, loff_t ofs); +void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len); +void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len); +void single_erase_cmd(struct mtd_info *mtd, int page); +void multi_erase_cmd(struct mtd_info *mtd, int page); +void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf); +int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int page, int cached, int raw); +int nand_erase(struct mtd_info *mtd, struct erase_info *instr); +int nand_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const uint8_t *buf); +int nand_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops); + +void nand_init_ecc_hw(struct nand_chip *chip); +void nand_init_ecc_soft(struct nand_chip *chip); +void nand_init_ecc_hw_syndrome(struct nand_chip *chip); + +#endif /* __NAND_H */ diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 22d21277..5e122a10 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -43,6 +43,8 @@ #include <malloc.h> #include <module.h> +#include "nand.h" + #ifndef DOXYGEN_SHOULD_SKIP_THIS /* Define default oob placement schemes for large and small page devices */ @@ -75,12 +77,6 @@ static struct nand_ecclayout nand_oob_64 = { .length = 38}} }; -static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, - int new_state); - -static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops); - #define DEFINE_LED_TRIGGER(x) #define DEFINE_LED_TRIGGER_GLOBAL(x) #define led_trigger_register_simple(x, y) do {} while(0) @@ -94,24 +90,6 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, DEFINE_LED_TRIGGER(nand_led_trigger); /** - * nand_release_device - [GENERIC] release chip - * @mtd: MTD device structure - * - * Deselect, release chip lock and wake up anyone waiting on the device - */ -static void nand_release_device(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd->priv; - - /* De-select the NAND device */ - chip->select_chip(mtd, -1); - - /* Release the controller and the chip */ - chip->controller->active = NULL; - chip->state = FL_READY; -} - -/** * nand_read_byte - [DEFAULT] read one byte from the chip * @mtd: MTD device structure * @@ -172,23 +150,6 @@ static void nand_select_chip(struct mtd_info *mtd, int chipnr) } /** - * nand_write_buf - [DEFAULT] write buffer to chip - * @mtd: MTD device structure - * @buf: data buffer - * @len: number of bytes to write - * - * Default write function for 8bit buswith - */ -static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) -{ - int i; - struct nand_chip *chip = mtd->priv; - - for (i = 0; i < len; i++) - writeb(buf[i], chip->IO_ADDR_W); -} - -/** * nand_read_buf - [DEFAULT] read chip data into buffer * @mtd: MTD device structure * @buf: buffer to store date @@ -225,26 +186,6 @@ static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) } /** - * nand_write_buf16 - [DEFAULT] write buffer to chip - * @mtd: MTD device structure - * @buf: data buffer - * @len: number of bytes to write - * - * Default write function for 16bit buswith - */ -static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) -{ - int i; - struct nand_chip *chip = mtd->priv; - u16 *p = (u16 *) buf; - len >>= 1; - - for (i = 0; i < len; i++) - writew(p[i], chip->IO_ADDR_W); - -} - -/** * nand_read_buf16 - [DEFAULT] read chip data into buffer * @mtd: MTD device structure * @buf: buffer to store date @@ -304,8 +245,6 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) if (getchip) { chipnr = (int)(ofs >> chip->chip_shift); - nand_get_device(chip, mtd, FL_READING); - /* Select the NAND device */ chip->select_chip(mtd, chipnr); } @@ -324,70 +263,10 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) res = 1; } - if (getchip) - nand_release_device(mtd); - return res; } /** - * nand_default_block_markbad - [DEFAULT] mark a block bad - * @mtd: MTD device structure - * @ofs: offset from device start - * - * This is the default implementation, which can be overridden by - * a hardware specific driver. -*/ -static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) -{ - struct nand_chip *chip = mtd->priv; - uint8_t buf[2] = { 0, 0 }; - int block, ret; - - /* Get block number */ - block = (int)(ofs >> chip->bbt_erase_shift); - if (chip->bbt) - chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); - - /* Do we have a flash based bad block table ? */ - if (chip->options & NAND_USE_FLASH_BBT) - ret = nand_update_bbt(mtd, ofs); - else { - /* We write two bytes, so we dont have to mess with 16 bit - * access - */ - nand_get_device(chip, mtd, FL_WRITING); - ofs += mtd->oobsize; - chip->ops.len = chip->ops.ooblen = 2; - chip->ops.datbuf = NULL; - chip->ops.oobbuf = buf; - chip->ops.ooboffs = chip->badblockpos & ~0x01; - - ret = nand_do_write_oob(mtd, ofs, &chip->ops); - nand_release_device(mtd); - } - if (!ret) - mtd->ecc_stats.badblocks++; - - return ret; -} - -/** - * nand_check_wp - [GENERIC] check if the chip is write protected - * @mtd: MTD device structure - * Check, if the device is write protected - * - * The function expects, that the device is already selected - */ -static int nand_check_wp(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd->priv; - /* Check the WP bit */ - chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); - return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1; -} - -/** * nand_block_checkbad - [GENERIC] Check if a block is marked bad * @mtd: MTD device structure * @ofs: offset from device start @@ -397,16 +276,19 @@ static int nand_check_wp(struct mtd_info *mtd) * Check, if the block is bad. Either by reading the bad block table or * calling of the scan function. */ -static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, +int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt) { struct nand_chip *chip = mtd->priv; +#ifdef CONFIG_NAND_BBT if (!chip->bbt) return chip->block_bad(mtd, ofs, getchip); - /* Return info from the table */ return nand_isbad_bbt(mtd, ofs, allowbbt); +#else + return chip->block_bad(mtd, ofs, getchip); +#endif } /* @@ -652,32 +534,6 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, } /** - * nand_get_device - [GENERIC] Get chip for selected access - * @chip: the nand chip descriptor - * @mtd: MTD device structure - * @new_state: the state which is requested - * - * Get the device and lock it for exclusive access - */ -static int -nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state) -{ - retry: - /* Hardware controller shared among independend devices */ - if (!chip->controller->active) - chip->controller->active = chip; - - if (chip->controller->active == chip && chip->state == FL_READY) { - chip->state = new_state; - return 0; - } - if (new_state == FL_PM_SUSPENDED) { - return (chip->state == FL_PM_SUSPENDED) ? 0 : -EAGAIN; - } - goto retry; -} - -/** * nand_wait - [DEFAULT] wait until the command is done * @mtd: MTD device structure * @chip: NAND chip structure @@ -739,150 +595,13 @@ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, } /** - * nand_read_page_swecc - [REPLACABLE] software ecc based page read function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - */ -static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf) -{ - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; - uint8_t *p = buf; - uint8_t *ecc_calc = chip->buffers->ecccalc; - uint8_t *ecc_code = chip->buffers->ecccode; - uint32_t *eccpos = chip->ecc.layout->eccpos; - - chip->ecc.read_page_raw(mtd, chip, buf); - - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) - chip->ecc.calculate(mtd, p, &ecc_calc[i]); - - for (i = 0; i < chip->ecc.total; i++) - ecc_code[i] = chip->oob_poi[eccpos[i]]; - - eccsteps = chip->ecc.steps; - p = buf; - - for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - int stat; - - stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); - if (stat < 0) - mtd->ecc_stats.failed++; - else - mtd->ecc_stats.corrected += stat; - } - return 0; -} - -/** - * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * - * Not for syndrome calculating ecc controllers which need a special oob layout - */ -static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf) -{ - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; - uint8_t *p = buf; - uint8_t *ecc_calc = chip->buffers->ecccalc; - uint8_t *ecc_code = chip->buffers->ecccode; - uint32_t *eccpos = chip->ecc.layout->eccpos; - - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - chip->ecc.hwctl(mtd, NAND_ECC_READ); - chip->read_buf(mtd, p, eccsize); - chip->ecc.calculate(mtd, p, &ecc_calc[i]); - } - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - - for (i = 0; i < chip->ecc.total; i++) - ecc_code[i] = chip->oob_poi[eccpos[i]]; - - eccsteps = chip->ecc.steps; - p = buf; - - for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - int stat; - - stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); - if (stat < 0) - mtd->ecc_stats.failed++; - else - mtd->ecc_stats.corrected += stat; - } - return 0; -} - -/** - * nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: buffer to store read data - * - * The hw generator calculates the error syndrome automatically. Therefor - * we need a special oob layout and handling. - */ -static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf) -{ - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; - uint8_t *p = buf; - uint8_t *oob = chip->oob_poi; - - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - int stat; - - chip->ecc.hwctl(mtd, NAND_ECC_READ); - chip->read_buf(mtd, p, eccsize); - - if (chip->ecc.prepad) { - chip->read_buf(mtd, oob, chip->ecc.prepad); - oob += chip->ecc.prepad; - } - - chip->ecc.hwctl(mtd, NAND_ECC_READSYN); - chip->read_buf(mtd, oob, eccbytes); - stat = chip->ecc.correct(mtd, p, oob, NULL); - - if (stat < 0) - mtd->ecc_stats.failed++; - else - mtd->ecc_stats.corrected += stat; - - oob += eccbytes; - - if (chip->ecc.postpad) { - chip->read_buf(mtd, oob, chip->ecc.postpad); - oob += chip->ecc.postpad; - } - } - - /* Calculate remaining oob bytes */ - i = mtd->oobsize - (oob - chip->oob_poi); - if (i) - chip->read_buf(mtd, oob, i); - - return 0; -} - -/** * nand_transfer_oob - [Internal] Transfer oob to client buffer * @chip: nand chip structure * @oob: oob destination address * @ops: oob ops structure * @len: size of oob to transfer */ +#ifdef CONFIG_NAND_READ_OOB static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, struct mtd_oob_ops *ops, size_t len) { @@ -923,6 +642,7 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, } return NULL; } +#endif /** * nand_do_read_ops - [Internal] Read data with ECC @@ -988,6 +708,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, buf += bytes; +#ifdef CONFIG_NAND_READ_OOB if (unlikely(oob)) { /* Raw mode does data:oob:data:oob */ if (ops->mode != MTD_OOB_RAW) { @@ -1002,7 +723,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, buf = nand_transfer_oob(chip, buf, ops, mtd->oobsize); } - +#endif if (!(chip->options & NAND_NO_READRDY)) { /* * Apply delay or wait for ready/busy pin. Do @@ -1081,8 +802,6 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, if (!len) return 0; - nand_get_device(chip, mtd, FL_READING); - chip->ops.len = len; chip->ops.datbuf = buf; chip->ops.oobbuf = NULL; @@ -1091,8 +810,6 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, *retlen = chip->ops.retlen; - nand_release_device(mtd); - return ret; } @@ -1103,7 +820,7 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, * @page: page number to read * @sndcmd: flag whether to issue read command or not */ -static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, +int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page, int sndcmd) { if (sndcmd) { @@ -1115,127 +832,6 @@ static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, } /** - * nand_read_oob_syndrome - [REPLACABLE] OOB data read function for HW ECC - * with syndromes - * @mtd: mtd info structure - * @chip: nand chip info structure - * @page: page number to read - * @sndcmd: flag whether to issue read command or not - */ -static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - int page, int sndcmd) -{ - uint8_t *buf = chip->oob_poi; - int length = mtd->oobsize; - int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; - int eccsize = chip->ecc.size; - uint8_t *bufpoi = buf; - int i, toread, sndrnd = 0, pos; - - chip->cmdfunc(mtd, NAND_CMD_READ0, chip->ecc.size, page); - for (i = 0; i < chip->ecc.steps; i++) { - if (sndrnd) { - pos = eccsize + i * (eccsize + chunk); - if (mtd->writesize > 512) - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, pos, -1); - else - chip->cmdfunc(mtd, NAND_CMD_READ0, pos, page); - } else - sndrnd = 1; - toread = min_t(int, length, chunk); - chip->read_buf(mtd, bufpoi, toread); - bufpoi += toread; - length -= toread; - } - if (length > 0) - chip->read_buf(mtd, bufpoi, length); - - return 1; -} - -/** - * nand_write_oob_std - [REPLACABLE] the most common OOB data write function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @page: page number to write - */ -static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, - int page) -{ - int status = 0; - const uint8_t *buf = chip->oob_poi; - int length = mtd->oobsize; - - chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); - chip->write_buf(mtd, buf, length); - /* Send command to program the OOB data */ - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - - status = chip->waitfunc(mtd, chip); - - return status & NAND_STATUS_FAIL ? -EIO : 0; -} - -/** - * nand_write_oob_syndrome - [REPLACABLE] OOB data write function for HW ECC - * with syndrome - only for large page flash ! - * @mtd: mtd info structure - * @chip: nand chip info structure - * @page: page number to write - */ -static int nand_write_oob_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, int page) -{ - int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; - int eccsize = chip->ecc.size, length = mtd->oobsize; - int i, len, pos, status = 0, sndcmd = 0, steps = chip->ecc.steps; - const uint8_t *bufpoi = chip->oob_poi; - - /* - * data-ecc-data-ecc ... ecc-oob - * or - * data-pad-ecc-pad-data-pad .... ecc-pad-oob - */ - if (!chip->ecc.prepad && !chip->ecc.postpad) { - pos = steps * (eccsize + chunk); - steps = 0; - } else - pos = eccsize; - - chip->cmdfunc(mtd, NAND_CMD_SEQIN, pos, page); - for (i = 0; i < steps; i++) { - if (sndcmd) { - if (mtd->writesize <= 512) { - uint32_t fill = 0xFFFFFFFF; - - len = eccsize; - while (len > 0) { - int num = min_t(int, len, 4); - chip->write_buf(mtd, (uint8_t *)&fill, - num); - len -= num; - } - } else { - pos = eccsize + i * (eccsize + chunk); - chip->cmdfunc(mtd, NAND_CMD_RNDIN, pos, -1); - } - } else - sndcmd = 1; - len = min_t(int, length, chunk); - chip->write_buf(mtd, bufpoi, len); - bufpoi += len; - length -= len; - } - if (length > 0) - chip->write_buf(mtd, bufpoi, length); - - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - status = chip->waitfunc(mtd, chip); - - return status & NAND_STATUS_FAIL ? -EIO : 0; -} - -/** * nand_do_read_oob - [Intern] NAND read out-of-band * @mtd: MTD device structure * @from: offset to read from @@ -1243,6 +839,7 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd, * * NAND read out-of-band data from the spare area */ +#ifdef CONFIG_NAND_READ_OOB static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { @@ -1339,7 +936,6 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, static int nand_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { - struct nand_chip *chip = mtd->priv; int ret = -ENOSYS; ops->retlen = 0; @@ -1351,8 +947,6 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, return -EINVAL; } - nand_get_device(chip, mtd, FL_READING); - switch(ops->mode) { case MTD_OOB_PLACE: case MTD_OOB_AUTO: @@ -1369,742 +963,16 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, ret = nand_do_read_ops(mtd, from, ops); out: - nand_release_device(mtd); return ret; } - - -/** - * nand_write_page_raw - [Intern] raw page write function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: data buffer - */ -static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) -{ - chip->write_buf(mtd, buf, mtd->writesize); - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); -} - -/** - * nand_write_page_swecc - [REPLACABLE] software ecc based page write function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: data buffer - */ -static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) -{ - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; - uint8_t *ecc_calc = chip->buffers->ecccalc; - const uint8_t *p = buf; - uint32_t *eccpos = chip->ecc.layout->eccpos; - - /* Software ecc calculation */ - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) - chip->ecc.calculate(mtd, p, &ecc_calc[i]); - - for (i = 0; i < chip->ecc.total; i++) - chip->oob_poi[eccpos[i]] = ecc_calc[i]; - - chip->ecc.write_page_raw(mtd, chip, buf); -} - -/** - * nand_write_page_hwecc - [REPLACABLE] hardware ecc based page write function - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: data buffer - */ -static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) -{ - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; - uint8_t *ecc_calc = chip->buffers->ecccalc; - const uint8_t *p = buf; - uint32_t *eccpos = chip->ecc.layout->eccpos; - - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - chip->ecc.hwctl(mtd, NAND_ECC_WRITE); - chip->write_buf(mtd, p, eccsize); - chip->ecc.calculate(mtd, p, &ecc_calc[i]); - } - - for (i = 0; i < chip->ecc.total; i++) - chip->oob_poi[eccpos[i]] = ecc_calc[i]; - - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); -} - -/** - * nand_write_page_syndrome - [REPLACABLE] hardware ecc syndrom based page write - * @mtd: mtd info structure - * @chip: nand chip info structure - * @buf: data buffer - * - * The hw generator calculates the error syndrome automatically. Therefor - * we need a special oob layout and handling. - */ -static void nand_write_page_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf) -{ - int i, eccsize = chip->ecc.size; - int eccbytes = chip->ecc.bytes; - int eccsteps = chip->ecc.steps; - const uint8_t *p = buf; - uint8_t *oob = chip->oob_poi; - - for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { - - chip->ecc.hwctl(mtd, NAND_ECC_WRITE); - chip->write_buf(mtd, p, eccsize); - - if (chip->ecc.prepad) { - chip->write_buf(mtd, oob, chip->ecc.prepad); - oob += chip->ecc.prepad; - } - - chip->ecc.calculate(mtd, p, oob); - chip->write_buf(mtd, oob, eccbytes); - oob += eccbytes; - - if (chip->ecc.postpad) { - chip->write_buf(mtd, oob, chip->ecc.postpad); - oob += chip->ecc.postpad; - } - } - - /* Calculate remaining oob bytes */ - i = mtd->oobsize - (oob - chip->oob_poi); - if (i) - chip->write_buf(mtd, oob, i); -} - -/** - * nand_write_page - [REPLACEABLE] write one page - * @mtd: MTD device structure - * @chip: NAND chip descriptor - * @buf: the data to write - * @page: page number to write - * @cached: cached programming - * @raw: use _raw version of write_page - */ -static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int page, int cached, int raw) -{ - int status; - - chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); - - if (unlikely(raw)) - chip->ecc.write_page_raw(mtd, chip, buf); - else - chip->ecc.write_page(mtd, chip, buf); - - /* - * Cached progamming disabled for now, Not sure if its worth the - * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s) - */ - cached = 0; - - if (!cached || !(chip->options & NAND_CACHEPRG)) { - - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - status = chip->waitfunc(mtd, chip); - /* - * See if operation failed and additional status checks are - * available - */ - if ((status & NAND_STATUS_FAIL) && (chip->errstat)) - status = chip->errstat(mtd, chip, FL_WRITING, status, - page); - - if (status & NAND_STATUS_FAIL) { - return -EIO; - } - } else { - chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1); - status = chip->waitfunc(mtd, chip); - } - -#ifdef CONFIG_MTD_NAND_VERIFY_WRITE - /* Send command to read back the data */ - chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); - - if (chip->verify_buf(mtd, buf, mtd->writesize)) - return -EIO; #endif - return 0; -} - -/** - * nand_fill_oob - [Internal] Transfer client buffer to oob - * @chip: nand chip structure - * @oob: oob data buffer - * @ops: oob ops structure - */ -static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, - struct mtd_oob_ops *ops) -{ - size_t len = ops->ooblen; - - switch(ops->mode) { - - case MTD_OOB_PLACE: - case MTD_OOB_RAW: - memcpy(chip->oob_poi + ops->ooboffs, oob, len); - return oob + len; - - case MTD_OOB_AUTO: { - struct nand_oobfree *free = chip->ecc.layout->oobfree; - uint32_t boffs = 0, woffs = ops->ooboffs; - size_t bytes = 0; - - for(; free->length && len; free++, len -= bytes) { - /* Write request not from offset 0 ? */ - if (unlikely(woffs)) { - if (woffs >= free->length) { - woffs -= free->length; - continue; - } - boffs = free->offset + woffs; - bytes = min_t(size_t, len, - (free->length - woffs)); - woffs = 0; - } else { - bytes = min_t(size_t, len, free->length); - boffs = free->offset; - } - memcpy(chip->oob_poi + boffs, oob, bytes); - oob += bytes; - } - return oob; - } - default: - BUG(); - } - return NULL; -} - -#define NOTALIGNED(x) (x & (chip->subpagesize - 1)) != 0 - -/** - * nand_do_write_ops - [Internal] NAND write with ECC - * @mtd: MTD device structure - * @to: offset to write to - * @ops: oob operations description structure - * - * NAND write with ECC - */ -static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops) -{ - int chipnr, realpage, page, blockmask, column; - struct nand_chip *chip = mtd->priv; - uint32_t writelen = ops->len; - uint8_t *oob = ops->oobbuf; - uint8_t *buf = ops->datbuf; - int ret, subpage; - - ops->retlen = 0; - if (!writelen) - return 0; - - /* reject writes, which are not page aligned */ - if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { - printk(KERN_NOTICE "nand_write: " - "Attempt to write not page aligned data\n"); - return -EINVAL; - } - - column = to & (mtd->writesize - 1); - subpage = column || (writelen & (mtd->writesize - 1)); - - if (subpage && oob) - return -EINVAL; - - chipnr = (int)(to >> chip->chip_shift); - chip->select_chip(mtd, chipnr); - - /* Check, if it is write protected */ - if (nand_check_wp(mtd)) { - return -EIO; - } - - realpage = (int)(to >> chip->page_shift); - page = realpage & chip->pagemask; - blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; - - /* Invalidate the page cache, when we write to the cached page */ - if (to <= (chip->pagebuf << chip->page_shift) && - (chip->pagebuf << chip->page_shift) < (to + ops->len)) - chip->pagebuf = -1; - - /* If we're not given explicit OOB data, let it be 0xFF */ - if (likely(!oob)) - memset(chip->oob_poi, 0xff, mtd->oobsize); - - while(1) { - int bytes = mtd->writesize; - int cached = writelen > bytes && page != blockmask; - uint8_t *wbuf = buf; - - /* Partial page write ? */ - if (unlikely(column || writelen < (mtd->writesize - 1))) { - cached = 0; - bytes = min_t(int, bytes - column, (int) writelen); - chip->pagebuf = -1; - memset(chip->buffers->databuf, 0xff, mtd->writesize); - memcpy(&chip->buffers->databuf[column], buf, bytes); - wbuf = chip->buffers->databuf; - } - - if (unlikely(oob)) - oob = nand_fill_oob(chip, oob, ops); - - ret = chip->write_page(mtd, chip, wbuf, page, cached, - (ops->mode == MTD_OOB_RAW)); - if (ret) - break; - - writelen -= bytes; - if (!writelen) - break; - - column = 0; - buf += bytes; - realpage++; - - page = realpage & chip->pagemask; - /* Check, if we cross a chip boundary */ - if (!page) { - chipnr++; - chip->select_chip(mtd, -1); - chip->select_chip(mtd, chipnr); - } - } - - ops->retlen = ops->len - writelen; - if (unlikely(oob)) - ops->oobretlen = ops->ooblen; - return ret; -} - -/** - * nand_write - [MTD Interface] NAND write with ECC - * @mtd: MTD device structure - * @to: offset to write to - * @len: number of bytes to write - * @retlen: pointer to variable to store the number of written bytes - * @buf: the data to write - * - * NAND write with ECC - */ -static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const uint8_t *buf) -{ - struct nand_chip *chip = mtd->priv; - int ret; - - /* Do not allow reads past end of device */ - if ((to + len) > mtd->size) - return -EINVAL; - if (!len) - return 0; - - nand_get_device(chip, mtd, FL_WRITING); - - chip->ops.len = len; - chip->ops.datbuf = (uint8_t *)buf; - chip->ops.oobbuf = NULL; - - ret = nand_do_write_ops(mtd, to, &chip->ops); - - *retlen = chip->ops.retlen; - - nand_release_device(mtd); - - return ret; -} - -/** - * nand_do_write_oob - [MTD Interface] NAND write out-of-band - * @mtd: MTD device structure - * @to: offset to write to - * @ops: oob operation description structure - * - * NAND write out-of-band - */ -static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops) -{ - int chipnr, page, status, len; - struct nand_chip *chip = mtd->priv; - - MTD_DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", - (unsigned int)to, (int)ops->ooblen); - - if (ops->mode == MTD_OOB_AUTO) - len = chip->ecc.layout->oobavail; - else - len = mtd->oobsize; - - /* Do not allow write past end of page */ - if ((ops->ooboffs + ops->ooblen) > len) { - MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " - "Attempt to write past end of page\n"); - return -EINVAL; - } - - if (unlikely(ops->ooboffs >= len)) { - MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " - "Attempt to start write outside oob\n"); - return -EINVAL; - } - - /* Do not allow reads past end of device */ - if (unlikely(to >= mtd->size || - ops->ooboffs + ops->ooblen > - ((mtd->size >> chip->page_shift) - - (to >> chip->page_shift)) * len)) { - MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " - "Attempt write beyond end of device\n"); - return -EINVAL; - } - - chipnr = (int)(to >> chip->chip_shift); - chip->select_chip(mtd, chipnr); - - /* Shift to get page */ - page = (int)(to >> chip->page_shift); - - /* - * Reset the chip. Some chips (like the Toshiba TC5832DC found in one - * of my DiskOnChip 2000 test units) will clear the whole data page too - * if we don't do this. I have no clue why, but I seem to have 'fixed' - * it in the doc2000 driver in August 1999. dwmw2. - */ - chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); - - /* Check, if it is write protected */ - if (nand_check_wp(mtd)) - return -EROFS; - - /* Invalidate the page cache, if we write to the cached page */ - if (page == chip->pagebuf) - chip->pagebuf = -1; - - memset(chip->oob_poi, 0xff, mtd->oobsize); - nand_fill_oob(chip, ops->oobbuf, ops); - status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); - memset(chip->oob_poi, 0xff, mtd->oobsize); - - if (status) - return status; - - ops->oobretlen = ops->ooblen; - - return 0; -} - -/** - * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band - * @mtd: MTD device structure - * @to: offset to write to - * @ops: oob operation description structure - */ -static int nand_write_oob(struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops) -{ - struct nand_chip *chip = mtd->priv; - int ret = -ENOSYS; - - ops->retlen = 0; - - /* Do not allow writes past end of device */ - if (ops->datbuf && (to + ops->len) > mtd->size) { - MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " - "Attempt read beyond end of device\n"); - return -EINVAL; - } - - nand_get_device(chip, mtd, FL_WRITING); - - switch(ops->mode) { - case MTD_OOB_PLACE: - case MTD_OOB_AUTO: - case MTD_OOB_RAW: - break; - - default: - goto out; - } - - if (!ops->datbuf) - ret = nand_do_write_oob(mtd, to, ops); - else - ret = nand_do_write_ops(mtd, to, ops); - - out: - nand_release_device(mtd); - return ret; -} - -/** - * single_erease_cmd - [GENERIC] NAND standard block erase command function - * @mtd: MTD device structure - * @page: the page address of the block which will be erased - * - * Standard erase command for NAND chips - */ -static void single_erase_cmd(struct mtd_info *mtd, int page) -{ - struct nand_chip *chip = mtd->priv; - /* Send commands to erase a block */ - chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); - chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); -} - -/** - * multi_erease_cmd - [GENERIC] AND specific block erase command function - * @mtd: MTD device structure - * @page: the page address of the block which will be erased - * - * AND multi block erase command function - * Erase 4 consecutive blocks - */ -static void multi_erase_cmd(struct mtd_info *mtd, int page) -{ - struct nand_chip *chip = mtd->priv; - /* Send commands to erase a block */ - chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++); - chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++); - chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++); - chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); - chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); -} - -/** - * nand_erase - [MTD Interface] erase block(s) - * @mtd: MTD device structure - * @instr: erase instruction - * - * Erase one ore more blocks - */ -static int nand_erase(struct mtd_info *mtd, struct erase_info *instr) -{ - return nand_erase_nand(mtd, instr, 0); -} - -#define BBT_PAGE_MASK 0xffffff3f -/** - * nand_erase_nand - [Internal] erase block(s) - * @mtd: MTD device structure - * @instr: erase instruction - * @allowbbt: allow erasing the bbt area - * - * Erase one ore more blocks - */ -int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, - int allowbbt) -{ - int page, len, status, pages_per_block, ret, chipnr; - struct nand_chip *chip = mtd->priv; - int rewrite_bbt[NAND_MAX_CHIPS]={0}; - unsigned int bbt_masked_page = 0xffffffff; - - MTD_DEBUG(MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%08x, len = %i\n", - (unsigned int)instr->addr, (unsigned int)instr->len); - - /* Start address must align on block boundary */ - if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) { - MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n"); - return -EINVAL; - } - - /* Length must align on block boundary */ - if (instr->len & ((1 << chip->phys_erase_shift) - 1)) { - MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " - "Length not block aligned\n"); - return -EINVAL; - } - - /* Do not allow erase past end of device */ - if ((instr->len + instr->addr) > mtd->size) { - MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " - "Erase past end of device\n"); - return -EINVAL; - } - - instr->fail_addr = 0xffffffff; - - /* Grab the lock and see if the device is available */ - nand_get_device(chip, mtd, FL_ERASING); - - /* Shift to get first page */ - page = (int)(instr->addr >> chip->page_shift); - chipnr = (int)(instr->addr >> chip->chip_shift); - - /* Calculate pages in each block */ - pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift); - - /* Select the NAND device */ - chip->select_chip(mtd, chipnr); - - /* Check, if it is write protected */ - if (nand_check_wp(mtd)) { - MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " - "Device is write protected!!!\n"); - instr->state = MTD_ERASE_FAILED; - goto erase_exit; - } - - /* - * If BBT requires refresh, set the BBT page mask to see if the BBT - * should be rewritten. Otherwise the mask is set to 0xffffffff which - * can not be matched. This is also done when the bbt is actually - * erased to avoid recusrsive updates - */ - if (chip->options & BBT_AUTO_REFRESH && !allowbbt) - bbt_masked_page = chip->bbt_td->pages[chipnr] & BBT_PAGE_MASK; - - /* Loop through the pages */ - len = instr->len; - - instr->state = MTD_ERASING; - - while (len) { - /* - * heck if we have a bad block, we do not erase bad blocks ! - */ - if (nand_block_checkbad(mtd, ((loff_t) page) << - chip->page_shift, 0, allowbbt)) { - printk(KERN_WARNING "nand_erase: attempt to erase a " - "bad block at page 0x%08x\n", page); - instr->state = MTD_ERASE_FAILED; - goto erase_exit; - } - - /* - * Invalidate the page cache, if we erase the block which - * contains the current cached page - */ - if (page <= chip->pagebuf && chip->pagebuf < - (page + pages_per_block)) - chip->pagebuf = -1; - - chip->erase_cmd(mtd, page & chip->pagemask); - - status = chip->waitfunc(mtd, chip); - - /* - * See if operation failed and additional status checks are - * available - */ - if ((status & NAND_STATUS_FAIL) && (chip->errstat)) - status = chip->errstat(mtd, chip, FL_ERASING, - status, page); - - /* See if block erase succeeded */ - if (status & NAND_STATUS_FAIL) { - MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " - "Failed erase, page 0x%08x\n", page); - instr->state = MTD_ERASE_FAILED; - instr->fail_addr = (page << chip->page_shift); - goto erase_exit; - } - - /* - * If BBT requires refresh, set the BBT rewrite flag to the - * page being erased - */ - if (bbt_masked_page != 0xffffffff && - (page & BBT_PAGE_MASK) == bbt_masked_page) - rewrite_bbt[chipnr] = (page << chip->page_shift); - - /* Increment page address and decrement length */ - len -= (1 << chip->phys_erase_shift); - page += pages_per_block; - - /* Check, if we cross a chip boundary */ - if (len && !(page & chip->pagemask)) { - chipnr++; - chip->select_chip(mtd, -1); - chip->select_chip(mtd, chipnr); - - /* - * If BBT requires refresh and BBT-PERCHIP, set the BBT - * page mask to see if this BBT should be rewritten - */ - if (bbt_masked_page != 0xffffffff && - (chip->bbt_td->options & NAND_BBT_PERCHIP)) - bbt_masked_page = chip->bbt_td->pages[chipnr] & - BBT_PAGE_MASK; - } - } - instr->state = MTD_ERASE_DONE; - - erase_exit: - - ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; - - /* Deselect and wake up anyone waiting on the device */ - nand_release_device(mtd); - - /* Do call back function */ - if (!ret) - mtd_erase_callback(instr); - - /* - * If BBT requires refresh and erase was successful, rewrite any - * selected bad block tables - */ - if (bbt_masked_page == 0xffffffff || ret) - return ret; - - for (chipnr = 0; chipnr < chip->numchips; chipnr++) { - if (!rewrite_bbt[chipnr]) - continue; - /* update the BBT for chip */ - MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt " - "(%d:0x%0x 0x%0x)\n", chipnr, rewrite_bbt[chipnr], - chip->bbt_td->pages[chipnr]); - nand_update_bbt(mtd, rewrite_bbt[chipnr]); - } - - /* Return more or less happy */ - return ret; -} - -/** - * nand_sync - [MTD Interface] sync - * @mtd: MTD device structure - * - * Sync is actually a wait for chip ready function - */ -static void nand_sync(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd->priv; - - MTD_DEBUG(MTD_DEBUG_LEVEL3, "nand_sync: called\n"); - - /* Grab the lock and see if the device is available */ - nand_get_device(chip, mtd, FL_SYNCING); - /* Release it and go back */ - nand_release_device(mtd); -} /** * nand_block_isbad - [MTD Interface] Check if block at offset is bad * @mtd: MTD device structure * @offs: offset relative to mtd start */ -static int nand_block_isbad(struct mtd_info *mtd, loff_t offs) +int nand_block_isbad(struct mtd_info *mtd, loff_t offs) { /* Check for invalid offset */ if (offs > mtd->size) @@ -2113,52 +981,6 @@ static int nand_block_isbad(struct mtd_info *mtd, loff_t offs) return nand_block_checkbad(mtd, offs, 1, 0); } -/** - * nand_block_markbad - [MTD Interface] Mark block at the given offset as bad - * @mtd: MTD device structure - * @ofs: offset relative to mtd start - */ -static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) -{ - struct nand_chip *chip = mtd->priv; - int ret; - - if ((ret = nand_block_isbad(mtd, ofs))) { - /* If it was bad already, return success and do nothing. */ - if (ret > 0) - return 0; - return ret; - } - - return chip->block_markbad(mtd, ofs); -} - -/** - * nand_suspend - [MTD Interface] Suspend the NAND flash - * @mtd: MTD device structure - */ -static int nand_suspend(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd->priv; - - return nand_get_device(chip, mtd, FL_PM_SUSPENDED); -} - -/** - * nand_resume - [MTD Interface] Resume the NAND flash - * @mtd: MTD device structure - */ -static void nand_resume(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd->priv; - - if (chip->state == FL_PM_SUSPENDED) - nand_release_device(mtd); - else - printk(KERN_ERR "nand_resume() called for a chip which is not " - "in suspended state\n"); -} - /* * Set default functions */ @@ -2184,17 +1006,20 @@ static void nand_set_defaults(struct nand_chip *chip, int busw) chip->read_word = nand_read_word; if (!chip->block_bad) chip->block_bad = nand_block_bad; +#ifdef CONFIG_NAND_WRITE if (!chip->block_markbad) chip->block_markbad = nand_default_block_markbad; if (!chip->write_buf) chip->write_buf = busw ? nand_write_buf16 : nand_write_buf; +#endif if (!chip->read_buf) chip->read_buf = busw ? nand_read_buf16 : nand_read_buf; if (!chip->verify_buf) chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf; +#ifdef CONFIG_NAND_BBT if (!chip->scan_bbt) chip->scan_bbt = nand_default_bbt; - +#endif if (!chip->controller) { chip->controller = &chip->hwcontrol; } @@ -2341,12 +1166,13 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize) chip->options &= ~NAND_SAMSUNG_LP_OPTIONS; +#ifdef CONFIG_NAND_WRITE /* Check for AND chips with 4 page planes */ if (chip->options & NAND_4PAGE_ARRAY) chip->erase_cmd = multi_erase_cmd; else chip->erase_cmd = single_erase_cmd; - +#endif /* Do not replace user supplied command function ! */ if (mtd->writesize > 512 && chip->cmdfunc == nand_command) chip->cmdfunc = nand_command_lp; @@ -2408,6 +1234,23 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips) return 0; } +static void __maybe_unused nand_check_hwecc(struct mtd_info *mtd, struct nand_chip *chip) +{ + if ((!chip->ecc.calculate || !chip->ecc.correct || + !chip->ecc.hwctl) && + (!chip->ecc.read_page || !chip->ecc.write_page)) { + printk(KERN_WARNING "No ECC functions supplied, " + "Hardware ECC not possible\n"); + BUG(); + } + + if (mtd->writesize < chip->ecc.size) { + printk(KERN_WARNING "%d byte HW ECC not possible on " + "%d byte page size\n", + chip->ecc.size, mtd->writesize); + BUG(); + } +} /** * nand_scan_tail - [NAND Interface] Scan for the NAND device @@ -2452,8 +1295,10 @@ int nand_scan_tail(struct mtd_info *mtd) } } +#ifdef CONFIG_NAND_WRITE if (!chip->write_page) chip->write_page = nand_write_page; +#endif /* * check ECC mode, default to software if 3byte/512byte hardware ECC is @@ -2461,71 +1306,42 @@ int nand_scan_tail(struct mtd_info *mtd) */ if (!chip->ecc.read_page_raw) chip->ecc.read_page_raw = nand_read_page_raw; +#ifdef CONFIG_NAND_WRITE if (!chip->ecc.write_page_raw) chip->ecc.write_page_raw = nand_write_page_raw; - +#endif switch (chip->ecc.mode) { +#ifdef CONFIG_NAND_ECC_HW case NAND_ECC_HW: - /* Use standard hwecc read page function ? */ - if (!chip->ecc.read_page) - chip->ecc.read_page = nand_read_page_hwecc; - if (!chip->ecc.write_page) - chip->ecc.write_page = nand_write_page_hwecc; - if (!chip->ecc.read_oob) - chip->ecc.read_oob = nand_read_oob_std; - if (!chip->ecc.write_oob) - chip->ecc.write_oob = nand_write_oob_std; - + nand_check_hwecc(mtd, chip); + nand_init_ecc_hw(chip); + break; +#endif +#ifdef CONFIG_NAND_ECC_HW_SYNDROME case NAND_ECC_HW_SYNDROME: - if ((!chip->ecc.calculate || !chip->ecc.correct || - !chip->ecc.hwctl) && - (!chip->ecc.read_page || - chip->ecc.read_page == nand_read_page_hwecc || - !chip->ecc.write_page || - chip->ecc.write_page == nand_write_page_hwecc)) { - printk(KERN_WARNING "No ECC functions supplied, " - "Hardware ECC not possible\n"); - BUG(); - } - /* Use standard syndrome read/write page function ? */ - if (!chip->ecc.read_page) - chip->ecc.read_page = nand_read_page_syndrome; - if (!chip->ecc.write_page) - chip->ecc.write_page = nand_write_page_syndrome; - if (!chip->ecc.read_oob) - chip->ecc.read_oob = nand_read_oob_syndrome; - if (!chip->ecc.write_oob) - chip->ecc.write_oob = nand_write_oob_syndrome; - - if (mtd->writesize >= chip->ecc.size) - break; - printk(KERN_WARNING "%d byte HW ECC not possible on " - "%d byte page size, fallback to SW ECC\n", - chip->ecc.size, mtd->writesize); - chip->ecc.mode = NAND_ECC_SOFT; - + nand_check_hwecc(mtd, chip); + nand_init_ecc_hw_syndrome(chip); + break; +#endif +#ifdef CONFIG_NAND_ECC_SOFT case NAND_ECC_SOFT: - chip->ecc.calculate = nand_calculate_ecc; - chip->ecc.correct = nand_correct_data; - chip->ecc.read_page = nand_read_page_swecc; - chip->ecc.write_page = nand_write_page_swecc; - chip->ecc.read_oob = nand_read_oob_std; - chip->ecc.write_oob = nand_write_oob_std; - chip->ecc.size = 256; - chip->ecc.bytes = 3; + nand_init_ecc_soft(chip); break; - +#endif +#ifdef CONFIG_NAND_ECC_NONE case NAND_ECC_NONE: printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. " "This is not recommended !!\n"); chip->ecc.read_page = nand_read_page_raw; +#ifdef CONFIG_NAND_WRITE chip->ecc.write_page = nand_write_page_raw; - chip->ecc.read_oob = nand_read_oob_std; chip->ecc.write_oob = nand_write_oob_std; +#endif + chip->ecc.read_oob = nand_read_oob_std; chip->ecc.size = mtd->writesize; chip->ecc.bytes = 0; break; - +#endif default: printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n", chip->ecc.mode); @@ -2583,28 +1399,33 @@ int nand_scan_tail(struct mtd_info *mtd) /* Fill in remaining MTD driver data */ mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; +#ifdef CONFIG_NAND_WRITE mtd->erase = nand_erase; - mtd->read = nand_read; mtd->write = nand_write; - mtd->read_oob = nand_read_oob; mtd->write_oob = nand_write_oob; - mtd->sync = nand_sync; +#endif + mtd->read = nand_read; +#ifdef CONFIG_NAND_READ_OOB + mtd->read_oob = nand_read_oob; +#endif mtd->lock = NULL; mtd->unlock = NULL; - mtd->suspend = nand_suspend; - mtd->resume = nand_resume; mtd->block_isbad = nand_block_isbad; +#ifdef CONFIG_NAND_WRITE mtd->block_markbad = nand_block_markbad; - +#endif /* propagate ecc.layout to mtd_info */ mtd->ecclayout = chip->ecc.layout; /* Check, if we should skip the bad block table scan */ if (chip->options & NAND_SKIP_BBTSCAN) return 0; - +#ifdef CONFIG_NAND_BBT /* Build bad block table */ return chip->scan_bbt(mtd); +#else + return 0; +#endif } /** diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 4a6bf390..bf3a7dbc 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -557,6 +557,7 @@ static int search_read_bbts(struct mtd_info *mtd, uint8_t * buf, struct nand_bbt * (Re)write the bad block table * */ +#ifdef CONFIG_NAND_WRITE static int write_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md, int chipsel) @@ -745,6 +746,14 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, "nand_bbt: Error while writing bad block table %d\n", res); return res; } +#else +static int write_bbt(struct mtd_info *mtd, uint8_t *buf, + struct nand_bbt_descr *td, struct nand_bbt_descr *md, + int chipsel) +{ + return 0; +} +#endif /** * nand_memory_bbt - [GENERIC] create a memory based bad block table diff --git a/drivers/mtd/nand/nand_hwecc.c b/drivers/mtd/nand/nand_hwecc.c new file mode 100644 index 00000000..95b08d3a --- /dev/null +++ b/drivers/mtd/nand/nand_hwecc.c @@ -0,0 +1,103 @@ +#include <common.h> +#include <errno.h> +#include <clock.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/err.h> +#include <linux/mtd/nand_ecc.h> +#include <asm/byteorder.h> +#include <asm/io.h> +#include <malloc.h> + +#include "nand.h" + +/** + * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * + * Not for syndrome calculating ecc controllers which need a special oob layout + */ +static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_code = chip->buffers->ecccode; + uint32_t *eccpos = chip->ecc.layout->eccpos; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + } + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + for (i = 0; i < chip->ecc.total; i++) + ecc_code[i] = chip->oob_poi[eccpos[i]]; + + eccsteps = chip->ecc.steps; + p = buf; + + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + return 0; +} + +/** + * nand_write_page_hwecc - [REPLACABLE] hardware ecc based page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + */ +#ifdef CONFIG_NAND_WRITE +static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *ecc_calc = chip->buffers->ecccalc; + const uint8_t *p = buf; + uint32_t *eccpos = chip->ecc.layout->eccpos; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + chip->write_buf(mtd, p, eccsize); + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + } + + for (i = 0; i < chip->ecc.total; i++) + chip->oob_poi[eccpos[i]] = ecc_calc[i]; + + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); +} +#endif + +void nand_init_ecc_hw(struct nand_chip *chip) +{ + /* Use standard hwecc read page function ? */ + if (!chip->ecc.read_page) + chip->ecc.read_page = nand_read_page_hwecc; +#ifdef CONFIG_NAND_READ_OOB + if (!chip->ecc.read_oob) + chip->ecc.read_oob = nand_read_oob_std; +#endif +#ifdef CONFIG_NAND_WRITE + if (!chip->ecc.write_oob) + chip->ecc.write_oob = nand_write_oob_std; + if (!chip->ecc.write_page) + chip->ecc.write_page = nand_write_page_hwecc; +#endif +} diff --git a/drivers/mtd/nand/nand_hwecc_syndrome.c b/drivers/mtd/nand/nand_hwecc_syndrome.c new file mode 100644 index 00000000..9a661809 --- /dev/null +++ b/drivers/mtd/nand/nand_hwecc_syndrome.c @@ -0,0 +1,225 @@ +#include <common.h> +#include <errno.h> +#include <clock.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/err.h> +#include <linux/mtd/nand_ecc.h> +#include <asm/byteorder.h> +#include <asm/io.h> +#include <malloc.h> +#include <module.h> + +/** + * nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * + * The hw generator calculates the error syndrome automatically. Therefor + * we need a special oob layout and handling. + */ +static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *oob = chip->oob_poi; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + + if (chip->ecc.prepad) { + chip->read_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + chip->ecc.hwctl(mtd, NAND_ECC_READSYN); + chip->read_buf(mtd, oob, eccbytes); + stat = chip->ecc.correct(mtd, p, oob, NULL); + + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->read_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + /* Calculate remaining oob bytes */ + i = mtd->oobsize - (oob - chip->oob_poi); + if (i) + chip->read_buf(mtd, oob, i); + + return 0; +} +/** + * nand_write_page_syndrome - [REPLACABLE] hardware ecc syndrom based page write + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * + * The hw generator calculates the error syndrome automatically. Therefor + * we need a special oob layout and handling. + */ +#ifdef CONFIG_NAND_WRITE +static void nand_write_page_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + const uint8_t *p = buf; + uint8_t *oob = chip->oob_poi; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + + chip->ecc.hwctl(mtd, NAND_ECC_WRITE); + chip->write_buf(mtd, p, eccsize); + + if (chip->ecc.prepad) { + chip->write_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + chip->ecc.calculate(mtd, p, oob); + chip->write_buf(mtd, oob, eccbytes); + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->write_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + /* Calculate remaining oob bytes */ + i = mtd->oobsize - (oob - chip->oob_poi); + if (i) + chip->write_buf(mtd, oob, i); +} +#endif + +/** + * nand_read_oob_syndrome - [REPLACABLE] OOB data read function for HW ECC + * with syndromes + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to read + * @sndcmd: flag whether to issue read command or not + */ +static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + int page, int sndcmd) +{ + uint8_t *buf = chip->oob_poi; + int length = mtd->oobsize; + int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; + int eccsize = chip->ecc.size; + uint8_t *bufpoi = buf; + int i, toread, sndrnd = 0, pos; + + chip->cmdfunc(mtd, NAND_CMD_READ0, chip->ecc.size, page); + for (i = 0; i < chip->ecc.steps; i++) { + if (sndrnd) { + pos = eccsize + i * (eccsize + chunk); + if (mtd->writesize > 512) + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, pos, -1); + else + chip->cmdfunc(mtd, NAND_CMD_READ0, pos, page); + } else + sndrnd = 1; + toread = min_t(int, length, chunk); + chip->read_buf(mtd, bufpoi, toread); + bufpoi += toread; + length -= toread; + } + if (length > 0) + chip->read_buf(mtd, bufpoi, length); + + return 1; +} + +/** + * nand_write_oob_syndrome - [REPLACABLE] OOB data write function for HW ECC + * with syndrome - only for large page flash ! + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to write + */ +#ifdef CONFIG_NAND_WRITE +static int nand_write_oob_syndrome(struct mtd_info *mtd, + struct nand_chip *chip, int page) +{ + int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; + int eccsize = chip->ecc.size, length = mtd->oobsize; + int i, len, pos, status = 0, sndcmd = 0, steps = chip->ecc.steps; + const uint8_t *bufpoi = chip->oob_poi; + + /* + * data-ecc-data-ecc ... ecc-oob + * or + * data-pad-ecc-pad-data-pad .... ecc-pad-oob + */ + if (!chip->ecc.prepad && !chip->ecc.postpad) { + pos = steps * (eccsize + chunk); + steps = 0; + } else + pos = eccsize; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, pos, page); + for (i = 0; i < steps; i++) { + if (sndcmd) { + if (mtd->writesize <= 512) { + uint32_t fill = 0xFFFFFFFF; + + len = eccsize; + while (len > 0) { + int num = min_t(int, len, 4); + chip->write_buf(mtd, (uint8_t *)&fill, + num); + len -= num; + } + } else { + pos = eccsize + i * (eccsize + chunk); + chip->cmdfunc(mtd, NAND_CMD_RNDIN, pos, -1); + } + } else + sndcmd = 1; + len = min_t(int, length, chunk); + chip->write_buf(mtd, bufpoi, len); + bufpoi += len; + length -= len; + } + if (length > 0) + chip->write_buf(mtd, bufpoi, length); + + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + + return status & NAND_STATUS_FAIL ? -EIO : 0; +} +#endif + +void nand_init_ecc_hw_syndrome(struct nand_chip *chip) +{ + /* Use standard syndrome read/write page function ? */ + if (!chip->ecc.read_page) + chip->ecc.read_page = nand_read_page_syndrome; + if (!chip->ecc.read_oob) + chip->ecc.read_oob = nand_read_oob_syndrome; +#ifdef CONFIG_NAND_WRITE + if (!chip->ecc.write_page) + chip->ecc.write_page = nand_write_page_syndrome; + if (!chip->ecc.write_oob) + chip->ecc.write_oob = nand_write_oob_syndrome; +#endif +} diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index f95975c9..cb53fc61 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -12,6 +12,13 @@ */ #include <common.h> #include <linux/mtd/nand.h> + +#ifdef CONFIG_NAND_INFO +#define __NANDSTR(str) str +#else +#define __NANDSTR(str) "" +#endif + /* * Chip ID list * @@ -26,47 +33,47 @@ struct nand_flash_dev nand_flash_ids[] = { #ifdef CONFIG_MTD_NAND_MUSEUM_IDS - {"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0}, - {"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0}, - {"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0}, - {"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, 0}, - {"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, 0}, - {"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, 0}, - {"NAND 4MiB 3,3V 8-bit", 0xd5, 512, 4, 0x2000, 0}, - {"NAND 4MiB 3,3V 8-bit", 0xe3, 512, 4, 0x2000, 0}, - {"NAND 4MiB 3,3V 8-bit", 0xe5, 512, 4, 0x2000, 0}, - {"NAND 8MiB 3,3V 8-bit", 0xd6, 512, 8, 0x2000, 0}, - - {"NAND 8MiB 1,8V 8-bit", 0x39, 512, 8, 0x2000, 0}, - {"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0}, - {"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16}, - {"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16}, + {__NANDSTR("NAND 1MiB 5V 8-bit"), 0x6e, 256, 1, 0x1000, 0}, + {__NANDSTR("NAND 2MiB 5V 8-bit"), 0x64, 256, 2, 0x1000, 0}, + {__NANDSTR("NAND 4MiB 5V 8-bit"), 0x6b, 512, 4, 0x2000, 0}, + {__NANDSTR("NAND 1MiB 3,3V 8-bit"), 0xe8, 256, 1, 0x1000, 0}, + {__NANDSTR("NAND 1MiB 3,3V 8-bit"), 0xec, 256, 1, 0x1000, 0}, + {__NANDSTR("NAND 2MiB 3,3V 8-bit"), 0xea, 256, 2, 0x1000, 0}, + {__NANDSTR("NAND 4MiB 3,3V 8-bit"), 0xd5, 512, 4, 0x2000, 0}, + {__NANDSTR("NAND 4MiB 3,3V 8-bit"), 0xe3, 512, 4, 0x2000, 0}, + {__NANDSTR("NAND 4MiB 3,3V 8-bit"), 0xe5, 512, 4, 0x2000, 0}, + {__NANDSTR("NAND 8MiB 3,3V 8-bit"), 0xd6, 512, 8, 0x2000, 0}, + + {__NANDSTR("NAND 8MiB 1,8V 8-bit"), 0x39, 512, 8, 0x2000, 0}, + {__NANDSTR("NAND 8MiB 3,3V 8-bit"), 0xe6, 512, 8, 0x2000, 0}, + {__NANDSTR("NAND 8MiB 1,8V 16-bit"), 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16}, + {__NANDSTR("NAND 8MiB 3,3V 16-bit"), 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16}, #endif - {"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0}, - {"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0}, - {"NAND 16MiB 1,8V 16-bit", 0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16}, - {"NAND 16MiB 3,3V 16-bit", 0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16}, + {__NANDSTR("NAND 16MiB 1,8V 8-bit"), 0x33, 512, 16, 0x4000, 0}, + {__NANDSTR("NAND 16MiB 3,3V 8-bit"), 0x73, 512, 16, 0x4000, 0}, + {__NANDSTR("NAND 16MiB 1,8V 16-bit"), 0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16}, + {__NANDSTR("NAND 16MiB 3,3V 16-bit"), 0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16}, - {"NAND 32MiB 1,8V 8-bit", 0x35, 512, 32, 0x4000, 0}, - {"NAND 32MiB 3,3V 8-bit", 0x75, 512, 32, 0x4000, 0}, - {"NAND 32MiB 1,8V 16-bit", 0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16}, - {"NAND 32MiB 3,3V 16-bit", 0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16}, + {__NANDSTR("NAND 32MiB 1,8V 8-bit"), 0x35, 512, 32, 0x4000, 0}, + {__NANDSTR("NAND 32MiB 3,3V 8-bit"), 0x75, 512, 32, 0x4000, 0}, + {__NANDSTR("NAND 32MiB 1,8V 16-bit"), 0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16}, + {__NANDSTR("NAND 32MiB 3,3V 16-bit"), 0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16}, - {"NAND 64MiB 1,8V 8-bit", 0x36, 512, 64, 0x4000, 0}, - {"NAND 64MiB 3,3V 8-bit", 0x76, 512, 64, 0x4000, 0}, - {"NAND 64MiB 1,8V 16-bit", 0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16}, - {"NAND 64MiB 3,3V 16-bit", 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16}, + {__NANDSTR("NAND 64MiB 1,8V 8-bit"), 0x36, 512, 64, 0x4000, 0}, + {__NANDSTR("NAND 64MiB 3,3V 8-bit"), 0x76, 512, 64, 0x4000, 0}, + {__NANDSTR("NAND 64MiB 1,8V 16-bit"), 0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16}, + {__NANDSTR("NAND 64MiB 3,3V 16-bit"), 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16}, - {"NAND 128MiB 1,8V 8-bit", 0x78, 512, 128, 0x4000, 0}, - {"NAND 128MiB 1,8V 8-bit", 0x39, 512, 128, 0x4000, 0}, - {"NAND 128MiB 3,3V 8-bit", 0x79, 512, 128, 0x4000, 0}, - {"NAND 128MiB 1,8V 16-bit", 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16}, - {"NAND 128MiB 1,8V 16-bit", 0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16}, - {"NAND 128MiB 3,3V 16-bit", 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16}, - {"NAND 128MiB 3,3V 16-bit", 0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + {__NANDSTR("NAND 128MiB 1,8V 8-bit"), 0x78, 512, 128, 0x4000, 0}, + {__NANDSTR("NAND 128MiB 1,8V 8-bit"), 0x39, 512, 128, 0x4000, 0}, + {__NANDSTR("NAND 128MiB 3,3V 8-bit"), 0x79, 512, 128, 0x4000, 0}, + {__NANDSTR("NAND 128MiB 1,8V 16-bit"), 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + {__NANDSTR("NAND 128MiB 1,8V 16-bit"), 0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + {__NANDSTR("NAND 128MiB 3,3V 16-bit"), 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + {__NANDSTR("NAND 128MiB 3,3V 16-bit"), 0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16}, - {"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0}, + {__NANDSTR("NAND 256MiB 3,3V 8-bit"), 0x71, 512, 256, 0x4000, 0}, /* * These are the new chips with large page size. The pagesize and the @@ -76,40 +83,40 @@ struct nand_flash_dev nand_flash_ids[] = { #define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) /*512 Megabit */ - {"NAND 64MiB 1,8V 8-bit", 0xA2, 0, 64, 0, LP_OPTIONS}, - {"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, LP_OPTIONS}, - {"NAND 64MiB 1,8V 16-bit", 0xB2, 0, 64, 0, LP_OPTIONS16}, - {"NAND 64MiB 3,3V 16-bit", 0xC2, 0, 64, 0, LP_OPTIONS16}, + {__NANDSTR("NAND 64MiB 1,8V 8-bit"), 0xA2, 0, 64, 0, LP_OPTIONS}, + {__NANDSTR("NAND 64MiB 3,3V 8-bit"), 0xF2, 0, 64, 0, LP_OPTIONS}, + {__NANDSTR("NAND 64MiB 1,8V 16-bit"), 0xB2, 0, 64, 0, LP_OPTIONS16}, + {__NANDSTR("NAND 64MiB 3,3V 16-bit"), 0xC2, 0, 64, 0, LP_OPTIONS16}, /* 1 Gigabit */ - {"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, LP_OPTIONS}, - {"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, LP_OPTIONS}, - {"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, LP_OPTIONS16}, - {"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, LP_OPTIONS16}, + {__NANDSTR("NAND 128MiB 1,8V 8-bit"), 0xA1, 0, 128, 0, LP_OPTIONS}, + {__NANDSTR("NAND 128MiB 3,3V 8-bit"), 0xF1, 0, 128, 0, LP_OPTIONS}, + {__NANDSTR("NAND 128MiB 1,8V 16-bit"), 0xB1, 0, 128, 0, LP_OPTIONS16}, + {__NANDSTR("NAND 128MiB 3,3V 16-bit"), 0xC1, 0, 128, 0, LP_OPTIONS16}, /* 2 Gigabit */ - {"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, LP_OPTIONS}, - {"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, LP_OPTIONS}, - {"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, LP_OPTIONS16}, - {"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, LP_OPTIONS16}, + {__NANDSTR("NAND 256MiB 1,8V 8-bit"), 0xAA, 0, 256, 0, LP_OPTIONS}, + {__NANDSTR("NAND 256MiB 3,3V 8-bit"), 0xDA, 0, 256, 0, LP_OPTIONS}, + {__NANDSTR("NAND 256MiB 1,8V 16-bit"), 0xBA, 0, 256, 0, LP_OPTIONS16}, + {__NANDSTR("NAND 256MiB 3,3V 16-bit"), 0xCA, 0, 256, 0, LP_OPTIONS16}, /* 4 Gigabit */ - {"NAND 512MiB 1,8V 8-bit", 0xAC, 0, 512, 0, LP_OPTIONS}, - {"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, LP_OPTIONS}, - {"NAND 512MiB 1,8V 16-bit", 0xBC, 0, 512, 0, LP_OPTIONS16}, - {"NAND 512MiB 3,3V 16-bit", 0xCC, 0, 512, 0, LP_OPTIONS16}, + {__NANDSTR("NAND 512MiB 1,8V 8-bit"), 0xAC, 0, 512, 0, LP_OPTIONS}, + {__NANDSTR("NAND 512MiB 3,3V 8-bit"), 0xDC, 0, 512, 0, LP_OPTIONS}, + {__NANDSTR("NAND 512MiB 1,8V 16-bit"), 0xBC, 0, 512, 0, LP_OPTIONS16}, + {__NANDSTR("NAND 512MiB 3,3V 16-bit"), 0xCC, 0, 512, 0, LP_OPTIONS16}, /* 8 Gigabit */ - {"NAND 1GiB 1,8V 8-bit", 0xA3, 0, 1024, 0, LP_OPTIONS}, - {"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, LP_OPTIONS}, - {"NAND 1GiB 1,8V 16-bit", 0xB3, 0, 1024, 0, LP_OPTIONS16}, - {"NAND 1GiB 3,3V 16-bit", 0xC3, 0, 1024, 0, LP_OPTIONS16}, + {__NANDSTR("NAND 1GiB 1,8V 8-bit"), 0xA3, 0, 1024, 0, LP_OPTIONS}, + {__NANDSTR("NAND 1GiB 3,3V 8-bit"), 0xD3, 0, 1024, 0, LP_OPTIONS}, + {__NANDSTR("NAND 1GiB 1,8V 16-bit"), 0xB3, 0, 1024, 0, LP_OPTIONS16}, + {__NANDSTR("NAND 1GiB 3,3V 16-bit"), 0xC3, 0, 1024, 0, LP_OPTIONS16}, /* 16 Gigabit */ - {"NAND 2GiB 1,8V 8-bit", 0xA5, 0, 2048, 0, LP_OPTIONS}, - {"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, LP_OPTIONS}, - {"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, LP_OPTIONS16}, - {"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, LP_OPTIONS16}, + {__NANDSTR("NAND 2GiB 1,8V 8-bit"), 0xA5, 0, 2048, 0, LP_OPTIONS}, + {__NANDSTR("NAND 2GiB 3,3V 8-bit"), 0xD5, 0, 2048, 0, LP_OPTIONS}, + {__NANDSTR("NAND 2GiB 1,8V 16-bit"), 0xB5, 0, 2048, 0, LP_OPTIONS16}, + {__NANDSTR("NAND 2GiB 3,3V 16-bit"), 0xC5, 0, 2048, 0, LP_OPTIONS16}, /* * Renesas AND 1 Gigabit. Those chips do not support extended id and @@ -121,7 +128,7 @@ struct nand_flash_dev nand_flash_ids[] = { * erased in one go There are more speed improvements for reads and * writes possible, but not implemented now */ - {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, + {__NANDSTR("AND 128MiB 3,3V 8-bit"), 0x01, 2048, 128, 0x4000, NAND_IS_AND | NAND_NO_AUTOINCR |NAND_NO_READRDY | NAND_4PAGE_ARRAY | BBT_AUTO_REFRESH }, @@ -133,15 +140,15 @@ struct nand_flash_dev nand_flash_ids[] = { * Manufacturer ID list */ struct nand_manufacturers nand_manuf_ids[] = { - {NAND_MFR_TOSHIBA, "Toshiba"}, - {NAND_MFR_SAMSUNG, "Samsung"}, - {NAND_MFR_FUJITSU, "Fujitsu"}, - {NAND_MFR_NATIONAL, "National"}, - {NAND_MFR_RENESAS, "Renesas"}, - {NAND_MFR_STMICRO, "ST Micro"}, - {NAND_MFR_HYNIX, "Hynix"}, - {NAND_MFR_MICRON, "Micron"}, - {NAND_MFR_AMD, "AMD"}, + {NAND_MFR_TOSHIBA, __NANDSTR("Toshiba")}, + {NAND_MFR_SAMSUNG, __NANDSTR("Samsung")}, + {NAND_MFR_FUJITSU, __NANDSTR("Fujitsu")}, + {NAND_MFR_NATIONAL, __NANDSTR("National")}, + {NAND_MFR_RENESAS, __NANDSTR("Renesas")}, + {NAND_MFR_STMICRO, __NANDSTR("ST Micro")}, + {NAND_MFR_HYNIX, __NANDSTR("Hynix")}, + {NAND_MFR_MICRON, __NANDSTR("Micron")}, + {NAND_MFR_AMD, __NANDSTR("AMD")}, {0x0, "Unknown"} }; diff --git a/drivers/mtd/nand/nand_omap_bch_decoder.c b/drivers/mtd/nand/nand_omap_bch_decoder.c new file mode 100644 index 00000000..356f71f0 --- /dev/null +++ b/drivers/mtd/nand/nand_omap_bch_decoder.c @@ -0,0 +1,389 @@ +/* + * drivers/mtd/nand/omap_omap_bch_decoder.c + * + * Whole BCH ECC Decoder (Post hardware generated syndrome decoding) + * + * Copyright (c) 2007 Texas Instruments + * + * Author: Sukumar Ghorai <s-ghorai@xxxxxx + * Michael Fillinger <m-fillinger@xxxxxx> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <common.h> + +#define mm 13 +#define kk_shorten 4096 +#define nn 8191 /* Length of codeword, n = 2**mm - 1 */ + +#define PPP 0x201B /* Primary Polynomial : x^13 + x^4 + x^3 + x + 1 */ +#define P 0x001B /* With omitted x^13 */ +#define POLY 12 /* degree of the primary Polynomial less one */ + +/** + * mpy_mod_gf - GALOIS field multiplier + * Input : A(x), B(x) + * Output : A(x)*B(x) mod P(x) + */ +static unsigned int mpy_mod_gf(unsigned int a, unsigned int b) +{ + unsigned int R = 0; + unsigned int R1 = 0; + unsigned int k = 0; + + for (k = 0; k < mm; k++) { + + R = (R << 1) & 0x1FFE; + if (R1 == 1) + R ^= P; + + if (((a >> (POLY - k)) & 1) == 1) + R ^= b; + + if (k < POLY) + R1 = (R >> POLY) & 1; + } + return R; +} + +/** + * chien - CHIEN search + * + * @location - Error location vector pointer + * + * Inputs : ELP(z) + * No. of found errors + * Size of input codeword + * Outputs : Up to 8 locations + * No. of errors + */ +static int chien(unsigned int select_4_8, int err_nums, + unsigned int err[], unsigned int *location) +{ + int i, count; /* Number of dectected errors */ + /* Contains accumulation of evaluation at x^i (i:1->8) */ + unsigned int gammas[8] = {0}; + unsigned int alpha; + unsigned int bit, ecc_bits; + unsigned int elp_sum; + + ecc_bits = (select_4_8 == 0) ? 52 : 104; + + /* Start evaluation at Alpha**8192 and decreasing */ + for (i = 0; i < 8; i++) + gammas[i] = err[i]; + + count = 0; + for (i = 1; (i <= nn) && (count < err_nums); i++) { + + /* Result of evaluation at root */ + elp_sum = 1 ^ gammas[0] ^ gammas[1] ^ + gammas[2] ^ gammas[3] ^ + gammas[4] ^ gammas[5] ^ + gammas[6] ^ gammas[7]; + + alpha = PPP >> 1; + gammas[0] = mpy_mod_gf(gammas[0], alpha); + alpha = mpy_mod_gf(alpha, (PPP >> 1)); /* x alphha^-2 */ + gammas[1] = mpy_mod_gf(gammas[1], alpha); + alpha = mpy_mod_gf(alpha, (PPP >> 1)); /* x alphha^-2 */ + gammas[2] = mpy_mod_gf(gammas[2], alpha); + alpha = mpy_mod_gf(alpha, (PPP >> 1)); /* x alphha^-3 */ + gammas[3] = mpy_mod_gf(gammas[3], alpha); + alpha = mpy_mod_gf(alpha, (PPP >> 1)); /* x alphha^-4 */ + gammas[4] = mpy_mod_gf(gammas[4], alpha); + alpha = mpy_mod_gf(alpha, (PPP >> 1)); /* x alphha^-5 */ + gammas[5] = mpy_mod_gf(gammas[5], alpha); + alpha = mpy_mod_gf(alpha, (PPP >> 1)); /* x alphha^-6 */ + gammas[6] = mpy_mod_gf(gammas[6], alpha); + alpha = mpy_mod_gf(alpha, (PPP >> 1)); /* x alphha^-7 */ + gammas[7] = mpy_mod_gf(gammas[7], alpha); + + if (elp_sum == 0) { + /* calculate bit position in main data area */ + bit = ((i-1) & ~7)|(7-((i-1) & 7)); + if (i >= 2 * ecc_bits) + location[count++] = + kk_shorten - (bit - 2 * ecc_bits) - 1; + } + } + + /* Failure: No. of detected errors != No. or corrected errors */ + if (count != err_nums) { + count = -1; + printk(KERN_ERR "BCH decoding failed\n"); + } + for (i = 0; i < count; i++) + pr_debug("%d ", location[i]); + + return count; +} + +/* synd : 16 Syndromes + * return: gamaas - Coefficients to the error polynomial + * return: : Number of detected errors +*/ +static unsigned int berlekamp(unsigned int select_4_8, + unsigned int synd[], unsigned int err[]) +{ + int loop, iteration; + unsigned int LL = 0; /* Detected errors */ + unsigned int d = 0; /* Distance between Syndromes and ELP[n](z) */ + unsigned int invd = 0; /* Inverse of d */ + /* Intermediate ELP[n](z). + * Final ELP[n](z) is Error Location Polynomial + */ + unsigned int gammas[16] = {0}; + /* Intermediate normalized ELP[n](z) : D[n](z) */ + unsigned int D[16] = {0}; + /* Temporary value that holds an ELP[n](z) coefficient */ + unsigned int next_gamma = 0; + + int e = 0; + unsigned int sign = 0; + unsigned int u = 0; + unsigned int v = 0; + unsigned int C1 = 0, C2 = 0; + unsigned int ss = 0; + unsigned int tmp_v = 0, tmp_s = 0; + unsigned int tmp_poly; + + /*-------------- Step 0 ------------------*/ + for (loop = 0; loop < 16; loop++) + gammas[loop] = 0; + gammas[0] = 1; + D[1] = 1; + + iteration = 0; + LL = 0; + while ((iteration < ((select_4_8+1)*2*4)) && + (LL <= ((select_4_8+1)*4))) { + + pr_debug("\nIteration.............%d\n", iteration); + d = 0; + /* Step: 0 */ + for (loop = 0; loop <= LL; loop++) { + tmp_poly = mpy_mod_gf( + gammas[loop], synd[iteration - loop]); + d ^= tmp_poly; + pr_debug("%02d. s=0 LL=%x poly %x\n", + loop, LL, tmp_poly); + } + + /* Step 1: 1 cycle only to perform inversion */ + v = d << 1; + e = -1; + sign = 1; + ss = 0x2000; + invd = 0; + u = PPP; + for (loop = 0; (d != 0) && (loop <= (2 * POLY)); loop++) { + pr_debug("%02d. s=1 LL=%x poly NULL\n", + loop, LL); + C1 = (v >> 13) & 1; + C2 = C1 & sign; + + sign ^= C2 ^ (e == 0); + + tmp_v = v; + tmp_s = ss; + + if (C1 == 1) { + v ^= u; + ss ^= invd; + } + v = (v << 1) & 0x3FFF; + if (C2 == 1) { + u = tmp_v; + invd = tmp_s; + e = -e; + } + invd >>= 1; + e--; + } + + for (loop = 0; (d != 0) && (loop <= (iteration + 1)); loop++) { + /* Step 2 + * Interleaved with Step 3, if L<(n-k) + * invd: Update of ELP[n](z) = ELP[n-1](z) - d.D[n-1](z) + */ + + /* Holds value of ELP coefficient until precedent + * value does not have to be used anymore + */ + tmp_poly = mpy_mod_gf(d, D[loop]); + pr_debug("%02d. s=2 LL=%x poly %x\n", + loop, LL, tmp_poly); + + next_gamma = gammas[loop] ^ tmp_poly; + if ((2 * LL) < (iteration + 1)) { + /* Interleaving with Step 3 + * for parallelized update of ELP(z) and D(z) + */ + } else { + /* Update of ELP(z) only -> stay in Step 2 */ + gammas[loop] = next_gamma; + if (loop == (iteration + 1)) { + /* to step 4 */ + break; + } + } + + /* Step 3 + * Always interleaved with Step 2 (case when L<(n-k)) + * Update of D[n-1](z) = ELP[n-1](z)/d + */ + D[loop] = mpy_mod_gf(gammas[loop], invd); + pr_debug("%02d. s=3 LL=%x poly %x\n", + loop, LL, D[loop]); + + /* Can safely update ELP[n](z) */ + gammas[loop] = next_gamma; + + if (loop == (iteration + 1)) { + /* If update finished */ + LL = iteration - LL + 1; + /* to step 4 */ + break; + } + /* Else, interleaving to step 2*/ + } + + /* Step 4: Update D(z): i:0->L */ + /* Final update of D[n](z) = D[n](z).z*/ + for (loop = 0; loop < 15; loop++) /* Left Shift */ + D[15 - loop] = D[14 - loop]; + + D[0] = 0; + + iteration++; + } /* while */ + + /* Processing finished, copy ELP to final registers : 0->2t-1*/ + for (loop = 0; loop < 8; loop++) + err[loop] = gammas[loop+1]; + + pr_debug("\n Err poly:"); + for (loop = 0; loop < 8; loop++) + pr_debug("0x%x ", err[loop]); + + return LL; +} + +/* + * syndrome - Generate syndrome components from hw generate syndrome + * r(x) = c(x) + e(x) + * s(x) = c(x) mod g(x) + e(x) mod g(x) = e(x) mod g(x) + * so receiver checks if the syndrome s(x) = r(x) mod g(x) is equal to zero. + * unsigned int s[16]; - Syndromes + */ +static void syndrome(unsigned int select_4_8, + unsigned char *ecc, unsigned int syn[]) +{ + unsigned int k, l, t; + unsigned int alpha_bit, R_bit; + int ecc_pos, ecc_min; + + /* 2t-1 = 15 (for t=8) minimal polynomials of the first 15 powers of a + * primitive elemmants of GF(m); Even powers minimal polynomials are + * duplicate of odd powers' minimal polynomials. + * Odd powers of alpha (1 to 15) + */ + unsigned int pow_alpha[8] = {0x0002, 0x0008, 0x0020, 0x0080, + 0x0200, 0x0800, 0x001B, 0x006C}; + + pr_debug("\n ECC[0..n]: "); + for (k = 0; k < 13; k++) + pr_debug("0x%x ", ecc[k]); + + if (select_4_8 == 0) { + t = 4; + ecc_pos = 55; /* bits(52-bits): 55->4 */ + ecc_min = 4; + } else { + t = 8; + ecc_pos = 103; /* bits: 103->0 */ + ecc_min = 0; + } + + /* total numbber of syndrom to be used is 2t */ + /* Step1: calculate the odd syndrome(s) */ + R_bit = ((ecc[ecc_pos/8] >> (7 - ecc_pos%8)) & 1); + ecc_pos--; + for (k = 0; k < t; k++) + syn[2 * k] = R_bit; + + while (ecc_pos >= ecc_min) { + R_bit = ((ecc[ecc_pos/8] >> (7 - ecc_pos%8)) & 1); + ecc_pos--; + + for (k = 0; k < t; k++) { + /* Accumulate value of x^i at alpha^(2k+1) */ + if (R_bit == 1) + syn[2*k] ^= pow_alpha[k]; + + /* Compute a**(2k+1), using LSFR */ + for (l = 0; l < (2 * k + 1); l++) { + alpha_bit = (pow_alpha[k] >> POLY) & 1; + pow_alpha[k] = (pow_alpha[k] << 1) & 0x1FFF; + if (alpha_bit == 1) + pow_alpha[k] ^= P; + } + } + } + + /* Step2: calculate the even syndrome(s) + * Compute S(a), where a is an even power of alpha + * Evenry even power of primitive element has the same minimal + * polynomial as some odd power of elemets. + * And based on S(a^2) = S^2(a) + */ + for (k = 0; k < t; k++) + syn[2*k+1] = mpy_mod_gf(syn[k], syn[k]); + + pr_debug("\n Syndromes: "); + for (k = 0; k < 16; k++) + pr_debug("0x%x ", syn[k]); +} + +/** + * decode_bch - BCH decoder for 4- and 8-bit error correction + * + * @ecc - ECC syndrome generated by hw BCH engine + * @err_loc - pointer to error location array + * + * This function does post sydrome generation (hw generated) decoding + * for:- + * Dimension of Galoise Field: m = 13 + * Length of codeword: n = 2**m - 1 + * Number of errors that can be corrected: 4- or 8-bits + * Length of information bit: kk = nn - rr + */ +int decode_bch(int select_4_8, unsigned char *ecc, unsigned int *err_loc) +{ + int no_of_err; + unsigned int syn[16] = {0,}; /* 16 Syndromes */ + unsigned int err_poly[8] = {0,}; + /* Coefficients to the error polynomial + * ELP(x) = 1 + err0.x + err1.x^2 + ... + err7.x^8 + */ + + /* Decoding involes three steps + * 1. Compute the syndrom from the received codeword, + * 2. Find the error location polynomial from a set of equations + * derived from the syndrome, + * 3. Use the error location polynomial to identify errants bits, + * + * And correction done by bit flips using error location and expected + * to be outseide of this implementation. + */ + syndrome(select_4_8, ecc, syn); + no_of_err = berlekamp(select_4_8, syn, err_poly); + if (no_of_err <= (4 << select_4_8)) + no_of_err = chien(select_4_8, no_of_err, err_poly, err_loc); + + return no_of_err; +} diff --git a/drivers/mtd/nand/nand_omap_gpmc.c b/drivers/mtd/nand/nand_omap_gpmc.c index 7c9bc320..fc485514 100644 --- a/drivers/mtd/nand/nand_omap_gpmc.c +++ b/drivers/mtd/nand/nand_omap_gpmc.c @@ -76,15 +76,15 @@ #include <mach/gpmc.h> #include <mach/gpmc_nand.h> -/* Enable me to get tons of debug messages -for use without jtag */ -#if 0 -#define gpmcnand_dbg(FORMAT, ARGS...) fprintf(stdout,\ - "gpmc_nand:%s:%d:Entry:"FORMAT"\n",\ - __func__, __LINE__, ARGS) -#else -#define gpmcnand_dbg(FORMAT, ARGS...) -#endif -#define gpmcnand_err(ARGS...) fprintf(stderr, "omapnand: " ARGS); +int decode_bch(int select_4_8, unsigned char *ecc, unsigned int *err_loc); + +static char *ecc_mode_strings[] = { + "software", + "hamming_hw_romcode", + "bch4_hw", + "bch8_hw", + "bch8_hw_romcode", +}; /** internal structure maintained for nand information */ struct gpmc_nand_info { @@ -103,10 +103,21 @@ struct gpmc_nand_info { unsigned inuse:1; unsigned wait_pol:1; unsigned char ecc_parity_pairs; - unsigned int ecc_config; + enum gpmc_ecc_mode ecc_mode; }; /* Typical BOOTROM oob layouts-requires hwecc **/ +static struct nand_ecclayout omap_oobinfo; +/* Define some generic bad / good block scan pattern which are used + * while scanning a device for factory marked good / bad blocks + */ +static uint8_t scan_ff_pattern[] = { 0xff }; +static struct nand_bbt_descr bb_descrip_flashbased = { + .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, + .offs = 0, + .len = 1, + .pattern = scan_ff_pattern, +}; /** Large Page x8 NAND device Layout */ static struct nand_ecclayout ecc_lp_x8 = { @@ -169,14 +180,14 @@ static int omap_dev_ready(struct mtd_info *mtd) uint64_t start = get_time_ns(); unsigned long comp; - gpmcnand_dbg("mtd=%x", (unsigned int)mtd); + debug("mtd=%x", (unsigned int)mtd); /* What do we mean by assert and de-assert? */ comp = (oinfo->wait_pol == NAND_WAITPOL_HIGH) ? oinfo->wait_mon_mask : 0x0; while (1) { /* Breakout condition */ if (is_timeout(start, oinfo->timeout)) { - gpmcnand_err("timedout\n"); + debug("timedout\n"); return -ETIMEDOUT; } /* if the wait is released, we are good to go */ @@ -201,7 +212,7 @@ static void gpmc_nand_wp(struct gpmc_nand_info *oinfo, int mode) { unsigned long config = readl(oinfo->gpmc_base + GPMC_CFG); - gpmcnand_dbg("mode=%x", mode); + debug("mode=%x", mode); if (mode) config &= ~(NAND_WP_BIT); /* WP is ON */ else @@ -226,7 +237,7 @@ static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) { struct nand_chip *nand = (struct nand_chip *)(mtd->priv); struct gpmc_nand_info *oinfo = (struct gpmc_nand_info *)(nand->priv); - gpmcnand_dbg("mtd=%x nand=%x cmd=%x ctrl = %x", (unsigned int)mtd, nand, + debug("mtd=%x nand=%x cmd=%x ctrl = %x", (unsigned int)mtd, nand, cmd, ctrl); switch (ctrl) { case NAND_CTRL_CHANGE | NAND_CTRL_CLE: @@ -260,12 +271,72 @@ static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) */ static unsigned int gen_true_ecc(u8 *ecc_buf) { - gpmcnand_dbg("ecc_buf=%x 1, 2 3 = %x %x %x", (unsigned int)ecc_buf, + debug("ecc_buf=%x 1, 2 3 = %x %x %x", (unsigned int)ecc_buf, ecc_buf[0], ecc_buf[1], ecc_buf[2]); return ecc_buf[0] | (ecc_buf[1] << 16) | ((ecc_buf[2] & 0xF0) << 20) | ((ecc_buf[2] & 0x0F) << 8); } +static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat, + uint8_t *ecc_code) +{ + struct nand_chip *nand = (struct nand_chip *)(mtd->priv); + struct gpmc_nand_info *oinfo = (struct gpmc_nand_info *)(nand->priv); + unsigned int reg; + unsigned int val1 = 0x0, val2 = 0x0; + unsigned int val3 = 0x0, val4 = 0x0; + int i; + int ecc_size = 8; + + switch (oinfo->ecc_mode) { + case OMAP_ECC_BCH4_CODE_HW: + ecc_size = 4; + /* fall through */ + case OMAP_ECC_BCH8_CODE_HW: + case OMAP_ECC_BCH8_CODE_HW_ROMCODE: + for (i = 0; i < 4; i++) { + /* + * Reading HW ECC_BCH_Results + * 0x240-0x24C, 0x250-0x25C, 0x260-0x26C, 0x270-0x27C + */ + reg = GPMC_ECC_BCH_RESULT_0 + (0x10 * i); + val1 = readl(oinfo->gpmc_base + reg); + val2 = readl(oinfo->gpmc_base + reg + 4); + if (ecc_size == 8) { + val3 = readl(oinfo->gpmc_base +reg + 8); + val4 = readl(oinfo->gpmc_base + reg + 12); + + *ecc_code++ = (val4 & 0xFF); + *ecc_code++ = ((val3 >> 24) & 0xFF); + *ecc_code++ = ((val3 >> 16) & 0xFF); + *ecc_code++ = ((val3 >> 8) & 0xFF); + *ecc_code++ = (val3 & 0xFF); + *ecc_code++ = ((val2 >> 24) & 0xFF); + } + *ecc_code++ = ((val2 >> 16) & 0xFF); + *ecc_code++ = ((val2 >> 8) & 0xFF); + *ecc_code++ = (val2 & 0xFF); + *ecc_code++ = ((val1 >> 24) & 0xFF); + *ecc_code++ = ((val1 >> 16) & 0xFF); + *ecc_code++ = ((val1 >> 8) & 0xFF); + *ecc_code++ = (val1 & 0xFF); + } + break; + case OMAP_ECC_HAMMING_CODE_HW_ROMCODE: + /* read ecc result */ + val1 = readl(oinfo->gpmc_base + GPMC_ECC1_RESULT); + *ecc_code++ = val1; /* P128e, ..., P1e */ + *ecc_code++ = val1 >> 16; /* P128o, ..., P1o */ + /* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */ + *ecc_code++ = ((val1 >> 8) & 0x0f) | ((val1 >> 20) & 0xf0); + break; + default: + return -EINVAL; + } + + return 0; +} + /** * @brief Compares the ecc read from nand spare area with ECC * registers values and corrects one bit error if it has occured @@ -288,107 +359,302 @@ static int omap_correct_data(struct mtd_info *mtd, uint8_t *dat, unsigned char bit; struct nand_chip *nand = (struct nand_chip *)(mtd->priv); struct gpmc_nand_info *oinfo = (struct gpmc_nand_info *)(nand->priv); + int ecc_type = OMAP_ECC_BCH8_CODE_HW; + int i, j, eccsize, eccflag, count; + unsigned int err_loc[8]; + int blockCnt = 0; + int select_4_8; - gpmcnand_dbg("mtd=%x dat=%x read_ecc=%x calc_ecc=%x", (unsigned int)mtd, + debug("mtd=%x dat=%x read_ecc=%x calc_ecc=%x", (unsigned int)mtd, (unsigned int)dat, (unsigned int)read_ecc, (unsigned int)calc_ecc); - /* Regenerate the orginal ECC */ - orig_ecc = gen_true_ecc(read_ecc); - new_ecc = gen_true_ecc(calc_ecc); - /* Get the XOR of real ecc */ - res = orig_ecc ^ new_ecc; - if (res) { - /* Get the hamming width */ - hm = hweight32(res); - /* Single bit errors can be corrected! */ - if (hm == oinfo->ecc_parity_pairs) { - /* Correctable data! */ - parity_bits = res >> 16; - bit = (parity_bits & 0x7); - byte = (parity_bits >> 3) & 0x1FF; - /* Flip the bit to correct */ - dat[byte] ^= (0x1 << bit); - - } else if (hm == 1) { - gpmcnand_err("Ecc is wrong\n"); - /* ECC itself is corrupted */ - return 2; - } else { - gpmcnand_err("bad compare! failed\n"); - /* detected 2 bit error */ - return -1; + if ((nand->ecc.mode == NAND_ECC_HW) && + (nand->ecc.size == 2048)) + blockCnt = 4; + else + blockCnt = 1; + + switch (oinfo->ecc_mode) { + case OMAP_ECC_HAMMING_CODE_HW_ROMCODE: + if (read_ecc[0] == 0xff && read_ecc[1] == 0xff && + read_ecc[2] == 0xff && calc_ecc[0] == 0x0 && + calc_ecc[1] == 0x0 && calc_ecc[0] == 0x0) + break; + + /* Regenerate the orginal ECC */ + orig_ecc = gen_true_ecc(read_ecc); + new_ecc = gen_true_ecc(calc_ecc); + /* Get the XOR of real ecc */ + res = orig_ecc ^ new_ecc; + if (res) { + /* Get the hamming width */ + hm = hweight32(res); + /* Single bit errors can be corrected! */ + if (hm == oinfo->ecc_parity_pairs) { + /* Correctable data! */ + parity_bits = res >> 16; + bit = (parity_bits & 0x7); + byte = (parity_bits >> 3) & 0x1FF; + /* Flip the bit to correct */ + dat[byte] ^= (0x1 << bit); + } else if (hm == 1) { + printf("Ecc is wrong\n"); + /* ECC itself is corrupted */ + return 2; + } else { + printf("bad compare! failed\n"); + /* detected 2 bit error */ + return -1; + } } + break; + case OMAP_ECC_BCH8_CODE_HW: + case OMAP_ECC_BCH8_CODE_HW_ROMCODE: + eccsize = 13; + select_4_8 = 1; + /* fall through */ + case OMAP_ECC_BCH4_CODE_HW: + if (ecc_type == OMAP_ECC_BCH4_CODE_HW) { + eccsize = 7; + select_4_8 = 0; + } + + omap_calculate_ecc(mtd, dat, calc_ecc); + for (i = 0; i < blockCnt; i++) { + /* check if any ecc error */ + eccflag = 0; + for (j = 0; (j < eccsize) && (eccflag == 0); j++) + if (calc_ecc[j] != 0) + eccflag = 1; + + if (eccflag == 1) { + eccflag = 0; + for (j = 0; (j < eccsize) && + (eccflag == 0); j++) + if (read_ecc[j] != 0xFF) + eccflag = 1; + } + + count = 0; + if (eccflag == 1) + count = decode_bch(select_4_8, calc_ecc, err_loc); + + for (j = 0; j < count; j++) { + if (err_loc[j] < 4096) + dat[err_loc[j] >> 3] ^= + 1 << (err_loc[j] & 7); + /* else, not interested to correct ecc */ + } + + calc_ecc = calc_ecc + eccsize; + read_ecc = read_ecc + eccsize; + dat += 512; + } + break; + default: + return -EINVAL; } - return 0; -} -/** - * @brief Using noninverted ECC can be considered ugly since writing a blank - * page ie. padding will clear the ECC bytes. This is no problem as long - * nobody is trying to write data on the seemingly unused page. Reading - * an erased page will produce an ECC mismatch between generated and read - * ECC bytes that has to be dealt with separately. - * - * @param mtd - mtd info structure - * @param dat data being written - * @param ecc_code ecc code returned back to nand layer - * - * @return 0 - */ -static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat, - uint8_t *ecc_code) -{ - struct nand_chip *nand = (struct nand_chip *)(mtd->priv); - struct gpmc_nand_info *oinfo = (struct gpmc_nand_info *)(nand->priv); - unsigned int val; - gpmcnand_dbg("mtd=%x dat=%x ecc_code=%x", (unsigned int)mtd, - (unsigned int)dat, (unsigned int)ecc_code); - debug("ecc 0 1 2 = %x %x %x", ecc_code[0], ecc_code[1], ecc_code[2]); - - /* Since we smartly tell mtd driver to use eccsize of 512, only - * ECC Reg1 will be used.. we just read that */ - val = readl(oinfo->gpmc_base + GPMC_ECC1_RESULT); - ecc_code[0] = val & 0xFF; - ecc_code[1] = (val >> 16) & 0xFF; - ecc_code[2] = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0); - - /* Stop reading anymore ECC vals and clear old results - * enable will be called if more reads are required */ - writel(0x000, oinfo->gpmc_base + GPMC_ECC_CONFIG); return 0; } -/* - * omap_enable_ecc - This function enables the hardware ecc functionality - * @param mtd - mtd info structure - * @param mode - Read/Write mode - */ static void omap_enable_hwecc(struct mtd_info *mtd, int mode) { struct nand_chip *nand = (struct nand_chip *)(mtd->priv); struct gpmc_nand_info *oinfo = (struct gpmc_nand_info *)(nand->priv); - gpmcnand_dbg("mtd=%x mode=%x", (unsigned int)mtd, mode); - switch (mode) { - case NAND_ECC_READ: - case NAND_ECC_WRITE: - /* Clear the ecc result registers - * select ecc reg as 1 - */ - writel(0x101, oinfo->gpmc_base + GPMC_ECC_CONTROL); + unsigned int bch_mod = 0, bch_wrapmode = 0, eccsize1 = 0, eccsize0 = 0; + unsigned int ecc_conf_val = 0, ecc_size_conf_val = 0; + int dev_width = nand->options & NAND_BUSWIDTH_16 ? 1 : 0; + int ecc_size = nand->ecc.size; + int cs = 0; + + switch (oinfo->ecc_mode) { + case OMAP_ECC_BCH4_CODE_HW: + if (mode == NAND_ECC_READ) { + eccsize1 = 0xD; eccsize0 = 0x48; + bch_mod = 0; + bch_wrapmode = 0x09; + } else { + eccsize1 = 0x20; eccsize0 = 0x00; + bch_mod = 0; + bch_wrapmode = 0x06; + } + break; + case OMAP_ECC_BCH8_CODE_HW: + case OMAP_ECC_BCH8_CODE_HW_ROMCODE: + if (mode == NAND_ECC_READ) { + eccsize1 = 0x1A; eccsize0 = 0x18; + bch_mod = 1; + bch_wrapmode = 0x04; + } else { + eccsize1 = 0x20; eccsize0 = 0x00; + bch_mod = 1; + bch_wrapmode = 0x06; + } + break; + case OMAP_ECC_HAMMING_CODE_HW_ROMCODE: + eccsize1 = ((ecc_size >> 1) - 1) << 22; + break; + case OMAP_ECC_SOFT: + return; + } + + /* clear ecc and enable bits */ + if (oinfo->ecc_mode == OMAP_ECC_HAMMING_CODE_HW_ROMCODE) { + writel(0x00000101, oinfo->gpmc_base + GPMC_ECC_CONTROL); /* Size 0 = 0xFF, Size1 is 0xFF - both are 512 bytes * tell all regs to generate size0 sized regs * we just have a single ECC engine for all CS */ - writel(0x3FCFF000, oinfo->gpmc_base + - GPMC_ECC_SIZE_CONFIG); - writel(oinfo->ecc_config, oinfo->gpmc_base + - GPMC_ECC_CONFIG); + ecc_size_conf_val = 0x3FCFF000; + ecc_conf_val = (dev_width << 7) | (cs << 1) | (0x1); + } else { + writel(0x1, oinfo->gpmc_base + GPMC_ECC_CONTROL); + ecc_size_conf_val = (eccsize1 << 22) | (eccsize0 << 12); + ecc_conf_val = ((0x01 << 16) | (bch_mod << 12) + | (bch_wrapmode << 8) | (dev_width << 7) + | (0x03 << 4) | (cs << 1) | (0x1)); + } + + writel(ecc_size_conf_val, oinfo->gpmc_base + GPMC_ECC_SIZE_CONFIG); + writel(ecc_conf_val, oinfo->gpmc_base + GPMC_ECC_CONFIG); + writel(0x00000101, oinfo->gpmc_base + GPMC_ECC_CONTROL); +} + +static int omap_gpmc_eccmode(struct gpmc_nand_info *oinfo, + enum gpmc_ecc_mode mode) +{ + struct mtd_info *minfo = &oinfo->minfo; + struct nand_chip *nand = &oinfo->nand; + int offset; + int i, j; + + if (nand->options & NAND_BUSWIDTH_16) + nand->badblock_pattern = &bb_descrip_flashbased; + else + nand->badblock_pattern = NULL; + + if (oinfo->nand.options & NAND_BUSWIDTH_16) + offset = 2; + else + offset = 1; + + if (mode != OMAP_ECC_SOFT) { + nand->ecc.layout = &omap_oobinfo; + nand->ecc.calculate = omap_calculate_ecc; + nand->ecc.hwctl = omap_enable_hwecc; + nand->ecc.correct = omap_correct_data; + nand->ecc.read_page = NULL; + nand->ecc.write_page = NULL; + nand->ecc.read_oob = NULL; + nand->ecc.write_oob = NULL; + nand->ecc.mode = NAND_ECC_HW; + } + + switch (mode) { + case OMAP_ECC_HAMMING_CODE_HW_ROMCODE: + oinfo->nand.ecc.bytes = 3; + oinfo->nand.ecc.size = 512; + oinfo->ecc_parity_pairs = 12; + if (oinfo->nand.options & NAND_BUSWIDTH_16) { + offset = 2; + } else { + offset = 1; + oinfo->nand.badblock_pattern = &bb_descrip_flashbased; + } + omap_oobinfo.eccbytes = 3 * (minfo->oobsize / 16); + for (i = 0; i < omap_oobinfo.eccbytes; i++) + omap_oobinfo.eccpos[i] = i + offset; + omap_oobinfo.oobfree->offset = offset + omap_oobinfo.eccbytes; + omap_oobinfo.oobfree->length = minfo->oobsize - + offset - omap_oobinfo.eccbytes; + break; + case OMAP_ECC_BCH4_CODE_HW: + oinfo->nand.ecc.bytes = 4 * 7; + oinfo->nand.ecc.size = 4 * 512; + omap_oobinfo.oobfree->offset = offset; + omap_oobinfo.oobfree->length = minfo->oobsize - + offset - omap_oobinfo.eccbytes; + offset = minfo->oobsize - oinfo->nand.ecc.bytes; + for (i = 0; i < oinfo->nand.ecc.bytes; i++) + omap_oobinfo.eccpos[i] = i + offset; + break; + case OMAP_ECC_BCH8_CODE_HW: + oinfo->nand.ecc.bytes = 4 * 13; + oinfo->nand.ecc.size = 4 * 512; + omap_oobinfo.oobfree->offset = offset; + omap_oobinfo.oobfree->length = minfo->oobsize - + offset - omap_oobinfo.eccbytes; + offset = minfo->oobsize - oinfo->nand.ecc.bytes; + for (i = 0; i < oinfo->nand.ecc.bytes; i++) + omap_oobinfo.eccpos[i] = i + offset; + break; + case OMAP_ECC_BCH8_CODE_HW_ROMCODE: + /* + * Contradicting the datasheet the ecc checksum has to start + * at byte 2 in oob. I have no idea how the rom code can + * read this but it does. + */ + dev_warn(oinfo->pdev, "using rom loader ecc mode. " + "You can write properly but not read it back\n"); + + oinfo->nand.ecc.bytes = 4 * 13; + oinfo->nand.ecc.size = 4 * 512; + omap_oobinfo.oobfree->length = 0; + j = 0; + for (i = 2; i < 15; i++) + omap_oobinfo.eccpos[j++] = i; + for (i = 16; i < 29; i++) + omap_oobinfo.eccpos[j++] = i; + for (i = 30; i < 43; i++) + omap_oobinfo.eccpos[j++] = i; + for (i = 44; i < 57; i++) + omap_oobinfo.eccpos[j++] = i; break; - default: - gpmcnand_err("Error: Unrecognized Mode[%d]!\n", mode); + case OMAP_ECC_SOFT: + nand->ecc.layout = NULL; + nand->ecc.mode = NAND_ECC_SOFT; break; + default: + return -EINVAL; } + + oinfo->ecc_mode = mode; + + if (nand->buffers) + kfree(nand->buffers); + + /* second phase scan */ + if (nand_scan_tail(minfo)) + return -ENXIO; + + nand->options |= NAND_SKIP_BBTSCAN; + + return 0; +} + +static int omap_gpmc_eccmode_set(struct device_d *dev, struct param_d *param, const char *val) +{ + struct gpmc_nand_info *oinfo = dev->priv; + int i; + + if (!val) + return 0; + + for (i = 0; i < ARRAY_SIZE(ecc_mode_strings); i++) + if (!strcmp(ecc_mode_strings[i], val)) + break; + + if (i == ARRAY_SIZE(ecc_mode_strings)) { + dev_err(dev, "invalid ecc mode '%s'\n", val); + printf("valid modes:\n"); + for (i = 0; i < ARRAY_SIZE(ecc_mode_strings); i++) + printf("%s\n", ecc_mode_strings[i]); + return -EINVAL; + } + + return omap_gpmc_eccmode(oinfo, i); } /** @@ -408,23 +674,19 @@ static int gpmc_nand_probe(struct device_d *pdev) int err; struct nand_ecclayout *layout, *lsp, *llp; - gpmcnand_dbg("pdev=%x", (unsigned int)pdev); pdata = (struct gpmc_nand_platform_data *)pdev->platform_data; if (pdata == NULL) { - gpmcnand_err("platform data missing\n"); + dev_dbg(pdev, "platform data missing\n"); return -ENODEV; } - oinfo = calloc(1, sizeof(struct gpmc_nand_info)); - if (!oinfo) { - gpmcnand_err("oinfo alloc failed!\n"); - return -ENOMEM; - } + oinfo = xzalloc(sizeof(*oinfo)); /* fill up my data structures */ oinfo->pdev = pdev; oinfo->pdata = pdata; pdev->platform_data = (void *)oinfo; + pdev->priv = oinfo; nand = &oinfo->nand; nand->priv = (void *)oinfo; @@ -433,7 +695,7 @@ static int gpmc_nand_probe(struct device_d *pdev) minfo->priv = (void *)nand; if (pdata->cs >= GPMC_NUM_CS) { - gpmcnand_err("Invalid CS!\n"); + dev_dbg(pdev, "Invalid CS!\n"); err = -EINVAL; goto out_release_mem; } @@ -457,7 +719,7 @@ static int gpmc_nand_probe(struct device_d *pdev) /* If we are 16 bit dev, our gpmc config tells us that */ if ((readl(cs_base) & 0x3000) == 0x1000) { - debug("16 bit dev\n"); + dev_dbg(pdev, "16 bit dev\n"); nand->options |= NAND_BUSWIDTH_16; } @@ -472,7 +734,7 @@ static int gpmc_nand_probe(struct device_d *pdev) * until you get a failure or success */ if (pdata->wait_mon_pin > 4) { - gpmcnand_err("Invalid wait monitoring pin\n"); + dev_dbg(pdev, "Invalid wait monitoring pin\n"); err = -EINVAL; goto out_release_mem; } @@ -502,7 +764,7 @@ static int gpmc_nand_probe(struct device_d *pdev) if (pdata->nand_setup) { err = pdata->nand_setup(pdata); if (err) { - gpmcnand_err("pdataform setup failed\n"); + dev_dbg(pdev, "pdataform setup failed\n"); goto out_release_mem; } } @@ -548,45 +810,25 @@ static int gpmc_nand_probe(struct device_d *pdev) goto out_release_mem; } - if (pdata->plat_options & NAND_HWECC_ENABLE) { - nand->ecc.layout = layout; - - /* Program how many columns we expect+ - * enable the cs we want and enable the engine - */ - oinfo->ecc_config = (pdata->cs << 1) | - ((nand->options & NAND_BUSWIDTH_16) ? - (0x1 << 7) : 0x0) | 0x1; - nand->ecc.hwctl = omap_enable_hwecc; - nand->ecc.calculate = omap_calculate_ecc; - nand->ecc.correct = omap_correct_data; - nand->ecc.mode = NAND_ECC_HW; - nand->ecc.size = 512; - nand->ecc.bytes = 3; - nand->ecc.steps = nand->ecc.layout->eccbytes / nand->ecc.bytes; - oinfo->ecc_parity_pairs = 12; - } else - nand->ecc.mode = NAND_ECC_SOFT; - - /* second phase scan */ - if (nand_scan_tail(minfo)) { - err = -ENXIO; - goto out_release_mem; - } + nand->options |= NAND_SKIP_BBTSCAN; + omap_gpmc_eccmode(oinfo, pdata->ecc_mode); /* We are all set to register with the system now! */ err = add_mtd_device(minfo); if (err) { - gpmcnand_err("device registration failed\n"); + dev_dbg(pdev, "device registration failed\n"); goto out_release_mem; } + + dev_add_param(pdev, "eccmode", omap_gpmc_eccmode_set, NULL, 0); + return 0; out_release_mem: if (oinfo) free(oinfo); - gpmcnand_err("Failed!!\n"); + dev_dbg(pdev, "Failed!!\n"); return err; } diff --git a/drivers/mtd/nand/nand_swecc.c b/drivers/mtd/nand/nand_swecc.c new file mode 100644 index 00000000..93511cbe --- /dev/null +++ b/drivers/mtd/nand/nand_swecc.c @@ -0,0 +1,94 @@ +#include <common.h> +#include <errno.h> +#include <clock.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/err.h> +#include <linux/mtd/nand_ecc.h> +#include <asm/byteorder.h> +#include <asm/io.h> +#include <malloc.h> + +#include "nand.h" + +/** + * nand_read_page_swecc - [REPLACABLE] software ecc based page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + */ +static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_code = chip->buffers->ecccode; + uint32_t *eccpos = chip->ecc.layout->eccpos; + + chip->ecc.read_page_raw(mtd, chip, buf); + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + for (i = 0; i < chip->ecc.total; i++) + ecc_code[i] = chip->oob_poi[eccpos[i]]; + + eccsteps = chip->ecc.steps; + p = buf; + + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + return 0; +} + +/** + * nand_write_page_swecc - [REPLACABLE] software ecc based page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + */ +#ifdef CONFIG_NAND_WRITE +static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *ecc_calc = chip->buffers->ecccalc; + const uint8_t *p = buf; + uint32_t *eccpos = chip->ecc.layout->eccpos; + + /* Software ecc calculation */ + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + for (i = 0; i < chip->ecc.total; i++) + chip->oob_poi[eccpos[i]] = ecc_calc[i]; + + chip->ecc.write_page_raw(mtd, chip, buf); +} +#endif + +void nand_init_ecc_soft(struct nand_chip *chip) +{ + chip->ecc.calculate = nand_calculate_ecc; + chip->ecc.correct = nand_correct_data; + chip->ecc.read_page = nand_read_page_swecc; + chip->ecc.read_oob = nand_read_oob_std; +#ifdef CONFIG_NAND_WRITE + chip->ecc.write_page = nand_write_page_swecc; + chip->ecc.write_oob = nand_write_oob_std; +#endif + chip->ecc.size = 256; + chip->ecc.bytes = 3; +} diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c deleted file mode 100644 index d57294e6..00000000 --- a/drivers/mtd/nand/nand_util.c +++ /dev/null @@ -1,858 +0,0 @@ -/* - * drivers/nand/nand_util.c - * - * Copyright (C) 2006 by Weiss-Electronic GmbH. - * All rights reserved. - * - * @author: Guido Classen <clagix@gmail.com> - * @descr: NAND Flash support - * @references: borrowed heavily from Linux mtd-utils code: - * flash_eraseall.c by Arcom Control System Ltd - * nandwrite.c by Steven J. Hill (sjhill@realitydiluted.com) - * and Thomas Gleixner (tglx@linutronix.de) - * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * 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 <common.h> -#include <command.h> -#include <watchdog.h> -#include <malloc.h> - -#include <nand.h> -//#include <jffs2/jffs2.h> - -typedef struct erase_info erase_info_t; -typedef struct mtd_info mtd_info_t; - -/* support only for native endian JFFS2 */ -#define cpu_to_je16(x) (x) -#define cpu_to_je32(x) (x) - -/*****************************************************************************/ -static int nand_block_bad_scrub(struct mtd_info *mtd, loff_t ofs, int getchip) -{ - return 0; -} - -/** - * nand_erase_opts: - erase NAND flash with support for various options - * (jffs2 formating) - * - * @param meminfo NAND device to erase - * @param opts options, @see struct nand_erase_options - * @return 0 in case of success - * - * This code is ported from flash_eraseall.c from Linux mtd utils by - * Arcom Control System Ltd. - */ -int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) -{ - struct jffs2_unknown_node cleanmarker; - int clmpos = 0; - int clmlen = 8; - erase_info_t erase; - ulong erase_length; - int isNAND; - int bbtest = 1; - int result; - int percent_complete = -1; - int (*nand_block_bad_old)(struct mtd_info *, loff_t, int) = NULL; - const char *mtd_device = meminfo->name; - - memset(&erase, 0, sizeof(erase)); - - erase.mtd = meminfo; - erase.len = meminfo->erasesize; - erase.addr = opts->offset; - erase_length = opts->length; - - isNAND = meminfo->type == MTD_NANDFLASH ? 1 : 0; - - if (opts->jffs2) { - cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); - cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); - if (isNAND) { - struct nand_oobinfo *oobinfo = &meminfo->oobinfo; - - /* check for autoplacement */ - if (oobinfo->useecc == MTD_NANDECC_AUTOPLACE) { - /* get the position of the free bytes */ - if (!oobinfo->oobfree[0][1]) { - printf(" Eeep. Autoplacement selected " - "and no empty space in oob\n"); - return -1; - } - clmpos = oobinfo->oobfree[0][0]; - clmlen = oobinfo->oobfree[0][1]; - if (clmlen > 8) - clmlen = 8; - } else { - /* legacy mode */ - switch (meminfo->oobsize) { - case 8: - clmpos = 6; - clmlen = 2; - break; - case 16: - clmpos = 8; - clmlen = 8; - break; - case 64: - clmpos = 16; - clmlen = 8; - break; - } - } - - cleanmarker.totlen = cpu_to_je32(8); - } else { - cleanmarker.totlen = - cpu_to_je32(sizeof(struct jffs2_unknown_node)); - } - cleanmarker.hdr_crc = cpu_to_je32( - crc32_no_comp(0, (unsigned char *) &cleanmarker, - sizeof(struct jffs2_unknown_node) - 4)); - } - - /* scrub option allows to erase badblock. To prevent internal - * check from erase() method, set block check method to dummy - * and disable bad block table while erasing. - */ - if (opts->scrub) { - struct nand_chip *priv_nand = meminfo->priv; - - nand_block_bad_old = priv_nand->block_bad; - priv_nand->block_bad = nand_block_bad_scrub; - /* we don't need the bad block table anymore... - * after scrub, there are no bad blocks left! - */ - if (priv_nand->bbt) { - kfree(priv_nand->bbt); - } - priv_nand->bbt = NULL; - } - - for (; - erase.addr < opts->offset + erase_length; - erase.addr += meminfo->erasesize) { - - WATCHDOG_RESET (); - - if (!opts->scrub && bbtest) { - int ret = meminfo->block_isbad(meminfo, erase.addr); - if (ret > 0) { - if (!opts->quiet) - printf("\rSkipping bad block at " - "0x%08x " - " \n", - erase.addr); - continue; - - } else if (ret < 0) { - printf("\n%s: MTD get bad block failed: %d\n", - mtd_device, - ret); - return -1; - } - } - - result = meminfo->erase(meminfo, &erase); - if (result != 0) { - printf("\n%s: MTD Erase failure: %d\n", - mtd_device, result); - continue; - } - - /* format for JFFS2 ? */ - if (opts->jffs2) { - - /* write cleanmarker */ - if (isNAND) { - size_t written; - result = meminfo->write_oob(meminfo, - erase.addr + clmpos, - clmlen, - &written, - (unsigned char *) - &cleanmarker); - if (result != 0) { - printf("\n%s: MTD writeoob failure: %d\n", - mtd_device, result); - continue; - } - } else { - printf("\n%s: this erase routine only supports" - " NAND devices!\n", - mtd_device); - } - } - - if (!opts->quiet) { - int percent = (int) - ((unsigned long long) - (erase.addr+meminfo->erasesize-opts->offset) - * 100 / erase_length); - - /* output progress message only at whole percent - * steps to reduce the number of messages printed - * on (slow) serial consoles - */ - if (percent != percent_complete) { - percent_complete = percent; - - printf("\rErasing at 0x%x -- %3d%% complete.", - erase.addr, percent); - - if (opts->jffs2 && result == 0) - printf(" Cleanmarker written at 0x%x.", - erase.addr); - } - } - } - if (!opts->quiet) - printf("\n"); - - if (nand_block_bad_old) { - struct nand_chip *priv_nand = meminfo->priv; - - priv_nand->block_bad = nand_block_bad_old; - priv_nand->scan_bbt(meminfo); - } - - return 0; -} - -#define MAX_PAGE_SIZE 2048 -#define MAX_OOB_SIZE 64 - -/* - * buffer array used for writing data - */ -static unsigned char data_buf[MAX_PAGE_SIZE]; -static unsigned char oob_buf[MAX_OOB_SIZE]; - -/* OOB layouts to pass into the kernel as default */ -static struct nand_oobinfo none_oobinfo = { - .useecc = MTD_NANDECC_OFF, -}; - -static struct nand_oobinfo jffs2_oobinfo = { - .useecc = MTD_NANDECC_PLACE, - .eccbytes = 6, - .eccpos = { 0, 1, 2, 3, 6, 7 } -}; - -static struct nand_oobinfo yaffs_oobinfo = { - .useecc = MTD_NANDECC_PLACE, - .eccbytes = 6, - .eccpos = { 8, 9, 10, 13, 14, 15} -}; - -static struct nand_oobinfo autoplace_oobinfo = { - .useecc = MTD_NANDECC_AUTOPLACE -}; - -/** - * nand_write_opts: - write image to NAND flash with support for various options - * - * @param meminfo NAND device to erase - * @param opts write options (@see nand_write_options) - * @return 0 in case of success - * - * This code is ported from nandwrite.c from Linux mtd utils by - * Steven J. Hill and Thomas Gleixner. - */ -int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts) -{ - int imglen = 0; - int pagelen; - int baderaseblock; - int blockstart = -1; - loff_t offs; - int readlen; - int oobinfochanged = 0; - int percent_complete = -1; - struct nand_oobinfo old_oobinfo; - ulong mtdoffset = opts->offset; - ulong erasesize_blockalign; - u_char *buffer = opts->buffer; - size_t written; - int result; - - if (opts->pad && opts->writeoob) { - printf("Can't pad when oob data is present.\n"); - return -1; - } - - /* set erasesize to specified number of blocks - to match - * jffs2 (virtual) block size */ - if (opts->blockalign == 0) { - erasesize_blockalign = meminfo->erasesize; - } else { - erasesize_blockalign = meminfo->erasesize * opts->blockalign; - } - - /* make sure device page sizes are valid */ - if (!(meminfo->oobsize == 16 && meminfo->oobblock == 512) - && !(meminfo->oobsize == 8 && meminfo->oobblock == 256) - && !(meminfo->oobsize == 64 && meminfo->oobblock == 2048)) { - printf("Unknown flash (not normal NAND)\n"); - return -1; - } - - /* read the current oob info */ - memcpy(&old_oobinfo, &meminfo->oobinfo, sizeof(old_oobinfo)); - - /* write without ecc? */ - if (opts->noecc) { - memcpy(&meminfo->oobinfo, &none_oobinfo, - sizeof(meminfo->oobinfo)); - oobinfochanged = 1; - } - - /* autoplace ECC? */ - if (opts->autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) { - - memcpy(&meminfo->oobinfo, &autoplace_oobinfo, - sizeof(meminfo->oobinfo)); - oobinfochanged = 1; - } - - /* force OOB layout for jffs2 or yaffs? */ - if (opts->forcejffs2 || opts->forceyaffs) { - struct nand_oobinfo *oobsel = - opts->forcejffs2 ? &jffs2_oobinfo : &yaffs_oobinfo; - - if (meminfo->oobsize == 8) { - if (opts->forceyaffs) { - printf("YAFSS cannot operate on " - "256 Byte page size\n"); - goto restoreoob; - } - /* Adjust number of ecc bytes */ - jffs2_oobinfo.eccbytes = 3; - } - - memcpy(&meminfo->oobinfo, oobsel, sizeof(meminfo->oobinfo)); - } - - /* get image length */ - imglen = opts->length; - pagelen = meminfo->oobblock - + ((opts->writeoob != 0) ? meminfo->oobsize : 0); - - /* check, if file is pagealigned */ - if ((!opts->pad) && ((imglen % pagelen) != 0)) { - printf("Input block length is not page aligned\n"); - goto restoreoob; - } - - /* check, if length fits into device */ - if (((imglen / pagelen) * meminfo->oobblock) - > (meminfo->size - opts->offset)) { - printf("Image %d bytes, NAND page %d bytes, " - "OOB area %u bytes, device size %u bytes\n", - imglen, pagelen, meminfo->oobblock, meminfo->size); - printf("Input block does not fit into device\n"); - goto restoreoob; - } - - if (!opts->quiet) - printf("\n"); - - /* get data from input and write to the device */ - while (imglen && (mtdoffset < meminfo->size)) { - - WATCHDOG_RESET (); - - /* - * new eraseblock, check for bad block(s). Stay in the - * loop to be sure if the offset changes because of - * a bad block, that the next block that will be - * written to is also checked. Thus avoiding errors if - * the block(s) after the skipped block(s) is also bad - * (number of blocks depending on the blockalign - */ - while (blockstart != (mtdoffset & (~erasesize_blockalign+1))) { - blockstart = mtdoffset & (~erasesize_blockalign+1); - offs = blockstart; - baderaseblock = 0; - - /* check all the blocks in an erase block for - * bad blocks */ - do { - int ret = meminfo->block_isbad(meminfo, offs); - - if (ret < 0) { - printf("Bad block check failed\n"); - goto restoreoob; - } - if (ret == 1) { - baderaseblock = 1; - if (!opts->quiet) - printf("\rBad block at 0x%lx " - "in erase block from " - "0x%x will be skipped\n", - (long) offs, - blockstart); - } - - if (baderaseblock) { - mtdoffset = blockstart - + erasesize_blockalign; - } - offs += erasesize_blockalign - / opts->blockalign; - } while (offs < blockstart + erasesize_blockalign); - } - - readlen = meminfo->oobblock; - if (opts->pad && (imglen < readlen)) { - readlen = imglen; - memset(data_buf + readlen, 0xff, - meminfo->oobblock - readlen); - } - - /* read page data from input memory buffer */ - memcpy(data_buf, buffer, readlen); - buffer += readlen; - - if (opts->writeoob) { - /* read OOB data from input memory block, exit - * on failure */ - memcpy(oob_buf, buffer, meminfo->oobsize); - buffer += meminfo->oobsize; - - /* write OOB data first, as ecc will be placed - * in there*/ - result = meminfo->write_oob(meminfo, - mtdoffset, - meminfo->oobsize, - &written, - (unsigned char *) - &oob_buf); - - if (result != 0) { - printf("\nMTD writeoob failure: %d\n", - result); - goto restoreoob; - } - imglen -= meminfo->oobsize; - } - - /* write out the page data */ - result = meminfo->write(meminfo, - mtdoffset, - meminfo->oobblock, - &written, - (unsigned char *) &data_buf); - - if (result != 0) { - printf("writing NAND page at offset 0x%lx failed\n", - mtdoffset); - goto restoreoob; - } - imglen -= readlen; - - if (!opts->quiet) { - int percent = (int) - ((unsigned long long) - (opts->length-imglen) * 100 - / opts->length); - /* output progress message only at whole percent - * steps to reduce the number of messages printed - * on (slow) serial consoles - */ - if (percent != percent_complete) { - printf("\rWriting data at 0x%x " - "-- %3d%% complete.", - mtdoffset, percent); - percent_complete = percent; - } - } - - mtdoffset += meminfo->oobblock; - } - - if (!opts->quiet) - printf("\n"); - -restoreoob: - if (oobinfochanged) { - memcpy(&meminfo->oobinfo, &old_oobinfo, - sizeof(meminfo->oobinfo)); - } - - if (imglen > 0) { - printf("Data did not fit into device, due to bad blocks\n"); - return -1; - } - - /* return happy */ - return 0; -} - -/** - * nand_read_opts: - read image from NAND flash with support for various options - * - * @param meminfo NAND device to erase - * @param opts read options (@see struct nand_read_options) - * @return 0 in case of success - * - */ -int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts) -{ - int imglen = opts->length; - int pagelen; - int baderaseblock; - int blockstart = -1; - int percent_complete = -1; - loff_t offs; - size_t readlen; - ulong mtdoffset = opts->offset; - u_char *buffer = opts->buffer; - int result; - - /* make sure device page sizes are valid */ - if (!(meminfo->oobsize == 16 && meminfo->oobblock == 512) - && !(meminfo->oobsize == 8 && meminfo->oobblock == 256) - && !(meminfo->oobsize == 64 && meminfo->oobblock == 2048)) { - printf("Unknown flash (not normal NAND)\n"); - return -1; - } - - pagelen = meminfo->oobblock - + ((opts->readoob != 0) ? meminfo->oobsize : 0); - - /* check, if length is not larger than device */ - if (((imglen / pagelen) * meminfo->oobblock) - > (meminfo->size - opts->offset)) { - printf("Image %d bytes, NAND page %d bytes, " - "OOB area %u bytes, device size %u bytes\n", - imglen, pagelen, meminfo->oobblock, meminfo->size); - printf("Input block is larger than device\n"); - return -1; - } - - if (!opts->quiet) - printf("\n"); - - /* get data from input and write to the device */ - while (imglen && (mtdoffset < meminfo->size)) { - - WATCHDOG_RESET (); - - /* - * new eraseblock, check for bad block(s). Stay in the - * loop to be sure if the offset changes because of - * a bad block, that the next block that will be - * written to is also checked. Thus avoiding errors if - * the block(s) after the skipped block(s) is also bad - * (number of blocks depending on the blockalign - */ - while (blockstart != (mtdoffset & (~meminfo->erasesize+1))) { - blockstart = mtdoffset & (~meminfo->erasesize+1); - offs = blockstart; - baderaseblock = 0; - - /* check all the blocks in an erase block for - * bad blocks */ - do { - int ret = meminfo->block_isbad(meminfo, offs); - - if (ret < 0) { - printf("Bad block check failed\n"); - return -1; - } - if (ret == 1) { - baderaseblock = 1; - if (!opts->quiet) - printf("\rBad block at 0x%lx " - "in erase block from " - "0x%x will be skipped\n", - (long) offs, - blockstart); - } - - if (baderaseblock) { - mtdoffset = blockstart - + meminfo->erasesize; - } - offs += meminfo->erasesize; - - } while (offs < blockstart + meminfo->erasesize); - } - - - /* read page data to memory buffer */ - result = meminfo->read(meminfo, - mtdoffset, - meminfo->oobblock, - &readlen, - (unsigned char *) &data_buf); - - if (result != 0) { - printf("reading NAND page at offset 0x%lx failed\n", - mtdoffset); - return -1; - } - - if (imglen < readlen) { - readlen = imglen; - } - - memcpy(buffer, data_buf, readlen); - buffer += readlen; - imglen -= readlen; - - if (opts->readoob) { - result = meminfo->read_oob(meminfo, - mtdoffset, - meminfo->oobsize, - &readlen, - (unsigned char *) - &oob_buf); - - if (result != 0) { - printf("\nMTD readoob failure: %d\n", - result); - return -1; - } - - - if (imglen < readlen) { - readlen = imglen; - } - - memcpy(buffer, oob_buf, readlen); - - buffer += readlen; - imglen -= readlen; - } - - if (!opts->quiet) { - int percent = (int) - ((unsigned long long) - (opts->length-imglen) * 100 - / opts->length); - /* output progress message only at whole percent - * steps to reduce the number of messages printed - * on (slow) serial consoles - */ - if (percent != percent_complete) { - if (!opts->quiet) - printf("\rReading data from 0x%x " - "-- %3d%% complete.", - mtdoffset, percent); - percent_complete = percent; - } - } - - mtdoffset += meminfo->oobblock; - } - - if (!opts->quiet) - printf("\n"); - - if (imglen > 0) { - printf("Could not read entire image due to bad blocks\n"); - return -1; - } - - /* return happy */ - return 0; -} - -/****************************************************************************** - * Support for locking / unlocking operations of some NAND devices - *****************************************************************************/ - -#define NAND_CMD_LOCK 0x2a -#define NAND_CMD_LOCK_TIGHT 0x2c -#define NAND_CMD_UNLOCK1 0x23 -#define NAND_CMD_UNLOCK2 0x24 -#define NAND_CMD_LOCK_STATUS 0x7a - -/** - * nand_lock: Set all pages of NAND flash chip to the LOCK or LOCK-TIGHT - * state - * - * @param meminfo nand mtd instance - * @param tight bring device in lock tight mode - * - * @return 0 on success, -1 in case of error - * - * The lock / lock-tight command only applies to the whole chip. To get some - * parts of the chip lock and others unlocked use the following sequence: - * - * - Lock all pages of the chip using nand_lock(mtd, 0) (or the lockpre pin) - * - Call nand_unlock() once for each consecutive area to be unlocked - * - If desired: Bring the chip to the lock-tight state using nand_lock(mtd, 1) - * - * If the device is in lock-tight state software can't change the - * current active lock/unlock state of all pages. nand_lock() / nand_unlock() - * calls will fail. It is only posible to leave lock-tight state by - * an hardware signal (low pulse on _WP pin) or by power down. - */ -int nand_lock(nand_info_t *meminfo, int tight) -{ - int ret = 0; - int status; - struct nand_chip *this = meminfo->priv; - - /* select the NAND device */ - this->select_chip(meminfo, 0); - - this->cmdfunc(meminfo, - (tight ? NAND_CMD_LOCK_TIGHT : NAND_CMD_LOCK), - -1, -1); - - /* call wait ready function */ - status = this->waitfunc(meminfo, this, FL_WRITING); - - /* see if device thinks it succeeded */ - if (status & 0x01) { - ret = -1; - } - - /* de-select the NAND device */ - this->select_chip(meminfo, -1); - return ret; -} - -/** - * nand_get_lock_status: - query current lock state from one page of NAND - * flash - * - * @param meminfo nand mtd instance - * @param offset page address to query (muss be page aligned!) - * - * @return -1 in case of error - * >0 lock status: - * bitfield with the following combinations: - * NAND_LOCK_STATUS_TIGHT: page in tight state - * NAND_LOCK_STATUS_LOCK: page locked - * NAND_LOCK_STATUS_UNLOCK: page unlocked - * - */ -int nand_get_lock_status(nand_info_t *meminfo, ulong offset) -{ - int ret = 0; - int chipnr; - int page; - struct nand_chip *this = meminfo->priv; - - /* select the NAND device */ - chipnr = (int)(offset >> this->chip_shift); - this->select_chip(meminfo, chipnr); - - - if ((offset & (meminfo->oobblock - 1)) != 0) { - printf ("nand_get_lock_status: " - "Start address must be beginning of " - "nand page!\n"); - ret = -1; - goto out; - } - - /* check the Lock Status */ - page = (int)(offset >> this->page_shift); - this->cmdfunc(meminfo, NAND_CMD_LOCK_STATUS, -1, page & this->pagemask); - - ret = this->read_byte(meminfo) & (NAND_LOCK_STATUS_TIGHT - | NAND_LOCK_STATUS_LOCK - | NAND_LOCK_STATUS_UNLOCK); - - out: - /* de-select the NAND device */ - this->select_chip(meminfo, -1); - return ret; -} - -/** - * nand_unlock: - Unlock area of NAND pages - * only one consecutive area can be unlocked at one time! - * - * @param meminfo nand mtd instance - * @param start start byte address - * @param length number of bytes to unlock (must be a multiple of - * page size nand->oobblock) - * - * @return 0 on success, -1 in case of error - */ -int nand_unlock(nand_info_t *meminfo, ulong start, ulong length) -{ - int ret = 0; - int chipnr; - int status; - int page; - struct nand_chip *this = meminfo->priv; - printf ("nand_unlock: start: %08x, length: %d!\n", - (int)start, (int)length); - - /* select the NAND device */ - chipnr = (int)(start >> this->chip_shift); - this->select_chip(meminfo, chipnr); - - /* check the WP bit */ - this->cmdfunc(meminfo, NAND_CMD_STATUS, -1, -1); - if ((this->read_byte(meminfo) & 0x80) == 0) { - printf ("nand_unlock: Device is write protected!\n"); - ret = -1; - goto out; - } - - if ((start & (meminfo->oobblock - 1)) != 0) { - printf ("nand_unlock: Start address must be beginning of " - "nand page!\n"); - ret = -1; - goto out; - } - - if (length == 0 || (length & (meminfo->oobblock - 1)) != 0) { - printf ("nand_unlock: Length must be a multiple of nand page " - "size!\n"); - ret = -1; - goto out; - } - - /* submit address of first page to unlock */ - page = (int)(start >> this->page_shift); - this->cmdfunc(meminfo, NAND_CMD_UNLOCK1, -1, page & this->pagemask); - - /* submit ADDRESS of LAST page to unlock */ - page += (int)(length >> this->page_shift) - 1; - this->cmdfunc(meminfo, NAND_CMD_UNLOCK2, -1, page & this->pagemask); - - /* call wait ready function */ - status = this->waitfunc(meminfo, this, FL_WRITING); - /* see if device thinks it succeeded */ - if (status & 0x01) { - /* there was an error */ - ret = -1; - goto out; - } - - out: - /* de-select the NAND device */ - this->select_chip(meminfo, -1); - return ret; -} - diff --git a/drivers/mtd/nand/nand_write.c b/drivers/mtd/nand/nand_write.c new file mode 100644 index 00000000..89dc47b3 --- /dev/null +++ b/drivers/mtd/nand/nand_write.c @@ -0,0 +1,746 @@ +#include <common.h> +#include <errno.h> +#include <clock.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/err.h> +#include <linux/mtd/nand_ecc.h> +#include <asm/byteorder.h> +#include <asm/io.h> +#include <malloc.h> +#include <module.h> + +#include "nand.h" + +static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops); + +/** + * nand_write_buf - [DEFAULT] write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + * + * Default write function for 8bit buswith + */ +void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + + for (i = 0; i < len; i++) + writeb(buf[i], chip->IO_ADDR_W); +} + +/** + * nand_write_buf16 - [DEFAULT] write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + * + * Default write function for 16bit buswith + */ +void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + u16 *p = (u16 *) buf; + len >>= 1; + + for (i = 0; i < len; i++) + writew(p[i], chip->IO_ADDR_W); + +} + +/** + * nand_default_block_markbad - [DEFAULT] mark a block bad + * @mtd: MTD device structure + * @ofs: offset from device start + * + * This is the default implementation, which can be overridden by + * a hardware specific driver. + */ +int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *chip = mtd->priv; + uint8_t buf[2] = { 0, 0 }; + int block, ret; + + /* Get block number */ + block = (int)(ofs >> chip->bbt_erase_shift); + if (chip->bbt) + chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); + + /* Do we have a flash based bad block table ? */ + if (chip->options & NAND_USE_FLASH_BBT) + ret = nand_update_bbt(mtd, ofs); + else { + /* We write two bytes, so we dont have to mess with 16 bit + * access + */ + ofs += mtd->oobsize; + chip->ops.len = chip->ops.ooblen = 2; + chip->ops.datbuf = NULL; + chip->ops.oobbuf = buf; + chip->ops.ooboffs = chip->badblockpos & ~0x01; + + ret = nand_do_write_oob(mtd, ofs, &chip->ops); + } + if (!ret) + mtd->ecc_stats.badblocks++; + + return ret; +} + +/** + * nand_check_wp - [GENERIC] check if the chip is write protected + * @mtd: MTD device structure + * Check, if the device is write protected + * + * The function expects, that the device is already selected + */ +static int nand_check_wp(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + /* Check the WP bit */ + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1; +} + +/** + * nand_write_oob_std - [REPLACABLE] the most common OOB data write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @page: page number to write + */ +int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + int status = 0; + const uint8_t *buf = chip->oob_poi; + int length = mtd->oobsize; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); + chip->write_buf(mtd, buf, length); + /* Send command to program the OOB data */ + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + + status = chip->waitfunc(mtd, chip); + + return status & NAND_STATUS_FAIL ? -EIO : 0; +} + +/** + * nand_write_page_raw - [Intern] raw page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + */ +void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + chip->write_buf(mtd, buf, mtd->writesize); + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); +} + +/** + * nand_write_page - [REPLACEABLE] write one page + * @mtd: MTD device structure + * @chip: NAND chip descriptor + * @buf: the data to write + * @page: page number to write + * @cached: cached programming + * @raw: use _raw version of write_page + */ +int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int page, int cached, int raw) +{ + int status; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); + + if (unlikely(raw)) + chip->ecc.write_page_raw(mtd, chip, buf); + else + chip->ecc.write_page(mtd, chip, buf); + + /* + * Cached progamming disabled for now, Not sure if its worth the + * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s) + */ + cached = 0; + + if (!cached || !(chip->options & NAND_CACHEPRG)) { + + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + /* + * See if operation failed and additional status checks are + * available + */ + if ((status & NAND_STATUS_FAIL) && (chip->errstat)) + status = chip->errstat(mtd, chip, FL_WRITING, status, + page); + + if (status & NAND_STATUS_FAIL) { + return -EIO; + } + } else { + chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + } + +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE + /* Send command to read back the data */ + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + + if (chip->verify_buf(mtd, buf, mtd->writesize)) + return -EIO; +#endif + return 0; +} + +/** + * nand_fill_oob - [Internal] Transfer client buffer to oob + * @chip: nand chip structure + * @oob: oob data buffer + * @ops: oob ops structure + */ +static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, + struct mtd_oob_ops *ops) +{ + size_t len = ops->ooblen; + + switch(ops->mode) { + + case MTD_OOB_PLACE: + case MTD_OOB_RAW: + memcpy(chip->oob_poi + ops->ooboffs, oob, len); + return oob + len; + + case MTD_OOB_AUTO: { + struct nand_oobfree *free = chip->ecc.layout->oobfree; + uint32_t boffs = 0, woffs = ops->ooboffs; + size_t bytes = 0; + + for(; free->length && len; free++, len -= bytes) { + /* Write request not from offset 0 ? */ + if (unlikely(woffs)) { + if (woffs >= free->length) { + woffs -= free->length; + continue; + } + boffs = free->offset + woffs; + bytes = min_t(size_t, len, + (free->length - woffs)); + woffs = 0; + } else { + bytes = min_t(size_t, len, free->length); + boffs = free->offset; + } + memcpy(chip->oob_poi + boffs, oob, bytes); + oob += bytes; + } + return oob; + } + default: + BUG(); + } + return NULL; +} + +#define NOTALIGNED(x) (x & (chip->subpagesize - 1)) != 0 + +/** + * nand_do_write_ops - [Internal] NAND write with ECC + * @mtd: MTD device structure + * @to: offset to write to + * @ops: oob operations description structure + * + * NAND write with ECC + */ +int nand_do_write_ops(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + int chipnr, realpage, page, blockmask, column; + struct nand_chip *chip = mtd->priv; + uint32_t writelen = ops->len; + uint8_t *oob = ops->oobbuf; + uint8_t *buf = ops->datbuf; + int ret, subpage; + + ops->retlen = 0; + if (!writelen) + return 0; + + /* reject writes, which are not page aligned */ + if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { + printk(KERN_NOTICE "nand_write: " + "Attempt to write not page aligned data\n"); + return -EINVAL; + } + + column = to & (mtd->writesize - 1); + subpage = column || (writelen & (mtd->writesize - 1)); + + if (subpage && oob) + return -EINVAL; + + chipnr = (int)(to >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) { + return -EIO; + } + + realpage = (int)(to >> chip->page_shift); + page = realpage & chip->pagemask; + blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; + + /* Invalidate the page cache, when we write to the cached page */ + if (to <= (chip->pagebuf << chip->page_shift) && + (chip->pagebuf << chip->page_shift) < (to + ops->len)) + chip->pagebuf = -1; + + /* If we're not given explicit OOB data, let it be 0xFF */ + if (likely(!oob)) + memset(chip->oob_poi, 0xff, mtd->oobsize); + + while(1) { + int bytes = mtd->writesize; + int cached = writelen > bytes && page != blockmask; + uint8_t *wbuf = buf; + + /* Partial page write ? */ + if (unlikely(column || writelen < (mtd->writesize - 1))) { + cached = 0; + bytes = min_t(int, bytes - column, (int) writelen); + chip->pagebuf = -1; + memset(chip->buffers->databuf, 0xff, mtd->writesize); + memcpy(&chip->buffers->databuf[column], buf, bytes); + wbuf = chip->buffers->databuf; + } + + if (unlikely(oob)) + oob = nand_fill_oob(chip, oob, ops); + + ret = chip->write_page(mtd, chip, wbuf, page, cached, + (ops->mode == MTD_OOB_RAW)); + if (ret) + break; + + writelen -= bytes; + if (!writelen) + break; + + column = 0; + buf += bytes; + realpage++; + + page = realpage & chip->pagemask; + /* Check, if we cross a chip boundary */ + if (!page) { + chipnr++; + chip->select_chip(mtd, -1); + chip->select_chip(mtd, chipnr); + } + } + + ops->retlen = ops->len - writelen; + if (unlikely(oob)) + ops->oobretlen = ops->ooblen; + return ret; +} + +/** + * nand_write - [MTD Interface] NAND write with ECC + * @mtd: MTD device structure + * @to: offset to write to + * @len: number of bytes to write + * @retlen: pointer to variable to store the number of written bytes + * @buf: the data to write + * + * NAND write with ECC + */ +int nand_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const uint8_t *buf) +{ + struct nand_chip *chip = mtd->priv; + int ret; + + /* Do not allow reads past end of device */ + if ((to + len) > mtd->size) + return -EINVAL; + if (!len) + return 0; + + chip->ops.len = len; + chip->ops.datbuf = (uint8_t *)buf; + chip->ops.oobbuf = NULL; + + ret = nand_do_write_ops(mtd, to, &chip->ops); + + *retlen = chip->ops.retlen; + + return ret; +} + +/** + * nand_do_write_oob - [MTD Interface] NAND write out-of-band + * @mtd: MTD device structure + * @to: offset to write to + * @ops: oob operation description structure + * + * NAND write out-of-band + */ +static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + int chipnr, page, status, len; + struct nand_chip *chip = mtd->priv; + + MTD_DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", + (unsigned int)to, (int)ops->ooblen); + + if (ops->mode == MTD_OOB_AUTO) + len = chip->ecc.layout->oobavail; + else + len = mtd->oobsize; + + /* Do not allow write past end of page */ + if ((ops->ooboffs + ops->ooblen) > len) { + MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " + "Attempt to write past end of page\n"); + return -EINVAL; + } + + if (unlikely(ops->ooboffs >= len)) { + MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt to start write outside oob\n"); + return -EINVAL; + } + + /* Do not allow reads past end of device */ + if (unlikely(to >= mtd->size || + ops->ooboffs + ops->ooblen > + ((mtd->size >> chip->page_shift) - + (to >> chip->page_shift)) * len)) { + MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt write beyond end of device\n"); + return -EINVAL; + } + + chipnr = (int)(to >> chip->chip_shift); + chip->select_chip(mtd, chipnr); + + /* Shift to get page */ + page = (int)(to >> chip->page_shift); + + /* + * Reset the chip. Some chips (like the Toshiba TC5832DC found in one + * of my DiskOnChip 2000 test units) will clear the whole data page too + * if we don't do this. I have no clue why, but I seem to have 'fixed' + * it in the doc2000 driver in August 1999. dwmw2. + */ + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) + return -EROFS; + + /* Invalidate the page cache, if we write to the cached page */ + if (page == chip->pagebuf) + chip->pagebuf = -1; + + memset(chip->oob_poi, 0xff, mtd->oobsize); + nand_fill_oob(chip, ops->oobbuf, ops); + status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); + memset(chip->oob_poi, 0xff, mtd->oobsize); + + if (status) + return status; + + ops->oobretlen = ops->ooblen; + + return 0; +} + +/** + * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band + * @mtd: MTD device structure + * @to: offset to write to + * @ops: oob operation description structure + */ +int nand_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + int ret = -ENOSYS; + + ops->retlen = 0; + + /* Do not allow writes past end of device */ + if (ops->datbuf && (to + ops->len) > mtd->size) { + MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt read beyond end of device\n"); + return -EINVAL; + } + + switch(ops->mode) { + case MTD_OOB_PLACE: + case MTD_OOB_AUTO: + case MTD_OOB_RAW: + break; + + default: + goto out; + } + + if (!ops->datbuf) + ret = nand_do_write_oob(mtd, to, ops); + else + ret = nand_do_write_ops(mtd, to, ops); + + out: + return ret; +} + +/** + * single_erease_cmd - [GENERIC] NAND standard block erase command function + * @mtd: MTD device structure + * @page: the page address of the block which will be erased + * + * Standard erase command for NAND chips + */ +void single_erase_cmd(struct mtd_info *mtd, int page) +{ + struct nand_chip *chip = mtd->priv; + /* Send commands to erase a block */ + chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); + chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); +} + +/** + * multi_erease_cmd - [GENERIC] AND specific block erase command function + * @mtd: MTD device structure + * @page: the page address of the block which will be erased + * + * AND multi block erase command function + * Erase 4 consecutive blocks + */ +void multi_erase_cmd(struct mtd_info *mtd, int page) +{ + struct nand_chip *chip = mtd->priv; + /* Send commands to erase a block */ + chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++); + chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++); + chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++); + chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); + chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1); +} + +/** + * nand_erase - [MTD Interface] erase block(s) + * @mtd: MTD device structure + * @instr: erase instruction + * + * Erase one ore more blocks + */ +int nand_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + return nand_erase_nand(mtd, instr, 0); +} + +#define BBT_PAGE_MASK 0xffffff3f +/** + * nand_erase_nand - [Internal] erase block(s) + * @mtd: MTD device structure + * @instr: erase instruction + * @allowbbt: allow erasing the bbt area + * + * Erase one ore more blocks + */ +int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, + int allowbbt) +{ + int page, len, status, pages_per_block, ret, chipnr; + struct nand_chip *chip = mtd->priv; + int rewrite_bbt[NAND_MAX_CHIPS]={0}; + unsigned int bbt_masked_page = 0xffffffff; + + MTD_DEBUG(MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%08x, len = %i\n", + (unsigned int)instr->addr, (unsigned int)instr->len); + + /* Start address must align on block boundary */ + if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) { + MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n"); + return -EINVAL; + } + + /* Length must align on block boundary */ + if (instr->len & ((1 << chip->phys_erase_shift) - 1)) { + MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " + "Length not block aligned\n"); + return -EINVAL; + } + + /* Do not allow erase past end of device */ + if ((instr->len + instr->addr) > mtd->size) { + MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " + "Erase past end of device\n"); + return -EINVAL; + } + + instr->fail_addr = 0xffffffff; + + /* Shift to get first page */ + page = (int)(instr->addr >> chip->page_shift); + chipnr = (int)(instr->addr >> chip->chip_shift); + + /* Calculate pages in each block */ + pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift); + + /* Select the NAND device */ + chip->select_chip(mtd, chipnr); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) { + MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " + "Device is write protected!!!\n"); + instr->state = MTD_ERASE_FAILED; + goto erase_exit; + } + + /* + * If BBT requires refresh, set the BBT page mask to see if the BBT + * should be rewritten. Otherwise the mask is set to 0xffffffff which + * can not be matched. This is also done when the bbt is actually + * erased to avoid recusrsive updates + */ + if (chip->options & BBT_AUTO_REFRESH && !allowbbt) + bbt_masked_page = chip->bbt_td->pages[chipnr] & BBT_PAGE_MASK; + + /* Loop through the pages */ + len = instr->len; + + instr->state = MTD_ERASING; + + while (len) { + /* + * heck if we have a bad block, we do not erase bad blocks ! + */ + if (nand_block_checkbad(mtd, ((loff_t) page) << + chip->page_shift, 0, allowbbt)) { + printk(KERN_WARNING "nand_erase: attempt to erase a " + "bad block at page 0x%08x\n", page); + instr->state = MTD_ERASE_FAILED; + goto erase_exit; + } + + /* + * Invalidate the page cache, if we erase the block which + * contains the current cached page + */ + if (page <= chip->pagebuf && chip->pagebuf < + (page + pages_per_block)) + chip->pagebuf = -1; + + chip->erase_cmd(mtd, page & chip->pagemask); + + status = chip->waitfunc(mtd, chip); + + /* + * See if operation failed and additional status checks are + * available + */ + if ((status & NAND_STATUS_FAIL) && (chip->errstat)) + status = chip->errstat(mtd, chip, FL_ERASING, + status, page); + + /* See if block erase succeeded */ + if (status & NAND_STATUS_FAIL) { + MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " + "Failed erase, page 0x%08x\n", page); + instr->state = MTD_ERASE_FAILED; + instr->fail_addr = (page << chip->page_shift); + goto erase_exit; + } + + /* + * If BBT requires refresh, set the BBT rewrite flag to the + * page being erased + */ + if (bbt_masked_page != 0xffffffff && + (page & BBT_PAGE_MASK) == bbt_masked_page) + rewrite_bbt[chipnr] = (page << chip->page_shift); + + /* Increment page address and decrement length */ + len -= (1 << chip->phys_erase_shift); + page += pages_per_block; + + /* Check, if we cross a chip boundary */ + if (len && !(page & chip->pagemask)) { + chipnr++; + chip->select_chip(mtd, -1); + chip->select_chip(mtd, chipnr); + + /* + * If BBT requires refresh and BBT-PERCHIP, set the BBT + * page mask to see if this BBT should be rewritten + */ + if (bbt_masked_page != 0xffffffff && + (chip->bbt_td->options & NAND_BBT_PERCHIP)) + bbt_masked_page = chip->bbt_td->pages[chipnr] & + BBT_PAGE_MASK; + } + } + instr->state = MTD_ERASE_DONE; + + erase_exit: + + ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; + + /* Do call back function */ + if (!ret) + mtd_erase_callback(instr); + + /* + * If BBT requires refresh and erase was successful, rewrite any + * selected bad block tables + */ + if (bbt_masked_page == 0xffffffff || ret) + return ret; + + for (chipnr = 0; chipnr < chip->numchips; chipnr++) { + if (!rewrite_bbt[chipnr]) + continue; + /* update the BBT for chip */ + MTD_DEBUG(MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt " + "(%d:0x%0x 0x%0x)\n", chipnr, rewrite_bbt[chipnr], + chip->bbt_td->pages[chipnr]); + nand_update_bbt(mtd, rewrite_bbt[chipnr]); + } + + /* Return more or less happy */ + return ret; +} + +/** + * nand_block_markbad - [MTD Interface] Mark block at the given offset as bad + * @mtd: MTD device structure + * @ofs: offset relative to mtd start + */ +int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *chip = mtd->priv; + int ret; + + if ((ret = nand_block_isbad(mtd, ofs))) { + /* If it was bad already, return success and do nothing. */ + if (ret > 0) + return 0; + return ret; + } + + return chip->block_markbad(mtd, ofs); +} diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 5520132c..96ae16e0 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -9,7 +9,6 @@ struct ubi_volume_cdev_priv { struct ubi_device *ubi; struct ubi_volume *vol; int updating; - unsigned long mode; }; static ssize_t ubi_volume_cdev_read(struct cdev *cdev, void *buf, size_t size, @@ -82,21 +81,16 @@ static ssize_t ubi_volume_cdev_write(struct cdev* cdev, const void *buf, return err; } -static int ubi_volume_cdev_open(struct cdev *cdev, struct filep *f) +static int ubi_volume_cdev_open(struct cdev *cdev) { struct ubi_volume_cdev_priv *priv = cdev->priv; - /* only allow read or write, but not both */ - if ((f->flags & O_ACCMODE) == O_RDWR) - return -EINVAL; - priv->updating = 0; - priv->mode = f->flags & O_ACCMODE; return 0; } -static int ubi_volume_cdev_close(struct cdev *cdev, struct filep *f) +static int ubi_volume_cdev_close(struct cdev *cdev) { struct ubi_volume_cdev_priv *priv = cdev->priv; struct ubi_volume *vol = priv->vol; @@ -132,7 +126,7 @@ static off_t ubi_volume_cdev_lseek(struct cdev *cdev, off_t ofs) struct ubi_volume_cdev_priv *priv = cdev->priv; /* We can only update ubi volumes sequentially */ - if (priv->mode == O_WRONLY) + if (priv->updating) return -EINVAL; return ofs; diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 64794171..19e35dbb 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -84,6 +84,22 @@ config DRIVER_NET_TAP bool "tap Ethernet driver" depends on LINUX +config DRIVER_NET_TSE + depends on NIOS2 + bool "Altera TSE ethernet driver" + select MIIDEV + help + This option enables support for the Altera TSE MAC. + +config TSE_USE_DEDICATED_DESC_MEM + depends on DRIVER_NET_TSE + bool "Altera TSE uses dedicated descriptor memory" + help + This option tells the TSE driver to use an onchip memory + to store SGDMA descriptors. Descriptor memory is not + reserved with a malloc but directly mapped to the memory + address (defined in config.h) + source "drivers/net/usb/Kconfig" endmenu diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 96d3d327..f02618bd 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -1,7 +1,7 @@ -obj-$(CONFIG_DRIVER_NET_CS8900) += cs8900.o +obj-$(CONFIG_DRIVER_NET_CS8900) += cs8900.o obj-$(CONFIG_DRIVER_NET_SMC911X) += smc911x.o obj-$(CONFIG_DRIVER_NET_SMC91111) += smc91111.o -obj-$(CONFIG_DRIVER_NET_DM9000) += dm9000.o +obj-$(CONFIG_DRIVER_NET_DM9000) += dm9000.o obj-$(CONFIG_DRIVER_NET_NETX) += netx_eth.o obj-$(CONFIG_DRIVER_NET_AT91_ETHER) += at91_ether.o obj-$(CONFIG_DRIVER_NET_MPC5200) += fec_mpc5200.o @@ -11,3 +11,4 @@ obj-$(CONFIG_DRIVER_NET_MACB) += macb.o obj-$(CONFIG_DRIVER_NET_TAP) += tap.o obj-$(CONFIG_MIIDEV) += miidev.o obj-$(CONFIG_NET_USB) += usb/ +obj-$(CONFIG_DRIVER_NET_TSE) += altera_tse.o diff --git a/drivers/net/altera_tse.c b/drivers/net/altera_tse.c new file mode 100644 index 00000000..d922a9aa --- /dev/null +++ b/drivers/net/altera_tse.c @@ -0,0 +1,579 @@ +/* + * Altera TSE Network driver + * + * Copyright (C) 2008 Altera Corporation. + * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw> + * Copyright (C) 2011 Franck JULLIEN, <elec4fun@gmail.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 <common.h> +#include <net.h> +#include <miidev.h> +#include <init.h> +#include <clock.h> +#include <linux/mii.h> + +#include <asm/io.h> +#include <asm/dma-mapping.h> + +#include "altera_tse.h" + +/* This is a generic routine that the SGDMA mode-specific routines + * call to populate a descriptor. + * arg1 :pointer to first SGDMA descriptor. + * arg2 :pointer to next SGDMA descriptor. + * arg3 :Address to where data to be written. + * arg4 :Address from where data to be read. + * arg5 :no of byte to transaction. + * arg6 :variable indicating to generate start of packet or not + * arg7 :read fixed + * arg8 :write fixed + * arg9 :read burst + * arg10 :write burst + * arg11 :atlantic_channel number + */ +static void alt_sgdma_construct_descriptor_burst( + struct alt_sgdma_descriptor *desc, + struct alt_sgdma_descriptor *next, + uint32_t *read_addr, + uint32_t *write_addr, + uint16_t length_or_eop, + uint8_t generate_eop, + uint8_t read_fixed, + uint8_t write_fixed_or_sop, + uint8_t read_burst, + uint8_t write_burst, + uint8_t atlantic_channel) +{ + uint32_t temp; + + /* + * Mark the "next" descriptor as "not" owned by hardware. This prevents + * The SGDMA controller from continuing to process the chain. This is + * done as a single IO write to bypass cache, without flushing + * the entire descriptor, since only the 8-bit descriptor status must + * be flushed. + */ + if (!next) + printf("Next descriptor not defined!!\n"); + + temp = readb(&next->descriptor_control); + writeb(temp & ~ALT_SGDMA_DESCRIPTOR_CONTROL_OWNED_BY_HW_MSK, + &next->descriptor_control); + + writel((uint32_t)read_addr, &desc->source); + writel((uint32_t)write_addr, &desc->destination); + writel((uint32_t)next, &desc->next); + + writel(0, &desc->source_pad); + writel(0, &desc->destination_pad); + writel(0, &desc->next_pad); + writew(length_or_eop, &desc->bytes_to_transfer); + writew(0, &desc->actual_bytes_transferred); + writeb(0, &desc->descriptor_status); + + /* SGDMA burst not currently supported */ + writeb(0, &desc->read_burst); + writeb(0, &desc->write_burst); + + /* + * Set the descriptor control block as follows: + * - Set "owned by hardware" bit + * - Optionally set "generate EOP" bit + * - Optionally set the "read from fixed address" bit + * - Optionally set the "write to fixed address bit (which serves + * serves as a "generate SOP" control bit in memory-to-stream mode). + * - Set the 4-bit atlantic channel, if specified + * + * Note this step is performed after all other descriptor information + * has been filled out so that, if the controller already happens to be + * pointing at this descriptor, it will not run (via the "owned by + * hardware" bit) until all other descriptor has been set up. + */ + + writeb((ALT_SGDMA_DESCRIPTOR_CONTROL_OWNED_BY_HW_MSK) | + (generate_eop ? ALT_SGDMA_DESCRIPTOR_CONTROL_GENERATE_EOP_MSK : 0) | + (read_fixed ? ALT_SGDMA_DESCRIPTOR_CONTROL_READ_FIXED_ADDRESS_MSK : 0) | + (write_fixed_or_sop ? ALT_SGDMA_DESCRIPTOR_CONTROL_WRITE_FIXED_ADDRESS_MSK : 0) | + (atlantic_channel ? ((atlantic_channel & 0x0F) << 3) : 0), + &desc->descriptor_control); +} + +static int alt_sgdma_do_sync_transfer(struct alt_sgdma_registers *dev, + struct alt_sgdma_descriptor *desc) +{ + uint32_t temp; + uint64_t start; + uint64_t tout; + + /* Wait for any pending transfers to complete */ + tout = ALT_TSE_SGDMA_BUSY_WATCHDOG_TOUT * MSECOND; + + start = get_time_ns(); + + while (readl(&dev->status) & ALT_SGDMA_STATUS_BUSY_MSK) { + if (is_timeout(start, tout)) { + debug("Timeout waiting sgdma in do sync!\n"); + break; + } + } + + /* + * Clear any (previous) status register information + * that might occlude our error checking later. + */ + writel(0xFF, &dev->status); + + /* Point the controller at the descriptor */ + writel((uint32_t)desc, &dev->next_descriptor_pointer); + debug("next desc in sgdma 0x%x\n", (uint32_t)dev->next_descriptor_pointer); + + /* + * Set up SGDMA controller to: + * - Disable interrupt generation + * - Run once a valid descriptor is written to controller + * - Stop on an error with any particular descriptor + */ + writel(ALT_SGDMA_CONTROL_RUN_MSK | ALT_SGDMA_CONTROL_STOP_DMA_ER_MSK, + &dev->control); + + /* Wait for the descriptor (chain) to complete */ + debug("wait for sgdma...."); + start = get_time_ns(); + + while (readl(&dev->status) & ALT_SGDMA_STATUS_BUSY_MSK) { + if (is_timeout(start, tout)) { + debug("Timeout waiting sgdma in do sync!\n"); + break; + } + } + + debug("done\n"); + + /* Clear Run */ + temp = readl(&dev->control); + writel(temp & ~ALT_SGDMA_CONTROL_RUN_MSK, &dev->control); + + /* Get & clear status register contents */ + debug("tx sgdma status = 0x%x", readl(&dev->status)); + writel(0xFF, &dev->status); + + return 0; +} + +static int alt_sgdma_do_async_transfer(struct alt_sgdma_registers *dev, + struct alt_sgdma_descriptor *desc) +{ + uint64_t start; + uint64_t tout; + + /* Wait for any pending transfers to complete */ + tout = ALT_TSE_SGDMA_BUSY_WATCHDOG_TOUT * MSECOND; + + start = get_time_ns(); + + while (readl(&dev->status) & ALT_SGDMA_STATUS_BUSY_MSK) { + if (is_timeout(start, tout)) { + debug("Timeout waiting sgdma in do async!\n"); + break; + } + } + + /* + * Clear any (previous) status register information + * that might occlude our error checking later. + */ + writel(0xFF, &dev->status); + + /* Point the controller at the descriptor */ + writel((uint32_t)desc, &dev->next_descriptor_pointer); + + /* + * Set up SGDMA controller to: + * - Disable interrupt generation + * - Run once a valid descriptor is written to controller + * - Stop on an error with any particular descriptor + */ + writel(ALT_SGDMA_CONTROL_RUN_MSK | ALT_SGDMA_CONTROL_STOP_DMA_ER_MSK, + &dev->control); + + return 0; +} + +static int tse_get_ethaddr(struct eth_device *edev, unsigned char *m) +{ + struct altera_tse_priv *priv = edev->priv; + struct alt_tse_mac *mac_dev = priv->mac_dev; + + m[5] = (readl(&mac_dev->mac_addr_1) >> 8) && 0xFF; + m[4] = (readl(&mac_dev->mac_addr_1)) && 0xFF; + m[3] = (readl(&mac_dev->mac_addr_0) >> 24) && 0xFF; + m[2] = (readl(&mac_dev->mac_addr_0) >> 16) && 0xFF; + m[1] = (readl(&mac_dev->mac_addr_0) >> 8) && 0xFF; + m[0] = (readl(&mac_dev->mac_addr_0)) && 0xFF; + + return 0; +} + +static int tse_set_ethaddr(struct eth_device *edev, unsigned char *m) +{ + struct altera_tse_priv *priv = edev->priv; + struct alt_tse_mac *mac_dev = priv->mac_dev; + + debug("Setting MAC address to %02x:%02x:%02x:%02x:%02x:%02x\n", + m[0], m[1], m[2], m[3], m[4], m[5]); + + writel(m[3] << 24 | m[2] << 16 | m[1] << 8 | m[0], &mac_dev->mac_addr_0); + writel((m[5] << 8 | m[4]) & 0xFFFF, &mac_dev->mac_addr_1); + + return 0; +} + +static int tse_phy_read(struct mii_device *mdev, int phy_addr, int reg) +{ + struct eth_device *edev = mdev->edev; + struct alt_tse_mac *mac_dev; + uint32_t *mdio_regs; + + mac_dev = (struct alt_tse_mac *)edev->iobase; + writel(phy_addr, &mac_dev->mdio_phy1_addr); + + mdio_regs = (uint32_t *)&mac_dev->mdio_phy1; + + return readl(&mdio_regs[reg]) & 0xFFFF; +} + +static int tse_phy_write(struct mii_device *mdev, int phy_addr, int reg, int val) +{ + struct eth_device *edev = mdev->edev; + struct alt_tse_mac *mac_dev; + uint32_t *mdio_regs; + + mac_dev = (struct alt_tse_mac *)edev->iobase; + writel(phy_addr, &mac_dev->mdio_phy1_addr); + + mdio_regs = (uint32_t *)&mac_dev->mdio_phy1; + + writel((uint32_t)val, &mdio_regs[reg]); + + return 0; +} + +static void tse_reset(struct eth_device *edev) +{ + /* stop sgdmas, disable tse receive */ + struct altera_tse_priv *priv = edev->priv; + struct alt_tse_mac *mac_dev = priv->mac_dev; + struct alt_sgdma_registers *rx_sgdma = priv->sgdma_rx; + struct alt_sgdma_registers *tx_sgdma = priv->sgdma_tx; + struct alt_sgdma_descriptor *rx_desc = (struct alt_sgdma_descriptor *)&priv->rx_desc[0]; + struct alt_sgdma_descriptor *tx_desc = (struct alt_sgdma_descriptor *)&priv->tx_desc[0]; + uint64_t start; + uint64_t tout; + + tout = ALT_TSE_SGDMA_BUSY_WATCHDOG_TOUT * MSECOND; + + /* clear rx desc & wait for sgdma to complete */ + writeb(0, &rx_desc->descriptor_control); + writel(0, &rx_sgdma->control); + + writel(ALT_SGDMA_CONTROL_SOFTWARERESET_MSK, &rx_sgdma->control); + writel(ALT_SGDMA_CONTROL_SOFTWARERESET_MSK, &rx_sgdma->control); + mdelay(100); + + start = get_time_ns(); + + while (readl(&rx_sgdma->status) & ALT_SGDMA_STATUS_BUSY_MSK) { + if (is_timeout(start, tout)) { + printf("Timeout waiting for rx sgdma!\n"); + writel(ALT_SGDMA_CONTROL_SOFTWARERESET_MSK, &rx_sgdma->control); + writel(ALT_SGDMA_CONTROL_SOFTWARERESET_MSK, &rx_sgdma->control); + break; + } + } + + /* clear tx desc & wait for sgdma to complete */ + writeb(0, &tx_desc->descriptor_control); + writel(0, &tx_sgdma->control); + + writel(ALT_SGDMA_CONTROL_SOFTWARERESET_MSK, &tx_sgdma->control); + writel(ALT_SGDMA_CONTROL_SOFTWARERESET_MSK, &tx_sgdma->control); + mdelay(100); + + start = get_time_ns(); + + while (readl(&tx_sgdma->status) & ALT_SGDMA_STATUS_BUSY_MSK) { + if (is_timeout(start, tout)) { + printf("Timeout waiting for tx sgdma!\n"); + writel(ALT_SGDMA_CONTROL_SOFTWARERESET_MSK, &tx_sgdma->control); + writel(ALT_SGDMA_CONTROL_SOFTWARERESET_MSK, &tx_sgdma->control); + break; + } + } + + /* reset the mac */ + writel(ALTERA_TSE_CMD_TX_ENA_MSK | ALTERA_TSE_CMD_RX_ENA_MSK | + ALTERA_TSE_CMD_SW_RESET_MSK, &mac_dev->command_config); + + start = get_time_ns(); + tout = ALT_TSE_SW_RESET_WATCHDOG_TOUT * MSECOND; + + while (readl(&mac_dev->command_config) & ALTERA_TSE_CMD_SW_RESET_MSK) { + if (is_timeout(start, tout)) { + printf("TSEMAC SW reset bit never cleared!\n"); + break; + } + } +} + +static int tse_eth_open(struct eth_device *edev) +{ + struct altera_tse_priv *priv = edev->priv; + + miidev_wait_aneg(priv->miidev); + miidev_print_status(priv->miidev); + + return 0; +} + +static int tse_eth_send(struct eth_device *edev, void *packet, int length) +{ + + struct altera_tse_priv *priv = edev->priv; + struct alt_sgdma_registers *tx_sgdma = priv->sgdma_tx; + struct alt_sgdma_descriptor *tx_desc = (struct alt_sgdma_descriptor *)priv->tx_desc; + + struct alt_sgdma_descriptor *tx_desc_cur = (struct alt_sgdma_descriptor *)&tx_desc[0]; + + flush_dcache_range((uint32_t)packet, (uint32_t)packet + length); + alt_sgdma_construct_descriptor_burst( + (struct alt_sgdma_descriptor *)&tx_desc[0], + (struct alt_sgdma_descriptor *)&tx_desc[1], + (uint32_t *)packet, /* read addr */ + (uint32_t *)0, /* */ + length, /* length or EOP ,will change for each tx */ + 0x1, /* gen eop */ + 0x0, /* read fixed */ + 0x1, /* write fixed or sop */ + 0x0, /* read burst */ + 0x0, /* write burst */ + 0x0 /* channel */ + ); + + alt_sgdma_do_sync_transfer(tx_sgdma, tx_desc_cur); + + return 0;; +} + +static void tse_eth_halt(struct eth_device *edev) +{ + struct altera_tse_priv *priv = edev->priv; + struct alt_sgdma_registers *rx_sgdma = priv->sgdma_rx; + struct alt_sgdma_registers *tx_sgdma = priv->sgdma_tx; + + writel(0, &rx_sgdma->control); /* Stop the controller and reset settings */ + writel(0, &tx_sgdma->control); /* Stop the controller and reset settings */ +} + +static int tse_eth_rx(struct eth_device *edev) +{ + uint16_t packet_length = 0; + + struct altera_tse_priv *priv = edev->priv; + struct alt_sgdma_descriptor *rx_desc = (struct alt_sgdma_descriptor *)priv->rx_desc; + struct alt_sgdma_descriptor *rx_desc_cur = &rx_desc[0]; + struct alt_sgdma_registers *rx_sgdma = priv->sgdma_rx; + + if (rx_desc_cur->descriptor_status & + ALT_SGDMA_DESCRIPTOR_STATUS_TERMINATED_BY_EOP_MSK) { + + packet_length = rx_desc->actual_bytes_transferred; + net_receive(NetRxPackets[0], packet_length); + + /* Clear Run */ + rx_sgdma->control = (rx_sgdma->control & (~ALT_SGDMA_CONTROL_RUN_MSK)); + + /* start descriptor again */ + flush_dcache_range((uint32_t)(NetRxPackets[0]), (uint32_t)(NetRxPackets[0]) + PKTSIZE); + alt_sgdma_construct_descriptor_burst( + (struct alt_sgdma_descriptor *)&rx_desc[0], + (struct alt_sgdma_descriptor *)&rx_desc[1], + (uint32_t)0x0, /* read addr */ + (uint32_t *)NetRxPackets[0], /* */ + 0x0, /* length or EOP */ + 0x0, /* gen eop */ + 0x0, /* read fixed */ + 0x0, /* write fixed or sop */ + 0x0, /* read burst */ + 0x0, /* write burst */ + 0x0 /* channel */ + ); + + /* setup the sgdma */ + alt_sgdma_do_async_transfer(priv->sgdma_rx, &rx_desc[0]); + } + + return 0; +} + +static int tse_init_dev(struct eth_device *edev) +{ + struct altera_tse_priv *priv = edev->priv; + struct alt_tse_mac *mac_dev = priv->mac_dev; + struct alt_sgdma_descriptor *tx_desc = priv->tx_desc; + struct alt_sgdma_descriptor *rx_desc = priv->rx_desc; + struct alt_sgdma_descriptor *rx_desc_cur; + + rx_desc_cur = (struct alt_sgdma_descriptor *)&rx_desc[0]; + + tse_reset(edev); + + /* need to create sgdma */ + alt_sgdma_construct_descriptor_burst( + (struct alt_sgdma_descriptor *)&tx_desc[0], + (struct alt_sgdma_descriptor *)&tx_desc[1], + (uint32_t *)NULL, /* read addr */ + (uint32_t *)0, /* */ + 0, /* length or EOP ,will change for each tx */ + 0x1, /* gen eop */ + 0x0, /* read fixed */ + 0x1, /* write fixed or sop */ + 0x0, /* read burst */ + 0x0, /* write burst */ + 0x0 /* channel */ + ); + + flush_dcache_range((uint32_t)(NetRxPackets[0]), (uint32_t)(NetRxPackets[0]) + PKTSIZE); + alt_sgdma_construct_descriptor_burst( + (struct alt_sgdma_descriptor *)&rx_desc[0], + (struct alt_sgdma_descriptor *)&rx_desc[1], + (uint32_t)0x0, /* read addr */ + (uint32_t *)NetRxPackets[0], /* */ + 0x0, /* length or EOP */ + 0x0, /* gen eop */ + 0x0, /* read fixed */ + 0x0, /* write fixed or sop */ + 0x0, /* read burst */ + 0x0, /* write burst */ + 0x0 /* channel */ + ); + + /* start rx async transfer */ + alt_sgdma_do_async_transfer(priv->sgdma_rx, rx_desc_cur); + + /* Initialize MAC registers */ + writel(PKTSIZE, &mac_dev->max_frame_length); + + /* NO Shift */ + writel(0, &mac_dev->rx_cmd_stat); + writel(0, &mac_dev->tx_cmd_stat); + + /* enable MAC */ + writel(ALTERA_TSE_CMD_TX_ENA_MSK | ALTERA_TSE_CMD_RX_ENA_MSK, &mac_dev->command_config); + + miidev_restart_aneg(priv->miidev); + + return 0; +} + +static int tse_probe(struct device_d *dev) +{ + struct altera_tse_priv *priv; + struct mii_device *miidev; + struct eth_device *edev; + struct alt_sgdma_descriptor *rx_desc; + struct alt_sgdma_descriptor *tx_desc; +#ifndef CONFIG_TSE_USE_DEDICATED_DESC_MEM + uint32_t dma_handle; +#endif + edev = xzalloc(sizeof(struct eth_device) + sizeof(struct altera_tse_priv)); + miidev = xzalloc(sizeof(struct mii_device)); + + dev->type_data = edev; + edev->priv = (struct altera_tse_priv *)(edev + 1); + + edev->iobase = dev->map_base; + + priv = edev->priv; + + edev->init = tse_init_dev; + edev->open = tse_eth_open; + edev->send = tse_eth_send; + edev->recv = tse_eth_rx; + edev->halt = tse_eth_halt; + edev->get_ethaddr = tse_get_ethaddr; + edev->set_ethaddr = tse_set_ethaddr; + +#ifdef CONFIG_TSE_USE_DEDICATED_DESC_MEM + tx_desc = (struct alt_sgdma_descriptor *)NIOS_SOPC_TSE_DESC_MEM_BASE; + rx_desc = tx_desc + 2; +#else + tx_desc = dma_alloc_coherent(sizeof(*tx_desc) * (3 + PKTBUFSRX), &dma_handle); + rx_desc = tx_desc + 2; + + if (!tx_desc) { + free(edev); + free(miidev); + return 0; + } +#endif + + memset(rx_desc, 0, (sizeof *rx_desc) * (PKTBUFSRX + 1)); + memset(tx_desc, 0, (sizeof *tx_desc) * 2); + + priv->mac_dev = (struct alt_tse_mac *)dev->map_base; + priv->sgdma_rx = (struct alt_sgdma_registers *)NIOS_SOPC_SGDMA_RX_BASE; + priv->sgdma_tx = (struct alt_sgdma_registers *)NIOS_SOPC_SGDMA_TX_BASE; + priv->rx_desc = rx_desc; + priv->tx_desc = tx_desc; + + priv->miidev = miidev; + + miidev->read = tse_phy_read; + miidev->write = tse_phy_write; + miidev->flags = 0; + miidev->edev = edev; + + if (dev->platform_data != NULL) + miidev->address = *((int8_t *)(dev->platform_data)); + else { + printf("No PHY address specified.\n"); + return -ENODEV; + } + + mii_register(miidev); + + return eth_register(edev); +} + +static struct driver_d altera_tse_driver = { + .name = "altera_tse", + .probe = tse_probe, +}; + +static int tse_init(void) +{ + register_driver(&altera_tse_driver); + return 0; +} + +device_initcall(tse_init); + diff --git a/drivers/net/altera_tse.h b/drivers/net/altera_tse.h new file mode 100644 index 00000000..c907c742 --- /dev/null +++ b/drivers/net/altera_tse.h @@ -0,0 +1,303 @@ +/* + * Altera 10/100/1000 triple speed ethernet mac + * + * Copyright (C) 2008 Altera Corporation. + * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw> + * Copyright (C) 2011 Franck JULLIEN <elec4fun@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef _ALTERA_TSE_H_ +#define _ALTERA_TSE_H_ + +/* SGDMA Stuff */ +#define ALT_SGDMA_STATUS_ERROR_MSK (0x00000001) +#define ALT_SGDMA_STATUS_EOP_ENCOUNTERED_MSK (0x00000002) +#define ALT_SGDMA_STATUS_DESC_COMPLETED_MSK (0x00000004) +#define ALT_SGDMA_STATUS_CHAIN_COMPLETED_MSK (0x00000008) +#define ALT_SGDMA_STATUS_BUSY_MSK (0x00000010) + +#define ALT_SGDMA_CONTROL_IE_ERROR_MSK (0x00000001) +#define ALT_SGDMA_CONTROL_IE_EOP_ENCOUNTERED_MSK (0x00000002) +#define ALT_SGDMA_CONTROL_IE_DESC_COMPLETED_MSK (0x00000004) +#define ALT_SGDMA_CONTROL_IE_CHAIN_COMPLETED_MSK (0x00000008) +#define ALT_SGDMA_CONTROL_IE_GLOBAL_MSK (0x00000010) +#define ALT_SGDMA_CONTROL_RUN_MSK (0x00000020) +#define ALT_SGDMA_CONTROL_STOP_DMA_ER_MSK (0x00000040) +#define ALT_SGDMA_CONTROL_IE_MAX_DESC_PROCESSED_MSK (0x00000080) +#define ALT_SGDMA_CONTROL_MAX_DESC_PROCESSED_MSK (0x0000FF00) +#define ALT_SGDMA_CONTROL_SOFTWARERESET_MSK (0x00010000) +#define ALT_SGDMA_CONTROL_PARK_MSK (0x00020000) +#define ALT_SGDMA_CONTROL_CLEAR_INTERRUPT_MSK (0x80000000) + +#define ALTERA_TSE_SGDMA_INTR_MASK (ALT_SGDMA_CONTROL_IE_CHAIN_COMPLETED_MSK \ + | ALT_SGDMA_STATUS_DESC_COMPLETED_MSK \ + | ALT_SGDMA_CONTROL_IE_GLOBAL_MSK) + +/* + * Descriptor control bit masks & offsets + * + * Note: The control byte physically occupies bits [31:24] in memory. + * The following bit-offsets are expressed relative to the LSB of + * the control register bitfield. + */ +#define ALT_SGDMA_DESCRIPTOR_CONTROL_GENERATE_EOP_MSK (0x00000001) +#define ALT_SGDMA_DESCRIPTOR_CONTROL_READ_FIXED_ADDRESS_MSK (0x00000002) +#define ALT_SGDMA_DESCRIPTOR_CONTROL_WRITE_FIXED_ADDRESS_MSK (0x00000004) +#define ALT_SGDMA_DESCRIPTOR_CONTROL_ATLANTIC_CHANNEL_MSK (0x00000008) +#define ALT_SGDMA_DESCRIPTOR_CONTROL_OWNED_BY_HW_MSK (0x00000080) + +/* + * Descriptor status bit masks & offsets + * + * Note: The status byte physically occupies bits [23:16] in memory. + * The following bit-offsets are expressed relative to the LSB of + * the status register bitfield. + */ +#define ALT_SGDMA_DESCRIPTOR_STATUS_E_CRC_MSK (0x00000001) +#define ALT_SGDMA_DESCRIPTOR_STATUS_E_PARITY_MSK (0x00000002) +#define ALT_SGDMA_DESCRIPTOR_STATUS_E_OVERFLOW_MSK (0x00000004) +#define ALT_SGDMA_DESCRIPTOR_STATUS_E_SYNC_MSK (0x00000008) +#define ALT_SGDMA_DESCRIPTOR_STATUS_E_UEOP_MSK (0x00000010) +#define ALT_SGDMA_DESCRIPTOR_STATUS_E_MEOP_MSK (0x00000020) +#define ALT_SGDMA_DESCRIPTOR_STATUS_E_MSOP_MSK (0x00000040) +#define ALT_SGDMA_DESCRIPTOR_STATUS_TERMINATED_BY_EOP_MSK (0x00000080) +#define ALT_SGDMA_DESCRIPTOR_STATUS_ERROR_MSK (0x0000007F) + +/* + * The SGDMA controller buffer descriptor allocates + * 64 bits for each address. To support ANSI C, the + * struct implementing a descriptor places 32-bits + * of padding directly above each address; each pad must + * be cleared when initializing a descriptor. + */ + +/* + * Buffer Descriptor data structure + * + */ +struct alt_sgdma_descriptor { + unsigned int *source; /* the address of data to be read. */ + unsigned int source_pad; + + unsigned int *destination; /* the address to write data */ + unsigned int destination_pad; + + unsigned int *next; /* the next descriptor in the list. */ + unsigned int next_pad; + + unsigned short bytes_to_transfer; /* the number of bytes to transfer */ + unsigned char read_burst; + unsigned char write_burst; + + unsigned short actual_bytes_transferred;/* bytes transferred by DMA */ + unsigned char descriptor_status; + unsigned char descriptor_control; + +} __attribute__ ((packed, aligned(1))); + +/* SG-DMA Control/Status Slave registers map */ + +struct alt_sgdma_registers { + unsigned int status; + unsigned int status_pad[3]; + unsigned int control; + unsigned int control_pad[3]; + unsigned int next_descriptor_pointer; + unsigned int descriptor_pad[3]; +}; + +/* TSE Stuff */ +#define ALTERA_TSE_CMD_TX_ENA_MSK (0x00000001) +#define ALTERA_TSE_CMD_RX_ENA_MSK (0x00000002) +#define ALTERA_TSE_CMD_XON_GEN_MSK (0x00000004) +#define ALTERA_TSE_CMD_ETH_SPEED_MSK (0x00000008) +#define ALTERA_TSE_CMD_PROMIS_EN_MSK (0x00000010) +#define ALTERA_TSE_CMD_PAD_EN_MSK (0x00000020) +#define ALTERA_TSE_CMD_CRC_FWD_MSK (0x00000040) +#define ALTERA_TSE_CMD_PAUSE_FWD_MSK (0x00000080) +#define ALTERA_TSE_CMD_PAUSE_IGNORE_MSK (0x00000100) +#define ALTERA_TSE_CMD_TX_ADDR_INS_MSK (0x00000200) +#define ALTERA_TSE_CMD_HD_ENA_MSK (0x00000400) +#define ALTERA_TSE_CMD_EXCESS_COL_MSK (0x00000800) +#define ALTERA_TSE_CMD_LATE_COL_MSK (0x00001000) +#define ALTERA_TSE_CMD_SW_RESET_MSK (0x00002000) +#define ALTERA_TSE_CMD_MHASH_SEL_MSK (0x00004000) +#define ALTERA_TSE_CMD_LOOPBACK_MSK (0x00008000) +/* Bits (18:16) = address select */ +#define ALTERA_TSE_CMD_TX_ADDR_SEL_MSK (0x00070000) +#define ALTERA_TSE_CMD_MAGIC_ENA_MSK (0x00080000) +#define ALTERA_TSE_CMD_SLEEP_MSK (0x00100000) +#define ALTERA_TSE_CMD_WAKEUP_MSK (0x00200000) +#define ALTERA_TSE_CMD_XOFF_GEN_MSK (0x00400000) +#define ALTERA_TSE_CMD_CNTL_FRM_ENA_MSK (0x00800000) +#define ALTERA_TSE_CMD_NO_LENGTH_CHECK_MSK (0x01000000) +#define ALTERA_TSE_CMD_ENA_10_MSK (0x02000000) +#define ALTERA_TSE_CMD_RX_ERR_DISC_MSK (0x04000000) +/* Bits (30..27) reserved */ +#define ALTERA_TSE_CMD_CNT_RESET_MSK (0x80000000) + +#define ALTERA_TSE_TX_CMD_STAT_TX_SHIFT16 (0x00040000) +#define ALTERA_TSE_TX_CMD_STAT_OMIT_CRC (0x00020000) + +#define ALTERA_TSE_RX_CMD_STAT_RX_SHIFT16 (0x02000000) + +#define ALT_TSE_SW_RESET_WATCHDOG_CNTR 10000 +#define ALT_TSE_SGDMA_BUSY_WATCHDOG_CNTR 90000000 + +#define ALT_TSE_SW_RESET_WATCHDOG_TOUT 1 /* ms */ +#define ALT_TSE_SGDMA_BUSY_WATCHDOG_TOUT 5 /* ms */ + +struct alt_tse_mdio { + unsigned int control; /*PHY device operation control register */ + unsigned int status; /*PHY device operation status register */ + unsigned int phy_id1; /*Bits 31:16 of PHY identifier. */ + unsigned int phy_id2; /*Bits 15:0 of PHY identifier. */ + unsigned int auto_negotiation_advertisement; + unsigned int remote_partner_base_page_ability; + + unsigned int reg6; + unsigned int reg7; + unsigned int reg8; + unsigned int reg9; + unsigned int rega; + unsigned int regb; + unsigned int regc; + unsigned int regd; + unsigned int rege; + unsigned int regf; + unsigned int reg10; + unsigned int reg11; + unsigned int reg12; + unsigned int reg13; + unsigned int reg14; + unsigned int reg15; + unsigned int reg16; + unsigned int reg17; + unsigned int reg18; + unsigned int reg19; + unsigned int reg1a; + unsigned int reg1b; + unsigned int reg1c; + unsigned int reg1d; + unsigned int reg1e; + unsigned int reg1f; +}; + +/* MAC register Space */ + +struct alt_tse_mac { + unsigned int megacore_revision; + unsigned int scratch_pad; + unsigned int command_config; + unsigned int mac_addr_0; + unsigned int mac_addr_1; + unsigned int max_frame_length; + unsigned int pause_quanta; + unsigned int rx_sel_empty_threshold; + unsigned int rx_sel_full_threshold; + unsigned int tx_sel_empty_threshold; + unsigned int tx_sel_full_threshold; + unsigned int rx_almost_empty_threshold; + unsigned int rx_almost_full_threshold; + unsigned int tx_almost_empty_threshold; + unsigned int tx_almost_full_threshold; + unsigned int mdio_phy0_addr; + unsigned int mdio_phy1_addr; + + /* only if 100/1000 BaseX PCS, reserved otherwise */ + unsigned int reservedx44[5]; + + unsigned int reg_read_access_status; + unsigned int min_tx_ipg_length; + + /* IEEE 802.3 oEntity Managed Object Support */ + unsigned int aMACID_1; /*The MAC addresses */ + unsigned int aMACID_2; + unsigned int aFramesTransmittedOK; + unsigned int aFramesReceivedOK; + unsigned int aFramesCheckSequenceErrors; + unsigned int aAlignmentErrors; + unsigned int aOctetsTransmittedOK; + unsigned int aOctetsReceivedOK; + + /* IEEE 802.3 oPausedEntity Managed Object Support */ + unsigned int aTxPAUSEMACCtrlFrames; + unsigned int aRxPAUSEMACCtrlFrames; + + /* IETF MIB (MIB-II) Object Support */ + unsigned int ifInErrors; + unsigned int ifOutErrors; + unsigned int ifInUcastPkts; + unsigned int ifInMulticastPkts; + unsigned int ifInBroadcastPkts; + unsigned int ifOutDiscards; + unsigned int ifOutUcastPkts; + unsigned int ifOutMulticastPkts; + unsigned int ifOutBroadcastPkts; + + /* IETF RMON MIB Object Support */ + unsigned int etherStatsDropEvent; + unsigned int etherStatsOctets; + unsigned int etherStatsPkts; + unsigned int etherStatsUndersizePkts; + unsigned int etherStatsOversizePkts; + unsigned int etherStatsPkts64Octets; + unsigned int etherStatsPkts65to127Octets; + unsigned int etherStatsPkts128to255Octets; + unsigned int etherStatsPkts256to511Octets; + unsigned int etherStatsPkts512to1023Octets; + unsigned int etherStatsPkts1024to1518Octets; + + unsigned int etherStatsPkts1519toXOctets; + unsigned int etherStatsJabbers; + unsigned int etherStatsFragments; + + unsigned int reservedxE4; + + /*FIFO control register. */ + unsigned int tx_cmd_stat; + unsigned int rx_cmd_stat; + + unsigned int ipaccTxConf; + unsigned int ipaccRxConf; + unsigned int ipaccRxStat; + unsigned int ipaccRxStatSum; + + /*Multicast address resolution table */ + unsigned int hash_table[64]; + + /*Registers 0 to 31 within PHY device 0/1 */ + struct alt_tse_mdio mdio_phy0; + struct alt_tse_mdio mdio_phy1; + + /*4 Supplemental MAC Addresses */ + unsigned int supp_mac_addr_0_0; + unsigned int supp_mac_addr_0_1; + unsigned int supp_mac_addr_1_0; + unsigned int supp_mac_addr_1_1; + unsigned int supp_mac_addr_2_0; + unsigned int supp_mac_addr_2_1; + unsigned int supp_mac_addr_3_0; + unsigned int supp_mac_addr_3_1; + + unsigned int reservedx320[56]; +}; + +struct altera_tse_priv { + struct alt_tse_mac *mac_dev; + struct alt_sgdma_registers *sgdma_rx; + struct alt_sgdma_registers *sgdma_tx; + unsigned int rx_sgdma_irq; + unsigned int tx_sgdma_irq; + unsigned int has_descriptor_mem; + unsigned int descriptor_mem_base; + unsigned int descriptor_mem_size; + struct alt_sgdma_descriptor *rx_desc; + struct alt_sgdma_descriptor *tx_desc; + struct mii_device *miidev; +}; + +#endif /* _ALTERA_TSE_H_ */ diff --git a/drivers/net/smc911x.c b/drivers/net/smc911x.c index e5c0e7e0..dc5477b9 100644 --- a/drivers/net/smc911x.c +++ b/drivers/net/smc911x.c @@ -365,6 +365,7 @@ #define CHIP_9216 0x116a #define CHIP_9217 0x117a #define CHIP_9218 0x118a +#define CHIP_9221 0x9221 struct smc911x_priv { struct mii_device miidev; @@ -385,6 +386,7 @@ static const struct chip_id chip_ids[] = { { CHIP_9216, "LAN9216" }, { CHIP_9217, "LAN9217" }, { CHIP_9218, "LAN9218" }, + { CHIP_9221, "LAN9221" }, { 0, NULL }, }; diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index 64826264..b53dcc7c 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -8,4 +8,8 @@ config NET_USB_ASIX select MIIDEV bool "Asix compatible" +config NET_USB_SMSC95XX + select MIIDEV + bool "SMSC95xx" + endif diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile index 555f8c2f..564e44de 100644 --- a/drivers/net/usb/Makefile +++ b/drivers/net/usb/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_NET_USB) += usbnet.o obj-$(CONFIG_NET_USB_ASIX) += asix.o +obj-$(CONFIG_NET_USB_SMSC95XX) += smsc95xx.o diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c new file mode 100644 index 00000000..ae137fb9 --- /dev/null +++ b/drivers/net/usb/smsc95xx.c @@ -0,0 +1,938 @@ +/*************************************************************************** + * + * Copyright (C) 2007-2008 SMSC + * + * 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 <common.h> +#include <command.h> +#include <init.h> +#include <net.h> +#include <usb/usb.h> +#include <usb/usbnet.h> +#include <malloc.h> +#include <asm/byteorder.h> +#include <errno.h> +#include <miidev.h> +#include "smsc95xx.h" + +#define SMSC_CHIPNAME "smsc95xx" +#define SMSC_DRIVER_VERSION "1.0.4" +#define HS_USB_PKT_SIZE (512) +#define FS_USB_PKT_SIZE (64) +#define DEFAULT_HS_BURST_CAP_SIZE (16 * 1024 + 5 * HS_USB_PKT_SIZE) +#define DEFAULT_FS_BURST_CAP_SIZE (6 * 1024 + 33 * FS_USB_PKT_SIZE) +#define DEFAULT_BULK_IN_DELAY (0x00002000) +#define MAX_SINGLE_PACKET_SIZE (2048) +#define LAN95XX_EEPROM_MAGIC (0x9500) +#define EEPROM_MAC_OFFSET (0x01) +#define DEFAULT_TX_CSUM_ENABLE (1) +#define DEFAULT_RX_CSUM_ENABLE (1) +#define SMSC95XX_INTERNAL_PHY_ID (1) +#define SMSC95XX_TX_OVERHEAD (8) +#define SMSC95XX_TX_OVERHEAD_CSUM (12) + +#define ETH_ALEN 6 +#define NET_IP_ALIGN 2 +#define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */ +#define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */ + +#define netdev_warn(x, fmt, arg...) printf(fmt, ##arg) +#ifdef DEBUG +#define netif_dbg(x, y, z, fmt, arg...) printf(fmt, ##arg) +#else +#define netif_dbg(x, y, z, fmt, arg...) do {} while(0) +#endif + +#define FLOW_CTRL_RX 0x02 + +struct smsc95xx_priv { + u32 mac_cr; + int use_tx_csum; + int use_rx_csum; +}; + +static int turbo_mode = 0; + +static int smsc95xx_read_reg(struct usbnet *dev, u32 index, u32 *data) +{ + int ret; + + ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), + USB_VENDOR_REQUEST_READ_REGISTER, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 00, index, data, 4, USB_CTRL_GET_TIMEOUT); + + if (ret < 0) + netdev_warn(dev->net, "Failed to read register index 0x%08x\n", index); + + le32_to_cpus(data); + + debug("%s: 0x%08x 0x%08x\n", __func__, index, *data); + + return ret; +} + +static int smsc95xx_write_reg(struct usbnet *dev, u32 index, u32 data) +{ + int ret; + + cpu_to_le32s(&data); + + ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), + USB_VENDOR_REQUEST_WRITE_REGISTER, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 00, index, &data, 4, USB_CTRL_SET_TIMEOUT); + + if (ret < 0) + netdev_warn(dev->net, "Failed to write register index 0x%08x\n", index); + + debug("%s: 0x%08x 0x%08x\n", __func__, index, data); + + return ret; +} + +/* Loop until the read is completed with timeout + * called with phy_mutex held */ +static int smsc95xx_phy_wait_not_busy(struct usbnet *dev) +{ + u32 val; + int timeout = 1000; + + do { + smsc95xx_read_reg(dev, MII_ADDR, &val); + if (!(val & MII_BUSY_)) + return 0; + udelay(100); + } while (--timeout); + + return -EIO; +} + +static int smsc95xx_mdio_read(struct mii_device *mdev, int phy_id, int idx) +{ + struct eth_device *eth = mdev->edev; + struct usbnet *dev = eth->priv; + u32 val, addr; + + /* confirm MII not busy */ + if (smsc95xx_phy_wait_not_busy(dev)) { + netdev_warn(dev->net, "MII is busy in smsc95xx_mdio_read\n"); + return -EIO; + } + + /* set the address, index & direction (read from PHY) */ + addr = (phy_id << 11) | (idx << 6) | MII_READ_; + smsc95xx_write_reg(dev, MII_ADDR, addr); + + if (smsc95xx_phy_wait_not_busy(dev)) { + netdev_warn(dev->net, "Timed out reading MII reg %02X\n", idx); + return -EIO; + } + + smsc95xx_read_reg(dev, MII_DATA, &val); + + return val & 0xffff; +} + +static int smsc95xx_mdio_write(struct mii_device *mdev, int phy_id, int idx, + int regval) +{ + struct eth_device *eth = mdev->edev; + struct usbnet *dev = eth->priv; + u32 val, addr; + + /* confirm MII not busy */ + if (smsc95xx_phy_wait_not_busy(dev)) { + netdev_warn(dev->net, "MII is busy in smsc95xx_mdio_write\n"); + return -EBUSY; + } + + val = regval; + smsc95xx_write_reg(dev, MII_DATA, val); + + /* set the address, index & direction (write to PHY) */ + addr = (phy_id << 11) | (idx << 6) | MII_WRITE_; + smsc95xx_write_reg(dev, MII_ADDR, addr); + + if (smsc95xx_phy_wait_not_busy(dev)) + netdev_warn(dev->net, "Timed out writing MII reg %02X\n", idx); + + return 0; +} + +static int smsc95xx_wait_eeprom(struct usbnet *dev) +{ + int timeout = 1000; + u32 val; + + do { + smsc95xx_read_reg(dev, E2P_CMD, &val); + if (!(val & E2P_CMD_BUSY_) || (val & E2P_CMD_TIMEOUT_)) + break; + udelay(100); + } while (--timeout); + + if (val & (E2P_CMD_TIMEOUT_ | E2P_CMD_BUSY_)) { + netdev_warn(dev->net, "EEPROM read operation timeout\n"); + return -EIO; + } + + return 0; +} + +static int smsc95xx_eeprom_confirm_not_busy(struct usbnet *dev) +{ + int timeout = 1000; + u32 val; + + do { + smsc95xx_read_reg(dev, E2P_CMD, &val); + + if (!(val & E2P_CMD_BUSY_)) + return 0; + udelay(100); + } while (--timeout); + + netdev_warn(dev->net, "EEPROM is busy\n"); + return -EIO; +} + +static int smsc95xx_read_eeprom(struct usbnet *dev, u32 offset, u32 length, + u8 *data) +{ + u32 val; + int i, ret; + + ret = smsc95xx_eeprom_confirm_not_busy(dev); + if (ret) + return ret; + + for (i = 0; i < length; i++) { + val = E2P_CMD_BUSY_ | E2P_CMD_READ_ | (offset & E2P_CMD_ADDR_); + smsc95xx_write_reg(dev, E2P_CMD, val); + + ret = smsc95xx_wait_eeprom(dev); + if (ret < 0) + return ret; + + smsc95xx_read_reg(dev, E2P_DATA, &val); + + data[i] = val & 0xFF; + offset++; + } + + return 0; +} + +#define GPIO_CFG_GPEN_ (0xff000000) +#define GPIO_CFG_GPO0_EN_ (0x01000000) +#define GPIO_CFG_GPTYPE (0x00ff0000) +#define GPIO_CFG_GPO0_TYPE (0x00010000) +#define GPIO_CFG_GPDIR_ (0x0000ff00) +#define GPIO_CFG_GPO0_DIR_ (0x00000100) +#define GPIO_CFG_GPDATA_ (0x000000ff) +#define GPIO_CFG_GPO0_DATA_ (0x00000001) +#define LED_GPIO_CFG_FDX_LED (0x00010000) +#define LED_GPIO_CFG_GPBUF_08_ (0x00000100) +#define LED_GPIO_CFG_GPDIR_08_ (0x00000010) +#define LED_GPIO_CFG_GPDATA_08_ (0x00000001) +#define LED_GPIO_CFG_GPCTL_LED_ (0x00000001) + +#if 0 +static int smsc95xx_enable_gpio(struct usbnet *dev, int gpio, int type) +{ + int ret = -1; + u32 val, reg; + int dir_shift, enable_shift, type_shift; + + if (gpio < 8) { + reg = GPIO_CFG; + enable_shift = 24 + gpio; + type_shift = 16 + gpio; + dir_shift = 8 + gpio; + } else { + gpio -= 8; + reg = LED_GPIO_CFG; + enable_shift = 16 + gpio * 4; + type_shift = 8 + gpio; + dir_shift = 4 + gpio; + } + + ret = smsc95xx_read_reg(dev, reg, &val); + if (ret < 0) + return ret; + + val &= ~(1 << enable_shift); + + if (type) + val &= ~(1 << type_shift); + else + val |= (1 << type_shift); + + val |= (1 << dir_shift); + + ret = smsc95xx_write_reg(dev, reg, val); + + return ret < 0 ? ret : 0; +} + +static int smsc95xx_gpio_set_value(struct usbnet *dev, int gpio, int value) +{ + int ret = -1; + u32 tmp, reg; + + if (gpio > 10) + return -EINVAL; + + smsc95xx_enable_gpio(dev, gpio, 0); + + if (gpio < 8) { + reg = GPIO_CFG; + } else { + reg = LED_GPIO_CFG; + gpio -= 8; + } + + ret = smsc95xx_read_reg(dev, reg, &tmp); + if (ret < 0) + return ret; + + if (value) + tmp |= 1 << gpio; + else + tmp &= ~(1 << gpio); + + ret = smsc95xx_write_reg(dev, reg, tmp); + + return ret < 0 ? ret : 0; +} +#endif + +static void smsc95xx_set_multicast(struct usbnet *dev) +{ + struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); + u32 hash_hi = 0; + u32 hash_lo = 0; + + netif_dbg(dev, drv, dev->net, "receive own packets only\n"); + pdata->mac_cr &= ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_ | MAC_CR_HPFILT_); + + /* Initiate async writes, as we can't wait for completion here */ + smsc95xx_write_reg(dev, HASHH, hash_hi); + smsc95xx_write_reg(dev, HASHL, hash_lo); + smsc95xx_write_reg(dev, MAC_CR, pdata->mac_cr); +} + +/* Enable or disable Tx & Rx checksum offload engines */ +static int smsc95xx_set_csums(struct usbnet *dev) +{ + struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); + u32 read_buf; + int ret = smsc95xx_read_reg(dev, COE_CR, &read_buf); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read COE_CR: %d\n", ret); + return ret; + } + + if (pdata->use_tx_csum) + read_buf |= Tx_COE_EN_; + else + read_buf &= ~Tx_COE_EN_; + + if (pdata->use_rx_csum) + read_buf |= Rx_COE_EN_; + else + read_buf &= ~Rx_COE_EN_; + + ret = smsc95xx_write_reg(dev, COE_CR, read_buf); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write COE_CR: %d\n", ret); + return ret; + } + + netif_dbg(dev, hw, dev->net, "COE_CR = 0x%08x\n", read_buf); + return 0; +} + +static int smsc95xx_set_ethaddr(struct eth_device *edev, unsigned char *adr) +{ + struct usbnet *udev = container_of(edev, struct usbnet, edev); + + u32 addr_lo = adr[0] | adr[1] << 8 | + adr[2] << 16 | adr[3] << 24; + u32 addr_hi = adr[4] | adr[5] << 8; + int ret; + + ret = smsc95xx_write_reg(udev, ADDRL, addr_lo); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write ADDRL: %d\n", ret); + return ret; + } + + ret = smsc95xx_write_reg(udev, ADDRH, addr_hi); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write ADDRH: %d\n", ret); + return ret; + } + + return 0; +} + +static int smsc95xx_get_ethaddr(struct eth_device *edev, unsigned char *adr) +{ + struct usbnet *udev = container_of(edev, struct usbnet, edev); + + /* try reading mac address from EEPROM */ + if (smsc95xx_read_eeprom(udev, EEPROM_MAC_OFFSET, ETH_ALEN, + adr) == 0) { + return 0; + } + + return -EINVAL; +} + +/* starts the TX path */ +static void smsc95xx_start_tx_path(struct usbnet *dev) +{ + struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); + u32 reg_val; + + /* Enable Tx at MAC */ + pdata->mac_cr |= MAC_CR_TXEN_; + + smsc95xx_write_reg(dev, MAC_CR, pdata->mac_cr); + + /* Enable Tx at SCSRs */ + reg_val = TX_CFG_ON_; + smsc95xx_write_reg(dev, TX_CFG, reg_val); +} + +/* Starts the Receive path */ +static void smsc95xx_start_rx_path(struct usbnet *dev) +{ + struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); + + pdata->mac_cr |= MAC_CR_RXEN_; + + smsc95xx_write_reg(dev, MAC_CR, pdata->mac_cr); +} + +static int smsc95xx_phy_initialize(struct usbnet *dev) +{ + int timeout = 0; + int phy_id = 1; /* FIXME */ + uint16_t val, bmcr; + + /* Initialize MII structure */ + dev->miidev.read = smsc95xx_mdio_read; + dev->miidev.write = smsc95xx_mdio_write; + dev->miidev.address = 1; /* FIXME: asix_get_phy_addr(dev); */ + dev->miidev.flags = 0; + dev->miidev.edev = &dev->edev; +// dev->miidev.name = dev->edev.name; + + /* reset phy and wait for reset to complete */ + smsc95xx_mdio_write(&dev->miidev, phy_id, MII_BMCR, BMCR_RESET); + + do { + udelay(10 * 1000); + bmcr = smsc95xx_mdio_read(&dev->miidev, phy_id, MII_BMCR); + timeout++; + } while ((bmcr & MII_BMCR) && (timeout < 100)); + + if (timeout >= 100) { + netdev_warn(dev->net, "timeout on PHY Reset"); + return -EIO; + } + + smsc95xx_mdio_write(&dev->miidev, phy_id, MII_ADVERTISE, + ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP | + ADVERTISE_PAUSE_ASYM); + + /* read to clear */ + val = smsc95xx_mdio_read(&dev->miidev, phy_id, PHY_INT_SRC); + + smsc95xx_mdio_write(&dev->miidev, phy_id, PHY_INT_MASK, + PHY_INT_MASK_DEFAULT_); + + netif_dbg(dev, ifup, dev->net, "phy initialised successfully\n"); + return 0; +} + +static int smsc95xx_reset(struct usbnet *dev) +{ + struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); + u32 read_buf, write_buf, burst_cap = 0; + int ret = 0, timeout; + + netif_dbg(dev, ifup, dev->net, "entering %s\n", __func__); + + write_buf = HW_CFG_LRST_; + ret = smsc95xx_write_reg(dev, HW_CFG, write_buf); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write HW_CFG_LRST_ bit in HW_CFG register, ret = %d\n", + ret); + return ret; + } + + timeout = 0; + do { + ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); + return ret; + } + udelay(1000 * 10); + timeout++; + } while ((read_buf & HW_CFG_LRST_) && (timeout < 100)); + + if (timeout >= 100) { + netdev_warn(dev->net, "timeout waiting for completion of Lite Reset\n"); + return ret; + } + + write_buf = PM_CTL_PHY_RST_; + ret = smsc95xx_write_reg(dev, PM_CTRL, write_buf); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write PM_CTRL: %d\n", ret); + return ret; + } + + timeout = 0; + do { + ret = smsc95xx_read_reg(dev, PM_CTRL, &read_buf); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read PM_CTRL: %d\n", ret); + return ret; + } + udelay(1000 * 10); + timeout++; + } while ((read_buf & PM_CTL_PHY_RST_) && (timeout < 100)); + + if (timeout >= 100) { + netdev_warn(dev->net, "timeout waiting for PHY Reset\n"); + return ret; + } + + ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); + return ret; + } + + netif_dbg(dev, ifup, dev->net, + "Read Value from HW_CFG : 0x%08x\n", read_buf); + + read_buf |= HW_CFG_BIR_; + + ret = smsc95xx_write_reg(dev, HW_CFG, read_buf); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write HW_CFG_BIR_ bit in HW_CFG register, ret = %d\n", + ret); + return ret; + } + + ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); + return ret; + } + netif_dbg(dev, ifup, dev->net, + "Read Value from HW_CFG after writing HW_CFG_BIR_: 0x%08x\n", + read_buf); + + if (!turbo_mode) { + burst_cap = 0; + dev->rx_urb_size = MAX_SINGLE_PACKET_SIZE; + } else if (0) { /* highspeed */ + burst_cap = DEFAULT_HS_BURST_CAP_SIZE / HS_USB_PKT_SIZE; + dev->rx_urb_size = DEFAULT_HS_BURST_CAP_SIZE; + } else { + burst_cap = DEFAULT_FS_BURST_CAP_SIZE / FS_USB_PKT_SIZE; + dev->rx_urb_size = DEFAULT_FS_BURST_CAP_SIZE; + } + + netif_dbg(dev, ifup, dev->net, + "rx_urb_size=%ld\n", (ulong)dev->rx_urb_size); + + ret = smsc95xx_write_reg(dev, BURST_CAP, burst_cap); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write BURST_CAP: %d\n", ret); + return ret; + } + + ret = smsc95xx_read_reg(dev, BURST_CAP, &read_buf); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read BURST_CAP: %d\n", ret); + return ret; + } + netif_dbg(dev, ifup, dev->net, + "Read Value from BURST_CAP after writing: 0x%08x\n", + read_buf); + + read_buf = DEFAULT_BULK_IN_DELAY; + ret = smsc95xx_write_reg(dev, BULK_IN_DLY, read_buf); + if (ret < 0) { + netdev_warn(dev->net, "ret = %d\n", ret); + return ret; + } + + ret = smsc95xx_read_reg(dev, BULK_IN_DLY, &read_buf); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read BULK_IN_DLY: %d\n", ret); + return ret; + } + netif_dbg(dev, ifup, dev->net, + "Read Value from BULK_IN_DLY after writing: 0x%08x\n", + read_buf); + + ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); + return ret; + } + netif_dbg(dev, ifup, dev->net, + "Read Value from HW_CFG: 0x%08x\n", read_buf); + + if (turbo_mode) + read_buf |= (HW_CFG_MEF_ | HW_CFG_BCE_); + + read_buf &= ~HW_CFG_RXDOFF_; + + /* set Rx data offset=2, Make IP header aligns on word boundary. */ + read_buf |= NET_IP_ALIGN << 9; + + ret = smsc95xx_write_reg(dev, HW_CFG, read_buf); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write HW_CFG register, ret=%d\n", + ret); + return ret; + } + + ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); + return ret; + } + netif_dbg(dev, ifup, dev->net, + "Read Value from HW_CFG after writing: 0x%08x\n", read_buf); + + write_buf = 0xFFFFFFFF; + ret = smsc95xx_write_reg(dev, INT_STS, write_buf); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write INT_STS register, ret=%d\n", + ret); + return ret; + } + + ret = smsc95xx_read_reg(dev, ID_REV, &read_buf); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read ID_REV: %d\n", ret); + return ret; + } + netif_dbg(dev, ifup, dev->net, "ID_REV = 0x%08x\n", read_buf); + + /* Configure GPIO pins as LED outputs */ + write_buf = LED_GPIO_CFG_SPD_LED | LED_GPIO_CFG_LNK_LED | + LED_GPIO_CFG_FDX_LED; + ret = smsc95xx_write_reg(dev, LED_GPIO_CFG, write_buf); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write LED_GPIO_CFG register, ret=%d\n", + ret); + return ret; + } + + /* Init Tx */ + write_buf = 0; + ret = smsc95xx_write_reg(dev, FLOW, write_buf); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write FLOW: %d\n", ret); + return ret; + } + + read_buf = AFC_CFG_DEFAULT; + ret = smsc95xx_write_reg(dev, AFC_CFG, read_buf); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write AFC_CFG: %d\n", ret); + return ret; + } + + /* Don't need mac_cr_lock during initialisation */ + ret = smsc95xx_read_reg(dev, MAC_CR, &pdata->mac_cr); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read MAC_CR: %d\n", ret); + return ret; + } + + /* Init Rx */ + /* Set Vlan */ + write_buf = (u32)ETH_P_8021Q; + ret = smsc95xx_write_reg(dev, VLAN1, write_buf); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write VAN1: %d\n", ret); + return ret; + } + + ret = smsc95xx_set_csums(dev); + if (ret < 0) { + netdev_warn(dev->net, "Failed to set csum offload: %d\n", ret); + return ret; + } + + smsc95xx_set_multicast(dev); + + if (smsc95xx_phy_initialize(dev) < 0) + return -EIO; + + ret = smsc95xx_read_reg(dev, INT_EP_CTL, &read_buf); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read INT_EP_CTL: %d\n", ret); + return ret; + } + + /* enable PHY interrupts */ + read_buf |= INT_EP_CTL_PHY_INT_; + + ret = smsc95xx_write_reg(dev, INT_EP_CTL, read_buf); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write INT_EP_CTL: %d\n", ret); + return ret; + } + + smsc95xx_start_tx_path(dev); + smsc95xx_start_rx_path(dev); + + netif_dbg(dev, ifup, dev->net, "%s: return 0\n", __func__); + return 0; +} + +static struct usbnet *usbnet_global; + +static int smsc95xx_bind(struct usbnet *dev) +{ + struct smsc95xx_priv *pdata = NULL; + int ret; + + printf(SMSC_CHIPNAME " v" SMSC_DRIVER_VERSION "\n"); + + ret = usbnet_get_endpoints(dev); + if (ret < 0) { + netdev_warn(dev->net, "usbnet_get_endpoints failed: %d\n", ret); + return ret; + } + + dev->data[0] = (unsigned long)malloc(sizeof(struct smsc95xx_priv)); + + pdata = (struct smsc95xx_priv *)(dev->data[0]); + if (!pdata) { + netdev_warn(dev->net, "Unable to allocate struct smsc95xx_priv\n"); + return -ENOMEM; + } + + pdata->use_tx_csum = DEFAULT_TX_CSUM_ENABLE; + pdata->use_rx_csum = DEFAULT_RX_CSUM_ENABLE; + + /* Init all registers */ + ret = smsc95xx_reset(dev); + + dev->edev.get_ethaddr = smsc95xx_get_ethaddr; + dev->edev.set_ethaddr = smsc95xx_set_ethaddr; + mii_register(&dev->miidev); + + return 0; +} + +static void smsc95xx_unbind(struct usbnet *dev) +{ + struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); + if (pdata) { + netif_dbg(dev, ifdown, dev->net, "free pdata\n"); + free(pdata); + pdata = NULL; + dev->data[0] = 0; + } + + usbnet_global = NULL; +} + +static int smsc95xx_rx_fixup(struct usbnet *dev, void *buf, int len) +{ + while (len > 0) { + u32 header, align_count; + unsigned char *packet; + u16 size; + + memcpy(&header, buf, sizeof(header)); + le32_to_cpus(&header); + buf += 4 + NET_IP_ALIGN; + len -= 4 + NET_IP_ALIGN; + packet = buf; + + /* get the packet length */ + size = (u16)((header & RX_STS_FL_) >> 16); + align_count = (4 - ((size + NET_IP_ALIGN) % 4)) % 4; + + if (header & RX_STS_ES_) { + netif_dbg(dev, rx_err, dev->net, + "Error header=0x%08x\n", header); + } else { + /* ETH_FRAME_LEN + 4(CRC) + 2(COE) + 4(Vlan) */ + if (size > (ETH_FRAME_LEN + 12)) { + netif_dbg(dev, rx_err, dev->net, + "size err header=0x%08x\n", header); + return 0; + } + + /* last frame in this batch */ + if (len == size) { + net_receive(buf, len - 4); + return 1; + } + + net_receive(packet, len - 4); + } + + len -= size; + + /* padding bytes before the next frame starts */ + if (len) + len -= align_count; + } + + if (len < 0) { + netdev_warn(dev->net, "invalid rx length<0 %d\n", len); + return 0; + } + + return 1; +} +#if 0 +static u32 smsc95xx_calc_csum_preamble(struct sk_buff *skb) +{ + int len = skb->data - skb->head; + u16 high_16 = (u16)(skb->csum_offset + skb->csum_start - len); + u16 low_16 = (u16)(skb->csum_start - len); + return (high_16 << 16) | low_16; +} +#endif +static int smsc95xx_tx_fixup(struct usbnet *dev, + void *buf, int len, + void *nbuf, int *nlen) +{ + u32 tx_cmd_a, tx_cmd_b; + + tx_cmd_a = (u32)(len) | TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_; + cpu_to_le32s(&tx_cmd_a); + memcpy(nbuf, &tx_cmd_a, 4); + + tx_cmd_b = (u32)(len); + cpu_to_le32s(&tx_cmd_b); + memcpy(nbuf + 4, &tx_cmd_b, 4); + + memcpy(nbuf + 8, buf, len); + + *nlen = len + 8; + + return 0; +} + +static struct driver_info smsc95xx_info = { + .description = "smsc95xx USB 2.0 Ethernet", + .bind = smsc95xx_bind, + .unbind = smsc95xx_unbind, + .rx_fixup = smsc95xx_rx_fixup, + .tx_fixup = smsc95xx_tx_fixup, +}; + +static const struct usb_device_id products[] = { + { + /* SMSC9500 USB Ethernet Device */ + USB_DEVICE(0x0424, 0x9500), + .driver_info = &smsc95xx_info, + }, { + /* SMSC9505 USB Ethernet Device */ + USB_DEVICE(0x0424, 0x9505), + .driver_info = &smsc95xx_info, + }, { + /* SMSC9500A USB Ethernet Device */ + USB_DEVICE(0x0424, 0x9E00), + .driver_info = &smsc95xx_info, + }, { + /* SMSC9505A USB Ethernet Device */ + USB_DEVICE(0x0424, 0x9E01), + .driver_info = &smsc95xx_info, + }, { + /* SMSC9512/9514 USB Hub & Ethernet Device */ + USB_DEVICE(0x0424, 0xec00), + .driver_info = &smsc95xx_info, + }, { + /* SMSC9500 USB Ethernet Device (SAL10) */ + USB_DEVICE(0x0424, 0x9900), + .driver_info = &smsc95xx_info, + }, { + /* SMSC9505 USB Ethernet Device (SAL10) */ + USB_DEVICE(0x0424, 0x9901), + .driver_info = &smsc95xx_info, + }, { + /* SMSC9500A USB Ethernet Device (SAL10) */ + USB_DEVICE(0x0424, 0x9902), + .driver_info = &smsc95xx_info, + }, { + /* SMSC9505A USB Ethernet Device (SAL10) */ + USB_DEVICE(0x0424, 0x9903), + .driver_info = &smsc95xx_info, + }, { + /* SMSC9512/9514 USB Hub & Ethernet Device (SAL10) */ + USB_DEVICE(0x0424, 0x9904), + .driver_info = &smsc95xx_info, + }, { + /* SMSC9500A USB Ethernet Device (HAL) */ + USB_DEVICE(0x0424, 0x9905), + .driver_info = &smsc95xx_info, + }, { + /* SMSC9505A USB Ethernet Device (HAL) */ + USB_DEVICE(0x0424, 0x9906), + .driver_info = &smsc95xx_info, + }, { + /* SMSC9500 USB Ethernet Device (Alternate ID) */ + USB_DEVICE(0x0424, 0x9907), + .driver_info = &smsc95xx_info, + }, { + /* SMSC9500A USB Ethernet Device (Alternate ID) */ + USB_DEVICE(0x0424, 0x9908), + .driver_info = &smsc95xx_info, + }, { + /* SMSC9512/9514 USB Hub & Ethernet Device (Alternate ID) */ + USB_DEVICE(0x0424, 0x9909), + .driver_info = &smsc95xx_info, + }, + { }, /* END */ +}; + +static struct usb_driver smsc95xx_driver = { + .name = "smsc95xx", + .id_table = products, + .probe = usbnet_probe, + .disconnect = usbnet_disconnect, +}; + +static int __init smsc95xx_init(void) +{ + return usb_driver_register(&smsc95xx_driver); +} +device_initcall(smsc95xx_init); diff --git a/drivers/net/usb/smsc95xx.h b/drivers/net/usb/smsc95xx.h new file mode 100644 index 00000000..86bc4497 --- /dev/null +++ b/drivers/net/usb/smsc95xx.h @@ -0,0 +1,256 @@ + /*************************************************************************** + * + * Copyright (C) 2007-2008 SMSC + * + * 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. + * + *****************************************************************************/ + +#ifndef _SMSC95XX_H +#define _SMSC95XX_H + +/* Tx command words */ +#define TX_CMD_A_DATA_OFFSET_ (0x001F0000) +#define TX_CMD_A_FIRST_SEG_ (0x00002000) +#define TX_CMD_A_LAST_SEG_ (0x00001000) +#define TX_CMD_A_BUF_SIZE_ (0x000007FF) + +#define TX_CMD_B_CSUM_ENABLE (0x00004000) +#define TX_CMD_B_ADD_CRC_DISABLE_ (0x00002000) +#define TX_CMD_B_DISABLE_PADDING_ (0x00001000) +#define TX_CMD_B_PKT_BYTE_LENGTH_ (0x000007FF) + +/* Rx status word */ +#define RX_STS_FF_ (0x40000000) /* Filter Fail */ +#define RX_STS_FL_ (0x3FFF0000) /* Frame Length */ +#define RX_STS_ES_ (0x00008000) /* Error Summary */ +#define RX_STS_BF_ (0x00002000) /* Broadcast Frame */ +#define RX_STS_LE_ (0x00001000) /* Length Error */ +#define RX_STS_RF_ (0x00000800) /* Runt Frame */ +#define RX_STS_MF_ (0x00000400) /* Multicast Frame */ +#define RX_STS_TL_ (0x00000080) /* Frame too long */ +#define RX_STS_CS_ (0x00000040) /* Collision Seen */ +#define RX_STS_FT_ (0x00000020) /* Frame Type */ +#define RX_STS_RW_ (0x00000010) /* Receive Watchdog */ +#define RX_STS_ME_ (0x00000008) /* Mii Error */ +#define RX_STS_DB_ (0x00000004) /* Dribbling */ +#define RX_STS_CRC_ (0x00000002) /* CRC Error */ + +/* SCSRs */ +#define ID_REV (0x00) +#define ID_REV_CHIP_ID_MASK_ (0xFFFF0000) +#define ID_REV_CHIP_REV_MASK_ (0x0000FFFF) +#define ID_REV_CHIP_ID_9500_ (0x9500) + +#define INT_STS (0x08) +#define INT_STS_TX_STOP_ (0x00020000) +#define INT_STS_RX_STOP_ (0x00010000) +#define INT_STS_PHY_INT_ (0x00008000) +#define INT_STS_TXE_ (0x00004000) +#define INT_STS_TDFU_ (0x00002000) +#define INT_STS_TDFO_ (0x00001000) +#define INT_STS_RXDF_ (0x00000800) +#define INT_STS_GPIOS_ (0x000007FF) + +#define RX_CFG (0x0C) +#define RX_FIFO_FLUSH_ (0x00000001) + +#define TX_CFG (0x10) +#define TX_CFG_ON_ (0x00000004) +#define TX_CFG_STOP_ (0x00000002) +#define TX_CFG_FIFO_FLUSH_ (0x00000001) + +#define HW_CFG (0x14) +#define HW_CFG_BIR_ (0x00001000) +#define HW_CFG_LEDB_ (0x00000800) +#define HW_CFG_RXDOFF_ (0x00000600) +#define HW_CFG_DRP_ (0x00000040) +#define HW_CFG_MEF_ (0x00000020) +#define HW_CFG_LRST_ (0x00000008) +#define HW_CFG_PSEL_ (0x00000004) +#define HW_CFG_BCE_ (0x00000002) +#define HW_CFG_SRST_ (0x00000001) + +#define PM_CTRL (0x20) +#define PM_CTL_DEV_RDY_ (0x00000080) +#define PM_CTL_SUS_MODE_ (0x00000060) +#define PM_CTL_SUS_MODE_0 (0x00000000) +#define PM_CTL_SUS_MODE_1 (0x00000020) +#define PM_CTL_SUS_MODE_2 (0x00000060) +#define PM_CTL_PHY_RST_ (0x00000010) +#define PM_CTL_WOL_EN_ (0x00000008) +#define PM_CTL_ED_EN_ (0x00000004) +#define PM_CTL_WUPS_ (0x00000003) +#define PM_CTL_WUPS_NO_ (0x00000000) +#define PM_CTL_WUPS_ED_ (0x00000001) +#define PM_CTL_WUPS_WOL_ (0x00000002) +#define PM_CTL_WUPS_MULTI_ (0x00000003) + +#define LED_GPIO_CFG (0x24) +#define LED_GPIO_CFG_SPD_LED (0x01000000) +#define LED_GPIO_CFG_LNK_LED (0x00100000) +#define LED_GPIO_CFG_FDX_LED (0x00010000) + +#define GPIO_CFG (0x28) + +#define AFC_CFG (0x2C) + +/* Hi watermark = 15.5Kb (~10 mtu pkts) */ +/* low watermark = 3k (~2 mtu pkts) */ +/* backpressure duration = ~ 350us */ +/* Apply FC on any frame. */ +#define AFC_CFG_DEFAULT (0x00F830A1) + +#define E2P_CMD (0x30) +#define E2P_CMD_BUSY_ (0x80000000) +#define E2P_CMD_MASK_ (0x70000000) +#define E2P_CMD_READ_ (0x00000000) +#define E2P_CMD_EWDS_ (0x10000000) +#define E2P_CMD_EWEN_ (0x20000000) +#define E2P_CMD_WRITE_ (0x30000000) +#define E2P_CMD_WRAL_ (0x40000000) +#define E2P_CMD_ERASE_ (0x50000000) +#define E2P_CMD_ERAL_ (0x60000000) +#define E2P_CMD_RELOAD_ (0x70000000) +#define E2P_CMD_TIMEOUT_ (0x00000400) +#define E2P_CMD_LOADED_ (0x00000200) +#define E2P_CMD_ADDR_ (0x000001FF) + +#define MAX_EEPROM_SIZE (512) + +#define E2P_DATA (0x34) +#define E2P_DATA_MASK_ (0x000000FF) + +#define BURST_CAP (0x38) + +#define GPIO_WAKE (0x64) + +#define INT_EP_CTL (0x68) +#define INT_EP_CTL_INTEP_ (0x80000000) +#define INT_EP_CTL_MACRTO_ (0x00080000) +#define INT_EP_CTL_TX_STOP_ (0x00020000) +#define INT_EP_CTL_RX_STOP_ (0x00010000) +#define INT_EP_CTL_PHY_INT_ (0x00008000) +#define INT_EP_CTL_TXE_ (0x00004000) +#define INT_EP_CTL_TDFU_ (0x00002000) +#define INT_EP_CTL_TDFO_ (0x00001000) +#define INT_EP_CTL_RXDF_ (0x00000800) +#define INT_EP_CTL_GPIOS_ (0x000007FF) + +#define BULK_IN_DLY (0x6C) + +/* MAC CSRs */ +#define MAC_CR (0x100) +#define MAC_CR_RXALL_ (0x80000000) +#define MAC_CR_RCVOWN_ (0x00800000) +#define MAC_CR_LOOPBK_ (0x00200000) +#define MAC_CR_FDPX_ (0x00100000) +#define MAC_CR_MCPAS_ (0x00080000) +#define MAC_CR_PRMS_ (0x00040000) +#define MAC_CR_INVFILT_ (0x00020000) +#define MAC_CR_PASSBAD_ (0x00010000) +#define MAC_CR_HFILT_ (0x00008000) +#define MAC_CR_HPFILT_ (0x00002000) +#define MAC_CR_LCOLL_ (0x00001000) +#define MAC_CR_BCAST_ (0x00000800) +#define MAC_CR_DISRTY_ (0x00000400) +#define MAC_CR_PADSTR_ (0x00000100) +#define MAC_CR_BOLMT_MASK (0x000000C0) +#define MAC_CR_DFCHK_ (0x00000020) +#define MAC_CR_TXEN_ (0x00000008) +#define MAC_CR_RXEN_ (0x00000004) + +#define ADDRH (0x104) + +#define ADDRL (0x108) + +#define HASHH (0x10C) + +#define HASHL (0x110) + +#define MII_ADDR (0x114) +#define MII_WRITE_ (0x02) +#define MII_BUSY_ (0x01) +#define MII_READ_ (0x00) /* ~of MII Write bit */ + +#define MII_DATA (0x118) + +#define FLOW (0x11C) +#define FLOW_FCPT_ (0xFFFF0000) +#define FLOW_FCPASS_ (0x00000004) +#define FLOW_FCEN_ (0x00000002) +#define FLOW_FCBSY_ (0x00000001) + +#define VLAN1 (0x120) + +#define VLAN2 (0x124) + +#define WUFF (0x128) + +#define WUCSR (0x12C) + +#define COE_CR (0x130) +#define Tx_COE_EN_ (0x00010000) +#define Rx_COE_MODE_ (0x00000002) +#define Rx_COE_EN_ (0x00000001) + +/* Vendor-specific PHY Definitions */ + +/* Mode Control/Status Register */ +#define PHY_MODE_CTRL_STS (17) +#define MODE_CTRL_STS_EDPWRDOWN_ ((u16)0x2000) +#define MODE_CTRL_STS_ENERGYON_ ((u16)0x0002) + +#define SPECIAL_CTRL_STS (27) +#define SPECIAL_CTRL_STS_OVRRD_AMDIX_ ((u16)0x8000) +#define SPECIAL_CTRL_STS_AMDIX_ENABLE_ ((u16)0x4000) +#define SPECIAL_CTRL_STS_AMDIX_STATE_ ((u16)0x2000) + +#define PHY_INT_SRC (29) +#define PHY_INT_SRC_ENERGY_ON_ ((u16)0x0080) +#define PHY_INT_SRC_ANEG_COMP_ ((u16)0x0040) +#define PHY_INT_SRC_REMOTE_FAULT_ ((u16)0x0020) +#define PHY_INT_SRC_LINK_DOWN_ ((u16)0x0010) + +#define PHY_INT_MASK (30) +#define PHY_INT_MASK_ENERGY_ON_ ((u16)0x0080) +#define PHY_INT_MASK_ANEG_COMP_ ((u16)0x0040) +#define PHY_INT_MASK_REMOTE_FAULT_ ((u16)0x0020) +#define PHY_INT_MASK_LINK_DOWN_ ((u16)0x0010) +#define PHY_INT_MASK_DEFAULT_ (PHY_INT_MASK_ANEG_COMP_ | \ + PHY_INT_MASK_LINK_DOWN_) + +#define PHY_SPECIAL (31) +#define PHY_SPECIAL_SPD_ ((u16)0x001C) +#define PHY_SPECIAL_SPD_10HALF_ ((u16)0x0004) +#define PHY_SPECIAL_SPD_10FULL_ ((u16)0x0014) +#define PHY_SPECIAL_SPD_100HALF_ ((u16)0x0008) +#define PHY_SPECIAL_SPD_100FULL_ ((u16)0x0018) + +/* USB Vendor Requests */ +#define USB_VENDOR_REQUEST_WRITE_REGISTER 0xA0 +#define USB_VENDOR_REQUEST_READ_REGISTER 0xA1 +#define USB_VENDOR_REQUEST_GET_STATS 0xA2 + +/* Interrupt Endpoint status word bitfields */ +#define INT_ENP_TX_STOP_ ((u32)BIT(17)) +#define INT_ENP_RX_STOP_ ((u32)BIT(16)) +#define INT_ENP_PHY_INT_ ((u32)BIT(15)) +#define INT_ENP_TXE_ ((u32)BIT(14)) +#define INT_ENP_TDFU_ ((u32)BIT(13)) +#define INT_ENP_TDFO_ ((u32)BIT(12)) +#define INT_ENP_RXDF_ ((u32)BIT(11)) + +#endif /* _SMSC95XX_H */ diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index ffd877a4..9e05f4be 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -43,6 +43,11 @@ config DRIVER_SERIAL_BLACKFIN default y bool "Blackfin serial driver" +config DRIVER_SERIAL_ALTERA + depends on NIOS2 + default y + bool "Altera serial driver" + config DRIVER_SERIAL_NS16550 default n bool "NS16550 serial driver" diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 9f0e12b0..df9e705c 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -5,7 +5,7 @@ # serial_pl010.o # serial_xuartlite.o obj-$(CONFIG_DRIVER_SERIAL_ARM_DCC) += arm_dcc.o -obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o +obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o obj-$(CONFIG_DRIVER_SERIAL_IMX) += serial_imx.o obj-$(CONFIG_DRIVER_SERIAL_STM378X) += stm-serial.o obj-$(CONFIG_DRIVER_SERIAL_ATMEL) += atmel.o @@ -16,3 +16,4 @@ obj-$(CONFIG_DRIVER_SERIAL_BLACKFIN) += serial_blackfin.o obj-$(CONFIG_DRIVER_SERIAL_NS16550) += serial_ns16550.o obj-$(CONFIG_DRIVER_SERIAL_PL010) += serial_pl010.o obj-$(CONFIG_DRIVER_SERIAL_S3C24X0) += serial_s3c24x0.o +obj-$(CONFIG_DRIVER_SERIAL_ALTERA) += serial_altera.o diff --git a/drivers/serial/serial_altera.c b/drivers/serial/serial_altera.c new file mode 100644 index 00000000..54c71781 --- /dev/null +++ b/drivers/serial/serial_altera.c @@ -0,0 +1,95 @@ +/* + * (C) Copyright 2011, Franck JULLIEN, <elec4fun@gmail.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 <common.h> +#include <driver.h> +#include <init.h> +#include <malloc.h> +#include <asm/io.h> +#include <asm/nios2-io.h> + +static int altera_serial_setbaudrate(struct console_device *cdev, int baudrate) +{ + struct nios_uart *uart = (struct nios_uart *)cdev->dev->map_base; + uint16_t div; + + div = (CPU_FREQ / baudrate) - 1; + writew(div, &uart->divisor); + + return 0; +} + +static void altera_serial_putc(struct console_device *cdev, char c) +{ + struct nios_uart *uart = (struct nios_uart *)cdev->dev->map_base; + + while ((readw(&uart->status) & NIOS_UART_TRDY) == 0); + + writew(c, &uart->txdata); +} + +static int altera_serial_tstc(struct console_device *cdev) +{ + struct nios_uart *uart = (struct nios_uart *)cdev->dev->map_base; + + return readw(&uart->status) & NIOS_UART_RRDY; +} + +static int altera_serial_getc(struct console_device *cdev) +{ + struct nios_uart *uart = (struct nios_uart *)cdev->dev->map_base; + + while (altera_serial_tstc(cdev) == 0); + + return readw(&uart->rxdata) & 0x000000FF; +} + +static int altera_serial_probe(struct device_d *dev) +{ + struct console_device *cdev; + + cdev = xmalloc(sizeof(struct console_device)); + dev->type_data = cdev; + cdev->dev = dev; + cdev->f_caps = CONSOLE_STDIN | CONSOLE_STDOUT | CONSOLE_STDERR; + cdev->tstc = altera_serial_tstc; + cdev->putc = altera_serial_putc; + cdev->getc = altera_serial_getc; + cdev->setbrg = altera_serial_setbaudrate; + + console_register(cdev); + + return 0; +} + +static struct driver_d altera_serial_driver = { + .name = "altera_serial", + .probe = altera_serial_probe, +}; + +static int altera_serial_init(void) +{ + return register_driver(&altera_serial_driver); +} + +console_initcall(altera_serial_init); + diff --git a/drivers/serial/serial_ns16550.c b/drivers/serial/serial_ns16550.c index 290619fe..ab172e74 100644 --- a/drivers/serial/serial_ns16550.c +++ b/drivers/serial/serial_ns16550.c @@ -211,9 +211,7 @@ static int ns16550_probe(struct device_d *dev) if ((plat->reg_read == NULL) || (plat->reg_write == NULL)) return -EINVAL; - cdev = calloc(1, sizeof(struct console_device)); - if (cdev == NULL) - return -ENOMEM; + cdev = xzalloc(sizeof(*cdev)); dev->type_data = cdev; cdev->dev = dev; diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 76e033eb..df4e9e08 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -449,7 +449,7 @@ static struct usb_device *usb_alloc_new_device(void) return usbdev; } -static int __usb_init(void) +void usb_rescan(void) { struct usb_device *dev, *tmp; struct usb_host *host; @@ -477,27 +477,8 @@ static int __usb_init(void) } printf("%d USB Device(s) found\n", dev_index); - - return 0; } -static int do_usb(struct command *cmdtp, int argc, char *argv[]) -{ - __usb_init(); - - return 0; -} - -static const __maybe_unused char cmd_usb_help[] = -"Usage: usb\n" -"(re-)detect USB devices\n"; - -BAREBOX_CMD_START(usb) - .cmd = do_usb, - .usage = "(re-)detect USB devices", - BAREBOX_CMD_HELP(cmd_usb_help) -BAREBOX_CMD_END - /* * disables the asynch behaviour of the control message. This is used for data * transfers that uses the exclusiv access to the control and bulk messages. @@ -16,6 +16,8 @@ config FS_DEVFS default y prompt "devfs support" +source fs/fat/Kconfig + config PARTITION_NEED_MTD bool diff --git a/fs/Makefile b/fs/Makefile index 228af6eb..7cae2b6e 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -1,4 +1,6 @@ obj-$(CONFIG_FS_CRAMFS) += cramfs/ obj-$(CONFIG_FS_RAMFS) += ramfs.o +obj-y += devfs-core.o obj-$(CONFIG_FS_DEVFS) += devfs.o +obj-$(CONFIG_FS_FAT) += fat/ obj-y += fs.o diff --git a/fs/cramfs/cramfs.c b/fs/cramfs/cramfs.c index 73a37591..b9ab50f4 100644 --- a/fs/cramfs/cramfs.c +++ b/fs/cramfs/cramfs.c @@ -456,7 +456,6 @@ static void cramfs_remove(struct device_d *dev) } static struct fs_driver_d cramfs_driver = { - .type = FS_TYPE_CRAMFS, .open = cramfs_open, .close = cramfs_close, .read = cramfs_read, diff --git a/fs/devfs-core.c b/fs/devfs-core.c new file mode 100644 index 00000000..519e18e6 --- /dev/null +++ b/fs/devfs-core.c @@ -0,0 +1,236 @@ +/* + * devfs.c - a device file system for barebox + * + * Copyright (c) 2011 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 <common.h> +#include <driver.h> +#include <errno.h> +#include <malloc.h> +#include <ioctl.h> +#include <linux/err.h> +#include <linux/mtd/mtd.h> + +LIST_HEAD(cdev_list); + +struct cdev *cdev_by_name(const char *filename) +{ + struct cdev *cdev; + + list_for_each_entry(cdev, &cdev_list, list) { + if (!strcmp(cdev->name, filename)) + return cdev; + } + return NULL; +} + +struct cdev *cdev_open(const char *name, unsigned long flags) +{ + struct cdev *cdev = cdev_by_name(name); + int ret; + + if (!cdev) + return NULL; + + if (cdev->ops->open) { + ret = cdev->ops->open(cdev); + if (ret) + return NULL; + } + + return cdev; +} + +void cdev_close(struct cdev *cdev) +{ + if (cdev->ops->close) + cdev->ops->close(cdev); +} + +ssize_t cdev_read(struct cdev *cdev, void *buf, size_t count, ulong offset, ulong flags) +{ + if (!cdev->ops->read) + return -ENOSYS; + + return cdev->ops->read(cdev, buf, count, cdev->offset +offset, flags); +} + +ssize_t cdev_write(struct cdev *cdev, const void *buf, size_t count, ulong offset, ulong flags) +{ + if (!cdev->ops->write) + return -ENOSYS; + + return cdev->ops->write(cdev, buf, count, cdev->offset + offset, flags); +} + +int cdev_flush(struct cdev *cdev) +{ + if (!cdev->ops->flush) + return 0; + + return cdev->ops->flush(cdev); +} + +static int partition_ioctl(struct cdev *cdev, int request, void *buf) +{ + size_t offset; + struct mtd_info_user *user = buf; + + switch (request) { + case MEMSETBADBLOCK: + case MEMGETBADBLOCK: + offset = (off_t)buf; + offset += cdev->offset; + return cdev->ops->ioctl(cdev, request, (void *)offset); + case MEMGETINFO: + if (cdev->mtd) { + user->type = cdev->mtd->type; + user->flags = cdev->mtd->flags; + user->size = cdev->mtd->size; + user->erasesize = cdev->mtd->erasesize; + user->oobsize = cdev->mtd->oobsize; + user->mtd = cdev->mtd; + /* The below fields are obsolete */ + user->ecctype = -1; + user->eccsize = 0; + return 0; + } + if (!cdev->ops->ioctl) + return -EINVAL; + return cdev->ops->ioctl(cdev, request, buf); + default: + return -EINVAL; + } +} + +int cdev_ioctl(struct cdev *cdev, int request, void *buf) +{ + if (cdev->flags & DEVFS_IS_PARTITION) + return partition_ioctl(cdev, request, buf); + + if (!cdev->ops->ioctl) + return -EINVAL; + + return cdev->ops->ioctl(cdev, request, buf); +} + +int cdev_erase(struct cdev *cdev, size_t count, unsigned long offset) +{ + if (!cdev->ops->erase) + return -ENOSYS; + + return cdev->ops->erase(cdev, count, cdev->offset + offset); +} + +int devfs_create(struct cdev *new) +{ + struct cdev *cdev; + + cdev = cdev_by_name(new->name); + if (cdev) + return -EEXIST; + + list_add_tail(&new->list, &cdev_list); + if (new->dev) + list_add_tail(&new->devices_list, &new->dev->cdevs); + + return 0; +} + +int devfs_remove(struct cdev *cdev) +{ + if (cdev->open) + return -EBUSY; + + list_del(&cdev->list); + if (cdev->dev) + list_del(&cdev->devices_list); + + return 0; +} + +int devfs_add_partition(const char *devname, unsigned long offset, size_t size, + int flags, const char *name) +{ + struct cdev *cdev, *new; + + cdev = cdev_by_name(name); + if (cdev) + return -EEXIST; + + cdev = cdev_by_name(devname); + if (!cdev) + return -ENOENT; + + if (offset + size > cdev->size) + return -EINVAL; + + new = xzalloc(sizeof (*new)); + new->name = strdup(name); + new->ops = cdev->ops; + new->priv = cdev->priv; + new->size = size; + new->offset = offset + cdev->offset; + new->dev = cdev->dev; + new->flags = flags | DEVFS_IS_PARTITION; + +#ifdef CONFIG_PARTITION_NEED_MTD + if (cdev->mtd) { + new->mtd = mtd_add_partition(cdev->mtd, offset, size, flags, name); + if (IS_ERR(new->mtd)) { + int ret = PTR_ERR(new->mtd); + free(new); + return ret; + } + } +#endif + + devfs_create(new); + + return 0; +} + +int devfs_del_partition(const char *name) +{ + struct cdev *cdev; + int ret; + + cdev = cdev_by_name(name); + if (!cdev) + return -ENOENT; + + if (!(cdev->flags & DEVFS_IS_PARTITION)) + return -EINVAL; + if (cdev->flags & DEVFS_PARTITION_FIXED) + return -EPERM; + +#ifdef CONFIG_PARTITION_NEED_MTD + if (cdev->mtd) + mtd_del_partition(cdev->mtd); +#endif + + ret = devfs_remove(cdev); + if (ret) + return ret; + + free(cdev->name); + free(cdev); + + return 0; +} @@ -36,34 +36,7 @@ #include <linux/mtd/mtd-abi.h> #include <partition.h> -static LIST_HEAD(cdev_list); - -struct cdev *cdev_by_name(const char *filename) -{ - struct cdev *cdev; - - list_for_each_entry(cdev, &cdev_list, list) { - if (!strcmp(cdev->name, filename)) - return cdev; - } - return NULL; -} - -ssize_t cdev_read(struct cdev *cdev, void *buf, size_t count, ulong offset, ulong flags) -{ - if (!cdev->ops->read) - return -ENOSYS; - - return cdev->ops->read(cdev, buf, count, cdev->offset +offset, flags); -} - -ssize_t cdev_write(struct cdev *cdev, const void *buf, size_t count, ulong offset, ulong flags) -{ - if (!cdev->ops->write) - return -ENOSYS; - - return cdev->ops->write(cdev, buf, count, cdev->offset + offset, flags); -} +extern struct list_head cdev_list; static int devfs_read(struct device_d *_dev, FILE *f, void *buf, size_t size) { @@ -143,7 +116,7 @@ static int devfs_open(struct device_d *_dev, FILE *f, const char *filename) f->inode = cdev; if (cdev->ops->open) { - ret = cdev->ops->open(cdev, f); + ret = cdev->ops->open(cdev); if (ret) return ret; } @@ -159,7 +132,7 @@ static int devfs_close(struct device_d *_dev, FILE *f) int ret; if (cdev->ops->close) { - ret = cdev->ops->close(cdev, f); + ret = cdev->ops->close(cdev); if (ret) return ret; } @@ -169,49 +142,21 @@ static int devfs_close(struct device_d *_dev, FILE *f) return 0; } -static int partition_ioctl(struct cdev *cdev, int request, void *buf) +static int devfs_flush(struct device_d *_dev, FILE *f) { - size_t offset; - struct mtd_info_user *user = buf; - - switch (request) { - case MEMSETBADBLOCK: - case MEMGETBADBLOCK: - offset = (off_t)buf; - offset += cdev->offset; - return cdev->ops->ioctl(cdev, request, (void *)offset); - case MEMGETINFO: - if (cdev->mtd) { - user->type = cdev->mtd->type; - user->flags = cdev->mtd->flags; - user->size = cdev->mtd->size; - user->erasesize = cdev->mtd->erasesize; - user->oobsize = cdev->mtd->oobsize; - user->mtd = cdev->mtd; - /* The below fields are obsolete */ - user->ecctype = -1; - user->eccsize = 0; - return 0; - } - if (!cdev->ops->ioctl) - return -EINVAL; - return cdev->ops->ioctl(cdev, request, buf); - default: - return -EINVAL; - } + struct cdev *cdev = f->inode; + + if (cdev->ops->flush) + return cdev->ops->flush(cdev); + + return 0; } static int devfs_ioctl(struct device_d *_dev, FILE *f, int request, void *buf) { struct cdev *cdev = f->inode; - if (cdev->flags & DEVFS_IS_PARTITION) - return partition_ioctl(cdev, request, buf); - - if (!cdev->ops->ioctl) - return -EINVAL; - - return cdev->ops->ioctl(cdev, request, buf); + return cdev_ioctl(cdev, request, buf); } static int devfs_truncate(struct device_d *dev, FILE *f, ulong size) @@ -282,12 +227,12 @@ static void devfs_delete(struct device_d *dev) } static struct fs_driver_d devfs_driver = { - .type = FS_TYPE_DEVFS, .read = devfs_read, .write = devfs_write, .lseek = devfs_lseek, .open = devfs_open, .close = devfs_close, + .flush = devfs_flush, .ioctl = devfs_ioctl, .opendir = devfs_opendir, .readdir = devfs_readdir, @@ -312,101 +257,3 @@ static int devfs_init(void) } coredevice_initcall(devfs_init); - -int devfs_create(struct cdev *new) -{ - struct cdev *cdev; - - cdev = cdev_by_name(new->name); - if (cdev) - return -EEXIST; - - list_add_tail(&new->list, &cdev_list); - if (new->dev) - list_add_tail(&new->devices_list, &new->dev->cdevs); - - return 0; -} - -int devfs_remove(struct cdev *cdev) -{ - if (cdev->open) - return -EBUSY; - - list_del(&cdev->list); - if (cdev->dev) - list_del(&cdev->devices_list); - - return 0; -} - -int devfs_add_partition(const char *devname, unsigned long offset, size_t size, - int flags, const char *name) -{ - struct cdev *cdev, *new; - - cdev = cdev_by_name(name); - if (cdev) - return -EEXIST; - - cdev = cdev_by_name(devname); - if (!cdev) - return -ENOENT; - - if (offset + size > cdev->size) - return -EINVAL; - - new = xzalloc(sizeof (*new)); - new->name = strdup(name); - new->ops = cdev->ops; - new->priv = cdev->priv; - new->size = size; - new->offset = offset + cdev->offset; - new->dev = cdev->dev; - new->flags = flags | DEVFS_IS_PARTITION; - -#ifdef CONFIG_PARTITION_NEED_MTD - if (cdev->mtd) { - new->mtd = mtd_add_partition(cdev->mtd, offset, size, flags, name); - if (IS_ERR(new->mtd)) { - int ret = PTR_ERR(new->mtd); - free(new); - return ret; - } - } -#endif - - devfs_create(new); - - return 0; -} - -int devfs_del_partition(const char *name) -{ - struct cdev *cdev; - int ret; - - cdev = cdev_by_name(name); - if (!cdev) - return -ENOENT; - - if (!(cdev->flags & DEVFS_IS_PARTITION)) - return -EINVAL; - if (cdev->flags & DEVFS_PARTITION_FIXED) - return -EPERM; - -#ifdef CONFIG_PARTITION_NEED_MTD - if (cdev->mtd) - mtd_del_partition(cdev->mtd); -#endif - - ret = devfs_remove(cdev); - if (ret) - return ret; - - free(cdev->name); - free(cdev); - - return 0; -} - diff --git a/fs/fat/Kconfig b/fs/fat/Kconfig new file mode 100644 index 00000000..06997284 --- /dev/null +++ b/fs/fat/Kconfig @@ -0,0 +1,15 @@ +menuconfig FS_FAT + bool + prompt "FAT filesystem support" + +if FS_FAT + +config FS_FAT_WRITE + bool + prompt "FAT write support" + +config FS_FAT_LFN + bool + prompt "Support long filenames" + +endif diff --git a/fs/fat/Makefile b/fs/fat/Makefile new file mode 100644 index 00000000..efc89ec6 --- /dev/null +++ b/fs/fat/Makefile @@ -0,0 +1 @@ +obj-y += ff.o fat.o diff --git a/fs/fat/diskio.h b/fs/fat/diskio.h new file mode 100644 index 00000000..f0d29dc3 --- /dev/null +++ b/fs/fat/diskio.h @@ -0,0 +1,78 @@ +/*----------------------------------------------------------------------- +/ Low level disk interface modlue include file +/-----------------------------------------------------------------------*/ + +#ifndef _DISKIO + +#define _READONLY 0 /* 1: Remove write functions */ +#define _USE_IOCTL 1 /* 1: Use disk_ioctl fucntion */ + +#include "integer.h" + + +/* Status of Disk Functions */ +typedef BYTE DSTATUS; + +/* Results of Disk Functions */ +typedef enum { + RES_OK = 0, /* 0: Successful */ + RES_ERROR, /* 1: R/W Error */ + RES_WRPRT, /* 2: Write Protected */ + RES_NOTRDY, /* 3: Not Ready */ + RES_PARERR /* 4: Invalid Parameter */ +} DRESULT; + + +/*---------------------------------------*/ +/* Prototypes for disk control functions */ + +int assign_drives (int, int); +DSTATUS disk_initialize (FATFS *fatfs); +DSTATUS disk_status (FATFS *fatfs); +DRESULT disk_read (FATFS *fatfs, BYTE*, DWORD, BYTE); +#if _READONLY == 0 +DRESULT disk_write (FATFS *fatfs, const BYTE*, DWORD, BYTE); +#endif +DRESULT disk_ioctl (FATFS *fatfs, BYTE, void*); + + + +/* Disk Status Bits (DSTATUS) */ + +#define STA_NOINIT 0x01 /* Drive not initialized */ +#define STA_NODISK 0x02 /* No medium in the drive */ +#define STA_PROTECT 0x04 /* Write protected */ + + +/* Command code for disk_ioctrl fucntion */ + +/* Generic command (defined for FatFs) */ +#define CTRL_SYNC 0 /* Flush disk cache (for write functions) */ +#define GET_SECTOR_COUNT 1 /* Get media size (for only f_mkfs()) */ +#define GET_SECTOR_SIZE 2 /* Get sector size (for multiple sector size (_MAX_SS >= 1024)) */ +#define GET_BLOCK_SIZE 3 /* Get erase block size (for only f_mkfs()) */ +#define CTRL_ERASE_SECTOR 4 /* Force erased a block of sectors (for only _USE_ERASE) */ + +/* Generic command */ +#define CTRL_POWER 5 /* Get/Set power status */ +#define CTRL_LOCK 6 /* Lock/Unlock media removal */ +#define CTRL_EJECT 7 /* Eject media */ + +/* MMC/SDC specific ioctl command */ +#define MMC_GET_TYPE 10 /* Get card type */ +#define MMC_GET_CSD 11 /* Get CSD */ +#define MMC_GET_CID 12 /* Get CID */ +#define MMC_GET_OCR 13 /* Get OCR */ +#define MMC_GET_SDSTAT 14 /* Get SD status */ + +/* ATA/CF specific ioctl command */ +#define ATA_GET_REV 20 /* Get F/W revision */ +#define ATA_GET_MODEL 21 /* Get model name */ +#define ATA_GET_SN 22 /* Get serial number */ + +/* NAND specific ioctl command */ +#define NAND_FORMAT 30 /* Create physical format */ + + +#define _DISKIO +#endif diff --git a/fs/fat/fat.c b/fs/fat/fat.c new file mode 100644 index 00000000..4219801b --- /dev/null +++ b/fs/fat/fat.c @@ -0,0 +1,434 @@ +/* + * ramfs.c - a malloc based filesystem + * + * Copyright (c) 2007 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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 <common.h> +#include <driver.h> +#include <init.h> +#include <malloc.h> +#include <fs.h> +#include <command.h> +#include <errno.h> +#include <linux/stat.h> +#include <linux/ctype.h> +#include <xfuncs.h> +#include <fcntl.h> +#include "ff.h" +#include "integer.h" +#include "diskio.h" + +struct fat_priv { + struct cdev *cdev; + FATFS fat; +}; + +/* ---------------------------------------------------------------*/ + +DRESULT disk_read(FATFS *fat, BYTE *buf, DWORD sector, BYTE count) +{ + struct fat_priv *priv = fat->userdata; + int ret; + + debug("%s: sector: %ld count: %d\n", __func__, sector, count); + + ret = cdev_read(priv->cdev, buf, count << 9, sector * 512, 0); + if (ret != count << 9) + return ret; + + return 0; +} + +DRESULT disk_write(FATFS *fat, const BYTE *buf, DWORD sector, BYTE count) +{ + struct fat_priv *priv = fat->userdata; + int ret; + + debug("%s: buf: %p sector: %ld count: %d\n", + __func__, buf, sector, count); + + ret = cdev_write(priv->cdev, buf, count << 9, sector * 512, 0); + if (ret != count << 9) + return ret; + + return 0; +} + +DSTATUS disk_status(FATFS *fat) +{ + return 0; +} + +DWORD get_fattime(void) +{ + return 0; +} + +DRESULT disk_ioctl (FATFS *fat, BYTE command, void *buf) +{ + return 0; +} + +WCHAR ff_convert(WCHAR src, UINT dir) +{ + if (src <= 0x80) + return src; + else + return '?'; +} + +WCHAR ff_wtoupper(WCHAR chr) +{ + if (chr <= 0x80) + return toupper(chr); + else + return '?'; +} + +/* ---------------------------------------------------------------*/ + +#ifdef CONFIG_FS_FAT_WRITE +static int fat_create(struct device_d *dev, const char *pathname, mode_t mode) +{ + struct fat_priv *priv = dev->priv; + FIL f_file; + int ret; + + ret = f_open(&priv->fat, &f_file, pathname, FA_OPEN_ALWAYS); + if (ret) + return -EINVAL; + + f_close(&f_file); + + return 0; +} + +static int fat_unlink(struct device_d *dev, const char *pathname) +{ + struct fat_priv *priv = dev->priv; + int ret; + + ret = f_unlink(&priv->fat, pathname); + if (ret) + return ret; + + cdev_flush(priv->cdev); + + return 0; +} + +static int fat_mkdir(struct device_d *dev, const char *pathname) +{ + struct fat_priv *priv = dev->priv; + int ret; + + ret = f_mkdir(&priv->fat, pathname); + if (ret) + return ret; + + cdev_flush(priv->cdev); + + return 0; +} + +static int fat_rmdir(struct device_d *dev, const char *pathname) +{ + struct fat_priv *priv = dev->priv; + int ret; + + ret = f_unlink(&priv->fat, pathname); + if (ret) + return ret; + + cdev_flush(priv->cdev); + + return 0; +} + +static int fat_write(struct device_d *_dev, FILE *f, const void *buf, size_t insize) +{ + FIL *f_file = f->inode; + int outsize; + int ret; + + ret = f_write(f_file, buf, insize, &outsize); + + debug("%s: %d %d %d %p\n", __func__, ret, insize, outsize, f_file); + + if (ret) + return ret; + if (!outsize) + return -ENOSPC; + + return outsize; +} + +static int fat_truncate(struct device_d *dev, FILE *f, ulong size) +{ + FIL *f_file = f->inode; + unsigned long lastofs; + int ret; + + lastofs = f_file->fptr; + + ret = f_lseek(f_file, size); + if (ret) + return ret; + + ret = f_truncate(f_file); + if (ret) + return ret; + + ret = f_lseek(f_file, lastofs); + if (ret) + return ret; + + return 0; +} +#endif /* CONFIG_FS_FAT_WRITE */ + +static int fat_open(struct device_d *dev, FILE *file, const char *filename) +{ + struct fat_priv *priv = dev->priv; + FIL *f_file; + int ret; + unsigned long flags = 0; + + f_file = xzalloc(sizeof(*f_file)); + + switch (file->flags & O_ACCMODE) { + case O_RDONLY: + flags = FA_READ; + break; + case O_WRONLY: + flags = FA_WRITE; + break; + case O_RDWR: + flags = FA_READ | FA_WRITE; + break; + } + + ret = f_open(&priv->fat, f_file, filename, flags); + if (ret) { + free(f_file); + return -EINVAL; + } + + if (file->flags & O_APPEND) { + ret = f_lseek(f_file, f_file->fsize); + } + + file->inode = f_file; + file->size = f_file->fsize; + + return 0; +} + +static int fat_close(struct device_d *dev, FILE *f) +{ + struct fat_priv *priv = dev->priv; + FIL *f_file = f->inode; + + f_close(f_file); + + free(f_file); + + cdev_flush(priv->cdev); + + return 0; +} + +static int fat_read(struct device_d *_dev, FILE *f, void *buf, size_t insize) +{ + int ret; + FIL *f_file = f->inode; + int outsize; + + ret = f_read(f_file, buf, insize, &outsize); + + debug("%s: %d %d %d %p\n", __func__, ret, insize, outsize, f_file); + + if (ret) + return ret; + + return outsize; +} + +static off_t fat_lseek(struct device_d *dev, FILE *f, off_t pos) +{ + FIL *f_file = f->inode; + int ret; + + ret = f_lseek(f_file, pos); + if (ret) + return ret; + + return pos; +} + +static DIR* fat_opendir(struct device_d *dev, const char *pathname) +{ + struct fat_priv *priv = dev->priv; + DIR *dir; + FF_DIR *ff_dir; + int ret; + + debug("%s: %s\n", __func__, pathname); + + ff_dir = xzalloc(sizeof(*ff_dir)); + if (pathname) + ret = f_opendir(&priv->fat, ff_dir, pathname); + else + ret = f_opendir(&priv->fat, ff_dir, "/"); + + if (ret) + return NULL; + + dir = xzalloc(sizeof(*dir)); + + dir->priv = ff_dir; + + return dir; +} + +static struct dirent* fat_readdir(struct device_d *dev, DIR *dir) +{ + FF_DIR *ff_dir = dir->priv; + FILINFO finfo; + int ret; +#ifdef CONFIG_FS_FAT_LFN + char name[PATH_MAX]; +#endif + memset(&finfo, 0, sizeof(finfo)); +#ifdef CONFIG_FS_FAT_LFN + finfo.lfname = name; + finfo.lfsize = PATH_MAX; +#endif + ret = f_readdir(ff_dir, &finfo); + if (ret) + return NULL; + + if (finfo.fname[0] == '\0') + return NULL; + +#ifdef CONFIG_FS_FAT_LFN + if (*finfo.lfname) + strcpy(dir->d.d_name, finfo.lfname); + else +#endif + strcpy(dir->d.d_name, finfo.fname); + + return &dir->d; +} + +static int fat_closedir(struct device_d *dev, DIR *dir) +{ + FF_DIR *ff_dir = dir->priv; + + free(ff_dir); + free(dir); + + return 0; +} + +static int fat_stat(struct device_d *dev, const char *filename, struct stat *s) +{ + struct fat_priv *priv = dev->priv; + FILINFO finfo; + int ret; + + ret = f_stat(&priv->fat, filename, &finfo); + if (ret) + return ret; + + s->st_size = finfo.fsize; + s->st_mode = S_IRWXU | S_IRWXG | S_IRWXO; + + if (finfo.fattrib & AM_DIR) + s->st_mode |= S_IFDIR; + else + s->st_mode |= S_IFREG; + + return 0; +} + +static int fat_probe(struct device_d *dev) +{ + struct fs_device_d *fsdev = dev->type_data; + struct fat_priv *priv = xzalloc(sizeof(struct fat_priv)); + char *backingstore = fsdev->backingstore; + + dev->priv = priv; + + if (!strncmp(backingstore , "/dev/", 5)) + backingstore += 5; + + priv->cdev = cdev_open(backingstore, O_RDWR); + if (!priv->cdev) + return -EINVAL; + + priv->fat.userdata = priv; + f_mount(&priv->fat); + + return 0; +} + +static void fat_remove(struct device_d *dev) +{ + struct fat_priv *priv = dev->priv; + + cdev_close(priv->cdev); + + free(dev->priv); +} + +static struct fs_driver_d fat_driver = { + .open = fat_open, + .close = fat_close, + .read = fat_read, + .lseek = fat_lseek, + .opendir = fat_opendir, + .readdir = fat_readdir, + .closedir = fat_closedir, + .stat = fat_stat, +#ifdef CONFIG_FS_FAT_WRITE + .create = fat_create, + .unlink = fat_unlink, + .mkdir = fat_mkdir, + .rmdir = fat_rmdir, + .write = fat_write, + .truncate = fat_truncate, +#endif + .flags = 0, + .drv = { + .probe = fat_probe, + .remove = fat_remove, + .name = "fat", + .type_data = &fat_driver, + } +}; + +static int fat_init(void) +{ + return register_fs_driver(&fat_driver); +} + +coredevice_initcall(fat_init); + diff --git a/fs/fat/ff.c b/fs/fat/ff.c new file mode 100644 index 00000000..bcd0a939 --- /dev/null +++ b/fs/fat/ff.c @@ -0,0 +1,2761 @@ +/*----------------------------------------------------------------------------/ +/ FatFs - FAT file system module R0.08b (C)ChaN, 2011 +/-----------------------------------------------------------------------------/ +/ FatFs module is a generic FAT file system module for small embedded systems. +/ This is a free software that opened for education, research and commercial +/ developments under license policy of following terms. +/ +/ Copyright (C) 2011, ChaN, all right reserved. +/ +/ * The FatFs module is a free software and there is NO WARRANTY. +/ * No restriction on use. You can use, modify and redistribute it for +/ personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY. +/ * Redistributions of source code must retain the above copyright notice. +/ +/-----------------------------------------------------------------------------/ +/ Feb 26,'06 R0.00 Prototype. +/ +/ Apr 29,'06 R0.01 First stable version. +/ +/ Jun 01,'06 R0.02 Added FAT12 support. +/ Removed unbuffered mode. +/ Fixed a problem on small (<32M) partition. +/ Jun 10,'06 R0.02a Added a configuration option (_FS_MINIMUM). +/ +/ Sep 22,'06 R0.03 Added f_rename(). +/ Changed option _FS_MINIMUM to _FS_MINIMIZE. +/ Dec 11,'06 R0.03a Improved cluster scan algorithm to write files fast. +/ Fixed f_mkdir() creates incorrect directory on FAT32. +/ +/ Feb 04,'07 R0.04 Supported multiple drive system. +/ Changed some interfaces for multiple drive system. +/ Changed f_mountdrv() to f_mount(). +/ Added f_mkfs(). +/ Apr 01,'07 R0.04a Supported multiple partitions on a physical drive. +/ Added a capability of extending file size to f_lseek(). +/ Added minimization level 3. +/ Fixed an endian sensitive code in f_mkfs(). +/ May 05,'07 R0.04b Added a configuration option _USE_NTFLAG. +/ Added FSInfo support. +/ Fixed DBCS name can result FR_INVALID_NAME. +/ Fixed short seek (<= csize) collapses the file object. +/ +/ Aug 25,'07 R0.05 Changed arguments of f_read(), f_write() and f_mkfs(). +/ Fixed f_mkfs() on FAT32 creates incorrect FSInfo. +/ Fixed f_mkdir() on FAT32 creates incorrect directory. +/ Feb 03,'08 R0.05a Added f_truncate() and f_utime(). +/ Fixed off by one error at FAT sub-type determination. +/ Fixed btr in f_read() can be mistruncated. +/ Fixed cached sector is not flushed when create and close without write. +/ +/ Apr 01,'08 R0.06 Added fputc(), fputs(), fprintf() and fgets(). +/ Improved performance of f_lseek() on moving to the same or following cluster. +/ +/ Apr 01,'09 R0.07 Merged Tiny-FatFs as a configuration option. (_FS_TINY) +/ Added long file name feature. +/ Added multiple code page feature. +/ Added re-entrancy for multitask operation. +/ Added auto cluster size selection to f_mkfs(). +/ Added rewind option to f_readdir(). +/ Changed result code of critical errors. +/ Renamed string functions to avoid name collision. +/ Apr 14,'09 R0.07a Separated out OS dependent code on reentrant cfg. +/ Added multiple sector size feature. +/ Jun 21,'09 R0.07c Fixed f_unlink() can return 0 on error. +/ Fixed wrong cache control in f_lseek(). +/ Added relative path feature. +/ Added f_chdir() and f_chdrive(). +/ Added proper case conversion to extended char. +/ Nov 03,'09 R0.07e Separated out configuration options from ff.h to ffconf.h. +/ Fixed f_unlink() fails to remove a sub-dir on _FS_RPATH. +/ Fixed name matching error on the 13 char boundary. +/ Added a configuration option, _LFN_UNICODE. +/ Changed f_readdir() to return the SFN with always upper case on non-LFN cfg. +/ +/ May 15,'10 R0.08 Added a memory configuration option. (_USE_LFN = 3) +/ Added file lock feature. (_FS_SHARE) +/ Added fast seek feature. (_USE_FASTSEEK) +/ Changed some types on the API, XCHAR->TCHAR. +/ Changed fname member in the FILINFO structure on Unicode cfg. +/ String functions support UTF-8 encoding files on Unicode cfg. +/ Aug 16,'10 R0.08a Added f_getcwd(). (_FS_RPATH = 2) +/ Added sector erase feature. (_USE_ERASE) +/ Moved file lock semaphore table from fs object to the bss. +/ Fixed a wrong directory entry is created on non-LFN cfg when the given name contains ';'. +/ Fixed f_mkfs() creates wrong FAT32 volume. +/ Jan 15,'11 R0.08b Fast seek feature is also applied to f_read() and f_write(). +/ f_lseek() reports required table size on creating CLMP. +/ Extended format syntax of f_printf function. +/ Ignores duplicated directory separators in given path names. +/---------------------------------------------------------------------------*/ + +#include <string.h> +#include <errno.h> +#include <malloc.h> +#include <linux/ctype.h> +#include "ff.h" /* FatFs configurations and declarations */ +#include "diskio.h" /* Declarations of low level disk I/O functions */ + +#if _FATFS != 8237 +#error Wrong include file (ff.h). +#endif + +/* Definitions on sector size */ +#if _MAX_SS != 512 && _MAX_SS != 1024 && _MAX_SS != 2048 && _MAX_SS != 4096 +#error Wrong sector size. +#endif +#if _MAX_SS != 512 +#define SS(fs) ((fs)->ssize) /* Multiple sector size */ +#else +#define SS(fs) 512U /* Fixed sector size */ +#endif + +#define ABORT(fs, res) { fp->flag |= FA__ERROR; return res; } + +/* Misc definitions */ +#define LD_CLUST(dir) (((DWORD)LD_WORD(dir+DIR_FstClusHI)<<16) | LD_WORD(dir+DIR_FstClusLO)) +#define ST_CLUST(dir,cl) {ST_WORD(dir+DIR_FstClusLO, cl); ST_WORD(dir+DIR_FstClusHI, (DWORD)cl>>16);} + + +/* DBCS code ranges and SBCS extend char conversion table */ + +/* Codepage 437 (US OEM) */ +#define _DF1S 0 +#define _EXCVT { \ + 0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \ + 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, \ +} + +#define IsDBCS1(c) 0 +#define IsDBCS2(c) 0 + +/* Name status flags */ +#define NS 11 /* Index of name status byte in fn[] */ +#define NS_LOSS 0x01 /* Out of 8.3 format */ +#define NS_LFN 0x02 /* Force to create LFN entry */ +#define NS_LAST 0x04 /* Last segment */ +#define NS_BODY 0x08 /* Lower case flag (body) */ +#define NS_EXT 0x10 /* Lower case flag (ext) */ +#define NS_DOT 0x20 /* Dot entry */ + + +/* FAT sub-type boundaries */ +/* Note that the FAT spec by Microsoft says 4085 but Windows works with 4087! */ +#define MIN_FAT16 4086 /* Minimum number of clusters for FAT16 */ +#define MIN_FAT32 65526 /* Minimum number of clusters for FAT32 */ + + +/* FatFs refers the members in the FAT structures as byte array instead of +/ structure member because the structure is not binary compatible between +/ different platforms */ + +#define BS_jmpBoot 0 /* Jump instruction (3) */ +#define BS_OEMName 3 /* OEM name (8) */ +#define BPB_BytsPerSec 11 /* Sector size [byte] (2) */ +#define BPB_SecPerClus 13 /* Cluster size [sector] (1) */ +#define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (2) */ +#define BPB_NumFATs 16 /* Number of FAT copies (1) */ +#define BPB_RootEntCnt 17 /* Number of root dir entries for FAT12/16 (2) */ +#define BPB_TotSec16 19 /* Volume size [sector] (2) */ +#define BPB_Media 21 /* Media descriptor (1) */ +#define BPB_FATSz16 22 /* FAT size [sector] (2) */ +#define BPB_SecPerTrk 24 /* Track size [sector] (2) */ +#define BPB_NumHeads 26 /* Number of heads (2) */ +#define BPB_HiddSec 28 /* Number of special hidden sectors (4) */ +#define BPB_TotSec32 32 /* Volume size [sector] (4) */ +#define BS_DrvNum 36 /* Physical drive number (2) */ +#define BS_BootSig 38 /* Extended boot signature (1) */ +#define BS_VolID 39 /* Volume serial number (4) */ +#define BS_VolLab 43 /* Volume label (8) */ +#define BS_FilSysType 54 /* File system type (1) */ +#define BPB_FATSz32 36 /* FAT size [sector] (4) */ +#define BPB_ExtFlags 40 /* Extended flags (2) */ +#define BPB_FSVer 42 /* File system version (2) */ +#define BPB_RootClus 44 /* Root dir first cluster (4) */ +#define BPB_FSInfo 48 /* Offset of FSInfo sector (2) */ +#define BPB_BkBootSec 50 /* Offset of backup boot sectot (2) */ +#define BS_DrvNum32 64 /* Physical drive number (2) */ +#define BS_BootSig32 66 /* Extended boot signature (1) */ +#define BS_VolID32 67 /* Volume serial number (4) */ +#define BS_VolLab32 71 /* Volume label (8) */ +#define BS_FilSysType32 82 /* File system type (1) */ +#define FSI_LeadSig 0 /* FSI: Leading signature (4) */ +#define FSI_StrucSig 484 /* FSI: Structure signature (4) */ +#define FSI_Free_Count 488 /* FSI: Number of free clusters (4) */ +#define FSI_Nxt_Free 492 /* FSI: Last allocated cluster (4) */ +#define MBR_Table 446 /* MBR: Partition table offset (2) */ +#define SZ_PTE 16 /* MBR: Size of a partition table entry */ +#define BS_55AA 510 /* Boot sector signature (2) */ + +#define DIR_Name 0 /* Short file name (11) */ +#define DIR_Attr 11 /* Attribute (1) */ +#define DIR_NTres 12 /* NT flag (1) */ +#define DIR_CrtTime 14 /* Created time (2) */ +#define DIR_CrtDate 16 /* Created date (2) */ +#define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (2) */ +#define DIR_WrtTime 22 /* Modified time (2) */ +#define DIR_WrtDate 24 /* Modified date (2) */ +#define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (2) */ +#define DIR_FileSize 28 /* File size (4) */ +#define LDIR_Ord 0 /* LFN entry order and LLE flag (1) */ +#define LDIR_Attr 11 /* LFN attribute (1) */ +#define LDIR_Type 12 /* LFN type (1) */ +#define LDIR_Chksum 13 /* Sum of corresponding SFN entry */ +#define LDIR_FstClusLO 26 /* Filled by zero (0) */ +#define SZ_DIR 32 /* Size of a directory entry */ +#define LLE 0x40 /* Last long entry flag in LDIR_Ord */ +#define DDE 0xE5 /* Deleted directory enrty mark in DIR_Name[0] */ +#define NDDE 0x05 /* Replacement of a character collides with DDE */ + +#ifndef CONFIG_FS_FAT_LFN +#define DEF_NAMEBUF BYTE sfn[12] +#define INIT_BUF(dobj) (dobj).fn = sfn +#define FREE_BUF() + +#else +static WCHAR LfnBuf[_MAX_LFN+1]; +#define DEF_NAMEBUF BYTE sfn[12] +#define INIT_BUF(dobj) { (dobj).fn = sfn; (dobj).lfn = LfnBuf; } +#define FREE_BUF() +#endif + +/* + * Change window offset + */ +struct fat_sector { + DWORD sector; + struct list_head list; + unsigned char data[0]; +}; + +#ifdef CONFIG_FS_FAT_WRITE +static int push_fat_sector(FATFS *fs, DWORD sector) +{ + struct fat_sector *s; + + list_for_each_entry(s, &fs->dirtylist, list) { + if (s->sector == sector) { + memcpy(s->data, fs->win, SS(fs)); + return 0; + } + } + + s = xmalloc(sizeof(*s) + SS(fs)); + memcpy(s->data, fs->win, SS(fs)); + s->sector = sector; + list_add_tail(&s->list, &fs->dirtylist); + + return 0; +} +#endif + +static int get_fat_sector(FATFS *fs, DWORD sector) +{ + struct fat_sector *s; + + list_for_each_entry(s, &fs->dirtylist, list) { + if (s->sector == sector) { + memcpy(fs->win, s->data, SS(fs)); + return 0; + } + } + + return disk_read(fs, fs->win, sector, 1); +} + +#ifdef CONFIG_FS_FAT_WRITE +static int flush_dirty_fat(FATFS *fs) +{ + struct fat_sector *s, *tmp; + + list_for_each_entry_safe(s, tmp, &fs->dirtylist, list) { + disk_write(fs, s->data, s->sector, 1); + if (s->sector < (fs->fatbase + fs->fsize)) { + /* In FAT area */ + BYTE nf; + DWORD wsect = s->sector; + /* Reflect the change to all FAT copies */ + for (nf = fs->n_fats; nf > 1; nf--) { + wsect += fs->fsize; + disk_write(fs, s->data, wsect, 1); + } + } + list_del(&s->list); + free(s); + } + + return 0; +} +#endif + +static int move_window ( + FATFS *fs, /* File system object */ + DWORD sector /* Sector number to make appearance in the fs->win[] */ +) /* Move to zero only writes back dirty window */ +{ + DWORD wsect; + int ret; + + wsect = fs->winsect; + if (wsect != sector) { /* Changed current window */ +#ifdef CONFIG_FS_FAT_WRITE + /* Write back dirty window if needed */ + if (fs->wflag) { + ret = push_fat_sector(fs, wsect); + if (ret) + return ret; + fs->wflag = 0; + } +#endif + if (sector) { + ret = get_fat_sector(fs, sector); + if (ret) + return ret; + fs->winsect = sector; + } + } + + return 0; +} + +/* + * Clean-up cached data + */ +#ifdef CONFIG_FS_FAT_WRITE +static +int sync ( /* 0: successful, -EIO: failed */ + FATFS *fs /* File system object */ +) +{ + int res; + + res = move_window(fs, 0); + if (res == 0) { + /* Update FSInfo sector if needed */ + if (fs->fs_type == FS_FAT32 && fs->fsi_flag) { + fs->winsect = 0; + /* Create FSInfo structure */ + memset(fs->win, 0, 512); + ST_WORD(fs->win+BS_55AA, 0xAA55); + ST_DWORD(fs->win+FSI_LeadSig, 0x41615252); + ST_DWORD(fs->win+FSI_StrucSig, 0x61417272); + ST_DWORD(fs->win+FSI_Free_Count, fs->free_clust); + ST_DWORD(fs->win+FSI_Nxt_Free, fs->last_clust); + /* Write it into the FSInfo sector */ + disk_write(fs, fs->win, fs->fsi_sector, 1); + fs->fsi_flag = 0; + } + /* Make sure that no pending write process in the physical drive */ + if (disk_ioctl(fs, CTRL_SYNC, (void*)0) != RES_OK) + res = -EIO; + } + + return res; +} +#endif + +/* + * Get sector# from cluster# + */ +DWORD clust2sect ( /* !=0: Sector number, 0: Failed - invalid cluster# */ + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to be converted */ +) +{ + clst -= 2; + if (clst >= (fs->n_fatent - 2)) + return 0; /* Invalid cluster */ + return clst * fs->csize + fs->database; +} + +/* + * FAT access - Read value of a FAT entry + */ +DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, Else:Cluster status */ + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to get the link information */ +) +{ + UINT wc, bc; + BYTE *p; + + + if (clst < 2 || clst >= fs->n_fatent) /* Chack range */ + return 1; + + switch (fs->fs_type) { + case FS_FAT12 : + bc = (UINT)clst; bc += bc / 2; + if (move_window(fs, fs->fatbase + (bc / SS(fs)))) + break; + wc = fs->win[bc % SS(fs)]; bc++; + if (move_window(fs, fs->fatbase + (bc / SS(fs)))) + break; + wc |= fs->win[bc % SS(fs)] << 8; + return (clst & 1) ? (wc >> 4) : (wc & 0xFFF); + + case FS_FAT16 : + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)))) + break; + p = &fs->win[clst * 2 % SS(fs)]; + return LD_WORD(p); + + case FS_FAT32 : + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)))) + break; + p = &fs->win[clst * 4 % SS(fs)]; + return LD_DWORD(p) & 0x0FFFFFFF; + } + + return 0xFFFFFFFF; /* An error occurred at the disk I/O layer */ +} + + + + +/* + * FAT access - Change value of a FAT entry + */ +#ifdef CONFIG_FS_FAT_WRITE + +int put_fat ( + FATFS *fs, /* File system object */ + DWORD clst, /* Cluster# to be changed in range of 2 to fs->n_fatent - 1 */ + DWORD val /* New value to mark the cluster */ +) +{ + UINT bc; + BYTE *p; + int res; + + + if (clst < 2 || clst >= fs->n_fatent) { /* Check range */ + res = -ERESTARTSYS; + + } else { + switch (fs->fs_type) { + case FS_FAT12 : + bc = clst; bc += bc / 2; + res = move_window(fs, fs->fatbase + (bc / SS(fs))); + if (res != 0) + break; + p = &fs->win[bc % SS(fs)]; + *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; + bc++; + fs->wflag = 1; + res = move_window(fs, fs->fatbase + (bc / SS(fs))); + if (res != 0) + break; + p = &fs->win[bc % SS(fs)]; + *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); + break; + + case FS_FAT16 : + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))); + if (res != 0) + break; + p = &fs->win[clst * 2 % SS(fs)]; + ST_WORD(p, (WORD)val); + break; + + case FS_FAT32 : + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))); + if (res != 0) + break; + p = &fs->win[clst * 4 % SS(fs)]; + val |= LD_DWORD(p) & 0xF0000000; + ST_DWORD(p, val); + break; + + default : + res = -ERESTARTSYS; + } + fs->wflag = 1; + } + + return res; +} +#endif /* CONFIG_FS_FAT_WRITE */ + + + + +/* + * FAT handling - Remove a cluster chain + */ +#ifdef CONFIG_FS_FAT_WRITE +static +int remove_chain ( + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to remove a chain from */ +) +{ + int res; + DWORD nxt; +#if _USE_ERASE + DWORD scl = clst, ecl = clst, resion[2]; +#endif + + if (clst < 2 || clst >= fs->n_fatent) { /* Check range */ + res = -ERESTARTSYS; + + } else { + res = 0; + while (clst < fs->n_fatent) { /* Not a last link? */ + nxt = get_fat(fs, clst); /* Get cluster status */ + if (nxt == 0) + break; /* Empty cluster? */ + + if (nxt == 1) { + res = -ERESTARTSYS; + break; /* Internal error? */ + } + + if (nxt == 0xFFFFFFFF) { + res = -EIO; + break; /* Disk error? */ + } + + /* Mark the cluster "empty" */ + res = put_fat(fs, clst, 0); + if (res != 0) + break; + + if (fs->free_clust != 0xFFFFFFFF) { /* Update FSInfo */ + fs->free_clust++; + fs->fsi_flag = 1; + } +#if _USE_ERASE + if (ecl + 1 == nxt) { /* Next cluster is contiguous */ + ecl = nxt; + } else { + /* End of contiguous clusters */ + resion[0] = clust2sect(fs, scl); /* Start sector */ + resion[1] = clust2sect(fs, ecl) + fs->csize - 1; /* End sector */ + disk_ioctl(fs, CTRL_ERASE_SECTOR, resion); /* Erase the block */ + scl = ecl = nxt; + } +#endif + clst = nxt; /* Next cluster */ + } + } + + return res; +} +#endif + + + + +/* + * FAT handling - Stretch or Create a cluster chain + */ +#ifdef CONFIG_FS_FAT_WRITE +static +DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */ + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to stretch. 0 means create a new chain. */ +) +{ + DWORD cs, ncl, scl; + int res; + + + if (clst == 0) { + /* Create a new chain */ + scl = fs->last_clust; /* Get suggested start point */ + if (!scl || scl >= fs->n_fatent) scl = 1; + } else { + /* Stretch the current chain */ + cs = get_fat(fs, clst); /* Check the cluster status */ + if (cs < 2) + return 1; /* It is an invalid cluster */ + if (cs < fs->n_fatent) + return cs; /* It is already followed by next cluster */ + scl = clst; + } + + ncl = scl; /* Start cluster */ + for (;;) { + ncl++; /* Next cluster */ + if (ncl >= fs->n_fatent) { /* Wrap around */ + ncl = 2; + if (ncl > scl) + return 0; /* No free cluster */ + } + cs = get_fat(fs, ncl); /* Get the cluster status */ + if (cs == 0) + break; /* Found a free cluster */ + if (cs == 0xFFFFFFFF || cs == 1)/* An error occurred */ + return cs; + if (ncl == scl) + return 0; /* No free cluster */ + } + + /* Mark the new cluster "last link" */ + res = put_fat(fs, ncl, 0x0FFFFFFF); + if (res == 0 && clst != 0) { + res = put_fat(fs, clst, ncl); /* Link it to the previous one if needed */ + } + if (res == 0) { + /* Update FSINFO */ + fs->last_clust = ncl; + if (fs->free_clust != 0xFFFFFFFF) { + fs->free_clust--; + fs->fsi_flag = 1; + } + } else { + ncl = (res == -EIO) ? 0xFFFFFFFF : 1; + } + + return ncl; /* Return new cluster number or error code */ +} +#endif /* CONFIG_FS_FAT_WRITE */ + +/* + * Directory handling - Set directory index + */ +static int dir_sdi ( + FF_DIR *dj, /* Pointer to directory object */ + WORD idx /* Directory index number */ +) +{ + DWORD clst; + WORD ic; + + dj->index = idx; + clst = dj->sclust; + + if (clst == 1 || clst >= dj->fs->n_fatent) /* Check start cluster range */ + return -ERESTARTSYS; + + if (!clst && dj->fs->fs_type == FS_FAT32) /* Replace cluster# 0 with root cluster# if in FAT32 */ + clst = dj->fs->dirbase; + + if (clst == 0) { + /* Static table (root-dir in FAT12/16) */ + dj->clust = clst; + + if (idx >= dj->fs->n_rootdir) + /* Index is out of range */ + return -ERESTARTSYS; + + dj->sect = dj->fs->dirbase + idx / (SS(dj->fs) / SZ_DIR); /* Sector# */ + } else { + /* Dynamic table (sub-dirs or root-dir in FAT32) */ + ic = SS(dj->fs) / SZ_DIR * dj->fs->csize; /* Entries per cluster */ + while (idx >= ic) { /* Follow cluster chain */ + clst = get_fat(dj->fs, clst); /* Get next cluster */ + if (clst == 0xFFFFFFFF) + return -EIO; /* Disk error */ + if (clst < 2 || clst >= dj->fs->n_fatent) + /* Reached to end of table or int error */ + return -ERESTARTSYS; + idx -= ic; + } + dj->clust = clst; + dj->sect = clust2sect(dj->fs, clst) + idx / (SS(dj->fs) / SZ_DIR); /* Sector# */ + } + + dj->dir = dj->fs->win + (idx % (SS(dj->fs) / SZ_DIR)) * SZ_DIR; /* Ptr to the entry in the sector */ + + return 0; /* Seek succeeded */ +} + + + + +/* + * Directory handling - Move directory index next + */ +static int dir_next ( /* 0:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not stretch */ + FF_DIR *dj, /* Pointer to directory object */ + int stretch /* 0: Do not stretch table, 1: Stretch table if needed */ +) +{ + DWORD clst; + WORD i; + + + i = dj->index + 1; + if (!i || !dj->sect) /* Report EOT when index has reached 65535 */ + return -ENOENT; + + if (!(i % (SS(dj->fs) / SZ_DIR))) { /* Sector changed? */ + dj->sect++; /* Next sector */ + + if (dj->clust == 0) { + /* Static table */ + if (i >= dj->fs->n_rootdir) /* Report EOT when end of table */ + return -ENOENT; + } + else { + /* Dynamic table */ + if (((i / (SS(dj->fs) / SZ_DIR)) & (dj->fs->csize - 1)) == 0) { /* Cluster changed? */ + clst = get_fat(dj->fs, dj->clust); /* Get next cluster */ + + if (clst <= 1) + return -ERESTARTSYS; + + if (clst == 0xFFFFFFFF) + return -EIO; + + if (clst >= dj->fs->n_fatent) { /* When it reached end of dynamic table */ +#ifdef CONFIG_FS_FAT_WRITE + BYTE c; + + if (!stretch) + return -ENOENT; /* When do not stretch, report EOT */ + + clst = create_chain(dj->fs, dj->clust); /* Stretch cluster chain */ + + if (clst == 0) + return -ENOSPC; /* No free cluster */ + + if (clst == 1) + return -ERESTARTSYS; + + if (clst == 0xFFFFFFFF) + return -EIO; + + /* Clean-up stretched table */ + if (move_window(dj->fs, 0)) + return -EIO; /* Flush active window */ + + memset(dj->fs->win, 0, SS(dj->fs)); /* Clear window buffer */ + dj->fs->winsect = clust2sect(dj->fs, clst); /* Cluster start sector */ + + for (c = 0; c < dj->fs->csize; c++) { + /* Fill the new cluster with 0 */ + dj->fs->wflag = 1; + if (move_window(dj->fs, 0)) + return -EIO; + dj->fs->winsect++; + } + dj->fs->winsect -= c; /* Rewind window address */ +#else + return -ENOENT; /* Report EOT */ +#endif + } + dj->clust = clst; /* Initialize data for new cluster */ + dj->sect = clust2sect(dj->fs, clst); + } + } + } + + dj->index = i; + dj->dir = dj->fs->win + (i % (SS(dj->fs) / SZ_DIR)) * SZ_DIR; + + return 0; +} + +/* + * LFN handling - Test/Pick/Fit an LFN segment from/to directory entry + */ +#ifdef CONFIG_FS_FAT_LFN + +/* Offset of LFN chars in the directory entry */ +static const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; + +static int cmp_lfn ( /* 1:Matched, 0:Not matched */ + WCHAR *lfnbuf, /* Pointer to the LFN to be compared */ + BYTE *dir /* Pointer to the directory entry containing a part of LFN */ +) +{ + UINT i, s; + WCHAR wc, uc; + + /* Get offset in the LFN buffer */ + i = ((dir[LDIR_Ord] & ~LLE) - 1) * 13; + s = 0; wc = 1; + do { + /* Pick an LFN character from the entry */ + uc = LD_WORD(dir+LfnOfs[s]); + if (wc) { + /* Last char has not been processed */ + wc = ff_wtoupper(uc); + if (i >= _MAX_LFN || wc != ff_wtoupper(lfnbuf[i++])) /* Compare it */ + return 0; /* Not matched */ + } else { + if (uc != 0xFFFF) return 0; /* Check filler */ + } + } while (++s < 13); /* Repeat until all chars in the entry are checked */ + + if ((dir[LDIR_Ord] & LLE) && wc && lfnbuf[i]) + /* Last segment matched but different length */ + return 0; + + return 1; /* The part of LFN matched */ +} + + + +static +int pick_lfn ( /* 1:Succeeded, 0:Buffer overflow */ + WCHAR *lfnbuf, /* Pointer to the Unicode-LFN buffer */ + BYTE *dir /* Pointer to the directory entry */ +) +{ + UINT i, s; + WCHAR wc, uc; + + i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */ + + s = 0; + wc = 1; + do { + /* Pick an LFN character from the entry */ + uc = LD_WORD(dir+LfnOfs[s]); + if (wc) { + /* Last char has not been processed */ + if (i >= _MAX_LFN) + return 0; /* Buffer overflow? */ + lfnbuf[i++] = wc = uc; /* Store it */ + } else { + if (uc != 0xFFFF) + return 0; /* Check filler */ + } + } while (++s < 13); /* Read all character in the entry */ + + if (dir[LDIR_Ord] & LLE) { + /* Put terminator if it is the last LFN part */ + if (i >= _MAX_LFN) + return 0; /* Buffer overflow? */ + lfnbuf[i] = 0; + } + + return 1; +} + + +#ifdef CONFIG_FS_FAT_WRITE +static +void fit_lfn ( + const WCHAR *lfnbuf, /* Pointer to the LFN buffer */ + BYTE *dir, /* Pointer to the directory entry */ + BYTE ord, /* LFN order (1-20) */ + BYTE sum /* SFN sum */ +) +{ + UINT i, s; + WCHAR wc; + + + dir[LDIR_Chksum] = sum; /* Set check sum */ + dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */ + dir[LDIR_Type] = 0; + ST_WORD(dir+LDIR_FstClusLO, 0); + + i = (ord - 1) * 13; /* Get offset in the LFN buffer */ + s = wc = 0; + do { + if (wc != 0xFFFF) + wc = lfnbuf[i++]; /* Get an effective char */ + ST_WORD(dir + LfnOfs[s], wc); /* Put it */ + if (!wc) + wc = 0xFFFF; /* Padding chars following last char */ + } while (++s < 13); + if (wc == 0xFFFF || !lfnbuf[i]) + ord |= LLE; /* Bottom LFN part is the start of LFN sequence */ + dir[LDIR_Ord] = ord; /* Set the LFN order */ +} + +#endif +#endif + + + +/* + * Create numbered name + */ +#ifdef CONFIG_FS_FAT_LFN +void gen_numname ( + BYTE *dst, /* Pointer to generated SFN */ + const BYTE *src, /* Pointer to source SFN to be modified */ + const WCHAR *lfn, /* Pointer to LFN */ + WORD seq /* Sequence number */ +) +{ + BYTE ns[8], c; + UINT i, j; + + + memcpy(dst, src, 11); + + if (seq > 5) { /* On many collisions, generate a hash number instead of sequential number */ + do { + seq = (seq >> 1) + (seq << 15) + (WORD) * lfn++; + } while (*lfn); + } + + /* itoa (hexdecimal) */ + i = 7; + do { + c = (seq % 16) + '0'; + if (c > '9') + c += 7; + ns[i--] = c; + seq /= 16; + } while (seq); + + ns[i] = '~'; + + /* Append the number */ + for (j = 0; j < i && dst[j] != ' '; j++) { + if (IsDBCS1(dst[j])) { + if (j == i - 1) + break; + j++; + } + } + + do { + dst[j++] = (i < 8) ? ns[i++] : ' '; + } while (j < 8); +} +#endif + +/* + * Calculate sum of an SFN + */ +#ifdef CONFIG_FS_FAT_LFN +static BYTE sum_sfn ( + const BYTE *dir /* Ptr to directory entry */ +) +{ + BYTE sum = 0; + UINT n = 11; + + do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n); + return sum; +} +#endif + +/* + * Directory handling - Find an object in the directory + */ + +static int dir_find ( + FF_DIR *dj /* Pointer to the directory object linked to the file name */ +) +{ + int res; + BYTE c, *dir; +#ifdef CONFIG_FS_FAT_LFN + BYTE a, ord, sum; +#endif + + res = dir_sdi(dj, 0); /* Rewind directory object */ + if (res != 0) + return res; + +#ifdef CONFIG_FS_FAT_LFN + ord = sum = 0xFF; +#endif + do { + res = move_window(dj->fs, dj->sect); + if (res != 0) + break; + dir = dj->dir; /* Ptr to the directory entry of current index */ + c = dir[DIR_Name]; + if (c == 0) { + /* Reached to end of table */ + res = -ENOENT; + break; + } +#ifdef CONFIG_FS_FAT_LFN /* LFN configuration */ + a = dir[DIR_Attr] & AM_MASK; + if (c == DDE || ((a & AM_VOL) && a != AM_LFN)) { + /* An entry without valid data */ + ord = 0xFF; + } else { + if (a == AM_LFN) { + /* An LFN entry is found */ + if (dj->lfn) { + if (c & LLE) { /* Is it start of LFN sequence? */ + sum = dir[LDIR_Chksum]; + c &= ~LLE; ord = c; /* LFN start order */ + dj->lfn_idx = dj->index; + } + /* Check validity of the LFN entry and compare it with given name */ + ord = (c == ord && sum == dir[LDIR_Chksum] && cmp_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF; + } + } else { + /* An SFN entry is found */ + if (!ord && sum == sum_sfn(dir)) + break; /* LFN matched? */ + + /* Reset LFN sequence */ + ord = 0xFF; + dj->lfn_idx = 0xFFFF; + if (!(dj->fn[NS] & NS_LOSS) && !memcmp(dir, dj->fn, 11)) + break; /* SFN matched? */ + } + } +#else /* Non LFN configuration */ + if (!(dir[DIR_Attr] & AM_VOL) && !memcmp(dir, dj->fn, 11)) /* Is it a valid entry? */ + break; +#endif + res = dir_next(dj, 0); /* Next entry */ + } while (res == 0); + + return res; +} + + + + +/* + * Read an object from the directory + */ +static int dir_read ( + FF_DIR *dj /* Pointer to the directory object that pointing the entry to be read */ +) +{ + int res; + BYTE c, *dir; +#ifdef CONFIG_FS_FAT_LFN + BYTE a, ord = 0xFF, sum = 0xFF; +#endif + + res = -ENOENT; + while (dj->sect) { + res = move_window(dj->fs, dj->sect); + if (res != 0) + break; + dir = dj->dir; /* Ptr to the directory entry of current index */ + c = dir[DIR_Name]; + if (c == 0) { + /* Reached to end of table */ + res = -ENOENT; + break; + } +#ifdef CONFIG_FS_FAT_LFN /* LFN configuration */ + a = dir[DIR_Attr] & AM_MASK; + if (c == DDE || c == '.' || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */ + ord = 0xFF; + } else { + if (a == AM_LFN) { + /* An LFN entry is found */ + if (c & LLE) { /* Is it start of LFN sequence? */ + sum = dir[LDIR_Chksum]; + c &= ~LLE; ord = c; + dj->lfn_idx = dj->index; + } + /* Check LFN validity and capture it */ + ord = (c == ord && sum == dir[LDIR_Chksum] && pick_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF; + } else { + /* An SFN entry is found */ + if (ord || sum != sum_sfn(dir)) /* Is there a valid LFN? */ + dj->lfn_idx = 0xFFFF; /* It has no LFN. */ + break; + } + } +#else /* Non LFN configuration */ + if (c != DDE && c != '.' && !(dir[DIR_Attr] & AM_VOL)) /* Is it a valid entry? */ + break; +#endif + res = dir_next(dj, 0); /* Next entry */ + if (res != 0) + break; + } + + if (res != 0) + dj->sect = 0; + + return res; +} + +/* + * Register an object to the directory + */ +#ifdef CONFIG_FS_FAT_WRITE +static +int dir_register ( /* 0:Successful, FR_DENIED:No free entry or too many SFN collision, -EIO:Disk error */ + FF_DIR *dj /* Target directory with object name to be created */ +) +{ + int res; + BYTE c, *dir; +#ifdef CONFIG_FS_FAT_LFN /* LFN configuration */ + WORD n, ne, is; + BYTE sn[12], *fn, sum; + WCHAR *lfn; + + + fn = dj->fn; lfn = dj->lfn; + memcpy(sn, fn, 12); + + if (sn[NS] & NS_LOSS) { + /* When LFN is out of 8.3 format, generate a numbered name */ + fn[NS] = 0; dj->lfn = 0; /* Find only SFN */ + for (n = 1; n < 100; n++) { + gen_numname(fn, sn, lfn, n); /* Generate a numbered name */ + res = dir_find(dj); /* Check if the name collides with existing SFN */ + if (res != 0) + break; + } + if (n == 100) + return -ENOSPC; /* Abort if too many collisions */ + if (res != -ENOENT) + return res; /* Abort if the result is other than 'not collided' */ + fn[NS] = sn[NS]; dj->lfn = lfn; + } + + if (sn[NS] & NS_LFN) { + /* When LFN is to be created, reserve an SFN + LFN entries. */ + for (ne = 0; lfn[ne]; ne++); + ne = (ne + 25) / 13; + } else { + /* Otherwise reserve only an SFN entry. */ + ne = 1; + } + + /* Reserve contiguous entries */ + res = dir_sdi(dj, 0); + if (res != 0) + return res; + n = is = 0; + do { + res = move_window(dj->fs, dj->sect); + if (res != 0) + break; + c = *dj->dir; /* Check the entry status */ + if (c == DDE || c == 0) { /* Is it a blank entry? */ + if (n == 0) + is = dj->index; /* First index of the contiguous entry */ + if (++n == ne) + break; /* A contiguous entry that required count is found */ + } else { + n = 0; /* Not a blank entry. Restart to search */ + } + res = dir_next(dj, 1); /* Next entry with table stretch */ + } while (res == 0); + + if (res == 0 && ne > 1) { + /* Initialize LFN entry if needed */ + res = dir_sdi(dj, is); + if (res == 0) { + sum = sum_sfn(dj->fn); /* Sum of the SFN tied to the LFN */ + ne--; + do { + /* Store LFN entries in bottom first */ + res = move_window(dj->fs, dj->sect); + if (res != 0) + break; + fit_lfn(dj->lfn, dj->dir, (BYTE)ne, sum); + dj->fs->wflag = 1; + res = dir_next(dj, 0); /* Next entry */ + } while (res == 0 && --ne); + } + } + +#else /* Non LFN configuration */ + res = dir_sdi(dj, 0); + if (res == 0) { + do { /* Find a blank entry for the SFN */ + res = move_window(dj->fs, dj->sect); + if (res != 0) + break; + c = *dj->dir; + if (c == DDE || c == 0) + break; /* Is it a blank entry? */ + res = dir_next(dj, 1); /* Next entry with table stretch */ + } while (res == 0); + } +#endif + + if (res == 0) { /* Initialize the SFN entry */ + res = move_window(dj->fs, dj->sect); + if (res == 0) { + dir = dj->dir; + memset(dir, 0, SZ_DIR); /* Clean the entry */ + memcpy(dir, dj->fn, 11); /* Put SFN */ +#ifdef CONFIG_FS_FAT_LFN + dir[DIR_NTres] = *(dj->fn+NS) & (NS_BODY | NS_EXT); /* Put NT flag */ +#endif + dj->fs->wflag = 1; + } + } + + return res; +} +#endif /* CONFIG_FS_FAT_WRITE */ + +/* + * Remove an object from the directory + */ +#if defined CONFIG_FS_FAT_WRITE +static int dir_remove ( /* 0: Successful, -EIO: A disk error */ + FF_DIR *dj /* Directory object pointing the entry to be removed */ +) +{ + int res; +#ifdef CONFIG_FS_FAT_LFN /* LFN configuration */ + WORD i; + + i = dj->index; /* SFN index */ + /* Goto the SFN or top of the LFN entries */ + res = dir_sdi(dj, (WORD)((dj->lfn_idx == 0xFFFF) ? i : dj->lfn_idx)); + if (res == 0) { + do { + res = move_window(dj->fs, dj->sect); + if (res != 0) + break; + *dj->dir = DDE; /* Mark the entry "deleted" */ + dj->fs->wflag = 1; + if (dj->index >= i) + break; /* When reached SFN, all entries of the object has been deleted. */ + res = dir_next(dj, 0); /* Next entry */ + } while (res == 0); + if (res == -ENOENT) + res = -ERESTARTSYS; + } + +#else /* Non LFN configuration */ + res = dir_sdi(dj, dj->index); + if (res == 0) { + res = move_window(dj->fs, dj->sect); + if (res == 0) { + *dj->dir = DDE; /* Mark the entry "deleted" */ + dj->fs->wflag = 1; + } + } +#endif + + return res; +} +#endif /* CONFIG_FS_FAT_WRITE */ + +/* + * Pick a segment and create the object name in directory form + */ +static int create_name ( + FF_DIR *dj, /* Pointer to the directory object */ + const TCHAR **path /* Pointer to pointer to the segment in the path string */ +) +{ +#ifdef _EXCVT + static const BYTE excvt[] = _EXCVT; /* Upper conversion table for extended chars */ +#endif + +#ifdef CONFIG_FS_FAT_LFN /* LFN configuration */ + BYTE b, cf; + WCHAR w, *lfn; + UINT i, ni, si, di; + const TCHAR *p; + + /* Create LFN in Unicode */ + for (p = *path; *p == '/' || *p == '\\'; p++) ; /* Strip duplicated separator */ + lfn = dj->lfn; + si = di = 0; + for (;;) { + w = p[si++]; /* Get a character */ + if (w < ' ' || w == '/' || w == '\\') + break; /* Break on end of segment */ + if (di >= _MAX_LFN) /* Reject too long name */ + return -ENAMETOOLONG; +#if !_LFN_UNICODE + w &= 0xFF; + if (IsDBCS1(w)) { /* Check if it is a DBC 1st byte (always false on SBCS cfg) */ + b = (BYTE)p[si++]; /* Get 2nd byte */ + if (!IsDBCS2(b)) + return -ENAMETOOLONG; /* Reject invalid sequence */ + w = (w << 8) + b; /* Create a DBC */ + } + w = ff_convert(w, 1); /* Convert ANSI/OEM to Unicode */ + if (!w) return -ENAMETOOLONG; /* Reject invalid code */ +#endif + if (w < 0x80 && strchr("\"*:<>\?|\x7F", w)) /* Reject illegal chars for LFN */ + return -ENAMETOOLONG; + lfn[di++] = w; /* Store the Unicode char */ + } + *path = &p[si]; /* Return pointer to the next segment */ + cf = (w < ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */ + while (di) { /* Strip trailing spaces and dots */ + w = lfn[di-1]; + if (w != ' ' && w != '.') + break; + di--; + } + if (!di) return -EINVAL; /* Reject nul string */ + + lfn[di] = 0; /* LFN is created */ + + /* Create SFN in directory form */ + memset(dj->fn, ' ', 11); + for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ; /* Strip leading spaces and dots */ + if (si) cf |= NS_LOSS | NS_LFN; + while (di && lfn[di - 1] != '.') di--; /* Find extension (di<=si: no extension) */ + + b = i = 0; ni = 8; + for (;;) { + w = lfn[si++]; /* Get an LFN char */ + if (!w) + break; /* Break on end of the LFN */ + if (w == ' ' || (w == '.' && si != di)) { /* Remove spaces and dots */ + cf |= NS_LOSS | NS_LFN; + continue; + } + + if (i >= ni || si == di) { /* Extension or end of SFN */ + if (ni == 11) { /* Long extension */ + cf |= NS_LOSS | NS_LFN; + break; + } + if (si != di) + cf |= NS_LOSS | NS_LFN; /* Out of 8.3 format */ + if (si > di) + break; /* No extension */ + si = di; i = 8; ni = 11;/* Enter extension section */ + b <<= 2; + continue; + } + + if (w >= 0x80) { /* Non ASCII char */ +#ifdef _EXCVT + w = ff_convert(w, 0); /* Unicode -> OEM code */ + if (w) + w = excvt[w - 0x80]; /* Convert extended char to upper (SBCS) */ +#else + w = ff_convert(ff_wtoupper(w), 0); /* Upper converted Unicode -> OEM code */ +#endif + cf |= NS_LFN; /* Force create LFN entry */ + } + + if (_DF1S && w >= 0x100) { /* Double byte char (always false on SBCS cfg) */ + if (i >= ni - 1) { + cf |= NS_LOSS | NS_LFN; i = ni; + continue; + } + dj->fn[i++] = (BYTE)(w >> 8); + } else { /* Single byte char */ + if (!w || strchr("+,;=[]", w)) { /* Replace illegal chars for SFN */ + w = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */ + } else { + if (isupper(w)) { + b |= 2; + } else { + if (islower(w)) { + b |= 1; w -= 0x20; + } + } + } + } + dj->fn[i++] = (BYTE)w; + } + + if (dj->fn[0] == DDE) + dj->fn[0] = NDDE; /* If the first char collides with deleted mark, replace it with 0x05 */ + + if (ni == 8) b <<= 2; + if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) /* Create LFN entry when there are composite capitals */ + cf |= NS_LFN; + if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format without extended char, NT flags are created */ + if ((b & 0x03) == 0x01) + cf |= NS_EXT; /* NT flag (Extension has only small capital) */ + if ((b & 0x0C) == 0x04) + cf |= NS_BODY; /* NT flag (Filename has only small capital) */ + } + + dj->fn[NS] = cf; /* SFN is created */ + + return 0; + +#else /* Non-LFN configuration */ + BYTE b, c, d, *sfn; + UINT ni, si, i; + const char *p; + + /* Create file name in directory form */ + for (p = *path; *p == '/' || *p == '\\'; p++); /* Strip duplicated separator */ + sfn = dj->fn; + memset(sfn, ' ', 11); + si = i = b = 0; ni = 8; + for (;;) { + c = (BYTE)p[si++]; + if (c <= ' ' || c == '/' || c == '\\') break; /* Break on end of segment */ + if (c == '.' || i >= ni) { + if (ni != 8 || c != '.') + return -EINVAL; + i = 8; ni = 11; + b <<= 2; continue; + } + if (c >= 0x80) { /* Extended char? */ + b |= 3; /* Eliminate NT flag */ +#ifdef _EXCVT + c = excvt[c-0x80]; /* Upper conversion (SBCS) */ +#else +#if !_DF1S /* ASCII only cfg */ + return -EINVAL; +#endif +#endif + } + if (IsDBCS1(c)) { /* Check if it is a DBC 1st byte (always false on SBCS cfg) */ + d = (BYTE)p[si++]; /* Get 2nd byte */ + if (!IsDBCS2(d) || i >= ni - 1) /* Reject invalid DBC */ + return -EINVAL; + sfn[i++] = c; + sfn[i++] = d; + } else { /* Single byte code */ + if (strchr("\"*+,:;<=>\?[]|\x7F", c)) /* Reject illegal chrs for SFN */ + return -EINVAL; + if (isupper(c)) { + b |= 2; + } else { + if (islower(c)) { + b |= 1; c -= 0x20; + } + } + sfn[i++] = c; + } + } + *path = &p[si]; /* Return pointer to the next segment */ + c = (c <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */ + + if (!i) + return -EINVAL; /* Reject nul string */ + if (sfn[0] == DDE) + sfn[0] = NDDE; /* When first char collides with DDE, replace it with 0x05 */ + + if (ni == 8) + b <<= 2; + if ((b & 0x03) == 0x01) + c |= NS_EXT; /* NT flag (Name extension has only small capital) */ + if ((b & 0x0C) == 0x04) + c |= NS_BODY; /* NT flag (Name body has only small capital) */ + + sfn[NS] = c; /* Store NT flag, File name is created */ + + return 0; +#endif +} + +/* + * Get file information from directory entry + */ +static void get_fileinfo ( /* No return code */ + FF_DIR *dj, /* Pointer to the directory object */ + FILINFO *fno /* Pointer to the file information to be filled */ +) +{ + UINT i; + BYTE nt, *dir; + TCHAR *p, c; + + + p = fno->fname; + if (dj->sect) { + dir = dj->dir; + nt = dir[DIR_NTres]; /* NT flag */ + for (i = 0; i < 8; i++) { /* Copy name body */ + c = dir[i]; + if (c == ' ') + break; + if (c == NDDE) + c = (TCHAR)DDE; +#ifdef CONFIG_FS_FAT_LFN + if ((nt & NS_BODY) && isupper(c)) + c += 0x20; +#endif +#if _LFN_UNICODE + if (IsDBCS1(c) && i < 7 && IsDBCS2(dir[i+1])) + c = (c << 8) | dir[++i]; + c = ff_convert(c, 1); + if (!c) c = '?'; +#endif + *p++ = c; + } + if (dir[8] != ' ') { /* Copy name extension */ + *p++ = '.'; + for (i = 8; i < 11; i++) { + c = dir[i]; + if (c == ' ') + break; +#ifdef CONFIG_FS_FAT_LFN + if ((nt & NS_EXT) && isupper(c)) + c += 0x20; +#endif +#if _LFN_UNICODE + if (IsDBCS1(c) && i < 10 && IsDBCS2(dir[i+1])) + c = (c << 8) | dir[++i]; + c = ff_convert(c, 1); + if (!c) + c = '?'; +#endif + *p++ = c; + } + } + fno->fattrib = dir[DIR_Attr]; /* Attribute */ + fno->fsize = LD_DWORD(dir+DIR_FileSize); /* Size */ + fno->fdate = LD_WORD(dir+DIR_WrtDate); /* Date */ + fno->ftime = LD_WORD(dir+DIR_WrtTime); /* Time */ + } + *p = 0; /* Terminate SFN str by a \0 */ + +#ifdef CONFIG_FS_FAT_LFN + if (fno->lfname && fno->lfsize) { + TCHAR *tp = fno->lfname; + WCHAR w, *lfn; + + i = 0; + if (dj->sect && dj->lfn_idx != 0xFFFF) {/* Get LFN if available */ + lfn = dj->lfn; + while ((w = *lfn++) != 0) { /* Get an LFN char */ +#if !_LFN_UNICODE + w = ff_convert(w, 0); /* Unicode -> OEM conversion */ + if (!w) { + /* Could not convert, no LFN */ + i = 0; + break; + } + /* Put 1st byte if it is a DBC (always false on SBCS cfg) */ + if (_DF1S && w >= 0x100) + tp[i++] = (TCHAR)(w >> 8); +#endif + if (i >= fno->lfsize - 1) { i = 0; break; } /* Buffer overflow, no LFN */ + tp[i++] = (TCHAR)w; + } + } + tp[i] = 0; /* Terminate the LFN str by a \0 */ + } +#endif +} + +/* + * Follow a file path + */ +static +int follow_path ( /* 0(0): successful, !=0: error code */ + FF_DIR *dj, /* Directory object to return last directory and found object */ + const TCHAR *path /* Full-path string to find a file or directory */ +) +{ + int res; + BYTE *dir, ns; + + if (*path == '/' || *path == '\\') /* Strip heading separator if exist */ + path++; + dj->sclust = 0; /* Start from the root dir */ + + if ((UINT)*path < ' ') { + /* Nul path means the start directory itself */ + res = dir_sdi(dj, 0); + dj->dir = 0; + return res; + } + + /* Follow path */ + for (;;) { + res = create_name(dj, &path); /* Get a segment */ + if (res != 0) + break; + res = dir_find(dj); /* Find it */ + ns = *(dj->fn+NS); + if (res != 0) { /* Failed to find the object */ + if (res != -ENOENT) + break; /* Abort if any hard error occured */ + /* Object not found */ + if (!(ns & NS_LAST)) + res = -ENOENT; + break; + } + if (ns & NS_LAST) + break; /* Last segment match. Function completed. */ + dir = dj->dir; /* There is next segment. Follow the sub directory */ + if (!(dir[DIR_Attr] & AM_DIR)) { /* Cannot follow because it is a file */ + res = -ENOTDIR; + break; + } + dj->sclust = LD_CLUST(dir); + } + + return res; +} + +/* + * Load boot record and check if it is an FAT boot record + */ +static int check_fs ( /* 0:The FAT BR, 1:Valid BR but not an FAT, 2:Not a BR, 3:Disk error */ + FATFS *fs, /* File system object */ + DWORD sect /* Sector# (lba) to check if it is an FAT boot record or not */ +) +{ + int ret; + + /* Load boot record */ + ret = disk_read(fs, fs->win, sect, 1); + if (ret) + return ret; + /* Check record signature (always placed at offset 510 even if the sector size is>512) */ + if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55) + return -ENODEV; + + /* Check "FAT" string */ + if ((LD_DWORD(&fs->win[BS_FilSysType]) & 0xFFFFFF) == 0x544146) + return 0; + + if ((LD_DWORD(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146) + return 0; + + return -ENODEV; +} + +/* + * Check if the file system object is valid or not + */ +static int chk_mounted ( /* 0(0): successful, !=0: any error occurred */ + FATFS *fs, /* Pointer to pointer to the found file system object */ + BYTE chk_wp /* !=0: Check media write protection for write access */ +) +{ + BYTE fmt, b; + DWORD bsect, fasize, tsect, sysect, nclst, szbfat; + WORD nrsv; + + INIT_LIST_HEAD(&fs->dirtylist); + + /* The logical drive must be mounted. */ + /* Following code attempts to mount a volume. (analyze BPB and initialize the fs object) */ + + fs->fs_type = 0; /* Clear the file system object */ +#if _MAX_SS != 512 /* Get disk sector size (variable sector size cfg only) */ + if (disk_ioctl(fs, GET_SECTOR_SIZE, &fs->ssize) != RES_OK) + return -EIO; +#endif + /* Search FAT partition on the drive. Supports only generic partitionings, FDISK and SFD. */ + fmt = check_fs(fs, bsect = 0); /* Check sector 0 if it is a VBR */ + if (fmt) + return fmt; /* No FAT volume is found */ + + /* Following code initializes the file system object */ + + /* (BPB_BytsPerSec must be equal to the physical sector size) */ + if (LD_WORD(fs->win+BPB_BytsPerSec) != SS(fs)) + return -EINVAL; + + /* Number of sectors per FAT */ + fasize = LD_WORD(fs->win+BPB_FATSz16); + if (!fasize) + fasize = LD_DWORD(fs->win+BPB_FATSz32); + fs->fsize = fasize; + + /* Number of FAT copies */ + fs->n_fats = b = fs->win[BPB_NumFATs]; + if (b != 1 && b != 2) + return -EINVAL; /* (Must be 1 or 2) */ + + /* Number of sectors for FAT area */ + fasize *= b; + + /* Number of sectors per cluster */ + fs->csize = b = fs->win[BPB_SecPerClus]; + if (!b || (b & (b - 1))) + return -EINVAL; /* (Must be power of 2) */ + + /* Number of root directory entries */ + fs->n_rootdir = LD_WORD(fs->win+BPB_RootEntCnt); + if (fs->n_rootdir % (SS(fs) / SZ_DIR)) + return -EINVAL; /* (BPB_RootEntCnt must be sector aligned) */ + + /* Number of sectors on the volume */ + tsect = LD_WORD(fs->win+BPB_TotSec16); + if (!tsect) tsect = LD_DWORD(fs->win+BPB_TotSec32); + + /* Number of reserved sectors */ + nrsv = LD_WORD(fs->win+BPB_RsvdSecCnt); + if (!nrsv) + return -EINVAL; /* (BPB_RsvdSecCnt must not be 0) */ + + /* Determine the FAT sub type */ + /* RSV+FAT+FF_DIR */ + sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZ_DIR); + if (tsect < sysect) + return -EINVAL; /* (Invalid volume size) */ + + /* Number of clusters */ + nclst = (tsect - sysect) / fs->csize; + if (!nclst) + return -EINVAL; /* (Invalid volume size) */ + fmt = FS_FAT12; + if (nclst >= MIN_FAT16) + fmt = FS_FAT16; + if (nclst >= MIN_FAT32) + fmt = FS_FAT32; + + /* Boundaries and Limits */ + /* Number of FAT entries */ + fs->n_fatent = nclst + 2; + /* Data start sector */ + fs->database = bsect + sysect; + /* FAT start sector */ + fs->fatbase = bsect + nrsv; + if (fmt == FS_FAT32) { + if (fs->n_rootdir) + return -EINVAL; /* (BPB_RootEntCnt must be 0) */ + /* Root directory start cluster */ + fs->dirbase = LD_DWORD(fs->win+BPB_RootClus); + /* (Required FAT size) */ + szbfat = fs->n_fatent * 4; + } else { + if (!fs->n_rootdir) + return -EINVAL;/* (BPB_RootEntCnt must not be 0) */ + /* Root directory start sector */ + fs->dirbase = fs->fatbase + fasize; + /* (Required FAT size) */ + szbfat = (fmt == FS_FAT16) ? + fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1); + } + /* (BPB_FATSz must not be less than required) */ + if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) + return -EINVAL; + +#ifdef CONFIG_FS_FAT_WRITE + /* Initialize cluster allocation information */ + fs->free_clust = 0xFFFFFFFF; + fs->last_clust = 0; + + /* Get fsinfo if available */ + if (fmt == FS_FAT32) { + fs->fsi_flag = 0; + fs->fsi_sector = bsect + LD_WORD(fs->win+BPB_FSInfo); + if (disk_read(fs, fs->win, fs->fsi_sector, 1) == RES_OK && + LD_WORD(fs->win+BS_55AA) == 0xAA55 && + LD_DWORD(fs->win+FSI_LeadSig) == 0x41615252 && + LD_DWORD(fs->win+FSI_StrucSig) == 0x61417272) { + fs->last_clust = LD_DWORD(fs->win+FSI_Nxt_Free); + fs->free_clust = LD_DWORD(fs->win+FSI_Free_Count); + } + } +#endif + fs->fs_type = fmt; /* FAT sub-type */ + fs->winsect = 0; /* Invalidate sector cache */ + fs->wflag = 0; + + return 0; +} + +/* + * Mount/Unmount a Logical Drive + */ +int f_mount ( + FATFS *fs /* Pointer to new file system object (NULL for unmount)*/ +) +{ + fs->fs_type = 0; /* Clear new fs object */ + + chk_mounted(fs, 0); + + return 0; +} + +/* + * Open or Create a File + */ +int f_open ( + FATFS *fatfs, + FIL *fp, /* Pointer to the blank file object */ + const TCHAR *path, /* Pointer to the file name */ + BYTE mode /* Access mode and file open mode flags */ +) +{ + int res = 0; + FF_DIR dj; + BYTE *dir; + DEF_NAMEBUF; + + + fp->fs = 0; /* Clear file object */ + +#ifdef CONFIG_FS_FAT_WRITE + mode &= FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW; + dj.fs = fatfs; +#else + mode &= FA_READ; + dj.fs = fatfs; +#endif + INIT_BUF(dj); + if (res == 0) + res = follow_path(&dj, path); /* Follow the file path */ + dir = dj.dir; + +#ifdef CONFIG_FS_FAT_WRITE /* R/W configuration */ + if (res == 0) { + if (!dir) /* Current dir itself */ + res = -EISDIR; + } + /* Create or Open a file */ + if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) { + DWORD dw, cl; + + if (res != 0) { /* No file, create new */ + if (res == -ENOENT) /* There is no file to open, create a new entry */ + res = dir_register(&dj); + mode |= FA_CREATE_ALWAYS; /* File is created */ + dir = dj.dir; /* New entry */ + } else { /* Any object is already existing */ + if (dir[DIR_Attr] & AM_RDO) { + res = -EROFS; + } else if (dir[DIR_Attr] & AM_DIR) { /* Cannot overwrite it (R/O or FF_DIR) */ + res = -EISDIR; + } else { + if (mode & FA_CREATE_NEW) /* Cannot create as new file */ + res = -EEXIST; + } + } + if (res == 0 && (mode & FA_CREATE_ALWAYS)) { /* Truncate it if overwrite mode */ + dw = get_fattime(); /* Created time */ + ST_DWORD(dir+DIR_CrtTime, dw); + dir[DIR_Attr] = 0; /* Reset attribute */ + ST_DWORD(dir+DIR_FileSize, 0); /* size = 0 */ + cl = LD_CLUST(dir); /* Get start cluster */ + ST_CLUST(dir, 0); /* cluster = 0 */ + dj.fs->wflag = 1; + if (cl) { /* Remove the cluster chain if exist */ + dw = dj.fs->winsect; + res = remove_chain(dj.fs, cl); + if (res == 0) { + dj.fs->last_clust = cl - 1; /* Reuse the cluster hole */ + res = move_window(dj.fs, dw); + } + } + } + } else { /* Open an existing file */ + if (res == 0) { /* Follow succeeded */ + if (dir[DIR_Attr] & AM_DIR) { /* It is a directory */ + res = -EISDIR; + } else { + if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */ + res = -EROFS; + } + } + } + if (res == 0) { + if (mode & FA_CREATE_ALWAYS) /* Set file change flag if created or overwritten */ + mode |= FA__WRITTEN; + fp->dir_sect = dj.fs->winsect; /* Pointer to the directory entry */ + fp->dir_ptr = dir; + } + +#else /* R/O configuration */ + if (res == 0) { /* Follow succeeded */ + if (!dir) { /* Current dir itself */ + res = -EISDIR; + } else { + if (dir[DIR_Attr] & AM_DIR) /* It is a directory */ + res = -EISDIR; + } + } +#endif + FREE_BUF(); + + if (res == 0) { + fp->flag = mode; /* File access mode */ + fp->sclust = LD_CLUST(dir); /* File start cluster */ + fp->fsize = LD_DWORD(dir+DIR_FileSize); /* File size */ + fp->fptr = 0; /* File pointer */ + fp->dsect = 0; + fp->fs = dj.fs; + } + + return res; +} + + + + +/* + * Read File + */ +int f_read ( + FIL *fp, /* Pointer to the file object */ + void *buff, /* Pointer to data buffer */ + UINT btr, /* Number of bytes to read */ + UINT *br /* Pointer to number of bytes read */ +) +{ + DWORD clst, sect, remain; + UINT rcnt, cc; + BYTE csect, *rbuff = buff; + + *br = 0; /* Initialize byte counter */ + + if (fp->flag & FA__ERROR) /* Aborted file? */ + return -ERESTARTSYS; + if (!(fp->flag & FA_READ)) /* Check access mode */ + return -EROFS; + remain = fp->fsize - fp->fptr; + if (btr > remain) + btr = (UINT)remain; /* Truncate btr by remaining bytes */ + + /* Repeat until all data read */ + for ( ; btr; rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) { + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */ + if (!csect) { /* On the cluster boundary? */ + if (fp->fptr == 0) { /* On the top of the file? */ + clst = fp->sclust; /* Follow from the origin */ + } else { /* Middle or end of the file */ + clst = get_fat(fp->fs, fp->clust); /* Follow cluster chain on the FAT */ + } + if (clst < 2) + ABORT(fp->fs, -ERESTARTSYS); + if (clst == 0xFFFFFFFF) + ABORT(fp->fs, -EIO); + fp->clust = clst; /* Update current cluster */ + } + sect = clust2sect(fp->fs, fp->clust); /* Get current sector */ + if (!sect) + ABORT(fp->fs, -ERESTARTSYS); + sect += csect; + cc = btr / SS(fp->fs); /* When remaining bytes >= sector size, */ + if (cc) { /* Read maximum contiguous sectors directly */ + if (csect + cc > fp->fs->csize) /* Clip at cluster boundary */ + cc = fp->fs->csize - csect; + if (disk_read(fp->fs, rbuff, sect, (BYTE)cc) != RES_OK) + ABORT(fp->fs, -EIO); +#if defined CONFIG_FS_FAT_WRITE + /* Replace one of the read sectors with cached data if it contains a dirty sector */ + if ((fp->flag & FA__DIRTY) && fp->dsect - sect < cc) + memcpy(rbuff + ((fp->dsect - sect) * SS(fp->fs)), fp->buf, SS(fp->fs)); +#endif + rcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ + continue; + } + if (fp->dsect != sect) { /* Load data sector if not in cache */ +#ifdef CONFIG_FS_FAT_WRITE + if (fp->flag & FA__DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fp->fs, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, -EIO); + fp->flag &= ~FA__DIRTY; + } +#endif + if (disk_read(fp->fs, fp->buf, sect, 1) != RES_OK) /* Fill sector cache */ + ABORT(fp->fs, -EIO); + } + fp->dsect = sect; + } + rcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs)); /* Get partial sector data from sector buffer */ + if (rcnt > btr) + rcnt = btr; + memcpy(rbuff, &fp->buf[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */ + } + + return 0; +} + + + + +#ifdef CONFIG_FS_FAT_WRITE +/* + * Write File + */ +int f_write ( + FIL *fp, /* Pointer to the file object */ + const void *buff, /* Pointer to the data to be written */ + UINT btw, /* Number of bytes to write */ + UINT *bw /* Pointer to number of bytes written */ +) +{ + DWORD clst, sect; + UINT wcnt, cc; + const BYTE *wbuff = buff; + BYTE csect; + + *bw = 0; /* Initialize byte counter */ + + if (fp->flag & FA__ERROR) /* Aborted file? */ + return -ERESTARTSYS; + if (!(fp->flag & FA_WRITE)) /* Check access mode */ + return -EROFS; + if ((DWORD)(fp->fsize + btw) < fp->fsize) + btw = 0; /* File size cannot reach 4GB */ + + /* Repeat until all data written */ + for ( ; btw; wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) { + /* On the sector boundary? */ + if ((fp->fptr % SS(fp->fs)) == 0) { + /* Sector offset in the cluster */ + csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); + /* On the cluster boundary? */ + if (!csect) { + /* On the top of the file? */ + if (fp->fptr == 0) { + clst = fp->sclust; /* Follow from the origin */ + if (clst == 0) /* When no cluster is allocated, */ + /* Create a new cluster chain */ + fp->sclust = clst = create_chain(fp->fs, 0); + } else { + /* Middle or end of the file */ + /* Follow or stretch cluster chain on the FAT */ + clst = create_chain(fp->fs, fp->clust); + } + if (clst == 0) + break; /* Could not allocate a new cluster (disk full) */ + if (clst == 1) + ABORT(fp->fs, -ERESTARTSYS); + if (clst == 0xFFFFFFFF) + ABORT(fp->fs, -EIO); + fp->clust = clst; /* Update current cluster */ + } + if (fp->flag & FA__DIRTY) { /* Write-back sector cache */ + printf("wr sector cache\n"); + if (disk_write(fp->fs, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, -EIO); + fp->flag &= ~FA__DIRTY; + } + sect = clust2sect(fp->fs, fp->clust); /* Get current sector */ + if (!sect) + ABORT(fp->fs, -ERESTARTSYS); + sect += csect; + cc = btw / SS(fp->fs); /* When remaining bytes >= sector size, */ + if (cc) { + /* Write maximum contiguous sectors directly */ + if (csect + cc > fp->fs->csize) /* Clip at cluster boundary */ + cc = fp->fs->csize - csect; + if (disk_write(fp->fs, wbuff, sect, (BYTE)cc) != RES_OK) + ABORT(fp->fs, -EIO); + if (fp->dsect - sect < cc) { + /* Refill sector cache if it gets invalidated by the direct write */ + memcpy(fp->buf, wbuff + ((fp->dsect - sect) * SS(fp->fs)), SS(fp->fs)); + fp->flag &= ~FA__DIRTY; + } + wcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ + continue; + } + if (fp->dsect != sect) { + /* Fill sector cache with file data */ + if (fp->fptr < fp->fsize && + disk_read(fp->fs, fp->buf, sect, 1) != RES_OK) + ABORT(fp->fs, -EIO); + } + + fp->dsect = sect; + } + /* Put partial sector into file I/O buffer */ + wcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs)); + if (wcnt > btw) + wcnt = btw; + memcpy(&fp->buf[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */ + fp->flag |= FA__DIRTY; + } + + if (fp->fptr > fp->fsize) + fp->fsize = fp->fptr; /* Update file size if needed */ + + fp->flag |= FA__WRITTEN; /* Set file change flag */ + + return 0; +} + +/* + * Synchronize the File Object + */ +int f_sync ( + FIL *fp /* Pointer to the file object */ +) +{ + int res; + DWORD tim; + BYTE *dir; + + /* Has the file been written? */ + if (!(fp->flag & FA__WRITTEN)) + return 0; + + if (fp->flag & FA__DIRTY) { + if (disk_write(fp->fs, fp->buf, fp->dsect, 1) != RES_OK) + return -EIO; + fp->flag &= ~FA__DIRTY; + } + + /* Update the directory entry */ + res = move_window(fp->fs, fp->dir_sect); + if (res) + return res; + + dir = fp->dir_ptr; + dir[DIR_Attr] |= AM_ARC; /* Set archive bit */ + ST_DWORD(dir+DIR_FileSize, fp->fsize); /* Update file size */ + ST_CLUST(dir, fp->sclust); /* Update start cluster */ + tim = get_fattime(); /* Update updated time */ + ST_DWORD(dir+DIR_WrtTime, tim); + fp->flag &= ~FA__WRITTEN; + fp->fs->wflag = 1; + res = sync(fp->fs); + + flush_dirty_fat(fp->fs); + + return res; +} + +#endif /* CONFIG_FS_FAT_WRITE */ + +/* + * Close File + */ +int f_close ( + FIL *fp /* Pointer to the file object to be closed */ +) +{ +#ifndef CONFIG_FS_FAT_WRITE + fp->fs = 0; /* Discard file object */ + return 0; +#else + int res; + + /* Flush cached data */ + res = f_sync(fp); + if (res == 0) + fp->fs = 0; /* Discard file object */ + return res; +#endif +} + +/* + * Seek File R/W Pointer + */ +int f_lseek ( + FIL *fp, /* Pointer to the file object */ + DWORD ofs /* File pointer from top of file */ +) +{ + DWORD clst, bcs, nsect, ifptr; + int res = 0; + + if (fp->flag & FA__ERROR) /* Check abort flag */ + return -ERESTARTSYS; + + if (ofs > fp->fsize /* In read-only mode, clip offset with the file size */ +#ifdef CONFIG_FS_FAT_WRITE + && !(fp->flag & FA_WRITE) +#endif + ) ofs = fp->fsize; + + ifptr = fp->fptr; + fp->fptr = nsect = 0; + if (ofs) { + bcs = (DWORD)fp->fs->csize * SS(fp->fs); /* Cluster size (byte) */ + if (ifptr > 0 && + (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */ + fp->fptr = (ifptr - 1) & ~(bcs - 1); /* start from the current cluster */ + ofs -= fp->fptr; + clst = fp->clust; + } else { /* When seek to back cluster, */ + clst = fp->sclust; /* start from the first cluster */ +#ifdef CONFIG_FS_FAT_WRITE + if (clst == 0) { /* If no cluster chain, create a new chain */ + clst = create_chain(fp->fs, 0); + if (clst == 1) + ABORT(fp->fs, -ERESTARTSYS); + if (clst == 0xFFFFFFFF) + ABORT(fp->fs, -EIO); + fp->sclust = clst; + } +#endif + fp->clust = clst; + } + if (clst != 0) { + while (ofs > bcs) { /* Cluster following loop */ +#ifdef CONFIG_FS_FAT_WRITE + if (fp->flag & FA_WRITE) { /* Check if in write mode or not */ + /* Force stretch if in write mode */ + clst = create_chain(fp->fs, clst); + /* When disk gets full, clip file size */ + if (clst == 0) { + ofs = bcs; + break; + } + } else +#endif + /* Follow cluster chain if not in write mode */ + clst = get_fat(fp->fs, clst); + if (clst == 0xFFFFFFFF) + ABORT(fp->fs, -EIO); + if (clst <= 1 || clst >= fp->fs->n_fatent) + ABORT(fp->fs, -ERESTARTSYS); + fp->clust = clst; + fp->fptr += bcs; + ofs -= bcs; + } + fp->fptr += ofs; + if (ofs % SS(fp->fs)) { + nsect = clust2sect(fp->fs, clst); /* Current sector */ + if (!nsect) + ABORT(fp->fs, -ERESTARTSYS); + nsect += ofs / SS(fp->fs); + } + } + } + if (fp->fptr % SS(fp->fs) && nsect != fp->dsect) { /* Fill sector cache if needed */ +#ifdef CONFIG_FS_FAT_WRITE + if (fp->flag & FA__DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fp->fs, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, -EIO); + fp->flag &= ~FA__DIRTY; + } +#endif + if (disk_read(fp->fs, fp->buf, nsect, 1) != RES_OK) /* Fill sector cache */ + ABORT(fp->fs, -EIO); + fp->dsect = nsect; + } +#ifdef CONFIG_FS_FAT_WRITE + if (fp->fptr > fp->fsize) { /* Set file change flag if the file size is extended */ + fp->fsize = fp->fptr; + fp->flag |= FA__WRITTEN; + } +#endif + + return res; +} + +/* + * Create a Directroy Object + */ +int f_opendir ( + FATFS *fatfs, + FF_DIR *dj, /* Pointer to directory object to create */ + const TCHAR *path /* Pointer to the directory path */ +) +{ + int res; + DEF_NAMEBUF; + + dj->fs = fatfs; + + INIT_BUF(*dj); + res = follow_path(dj, path); /* Follow the path to the directory */ + FREE_BUF(); + + if (res) + return res; + + if (dj->dir) { + /* It is not the root dir */ + if (dj->dir[DIR_Attr] & AM_DIR) { + /* The object is a directory */ + dj->sclust = LD_CLUST(dj->dir); + } else { + /* The object is not a directory */ + return -ENOTDIR; + } + } + + res = dir_sdi(dj, 0); /* Rewind dir */ + + return res; +} + + + + +/* + * Read Directory Entry in Sequense + */ +int f_readdir ( + FF_DIR *dj, /* Pointer to the open directory object */ + FILINFO *fno /* Pointer to file information to return */ +) +{ + int res; + DEF_NAMEBUF; + + if (!fno) { + res = dir_sdi(dj, 0); /* Rewind the directory object */ + return res; + } + + INIT_BUF(*dj); + res = dir_read(dj); /* Read an directory item */ + if (res == -ENOENT) { /* Reached end of dir */ + dj->sect = 0; + res = 0; + goto out; + } + + /* A valid entry is found */ + get_fileinfo(dj, fno); /* Get the object information */ + res = dir_next(dj, 0); /* Increment index for next */ + if (res == -ENOENT) { + dj->sect = 0; + res = 0; + } +out: + FREE_BUF(); + + return res; +} + +/* + * Get File Status + */ +int f_stat ( + FATFS *fatfs, + const TCHAR *path, /* Pointer to the file path */ + FILINFO *fno /* Pointer to file information to return */ +) +{ + int res; + FF_DIR dj; + DEF_NAMEBUF; + + dj.fs = fatfs; + + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == 0) { /* Follow completed */ + if (dj.dir) /* Found an object */ + get_fileinfo(&dj, fno); + else /* It is root dir */ + res = -ENOENT; + } + FREE_BUF(); + + return res; +} + +#ifdef CONFIG_FS_FAT_WRITE +/* + * Get Number of Free Clusters + */ +int f_getfree ( + FATFS *fatfs, + const TCHAR *path, /* Pointer to the logical drive number (root dir) */ + DWORD *nclst /* Pointer to the variable to return number of free clusters */ +) +{ + int res = 0; + DWORD n, clst, sect, stat; + UINT i; + BYTE fat, *p; + + + /* If free_clust is valid, return it without full cluster scan */ + if (fatfs->free_clust <= fatfs->n_fatent - 2) { + *nclst = fatfs->free_clust; + return 0; + } + + /* Get number of free clusters */ + fat = fatfs->fs_type; + n = 0; + if (fat == FS_FAT12) { + clst = 2; + do { + stat = get_fat(fatfs, clst); + if (stat == 0xFFFFFFFF) { + res = -EIO; + break; + } + if (stat == 1) { + res = -ERESTARTSYS; + break; + } + if (stat == 0) n++; + } while (++clst < fatfs->n_fatent); + } else { + clst = fatfs->n_fatent; + sect = fatfs->fatbase; + i = 0; p = 0; + do { + if (!i) { + res = move_window(fatfs, sect++); + if (res != 0) break; + p = fatfs->win; + i = SS(fatfs); + } + if (fat == FS_FAT16) { + if (LD_WORD(p) == 0) n++; + p += 2; i -= 2; + } else { + if ((LD_DWORD(p) & 0x0FFFFFFF) == 0) n++; + p += 4; i -= 4; + } + } while (--clst); + } + fatfs->free_clust = n; + if (fat == FS_FAT32) + fatfs->fsi_flag = 1; + *nclst = n; + + return res; +} + +/* + * Truncate File + */ +int f_truncate ( + FIL *fp /* Pointer to the file object */ +) +{ + int res = 0; + DWORD ncl; + + if (fp->flag & FA__ERROR) { /* Check abort flag */ + return -ERESTARTSYS; + } else { + if (!(fp->flag & FA_WRITE)) /* Check access mode */ + return -EROFS; + } + + if (fp->fsize <= fp->fptr) + return 0; + + fp->fsize = fp->fptr; /* Set file size to current R/W point */ + fp->flag |= FA__WRITTEN; + if (fp->fptr == 0) { + /* When set file size to zero, remove entire cluster chain */ + res = remove_chain(fp->fs, fp->sclust); + fp->sclust = 0; + } else { + /* When truncate a part of the file, remove remaining clusters */ + ncl = get_fat(fp->fs, fp->clust); + if (ncl == 0xFFFFFFFF) + return -EIO; + if (ncl == 1) + return -ERESTARTSYS; + if (ncl < fp->fs->n_fatent) { + res = put_fat(fp->fs, fp->clust, 0x0FFFFFFF); + if (res) + return res; + res = remove_chain(fp->fs, ncl); + } + } + + return res; +} + +/* + * Delete a File or Directory + */ +int f_unlink ( + FATFS *fatfs, + const TCHAR *path /* Pointer to the file or directory path */ +) +{ + int res; + FF_DIR dj, sdj; + BYTE *dir; + DWORD dclst; + DEF_NAMEBUF; + + dj.fs = fatfs; + + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + if (res) + goto out; + + if (dj.fn[NS] & NS_DOT) { + res = -EINVAL; /* Cannot remove dot entry */ + goto out; + } + + /* The object is accessible */ + dir = dj.dir; + if (!dir) { + res = -EINVAL; /* Cannot remove the start directory */ + goto out; + } + + if (dir[DIR_Attr] & AM_RDO) { + res = -EROFS; /* Cannot remove R/O object */ + goto out; + } + + dclst = LD_CLUST(dir); + if (dir[DIR_Attr] & AM_DIR) { /* Is it a sub-dir? */ + if (dclst < 2) { + res = -ERESTARTSYS; + goto out; + } + memcpy(&sdj, &dj, sizeof(FF_DIR)); /* Check if the sub-dir is empty or not */ + sdj.sclust = dclst; + res = dir_sdi(&sdj, 2); /* Exclude dot entries */ + if (res) + goto out; + + res = dir_read(&sdj); + if (res == 0) { + res = -ENOTEMPTY; /* Not empty dir */ + goto out; + } + if (res == -ENOENT) + res = 0; /* Empty */ + if (res) + goto out; + } + + res = dir_remove(&dj); /* Remove the directory entry */ + if (res) + goto out; + + if (dclst) { + /* Remove the cluster chain if exist */ + res = remove_chain(dj.fs, dclst); + if (res) + goto out; + } + + res = sync(dj.fs); + +out: + FREE_BUF(); + + return res; +} + +/* + * Create a Directory + */ +int f_mkdir ( + FATFS *fatfs, + const TCHAR *path /* Pointer to the directory path */ +) +{ + int res; + FF_DIR dj; + BYTE *dir, n; + DWORD dsc, dcl, pcl, tim = get_fattime(); + DEF_NAMEBUF; + + dj.fs = fatfs; + + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == 0) { + res = -EEXIST; /* Any object with same name is already existing */ + goto out; + } + + if (res != -ENOENT) + goto out; + + dcl = create_chain(dj.fs, 0); /* Allocate a cluster for the new directory table */ + if (dcl == 0) { + res = -ENOSPC; /* No space to allocate a new cluster */ + goto out; + } + if (dcl == 1) { + res = -ERESTARTSYS; + goto out; + } + + if (dcl == 0xFFFFFFFF) { + res = -EIO; + goto out; + } + + /* Flush FAT */ + res = move_window(dj.fs, 0); + if (res) + goto out; + + /* Initialize the new directory table */ + dsc = clust2sect(dj.fs, dcl); + dir = dj.fs->win; + memset(dir, 0, SS(dj.fs)); + memset(dir+DIR_Name, ' ', 8+3); /* Create "." entry */ + dir[DIR_Name] = '.'; + dir[DIR_Attr] = AM_DIR; + ST_DWORD(dir+DIR_WrtTime, tim); + ST_CLUST(dir, dcl); + memcpy(dir+SZ_DIR, dir, SZ_DIR); /* Create ".." entry */ + dir[33] = '.'; pcl = dj.sclust; + if (dj.fs->fs_type == FS_FAT32 && pcl == dj.fs->dirbase) + pcl = 0; + ST_CLUST(dir+SZ_DIR, pcl); + for (n = dj.fs->csize; n; n--) { /* Write dot entries and clear following sectors */ + dj.fs->winsect = dsc++; + dj.fs->wflag = 1; + res = move_window(dj.fs, 0); + if (res != 0) + goto out; + memset(dir, 0, SS(dj.fs)); + } + + res = dir_register(&dj); /* Register the object to the directoy */ + if (res) { + remove_chain(dj.fs, dcl); /* Could not register, remove cluster chain */ + goto out; + } + + dir = dj.dir; + dir[DIR_Attr] = AM_DIR; /* Attribute */ + ST_DWORD(dir+DIR_WrtTime, tim); /* Created time */ + ST_CLUST(dir, dcl); /* Table start cluster */ + dj.fs->wflag = 1; + res = sync(dj.fs); + +out: + FREE_BUF(); + + return res; +} + +/* + * Change Attribute + */ +int f_chmod ( + FATFS *fatfs, + const TCHAR *path, /* Pointer to the file path */ + BYTE value, /* Attribute bits */ + BYTE mask /* Attribute mask to change */ +) +{ + int res; + FF_DIR dj; + BYTE *dir; + DEF_NAMEBUF; + + dj.fs = fatfs; + + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + FREE_BUF(); + if (res == 0 && (dj.fn[NS] & NS_DOT)) + res = -ENOENT; + if (res == 0) { + dir = dj.dir; + if (!dir) { /* Is it a root directory? */ + res = -EINVAL; + } else { /* File or sub directory */ + mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */ + dir[DIR_Attr] = (value & mask) | (dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */ + dj.fs->wflag = 1; + res = sync(dj.fs); + } + } + + return res; +} + +/* + * Change Timestamp + */ +int f_utime ( + FATFS *fatfs, + const TCHAR *path, /* Pointer to the file/directory name */ + const FILINFO *fno /* Pointer to the time stamp to be set */ +) +{ + int res; + FF_DIR dj; + BYTE *dir; + DEF_NAMEBUF; + + dj.fs = fatfs; + + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + FREE_BUF(); + if (res == 0 && (dj.fn[NS] & NS_DOT)) + res = -ENOENT; + if (res == 0) { + dir = dj.dir; + if (!dir) { /* Root directory */ + res = -EINVAL; + } else { /* File or sub-directory */ + ST_WORD(dir+DIR_WrtTime, fno->ftime); + ST_WORD(dir+DIR_WrtDate, fno->fdate); + dj.fs->wflag = 1; + res = sync(dj.fs); + } + } + + return res; +} + +/* + * Rename File/Directory + */ +int f_rename ( + FATFS *fatfs, + const TCHAR *path_old, /* Pointer to the old name */ + const TCHAR *path_new /* Pointer to the new name */ +) +{ + int res; + FF_DIR djo, djn; + BYTE buf[21], *dir; + DWORD dw; + DEF_NAMEBUF; + + djo.fs = fatfs; + + djn.fs = djo.fs; + INIT_BUF(djo); + res = follow_path(&djo, path_old); /* Check old object */ + if (res == 0 && (djo.fn[NS] & NS_DOT)) { + res = -EINVAL; + goto out; + } + + if (res) + goto out; + + /* Old object is found */ + if (!djo.dir) { /* Is root dir? */ + res = -EINVAL; + goto out; + } + + memcpy(buf, djo.dir+DIR_Attr, 21); /* Save the object information except for name */ + memcpy(&djn, &djo, sizeof(FF_DIR)); /* Check new object */ + res = follow_path(&djn, path_new); + if (res == 0) { + res = -EEXIST; /* The new object name is already existing */ + goto out; + } + + if (res != -ENOENT) + goto out; + + /* It ia a valid path and no name collision */ + + res = dir_register(&djn); /* Register the new entry */ + if (res) + goto out; + + dir = djn.dir; /* Copy object information except for name */ + memcpy(dir+13, buf+2, 19); + dir[DIR_Attr] = buf[0] | AM_ARC; + djo.fs->wflag = 1; + /* Update .. entry in the directory if needed */ + if (djo.sclust != djn.sclust && (dir[DIR_Attr] & AM_DIR)) { + dw = clust2sect(djn.fs, LD_CLUST(dir)); + if (!dw) { + res = -ERESTARTSYS; + goto out; + } + + res = move_window(djn.fs, dw); + if (res) + goto out; + dir = djn.fs->win+SZ_DIR; /* .. entry */ + if (dir[1] == '.') { + dw = (djn.fs->fs_type == FS_FAT32 && djn.sclust == djn.fs->dirbase) ? 0 : djn.sclust; + ST_CLUST(dir, dw); + djn.fs->wflag = 1; + } + } + + res = dir_remove(&djo); /* Remove old entry */ + if (res) + goto out; + + res = sync(djo.fs); + +out: + FREE_BUF(); + + return res; +} +#endif /* CONFIG_FS_FAT_WRITE */ diff --git a/fs/fat/ff.h b/fs/fat/ff.h new file mode 100644 index 00000000..e86ca3aa --- /dev/null +++ b/fs/fat/ff.h @@ -0,0 +1,239 @@ +/*---------------------------------------------------------------------------/ +/ FatFs - FAT file system module include file R0.08b (C)ChaN, 2011 +/----------------------------------------------------------------------------/ +/ FatFs module is a generic FAT file system module for small embedded systems. +/ This is a free software that opened for education, research and commercial +/ developments under license policy of following trems. +/ +/ Copyright (C) 2011, ChaN, all right reserved. +/ +/ * The FatFs module is a free software and there is NO WARRANTY. +/ * No restriction on use. You can use, modify and redistribute it for +/ personal, non-profit or commercial product UNDER YOUR RESPONSIBILITY. +/ * Redistributions of source code must retain the above copyright notice. +/ +/----------------------------------------------------------------------------*/ + +#ifndef _FATFS +#define _FATFS 8237 /* Revision ID */ + +#include <asm/unaligned.h> +#include <linux/list.h> + +#include "integer.h" /* Basic integer types */ +#include "ffconf.h" /* FatFs configuration options */ + +#if _FATFS != _FFCONF +#error Wrong configuration file (ffconf.h). +#endif + +/* Type of path name strings on FatFs API */ + +#if _LFN_UNICODE /* Unicode string */ +#ifndef CONFIG_FS_FAT_LFN +#error _LFN_UNICODE must be 0 in non-LFN cfg. +#endif +#ifndef _INC_TCHAR +typedef WCHAR TCHAR; +#define _T(x) L ## x +#define _TEXT(x) L ## x +#endif + +#else /* ANSI/OEM string */ +#ifndef _INC_TCHAR +typedef char TCHAR; +#define _T(x) x +#define _TEXT(x) x +#endif + +#endif + + + +/* File system object structure (FATFS) */ + +typedef struct { + BYTE fs_type; /* FAT sub-type (0:Not mounted) */ + BYTE drv; /* Physical drive number */ + BYTE csize; /* Sectors per cluster (1,2,4...128) */ + BYTE n_fats; /* Number of FAT copies (1,2) */ + BYTE wflag; /* win[] dirty flag (1:must be written back) */ + BYTE fsi_flag; /* fsinfo dirty flag (1:must be written back) */ + WORD n_rootdir; /* Number of root directory entries (FAT12/16) */ +#if _MAX_SS != 512 + WORD ssize; /* Bytes per sector (512,1024,2048,4096) */ +#endif +#ifdef CONFIG_FS_FAT_WRITE + DWORD last_clust; /* Last allocated cluster */ + DWORD free_clust; /* Number of free clusters */ + DWORD fsi_sector; /* fsinfo sector (FAT32) */ +#endif + DWORD n_fatent; /* Number of FAT entries (= number of clusters + 2) */ + DWORD fsize; /* Sectors per FAT */ + DWORD fatbase; /* FAT start sector */ + DWORD dirbase; /* Root directory start sector (FAT32:Cluster#) */ + DWORD database; /* Data start sector */ + DWORD winsect; /* Current sector appearing in the win[] */ + BYTE win[_MAX_SS]; /* Disk access window for Directory, FAT (and Data on tiny cfg) */ + void *userdata; /* User data, ff core does not touch this */ + struct list_head dirtylist; +} FATFS; + + + +/* File object structure (FIL) */ + +typedef struct { + FATFS* fs; /* Pointer to the owner file system object */ + BYTE flag; /* File status flags */ + BYTE pad1; + DWORD fptr; /* File read/write pointer (0 on file open) */ + DWORD fsize; /* File size */ + DWORD sclust; /* File start cluster (0 when fsize==0) */ + DWORD clust; /* Current cluster */ + DWORD dsect; /* Current data sector */ +#ifdef CONFIG_FS_FAT_WRITE + DWORD dir_sect; /* Sector containing the directory entry */ + BYTE* dir_ptr; /* Ponter to the directory entry in the window */ +#endif +#if _USE_FASTSEEK + DWORD* cltbl; /* Pointer to the cluster link map table (null on file open) */ +#endif +#if _FS_SHARE + UINT lockid; /* File lock ID (index of file semaphore table) */ +#endif +#if !_FS_TINY + BYTE buf[_MAX_SS]; /* File data read/write buffer */ +#endif +} FIL; + + + +/* Directory object structure (DIR) */ + +typedef struct { + FATFS* fs; /* Pointer to the owner file system object */ + WORD index; /* Current read/write index number */ + DWORD sclust; /* Table start cluster (0:Root dir) */ + DWORD clust; /* Current cluster */ + DWORD sect; /* Current sector */ + BYTE* dir; /* Pointer to the current SFN entry in the win[] */ + BYTE* fn; /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */ +#ifdef CONFIG_FS_FAT_LFN + WCHAR* lfn; /* Pointer to the LFN working buffer */ + WORD lfn_idx; /* Last matched LFN index number (0xFFFF:No LFN) */ +#endif +} FF_DIR; + + + +/* File status structure (FILINFO) */ + +typedef struct { + DWORD fsize; /* File size */ + WORD fdate; /* Last modified date */ + WORD ftime; /* Last modified time */ + BYTE fattrib; /* Attribute */ + TCHAR fname[13]; /* Short file name (8.3 format) */ +#ifdef CONFIG_FS_FAT_LFN + TCHAR* lfname; /* Pointer to the LFN buffer */ + UINT lfsize; /* Size of LFN buffer in TCHAR */ +#endif +} FILINFO; + +/*--------------------------------------------------------------*/ +/* FatFs module application interface */ + +int f_mount (FATFS*); /* Mount/Unmount a logical drive */ +int f_open (FATFS*, FIL*, const TCHAR*, BYTE); /* Open or create a file */ +int f_read (FIL*, void*, UINT, UINT*); /* Read data from a file */ +int f_lseek (FIL*, DWORD); /* Move file pointer of a file object */ +int f_close (FIL*); /* Close an open file object */ +int f_opendir (FATFS*, FF_DIR*, const TCHAR*); /* Open an existing directory */ +int f_readdir (FF_DIR*, FILINFO*); /* Read a directory item */ +int f_stat (FATFS*, const TCHAR*, FILINFO*); /* Get file status */ +int f_write (FIL*, const void*, UINT, UINT*); /* Write data to a file */ +int f_getfree (FATFS*, const TCHAR*, DWORD*); /* Get number of free clusters on the drive */ +int f_truncate (FIL*); /* Truncate file */ +int f_sync (FIL*); /* Flush cached data of a writing file */ +int f_unlink (FATFS*, const TCHAR*); /* Delete an existing file or directory */ +int f_mkdir (FATFS*, const TCHAR*); /* Create a new directory */ +int f_chmod (FATFS*, const TCHAR*, BYTE, BYTE); /* Change attriburte of the file/dir */ +int f_utime (FATFS*, const TCHAR*, const FILINFO*); /* Change timestamp of the file/dir */ +int f_rename (FATFS*, const TCHAR*, const TCHAR*); /* Rename/Move a file or directory */ +int f_forward (FIL*, UINT(*)(const BYTE*,UINT), UINT, UINT*); /* Forward data to the stream */ +int f_mkfs (BYTE, BYTE, UINT); /* Create a file system on the drive */ +int f_chdrive (BYTE); /* Change current drive */ +int f_chdir (const TCHAR*); /* Change current directory */ +int f_getcwd (TCHAR*, UINT); /* Get current directory */ +int f_putc (TCHAR, FIL*); /* Put a character to the file */ +int f_puts (const TCHAR*, FIL*); /* Put a string to the file */ +int f_printf (FIL*, const TCHAR*, ...); /* Put a formatted string to the file */ +TCHAR* f_gets (TCHAR*, int, FIL*); /* Get a string from the file */ + +#ifndef EOF +#define EOF (-1) +#endif + +#define f_eof(fp) (((fp)->fptr == (fp)->fsize) ? 1 : 0) +#define f_error(fp) (((fp)->flag & FA__ERROR) ? 1 : 0) +#define f_tell(fp) ((fp)->fptr) +#define f_size(fp) ((fp)->fsize) + +/*--------------------------------------------------------------*/ +/* Additional user defined functions */ + +/* RTC function */ +DWORD get_fattime (void); + +/* Unicode support functions */ +WCHAR ff_convert (WCHAR, UINT); /* OEM-Unicode bidirectional conversion */ +WCHAR ff_wtoupper (WCHAR); /* Unicode upper-case conversion */ + +/*--------------------------------------------------------------*/ +/* Flags and offset address */ + + +/* File access control and file status flags (FIL.flag) */ + +#define FA_READ 0x01 +#define FA_OPEN_EXISTING 0x00 +#define FA__ERROR 0x80 + +#define FA_WRITE 0x02 +#define FA_CREATE_NEW 0x04 +#define FA_CREATE_ALWAYS 0x08 +#define FA_OPEN_ALWAYS 0x10 +#define FA__WRITTEN 0x20 +#define FA__DIRTY 0x40 + +/* FAT sub type (FATFS.fs_type) */ + +#define FS_FAT12 1 +#define FS_FAT16 2 +#define FS_FAT32 3 + + +/* File attribute bits for directory entry */ + +#define AM_RDO 0x01 /* Read only */ +#define AM_HID 0x02 /* Hidden */ +#define AM_SYS 0x04 /* System */ +#define AM_VOL 0x08 /* Volume label */ +#define AM_LFN 0x0F /* LFN entry */ +#define AM_DIR 0x10 /* Directory */ +#define AM_ARC 0x20 /* Archive */ +#define AM_MASK 0x3F /* Mask of defined bits */ + + +/* Fast seek function */ +#define CREATE_LINKMAP 0xFFFFFFFF + +/* Multi-byte word access macros */ + +#define LD_WORD(ptr) get_unaligned_le16(ptr) +#define LD_DWORD(ptr) get_unaligned_le32(ptr) +#define ST_WORD(ptr,val) put_unaligned_le16(val, ptr) +#define ST_DWORD(ptr,val) put_unaligned_le32(val, ptr) + +#endif /* _FATFS */ diff --git a/fs/fat/ffconf.h b/fs/fat/ffconf.h new file mode 100644 index 00000000..2f6a6c15 --- /dev/null +++ b/fs/fat/ffconf.h @@ -0,0 +1,145 @@ +/*---------------------------------------------------------------------------/ +/ FatFs - FAT file system module configuration file R0.08b (C)ChaN, 2011 +/----------------------------------------------------------------------------/ +/ +/ CAUTION! Do not forget to make clean the project after any changes to +/ the configuration options. +/ +/----------------------------------------------------------------------------*/ +#ifndef _FFCONF +#define _FFCONF 8237 /* Revision ID */ + + +/*---------------------------------------------------------------------------/ +/ Function and Buffer Configurations +/----------------------------------------------------------------------------*/ + +#define _FS_TINY 0 /* 0:Normal or 1:Tiny */ +/* When _FS_TINY is set to 1, FatFs uses the sector buffer in the file system +/ object instead of the sector buffer in the individual file object for file +/ data transfer. This reduces memory consumption 512 bytes each file object. */ + +#define _USE_STRFUNC 0 /* 0:Disable or 1/2:Enable */ +/* To enable string functions, set _USE_STRFUNC to 1 or 2. */ + + +#define _USE_MKFS 0 /* 0:Disable or 1:Enable */ +/* To enable f_mkfs function, set _USE_MKFS to 1 and set _FS_READONLY to 0 */ + + +#define _USE_FORWARD 0 /* 0:Disable or 1:Enable */ +/* To enable f_forward function, set _USE_FORWARD to 1 and set _FS_TINY to 1. */ + + +#define _USE_FASTSEEK 0 /* 0:Disable or 1:Enable */ +/* To enable fast seek feature, set _USE_FASTSEEK to 1. */ + + + +/*---------------------------------------------------------------------------/ +/ Locale and Namespace Configurations +/----------------------------------------------------------------------------*/ + +#define _CODE_PAGE 932 +/* The _CODE_PAGE specifies the OEM code page to be used on the target system. +/ Incorrect setting of the code page can cause a file open failure. +/ +/ 932 - Japanese Shift-JIS (DBCS, OEM, Windows) +/ 936 - Simplified Chinese GBK (DBCS, OEM, Windows) +/ 949 - Korean (DBCS, OEM, Windows) +/ 950 - Traditional Chinese Big5 (DBCS, OEM, Windows) +/ 1250 - Central Europe (Windows) +/ 1251 - Cyrillic (Windows) +/ 1252 - Latin 1 (Windows) +/ 1253 - Greek (Windows) +/ 1254 - Turkish (Windows) +/ 1255 - Hebrew (Windows) +/ 1256 - Arabic (Windows) +/ 1257 - Baltic (Windows) +/ 1258 - Vietnam (OEM, Windows) +/ 437 - U.S. (OEM) +/ 720 - Arabic (OEM) +/ 737 - Greek (OEM) +/ 775 - Baltic (OEM) +/ 850 - Multilingual Latin 1 (OEM) +/ 858 - Multilingual Latin 1 + Euro (OEM) +/ 852 - Latin 2 (OEM) +/ 855 - Cyrillic (OEM) +/ 866 - Russian (OEM) +/ 857 - Turkish (OEM) +/ 862 - Hebrew (OEM) +/ 874 - Thai (OEM, Windows) +/ 1 - ASCII only (Valid for non LFN cfg.) +*/ + +#define _MAX_LFN 255 /* Maximum LFN length to handle (12 to 255) */ + +#define ff_memalloc xzalloc +#define ff_memfree free +/* The _USE_LFN option switches the LFN support. +/ +/ 0: Disable LFN feature. _MAX_LFN and _LFN_UNICODE have no effect. +/ 1: Enable LFN with static working buffer on the BSS. Always NOT reentrant. +/ 2: Enable LFN with dynamic working buffer on the STACK. +/ 3: Enable LFN with dynamic working buffer on the HEAP. +/ +/ The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes. To enable LFN, +/ Unicode handling functions ff_convert() and ff_wtoupper() must be added +/ to the project. When enable to use heap, memory control functions +/ ff_memalloc() and ff_memfree() must be added to the project. */ + + +#define _LFN_UNICODE 0 /* 0:ANSI/OEM or 1:Unicode */ +/* To switch the character code set on FatFs API to Unicode, +/ enable LFN feature and set _LFN_UNICODE to 1. */ + +/*---------------------------------------------------------------------------/ +/ Physical Drive Configurations +/----------------------------------------------------------------------------*/ + +#define _VOLUMES 1 +/* Number of volumes (logical drives) to be used. */ + + +#define _MAX_SS 512 /* 512, 1024, 2048 or 4096 */ +/* Maximum sector size to be handled. +/ Always set 512 for memory card and hard disk but a larger value may be +/ required for on-board flash memory, floppy disk and optical disk. +/ When _MAX_SS is larger than 512, it configures FatFs to variable sector size +/ and GET_SECTOR_SIZE command must be implememted to the disk_ioctl function. */ + +#define _USE_ERASE 0 /* 0:Disable or 1:Enable */ +/* To enable sector erase feature, set _USE_ERASE to 1. CTRL_ERASE_SECTOR command +/ should be added to the disk_ioctl functio. */ + + + +/*---------------------------------------------------------------------------/ +/ System Configurations +/----------------------------------------------------------------------------*/ + +#define _WORD_ACCESS 0 /* 0 or 1 */ +/* Set 0 first and it is always compatible with all platforms. The _WORD_ACCESS +/ option defines which access method is used to the word data on the FAT volume. +/ +/ 0: Byte-by-byte access. +/ 1: Word access. Do not choose this unless following condition is met. +/ +/ When the byte order on the memory is big-endian or address miss-aligned word +/ access results incorrect behavior, the _WORD_ACCESS must be set to 0. +/ If it is not the case, the value can also be set to 1 to improve the +/ performance and code size. */ + + +/* A header file that defines sync object types on the O/S, such as +/ windows.h, ucos_ii.h and semphr.h, must be included prior to ff.h. */ + +#define _FS_TIMEOUT 1000 /* Timeout period in unit of time ticks */ +#define _SYNC_t HANDLE /* O/S dependent type of sync object. e.g. HANDLE, OS_EVENT*, ID and etc.. */ + +#define _FS_SHARE 0 /* 0:Disable or >=1:Enable */ +/* To enable file shareing feature, set _FS_SHARE to 1 or greater. The value + defines how many files can be opened simultaneously. */ + + +#endif /* _FFCONFIG */ diff --git a/fs/fat/integer.h b/fs/fat/integer.h new file mode 100644 index 00000000..04956aa7 --- /dev/null +++ b/fs/fat/integer.h @@ -0,0 +1,28 @@ +/*-------------------------------------------*/ +/* Integer type definitions for FatFs module */ +/*-------------------------------------------*/ + +#ifndef _INTEGER +#define _INTEGER + +/* These types must be 16-bit, 32-bit or larger integer */ +typedef int INT; +typedef unsigned int UINT; + +/* These types must be 8-bit integer */ +typedef char CHAR; +typedef unsigned char UCHAR; +typedef unsigned char BYTE; + +/* These types must be 16-bit integer */ +typedef short SHORT; +typedef unsigned short USHORT; +typedef unsigned short WORD; +typedef unsigned short WCHAR; + +/* These types must be 32-bit integer */ +typedef long LONG; +typedef unsigned long ULONG; +typedef unsigned long DWORD; + +#endif @@ -30,6 +30,7 @@ #include <xfuncs.h> #include <init.h> #include <module.h> +#include <libbb.h> void *read_file(const char *filename, size_t *size) { @@ -536,6 +537,26 @@ ssize_t write(int fd, const void *buf, size_t count) } EXPORT_SYMBOL(write); +int flush(int fd) +{ + struct device_d *dev; + struct fs_driver_d *fsdrv; + FILE *f = &files[fd]; + + if (check_fd(fd)) + return errno; + + dev = f->dev; + + fsdrv = (struct fs_driver_d *)dev->driver->type_data; + if (fsdrv->flush) + errno = fsdrv->flush(dev, f); + else + errno = 0; + + return errno; +} + off_t lseek(int fildes, off_t offset, int whence) { struct device_d *dev; @@ -759,25 +780,22 @@ int mount(const char *device, const char *fsname, const char *_path) if (!device) { printf("need a device for driver %s\n", fsname); errno = -ENODEV; - free(fsdev); - goto out; + goto out1; } } - sprintf(fsdev->dev.name, "%s", fsname); + safe_strncpy(fsdev->dev.name, fsname, MAX_DRIVER_NAME); fsdev->dev.type_data = fsdev; fsdev->dev.id = get_free_deviceid(fsdev->dev.name); if ((ret = register_device(&fsdev->dev))) { - free(fsdev); errno = ret; - goto out; + goto out1; } if (!fsdev->dev.driver) { /* driver didn't accept the device. Bail out */ - free(fsdev); errno = -EINVAL; - goto out; + goto out2; } if (parent_device) @@ -787,7 +805,7 @@ int mount(const char *device, const char *fsname, const char *_path) /* add mtab entry */ entry = &fsdev->mtab; - sprintf(entry->path, "%s", path); + safe_strncpy(entry->path, path, PATH_MAX); entry->dev = dev; entry->parent_device = parent_device; entry->next = NULL; @@ -801,6 +819,16 @@ int mount(const char *device, const char *fsname, const char *_path) e->next = entry; } errno = 0; + + free(path); + return 0; + +out2: + unregister_device(&fsdev->dev); +out1: + if (fsdev->backingstore) + free(fsdev->backingstore); + free(fsdev); out: free(path); return errno; @@ -543,7 +543,6 @@ static void ramfs_remove(struct device_d *dev) } static struct fs_driver_d ramfs_driver = { - .type = FS_TYPE_RAMFS, .create = ramfs_create, .unlink = ramfs_unlink, .open = ramfs_open, diff --git a/include/asm-generic/sections.h b/include/asm-generic/sections.h index c5d60a9d..1f48fb8c 100644 --- a/include/asm-generic/sections.h +++ b/include/asm-generic/sections.h @@ -4,5 +4,8 @@ extern char _text[], _stext[], _etext[]; extern char __bss_start[], __bss_stop[]; extern char _end[]; +extern void *_barebox_image_size; + +#define barebox_image_size (unsigned int)&_barebox_image_size #endif /* _ASM_GENERIC_SECTIONS_H_ */ diff --git a/include/block.h b/include/block.h new file mode 100644 index 00000000..aaab4e3b --- /dev/null +++ b/include/block.h @@ -0,0 +1,32 @@ +#ifndef __BLOCK_H +#define __BLOCK_H + +#include <driver.h> + +struct block_device; + +struct block_device_ops { + int (*read)(struct block_device *, void *buf, int block, int num_blocks); + int (*write)(struct block_device *, const void *buf, int block, int num_blocks); +}; + +struct block_device { + struct device_d *dev; + struct block_device_ops *ops; + int blockbits; + int num_blocks; + void *rdbuf; /* read buffer */ + int rdbufsize; + int rdblock; /* start block in read buffer */ + int rdblockend; /* end block in read buffer */ + void *wrbuf; /* write buffer */ + int wrblock; /* start block in write buffer */ + int wrbufblocks; /* number of blocks currently in write buffer */ + int wrbufsize; /* size of write buffer in blocks */ + struct cdev cdev; +}; + +int blockdevice_register(struct block_device *blk); +int blockdevice_unregister(struct block_device *blk); + +#endif /* __BLOCK_H */ diff --git a/include/common.h b/include/common.h index 35ad7b99..491bc980 100644 --- a/include/common.h +++ b/include/common.h @@ -219,6 +219,7 @@ int run_shell(void); int memory_display(char *addr, ulong offs, ulong nbytes, int size); extern const char version_string[]; +void barebox_banner(void); #define IOMEM(addr) ((void __force __iomem *)(addr)) diff --git a/include/driver.h b/include/driver.h index b9edca0e..6a4d45e3 100644 --- a/include/driver.h +++ b/include/driver.h @@ -290,8 +290,9 @@ struct file_operations { int (*ioctl)(struct cdev*, int, void *); off_t (*lseek)(struct cdev*, off_t); - int (*open)(struct cdev*, struct filep*); - int (*close)(struct cdev*, struct filep*); + int (*open)(struct cdev*); + int (*close)(struct cdev*); + int (*flush)(struct cdev*); int (*erase)(struct cdev*, size_t count, unsigned long offset); int (*protect)(struct cdev*, size_t count, unsigned long offset, int prot); int (*memmap)(struct cdev*, void **map, int flags); @@ -314,8 +315,13 @@ struct cdev { int devfs_create(struct cdev *); int devfs_remove(struct cdev *); struct cdev *cdev_by_name(const char *filename); +struct cdev *cdev_open(const char *name, unsigned long flags); +void cdev_close(struct cdev *cdev); +int cdev_flush(struct cdev *cdev); ssize_t cdev_read(struct cdev *cdev, void *buf, size_t count, ulong offset, ulong flags); ssize_t cdev_write(struct cdev *cdev, const void *buf, size_t count, ulong offset, ulong flags); +int cdev_ioctl(struct cdev *cdev, int cmd, void *buf); +int cdev_erase(struct cdev *cdev, size_t count, unsigned long offset); #define DEVFS_PARTITION_FIXED (1 << 0) #define DEVFS_PARTITION_READONLY (1 << 1) diff --git a/include/environment.h b/include/environment.h index 21a7ffa0..da032e21 100644 --- a/include/environment.h +++ b/include/environment.h @@ -44,12 +44,27 @@ struct env_context *get_current_context(void); char *var_val(struct variable_d *); char *var_name(struct variable_d *); +#ifdef CONFIG_ENVIRONMENT_VARIABLES const char *getenv(const char *); int setenv(const char *, const char *); +#else +static inline char *getenv(const char *var) +{ + return NULL; +} + +static inline int setenv(const char *var, const char *val) +{ + return 0; +} +#endif int env_pop_context(void); int env_push_context(void); +/* defaults to /dev/env0 */ +extern char *default_environment_path; + int envfs_load(char *filename, char *dirname); int envfs_save(char *filename, char *dirname); diff --git a/include/fs.h b/include/fs.h index 4c03978a..97d5995c 100644 --- a/include/fs.h +++ b/include/fs.h @@ -3,10 +3,6 @@ #include <driver.h> -#define FS_TYPE_CRAMFS 1 -#define FS_TYPE_RAMFS 2 -#define FS_TYPE_DEVFS 3 - #define PATH_MAX 1024 /* include/linux/limits.h */ struct partition; @@ -41,7 +37,6 @@ typedef struct filep { #define FS_DRIVER_NO_DEV 1 struct fs_driver_d { - ulong type; char *name; int (*probe) (struct device_d *dev); int (*mkdir)(struct device_d *dev, const char *pathname); @@ -58,6 +53,7 @@ struct fs_driver_d { int (*close)(struct device_d *dev, FILE *f); int (*read)(struct device_d *dev, FILE *f, void *buf, size_t size); int (*write)(struct device_d *dev, FILE *f, const void *buf, size_t size); + int (*flush)(struct device_d *dev, FILE *f); off_t (*lseek)(struct device_d *dev, FILE *f, off_t pos); struct dir* (*opendir)(struct device_d *dev, const char *pathname); @@ -103,6 +99,7 @@ int open(const char *pathname, int flags, ...); int creat(const char *pathname, mode_t mode); int unlink(const char *pathname); int close(int fd); +int flush(int fd); int stat(const char *filename, struct stat *s); int read(int fd, void *buf, size_t count); int ioctl(int fd, int request, void *buf); diff --git a/include/kfifo.h b/include/kfifo.h index 6f8be10f..3eb03cb1 100644 --- a/include/kfifo.h +++ b/include/kfifo.h @@ -43,7 +43,7 @@ void kfifo_free(struct kfifo *fifo); * bytes copied. */ unsigned int kfifo_put(struct kfifo *fifo, - unsigned char *buffer, unsigned int len); + const unsigned char *buffer, unsigned int len); /** * kfifo_get - gets some data from the FIFO diff --git a/include/libbb.h b/include/libbb.h index 4151230c..09629695 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -30,4 +30,6 @@ int copy_file(const char *src, const char *dst); int process_escape_sequence(const char *source, char *dest, int destlen); +char *simple_itoa(unsigned int i); + #endif /* __LIBBB_H */ diff --git a/include/linux/mtd/compat.h b/include/linux/mtd/compat.h deleted file mode 100644 index a99d86e6..00000000 --- a/include/linux/mtd/compat.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef _LINUX_COMPAT_H_ -#define _LINUX_COMPAT_H_ - -#define __user -#define __iomem - -#define ndelay(x) udelay(1) - -#define printk printf - -#define KERN_EMERG -#define KERN_ALERT -#define KERN_CRIT -#define KERN_ERR -#define KERN_WARNING -#define KERN_NOTICE -#define KERN_INFO -#define KERN_DEBUG - -#define kmalloc(size, flags) malloc(size) -#define kfree(ptr) free(ptr) - -/* - * ..and if you can't take the strict - * types, you can specify one yourself. - * - * Or not use min/max at all, of course. - */ -#define min_t(type,x,y) \ - ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) -#define max_t(type,x,y) \ - ({ type __x = (x); type __y = (y); __x > __y ? __x: __y; }) - -#ifndef BUG -#define BUG() do { \ - printf("barebox BUG at %s:%d!\n", __FILE__, __LINE__); \ -} while (0) - -#define BUG_ON(condition) do { if (condition) BUG(); } while(0) -#endif /* BUG */ - -#define likely(x) __builtin_expect(!!(x), 1) -#define unlikely(x) __builtin_expect(!!(x), 0) - -#define PAGE_SIZE 4096 -#endif diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 01980f37..29591e26 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -178,10 +178,6 @@ struct mtd_info { int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len); int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len); - /* Power Management functions */ - int (*suspend) (struct mtd_info *mtd); - void (*resume) (struct mtd_info *mtd); - /* Bad block management functions */ int (*block_isbad) (struct mtd_info *mtd, loff_t ofs); int (*block_markbad) (struct mtd_info *mtd, loff_t ofs); diff --git a/include/malloc.h b/include/malloc.h index 4b0567ed..7b9b062a 100644 --- a/include/malloc.h +++ b/include/malloc.h @@ -18,6 +18,7 @@ size_t malloc_usable_size(void*); void malloc_stats(void); int mallopt(int, int); struct mallinfo mallinfo(void); +void *sbrk(ptrdiff_t increment); #endif diff --git a/include/nand.h b/include/nand.h index 05358d02..b1762dfa 100644 --- a/include/nand.h +++ b/include/nand.h @@ -6,10 +6,15 @@ struct nand_bb; #ifdef CONFIG_NAND int dev_add_bb_dev(char *filename, const char *name); +int dev_remove_bb_dev(const char *name); #else static inline int dev_add_bb_dev(char *filename, const char *name) { return 0; } +static inline int dev_remove_bb_dev(const char *name) +{ + return 0; +} #endif #endif /* __NAND_H__ */ diff --git a/include/net.h b/include/net.h index 33d8a32f..31bf6a23 100644 --- a/include/net.h +++ b/include/net.h @@ -363,7 +363,7 @@ static inline int is_valid_ether_addr(const u8 *addr) return !is_multicast_ether_addr(addr) && !is_zero_ether_addr(addr); } -typedef void rx_handler_f(char *packet, unsigned int len); +typedef void rx_handler_f(void *ctx, char *packet, unsigned int len); void eth_set_current(struct eth_device *eth); struct eth_device *eth_get_current(void); @@ -388,6 +388,7 @@ struct net_connection { struct list_head list; rx_handler_f *handler; int proto; + void *priv; }; static inline char *net_alloc_packet(void) @@ -396,9 +397,10 @@ static inline char *net_alloc_packet(void) } struct net_connection *net_udp_new(IPaddr_t dest, uint16_t dport, - rx_handler_f *handler); + rx_handler_f *handler, void *ctx); -struct net_connection *net_icmp_new(IPaddr_t dest, rx_handler_f *handler); +struct net_connection *net_icmp_new(IPaddr_t dest, rx_handler_f *handler, + void *ctx); void net_unregister(struct net_connection *con); diff --git a/include/usb/usb.h b/include/usb/usb.h index 1e4d750c..6ef99774 100644 --- a/include/usb/usb.h +++ b/include/usb/usb.h @@ -252,6 +252,8 @@ int usb_clear_halt(struct usb_device *dev, int pipe); int usb_string(struct usb_device *dev, int index, char *buf, size_t size); int usb_set_interface(struct usb_device *dev, int interface, int alternate); +void usb_rescan(void); + /* big endian -> little endian conversion */ /* some CPUs are already little endian e.g. the ARM920T */ #define __swap_16(x) \ diff --git a/lib/copy_file.c b/lib/copy_file.c index 70835319..809befe3 100644 --- a/lib/copy_file.c +++ b/lib/copy_file.c @@ -17,6 +17,7 @@ int copy_file(const char *src, const char *dst) int srcfd = 0, dstfd = 0; int r, w; int ret = 1; + void *buf; rw_buf = xmalloc(RW_BUF_SIZE); @@ -40,10 +41,16 @@ int copy_file(const char *src, const char *dst) } if (!r) break; - w = write(dstfd, rw_buf, r); - if (w < 0) { - perror("write"); - goto out; + + buf = rw_buf; + while (r) { + w = write(dstfd, buf, r); + if (w < 0) { + perror("write"); + goto out; + } + buf += w; + r -= w; } } diff --git a/lib/kfifo.c b/lib/kfifo.c index bf5cee1d..27d44e9b 100644 --- a/lib/kfifo.c +++ b/lib/kfifo.c @@ -96,7 +96,7 @@ void kfifo_free(struct kfifo *fifo) * */ unsigned int kfifo_put(struct kfifo *fifo, - unsigned char *buffer, unsigned int len) + const unsigned char *buffer, unsigned int len) { unsigned int l; diff --git a/lib/libbb.c b/lib/libbb.c index 4d532f61..3d022026 100644 --- a/lib/libbb.c +++ b/lib/libbb.c @@ -114,3 +114,16 @@ char * safe_strncpy(char *dst, const char *src, size_t size) } EXPORT_SYMBOL(safe_strncpy); +char *simple_itoa(unsigned int i) +{ + /* 21 digits plus null terminator, good for 64-bit or smaller ints */ + static char local[22]; + char *p = &local[21]; + *p-- = '\0'; + do { + *p-- = '0' + i % 10; + i /= 10; + } while (i > 0); + return p + 1; +} +EXPORT_SYMBOL(simple_itoa); @@ -381,7 +381,7 @@ static void dhcp_send_request_packet(struct bootp *bp_offer) /* * Handle DHCP received packets. */ -static void dhcp_handler(char *packet, unsigned int len) +static void dhcp_handler(void *ctx, char *packet, unsigned int len) { char *pkt = net_eth_to_udp_payload(packet); struct udphdr *udp = net_eth_to_udphdr(packet); @@ -439,7 +439,7 @@ static int do_dhcp(struct command *cmdtp, int argc, char *argv[]) { int ret; - dhcp_con = net_udp_new(0xffffffff, PORT_BOOTPS, dhcp_handler); + dhcp_con = net_udp_new(0xffffffff, PORT_BOOTPS, dhcp_handler, NULL); if (IS_ERR(dhcp_con)) { ret = PTR_ERR(dhcp_con); goto out; @@ -116,7 +116,7 @@ static int dns_send(char *name) return ret; } -static void dns_handler(char *packet, unsigned len) +static void dns_handler(void *ctx, char *packet, unsigned len) { struct header *header; unsigned char *p, *e, *s; @@ -211,7 +211,7 @@ IPaddr_t resolv(char *host) debug("resolving host %s via nameserver %s\n", host, getenv("nameserver")); - dns_con = net_udp_new(ip, DNS_PORT, dns_handler); + dns_con = net_udp_new(ip, DNS_PORT, dns_handler, NULL); if (IS_ERR(dns_con)) return PTR_ERR(dns_con); dns_timer_start = get_time_ns(); @@ -167,8 +167,10 @@ int eth_register(struct eth_device *edev) if (edev->get_ethaddr(edev, ethaddr) == 0) { ethaddr_to_string(ethaddr, ethaddr_str); - dev_info(dev, "got MAC address from EEPROM: %s\n", ethaddr_str); - dev_set_param(dev, "ethaddr", ethaddr_str); + if (is_valid_ether_addr(ethaddr)) { + dev_info(dev, "got MAC address from EEPROM: %s\n", ethaddr_str); + dev_set_param(dev, "ethaddr", ethaddr_str); + } } if (!eth_current) { @@ -343,7 +343,8 @@ void net_set_gateway(IPaddr_t gw) static LIST_HEAD(connection_list); -static struct net_connection *net_new(IPaddr_t dest, rx_handler_f *handler) +static struct net_connection *net_new(IPaddr_t dest, rx_handler_f *handler, + void *ctx) { struct eth_device *edev = eth_get_current(); struct net_connection *con; @@ -366,6 +367,7 @@ static struct net_connection *net_new(IPaddr_t dest, rx_handler_f *handler) con = xzalloc(sizeof(*con)); con->packet = xmemalign(32, PKTSIZE); + con->priv = ctx; memset(con->packet, 0, PKTSIZE); con->et = (struct ethernet *)con->packet; @@ -402,9 +404,9 @@ out: } struct net_connection *net_udp_new(IPaddr_t dest, uint16_t dport, - rx_handler_f *handler) + rx_handler_f *handler, void *ctx) { - struct net_connection *con = net_new(dest, handler); + struct net_connection *con = net_new(dest, handler, ctx); if (IS_ERR(con)) return con; @@ -417,9 +419,10 @@ struct net_connection *net_udp_new(IPaddr_t dest, uint16_t dport, return con; } -struct net_connection *net_icmp_new(IPaddr_t dest, rx_handler_f *handler) +struct net_connection *net_icmp_new(IPaddr_t dest, rx_handler_f *handler, + void *ctx) { - struct net_connection *con = net_new(dest, handler); + struct net_connection *con = net_new(dest, handler, ctx); if (IS_ERR(con)) return con; @@ -564,7 +567,7 @@ static int net_handle_udp(unsigned char *pkt, int len) port = ntohs(udp->uh_dport); list_for_each_entry(con, &connection_list, list) { if (con->proto == IPPROTO_UDP && port == ntohs(con->udp->uh_sport)) { - con->handler(pkt, len); + con->handler(con->priv, pkt, len); return 0; } } @@ -579,7 +582,7 @@ static int net_handle_icmp(unsigned char *pkt, int len) list_for_each_entry(con, &connection_list, list) { if (con->proto == IPPROTO_ICMP) { - con->handler(pkt, len); + con->handler(con->priv, pkt, len); return 0; } } diff --git a/net/netconsole.c b/net/netconsole.c index fda524e1..2ac3e643 100644 --- a/net/netconsole.c +++ b/net/netconsole.c @@ -50,7 +50,7 @@ struct nc_priv { static struct nc_priv *g_priv; -static void nc_handler(char *pkt, unsigned len) +static void nc_handler(void *ctx, char *pkt, unsigned len) { struct nc_priv *priv = g_priv; unsigned char *packet = net_eth_to_udp_payload(pkt); @@ -65,7 +65,7 @@ static int nc_init(void) if (priv->con) net_unregister(priv->con); - priv->con = net_udp_new(priv->ip, priv->port, nc_handler); + priv->con = net_udp_new(priv->ip, priv->port, nc_handler, NULL); if (IS_ERR(priv->con)) { int ret = PTR_ERR(priv->con); priv->con = NULL; @@ -562,7 +562,7 @@ static int nfs_read_reply(unsigned char *pkt, unsigned len) Interfaces of barebox **************************************************************************/ -static void nfs_handler(char *packet, unsigned len) +static void nfs_handler(void *ctx, char *packet, unsigned len) { char *pkt = net_eth_to_udp_payload(packet); int ret; @@ -689,7 +689,7 @@ static int do_nfs(struct command *cmdtp, int argc, char *argv[]) return 1; } - nfs_con = net_udp_new(net_get_serverip(), 0, nfs_handler); + nfs_con = net_udp_new(net_get_serverip(), 0, nfs_handler, NULL); if (IS_ERR(nfs_con)) { nfs_err = PTR_ERR(nfs_con); goto err_udp; @@ -40,7 +40,7 @@ static int ping_send(void) return net_icmp_send(ping_con, 9); } -static void ping_handler(char *pkt, unsigned len) +static void ping_handler(void *ctx, char *pkt, unsigned len) { IPaddr_t tmp; struct iphdr *ip = net_eth_to_iphdr(pkt); @@ -66,7 +66,7 @@ static int do_ping(struct command *cmdtp, int argc, char *argv[]) return 1; } - ping_con = net_icmp_new(net_ping_ip, ping_handler); + ping_con = net_icmp_new(net_ping_ip, ping_handler, NULL); if (IS_ERR(ping_con)) { ret = PTR_ERR(ping_con); goto out; @@ -141,7 +141,7 @@ static int tftp_send(void) return ret; } -static void tftp_handler(char *packet, unsigned len) +static void tftp_handler(void *ctx, char *packet, unsigned len) { uint16_t proto; uint16_t *s; @@ -314,7 +314,7 @@ static int do_tftpb(struct command *cmdtp, int argc, char *argv[]) return 1; } - tftp_con = net_udp_new(net_get_serverip(), TFTP_PORT, tftp_handler); + tftp_con = net_udp_new(net_get_serverip(), TFTP_PORT, tftp_handler, NULL); if (IS_ERR(tftp_con)) { tftp_err = PTR_ERR(tftp_con); goto out_close; diff --git a/scripts/.gitignore b/scripts/.gitignore index 26317b28..11fd2df3 100644 --- a/scripts/.gitignore +++ b/scripts/.gitignore @@ -2,4 +2,5 @@ bareboxenv bin2c mkimage kallsyms - +gen_netx_image +omap_signGP diff --git a/scripts/Makefile b/scripts/Makefile index be8e3e05..d6ea48ca 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -9,6 +9,7 @@ hostprogs-y += bin2c hostprogs-y += mkimage hostprogs-y += bareboxenv hostprogs-$(CONFIG_ARCH_NETX) += gen_netx_image +hostprogs-$(CONFIG_ARCH_OMAP) += omap_signGP always := $(hostprogs-y) $(hostprogs-m) diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c index 1f11d848..b3591a91 100644 --- a/scripts/kallsyms.c +++ b/scripts/kallsyms.c @@ -76,10 +76,8 @@ static int read_symbol(FILE *in, struct sym_entry *s) rc = fscanf(in, "%llx %c %499s\n", &s->addr, &stype, str); if (rc != 3) { - if (rc != EOF) { - /* skip line */ - fgets(str, 500, in); - } + if (rc != EOF && fgets(str, 500, in) == NULL) + fprintf(stderr, "Read error or end of file.\n"); return -1; } diff --git a/scripts/omap_signGP.c b/scripts/omap_signGP.c new file mode 100644 index 00000000..d20d3570 --- /dev/null +++ b/scripts/omap_signGP.c @@ -0,0 +1,313 @@ +/** + * signGP.c - Read the x-load.bin file and write out the x-load.bin.ift file + * + * The signed image is the original pre-pended with the size of the image + * and the load address. If not entered on command line, file name is + * assumed to be x-load.bin in current directory and load address is + * 0x40200800. + * + * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <string.h> +#include <malloc.h> +#include <linux/types.h> + +#undef CH_WITH_CHRAM +struct chsettings { + __u32 section_key; + __u8 valid; + __u8 version; + __u16 reserved; + __u32 flags; +} __attribute__ ((__packed__)); + +/* __u32 cm_clksel_core; + __u32 reserved1; + __u32 cm_autoidle_dpll_mpu; + __u32 cm_clksel_dpll_mpu; + __u32 cm_div_m2_dpll_mpu; + __u32 cm_autoidle_dpll_core; + __u32 cm_clksel_dpll_core; + __u32 cm_div_m2_dpll_core; + __u32 cm_div_m3_dpll_core; + __u32 cm_div_m4_dpll_core; + __u32 cm_div_m5_dpll_core; + __u32 cm_div_m6_dpll_core; + __u32 cm_div_m7_dpll_core; + __u32 cm_autoidle_dpll_per; + __u32 cm_clksel_dpll_per; + __u32 cm_div_m2_dpll_per; + __u32 cm_div_m3_dpll_per; + __u32 cm_div_m4_dpll_per; + __u32 cm_div_m5_dpll_per; + __u32 cm_div_m6_dpll_per; + __u32 cm_div_m7_dpll_per; + __u32 cm_autoidle_dpll_usb; + __u32 cm_clksel_dpll_usb; + __u32 cm_div_m2_dpll_usb; +}*/ + +struct gp_header { + __u32 size; + __u32 load_addr; +} __attribute__ ((__packed__)); + +struct ch_toc { + __u32 section_offset; + __u32 section_size; + __u8 unused[12]; + __u8 section_name[12]; +} __attribute__ ((__packed__)); + +struct chram { + /* CHRAM */ + __u32 section_key_chr; + __u8 section_disable_chr; + __u8 pad_chr[3]; + /* EMIF1 */ + __u32 config_emif1; + __u32 refresh_emif1; + __u32 tim1_emif1; + __u32 tim2_emif1; + __u32 tim3_emif1; + __u32 pwrControl_emif1; + __u32 phy_cntr1_emif1; + __u32 phy_cntr2_emif1; + __u8 modereg1_emif1; + __u8 modereg2_emif1; + __u8 modereg3_emif1; + __u8 pad_emif1; + /* EMIF2 */ + __u32 config_emif2; + __u32 refresh_emif2; + __u32 tim1_emif2; + __u32 tim2_emif2; + __u32 tim3_emif2; + __u32 pwrControl_emif2; + __u32 phy_cntr1_emif2; + __u32 phy_cntr2_emif2; + __u8 modereg1_emif2; + __u8 modereg2_emif2; + __u8 modereg3_emif2; + __u8 pad_emif2; + + __u32 dmm_lisa_map; + __u8 flags; + __u8 pad[3]; +} __attribute__ ((__packed__)); + + +struct ch_chsettings_chram { + struct ch_toc toc_chsettings; + struct ch_toc toc_chram; + struct ch_toc toc_terminator; + struct chsettings section_chsettings; + struct chram section_chram; + __u8 padding1[512 - + (sizeof(struct ch_toc) * 3 + + sizeof(struct chsettings) + sizeof(struct chram))]; + /* struct gp_header gpheader; */ +} __attribute__ ((__packed__)); + +struct ch_chsettings_nochram { + struct ch_toc toc_chsettings; + struct ch_toc toc_terminator; + struct chsettings section_chsettings; + __u8 padding1[512 - + (sizeof(struct ch_toc) * 2 + + sizeof(struct chsettings))]; + /* struct gp_header gpheader; */ +} __attribute__ ((__packed__)); + + +#ifdef CH_WITH_CHRAM +static const struct ch_chsettings_chram config_header = { + /* CHSETTINGS TOC */ + {sizeof(struct ch_toc) * 4, + sizeof(struct chsettings), + "", + {"CHSETTINGS"} + }, + /* CHRAM TOC */ + {sizeof(struct ch_toc) * 4 + sizeof(struct chsettings), + sizeof(struct chram), + "", + {"CHRAM"} + }, + /* toc terminator */ + {0xFFFFFFFF, + 0xFFFFFFFF, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF} + }, + /* CHSETTINGS section */ + { + 0xC0C0C0C1, + 0, + 1, + 0, + 0}, + /* CHRAM section */ + { + 0xc0c0c0c2, + 0x01, + {0x00, 0x00, 0x00}, + + /* EMIF1 */ + 0x80800eb2, + 0x00000010, + 0x110d1624, + 0x3058161b, + 0x030060b2, + 0x00000200, + 0x901ff416, + 0x00000000, + 0x23, + 0x01, + 0x02, + 0x00, + + /* EMIF2 */ + 0x80800eb2, + 0x000002ba, + 0x110d1624, + 0x3058161b, + 0x03006542, + 0x00000200, + 0x901ff416, + 0x00000000, + 0x23, + 0x01, + 0x02, + 0x00, + + /* LISA map */ + 0x80700100, + 0x05, + {0x00, 0x00, 0x00}, + }, + "" +}; +#else +static struct ch_chsettings_nochram config_header + __attribute__((section(".config_header"))) = { + /* CHSETTINGS TOC */ + {(sizeof(struct ch_toc)) * 2, + sizeof(struct chsettings), + "", + {"CHSETTINGS"} + }, + /* toc terminator */ + {0xFFFFFFFF, + 0xFFFFFFFF, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF} + }, + /* CHSETTINGS section */ + { + 0xC0C0C0C1, + 0, + 1, + 0, + 0}, + "" +}; +#endif + + +#define err(...) do { int save_errno = errno; \ + fprintf(stderr, __VA_ARGS__); \ + errno = save_errno; \ + } while (0); +#define pdie(func, ...) do { perror(func); exit(1); } while (0); + +int main(int argc, char *argv[]) +{ + int i; + char ifname[FILENAME_MAX], ofname[FILENAME_MAX], ch; + FILE *ifile, *ofile; + unsigned long loadaddr, len; + struct stat sinfo; + int ch_add = 0; + + + /* Default to x-load.bin and 0x40200800. */ + strcpy(ifname, "x-load.bin"); + loadaddr = 0x40200800; + + if ((argc == 2) || (argc == 3) || (argc == 4)) + strcpy(ifname, argv[1]); + + if ((argc == 3) || (argc == 4)) + loadaddr = strtoul(argv[2], NULL, 16); + + if (argc == 4) + ch_add = strtoul(argv[3], NULL, 16); + + /* Form the output file name. */ + strcpy(ofname, ifname); + strcat(ofname, ".ift"); + + /* Open the input file. */ + ifile = fopen(ifname, "rb"); + if (ifile == NULL) { + err("Cannot open %s\n", ifname); + pdie("fopen"); + } + + /* Get file length. */ + stat(ifname, &sinfo); + len = sinfo.st_size; + + /* Open the output file and write it. */ + ofile = fopen(ofname, "wb"); + if (ofile == NULL) { + fclose(ifile); + err("Cannot open %s\n", ofname); + pdie("fopen"); + } + + if (ch_add) + if (fwrite(&config_header, 1, 512, ofile) <= 0) + pdie("fwrite"); + + if (fwrite(&len, 1, 4, ofile) <= 0) + pdie("fwrite"); + if (fwrite(&loadaddr, 1, 4, ofile) <= 0) + pdie("fwrite"); + for (i = 0; i < len; i++) { + if (fread(&ch, 1, 1, ifile) <= 0) + pdie("fread"); + if (fwrite(&ch, 1, 1, ofile) <= 0) + pdie("fwrite"); + } + + if (fclose(ifile)) + perror("warning: fclose"); + if (fclose(ofile)) + perror("warning: fclose"); + + return 0; +} |